How to create and look up thread pool resource in GlassFish
In order to create a custom thread pool task executor resource, one needs to implement 2 classes: the thread pool class, and its object factory class.
In my implementation, the thread pool class, test.ThreadPoolExecutor
is simply a subclass of java.util.concurrent.ThreadPoolExecutor
. It is implemented as a singleton so every lookup and injection always return the same instance.
package test;The factory class,
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
static final int defaultCorePoolSize = 5;
static final int defaultMaximumPoolSize = 10;
static final long defaultKeepAliveTime = 10;
static final TimeUnit defaultTimeUnit = TimeUnit.MINUTES;
static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
private static ThreadPoolExecutor instance;
private ThreadPoolExecutor() {
super(defaultCorePoolSize, defaultMaximumPoolSize, defaultKeepAliveTime, defaultTimeUnit, workQueue);
}
synchronized static ThreadPoolExecutor getInstance() {
if (instance == null) {
instance = new ThreadPoolExecutor();
}
return instance;
}
}
test.ThreadPoolExecutorFactory
, is required to implement javax.naming.spi.ObjectFactory
. It also implements a GlassFish-specific interface com.sun.appserv.server.LifecycleListener
so that it can also be registered as a GlassFish lifecycle module. Upon receiving a server termination event, this class will shutdown the thread pool resource.package test;Copy 2 class files to $GLASSFISH_HOME/domains/domain1/lib/classes directory, with package name, and restart domain:
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import com.sun.appserv.server.LifecycleEvent;
import com.sun.appserv.server.ServerLifecycleException;
public class ThreadPoolExecutorFactory implements javax.naming.spi.ObjectFactory,
com.sun.appserv.server.LifecycleListener, java.io.Serializable {
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
Hashtable<?, ?> environment) throws Exception {
ThreadPoolExecutor tp = ThreadPoolExecutor.getInstance();
try {
Reference reference = (Reference) obj;
Enumeration<?> enumeration = reference.getAll();
TimeUnit timeUnit = ThreadPoolExecutor.defaultTimeUnit;
long keepAliveTime = ThreadPoolExecutor.defaultKeepAliveTime;
while (enumeration.hasMoreElements()) {
RefAddr refAddr = (RefAddr) enumeration.nextElement();
String pname = refAddr.getType();
String pvalue = (String) refAddr.getContent();
if ("corePoolSize".equalsIgnoreCase(pname)) {
tp.setCorePoolSize(Integer.parseInt(pvalue));
} else if ("maximumPoolSize".equalsIgnoreCase(pname)) {
tp.setMaximumPoolSize(Integer.parseInt(pvalue));
} else if ("timeUnit".equalsIgnoreCase(pname)) {
timeUnit = TimeUnit.valueOf(pvalue);
} else if ("keepAliveTime".equalsIgnoreCase(pname)) {
keepAliveTime = Long.parseLong(pvalue);
} else if ("allowCoreThreadTimeOut".equalsIgnoreCase(pname)) {
tp.allowCoreThreadTimeOut(Boolean.parseBoolean(pvalue));
} else if ("prestartAllCoreThreads".equalsIgnoreCase(pname)) {
if (Boolean.parseBoolean(pvalue)) {
tp.prestartAllCoreThreads();
}
} else {
throw new IllegalArgumentException("Unrecognized property name: " + pname);
}
}
tp.setKeepAliveTime(keepAliveTime, timeUnit);
} catch (Exception e) {
throw (NamingException) (new NamingException()).initCause(e);
}
return tp;
}
public void handleEvent(LifecycleEvent event) throws ServerLifecycleException {
if (event.getEventType() == LifecycleEvent.TERMINATION_EVENT) {
ThreadPoolExecutor tp = ThreadPoolExecutor.getInstance();
System.out.println("About to purge and shutdown " + tp + ", active thread count: "
+ tp.getActiveCount());
tp.purge();
tp.shutdown();
}
}
}
ls $GLASSFISH_HOME/domains/domain1/lib/classes/test/Next, create the thread pool resource, and register lifecycle module. It can also be done in admin console, in a more user-friendly manner.
ThreadPoolExecutorFactory.class ThreadPoolExecutor.class
$ asadmin restart-domain
$ asadmin create-custom-resource --restype test.ThreadPoolExecutor --factoryclass test.ThreadPoolExecutorFactory --description "A ThreadPoolExecutor backed by LinkedBlockingQueue" --property corePoolSize=6:maximumPoolSize=50:keepAliveTime=4:timeUnit=MINUTES:allowCoreThreadTimeOut=true:prestartAllCoreThreads=true concurrency/TPTo list and delete custom resources and lifecycle modules:
Command create-custom-resource executed successfully.
$ asadmin create-lifecycle-module --classname "test.ThreadPoolExecutorFactory" --failurefatal=true concurrency/TP-shutdown
$ asadmin list-custom-resourcesThis is how it looks like in admin console, where you can easily manage it:
$ asadmin list-lifecycle-modules
$ asadmin delete-lifecycle-module concurrency/TP-shutdown
$ asadmin delete-custom-resource concurrency/TP

To use this resource, just inject or look up in application components. The following is a test servlet that verifies that the resource can be obtained with either @Resource or regular lookup, and that multiple lookups return the same instance.
package test;When running the test webapp at http://localhost:8080/test/, many log messages like these will appear in server.log:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.annotation.*;
import javax.naming.*;
@javax.servlet.annotation.WebServlet(urlPatterns = "/*")
public class TestServlet extends HttpServlet {
@Resource(name="java:app/env/concurrency/TP", mappedName="concurrency/TP")
private test.ThreadPoolExecutor tp;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
testLookup();
System.out.println("About to submit tasks to " + tp);
for(int i = 0; i < 30; i++) {
tp.execute(new MyRunnable());
}
}
private void testLookup() throws ServletException {
try {
for(int i = 0; i < 10; i++) {
ThreadPoolExecutor t = InitialContext.<ThreadPoolExecutor>doLookup("java:app/env/concurrency/TP");
System.out.println("ThreadPoolExecutor from lookup: " + t);
}
} catch (NamingException e) {
throw new ServletException(e);
}
}
private static class MyRunnable implements Runnable {
public void run() {
System.out.println("Executing task in " + Thread.currentThread());
}
}
}
ThreadPoolExecutor from lookup: test.ThreadPoolExecutor@12e0a75aIn the above output,
Executing task in Thread[pool-29-thread-6,5,grizzly-kernel
pool-29-thread-6
is the thread name, 5 (normal) is its priority, and grizzly-kernel
is thread group name. When shutting down the server, the thread pool is purged and shutdown, thanks to the lifecycle module concurrency/TP-shutdown
:Server shutdown initiatedThis is just a simplistic implementation of thead pool resource in GlassFish. There is a potential class loader leak and may cause ClassCastException and/or OutOfMemoryError in large applications.
About to purge and shutdown test.ThreadPoolExecutor@12e0a75a, active thread count: 0
JMXStartupService: Stopped JMXConnectorServer: null
JMXStartupService and JMXConnectors have been shut down.
Shutdown procedure finished
11 comments:
Hi,
Why not have some syntax highlighting?
Have to agree with Anon above - some syntax highlighting might be usefl?
Eventually, I found here an actually working example on how to utilize thread pools in Glassfish.
Thank you!
Only problem was that I had to use the exact JNDI name, as given when defining the custom resource, when looking up the Executor, not prepending with "java:comp:env/".
It's probably because you didn't declare a resource reference in your app to the custom resource created in the server. In my test code, the lookup part is not really needed since the resource reference is already injected into the servlet class with @Resource. This @Resource annotation also declares a resource ref in the current webapp which is looked up in testLookup() method.
I have no words for this post. Thanks a lot for the share. Keep posting such a kind of post on your blog.
Great write up! I was wondering though, would you be able to explain how exactly you came up with this solution?
As the old saying goes: "Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime."
That's quite a good idea. But is there no way of accessing the glassfish threadpool itself? This would let the application server handle resource management
GlassFish built-in thread pools are all for internal use by web container, ejb container, deployment processor, resource adapters, etc. They are not accessible to applications, because they don't want applicatoins to contend for server resources. However, I agree there should be a supported way of creating thread pool resources just for application use, but I haven't seen one.
There are efforts in jcp trying to standardize it around worker manager and concurrency utils, but both are inactive now:
JSR 236: Concurrency Utilities for JavaTM EE
JSR 237: Work Manager for Application Servers
Greate post.
Probably the part related to the LifecycleListener is not working.
You should put your jar/classes on glassfish3/glassfish/lib in order get the correct glassfish context.
Great and Useful Article.
Online Java Course
Java Online Training
Java Course Online
J2EE training
online J2EE training
Best Recommended books for Spring framework
Java Interview Questions
Java Training Institutes in Chennai
Java Training in Chennai
J2EE Training in Chennai
java j2ee training institutes in chennai
I enjoyed on reading your blog post. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. Please visit my website, Friv 4000 Games is where all the free friv games.
Friv 4000
Post a Comment