Configurable threading, resiliency and monitoring with injectable statistics for Java EE 7. Porcupine is the implementation of the bulkhead and handshaking patterns for Java EE 7.
Features:
- Conventional: ExecutorService is directly injectable. The thread pool derives the name from the field, but can be easily overridden.
- Drop-in install: a single dependency in the pom.xml is sufficient.
- Standard based: porcupine uses JSR 236: Concurrency Utilities for JavaTM EE
- Small: the entire framework is: 16kB.
- Extensible without configuration: All major components can be replaced (big thanks to @Specializes)
##Installation
<dependency>
<groupId>com.airhacks</groupId>
<artifactId>porcupine</artifactId>
<version>NEWEST_VERSION</version>
<scope>compile</scope>
</dependency>
With statistics injection into HTTP headers of all JAX-RS resources:
<dependency>
<groupId>com.airhacks</groupId>
<artifactId>porcupine-spy</artifactId>
<version>NEWEST_VERSION</version>
<scope>compile</scope>
</dependency>
##Conventional Usage
@Stateless
public class MessagesService {
@Inject
@Dedicated
ExecutorService light;
@Inject
@Dedicated
ExecutorService heavy;
Custom naming is also supported:
@Inject
@Dedicated("custom-pool")
private ExecutorService first;
##Statistics and monitoring
###Exposure
@Path("statistics")
@RequestScoped
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class StatisticsResource {
@Inject
Instance<List<Statistics>> statistics;
@GET
public List<Statistics> expose() {
return this.statistics.get();
}
}
###Sample output
Unified thread pool statistics are accessible via injection. Individual statistics are injectable with the @Dedicated
qualifier.
XML statistics
<statistics>
<pipelineName>light</pipelineName>
<remainingQueueCapacity>100</remainingQueueCapacity>
<completedTaskCount>1</completedTaskCount>
<activeThreadCount>0</activeThreadCount>
<largestThreadPoolSize>1</largestThreadPoolSize>
<currentThreadPoolSize>1</currentThreadPoolSize>
<totalNumberOfTasks>1</totalNumberOfTasks>
<maximumPoolSize>48</maximumPoolSize>
<rejectedExecutionHandlerName>ExecutorServiceExposer$$Lambda$3/1562863014</rejectedExecutionHandlerName>
<rejectedTasks>0</rejectedTasks>
</statistics>
<statistics>
<pipelineName>heavy</pipelineName>
<remainingQueueCapacity>16</remainingQueueCapacity>
<completedTaskCount>0</completedTaskCount>
<activeThreadCount>1</activeThreadCount>
<largestThreadPoolSize>1</largestThreadPoolSize>
<currentThreadPoolSize>1</currentThreadPoolSize>
<totalNumberOfTasks>1</totalNumberOfTasks>
<maximumPoolSize>8</maximumPoolSize>
<rejectedExecutionHandlerName>CallerRunsPolicy</rejectedExecutionHandlerName>
<rejectedTasks>0</rejectedTasks>
</statistics>
JSON statistics
[{
"pipelineName": "light",
"remainingQueueCapacity": 100,
"completedTaskCount": 1,
"activeThreadCount": 0,
"largestThreadPoolSize": 1,
"currentThreadPoolSize": 1,
"totalNumberOfTasks": 1,
"maximumPoolSize": 48,
"rejectedExecutionHandlerName": "ExecutorServiceExposer$$Lambda$3/1562863014",
"rejectedTasks": 0
},
{
"pipelineName": "heavy",
"remainingQueueCapacity": 16,
"completedTaskCount": 1,
"activeThreadCount": 0,
"largestThreadPoolSize": 1,
"currentThreadPoolSize": 1,
"totalNumberOfTasks": 1,
"maximumPoolSize": 8,
"rejectedExecutionHandlerName": "CallerRunsPolicy",
"rejectedTasks": 0
}]
@Specializes
public class CustomExecutorConfigurator extends ExecutorConfigurator {
@Override
public ExecutorConfiguration defaultConfigurator() {
return super.defaultConfigurator();
}
@Override
public ExecutorConfiguration forPipeline(String name) {
if ("heavy".equals(name)) {
return new ExecutorConfiguration.Builder().
corePoolSize(4).
maxPoolSize(8).
queueCapacity(16).
keepAliveTime(1).
callerRunsPolicy().
build();
}
return super.forPipeline(name);
}
}
HTTP/1.1 200 OK
Server: GlassFish Server Open Source Edition 4.1
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.1 Java/Oracle Corporation/1.8)
x-porcupine-statistics-light: {"pipelineName":"light","activeThreadCount":1,"completedTaskCount":1,
"corePoolSize":8,"currentThreadPoolSize":2,"largestThreadPoolSize":2,"maximumPoolSize":16,"rejectedTasks":0,
"remainingQueueCapacity":100,"minQueueCapacity":100,"totalNumberOfTasks":2}
x-porcupine-statistics-heavy: {"pipelineName":"heavy","rejectedExecutionHandlerName":"CallerRunsPolicy",
"activeThreadCount":0,"completedTaskCount":1,"corePoolSize":4,"currentThreadPoolSize":1,
"largestThreadPoolSize":1,"maximumPoolSize":8,"rejectedTasks":0,"remainingQueueCapacity":16,
"minQueueCapacity":16,"totalNumberOfTasks":1}
Content-Type: text/plain
Date: Wed, 18 Mar 2015 07:50:20 GMT
Content-Length: 36