How to create custom resources in Glassfish

This post shows how to create a custom resource in Glassfish application server. These steps should also work in JavaEE SDK 5, Sun Java System Application Server 9.x. This use case should be supported by all compliant JavaEE application servers, though configurations may vary.

0. Download and install Glassfish. Or You can download and the install official releases of JavaEE SDK 5 and Sun Java System Application Server 9.0 here

1. Create your custom resource class. The following is a simple example:


package foo;

/\*\*
 \* A simple custom resource class.
 \*/
public class Widget implements java.io.Serializable {
    private String name;
    
    public Widget() {
    }
    
    public Widget(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Widget " + name;
    }

    /\* (non-Javadoc)
     \* @see java.lang.Object#hashCode()
     \*/
    @Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = PRIME \* result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    /\* (non-Javadoc)
     \* @see java.lang.Object#equals(java.lang.Object)
     \*/
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final Widget other = (Widget) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

2. Create your custom resource factory class. The following is one such factory for the above resource class (NOTE: this is a simplistic impl of object factory and only works for hello-world-type of applications.  See here for a generic and real object factory class.)


package foo;

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;

/\*\*
 \* A simple factory class for creating custom JNDI resource.  In
 \* real applications, it should be able to configure and parameterize the resource.
 \*/
public class WidgetFactory implements javax.naming.spi.ObjectFactory {
    public WidgetFactory() {
    }

    public Object getObjectInstance(Object obj, 
                                    Name name, 
                                    Context nameCtx, 
                                    Hashtable<?, ?> environment) 
        throws Exception {
        Widget widget = new Widget("Rag");
        System.out.println("Creating " + widget);
        return widget;
    }
}

3. Start the application server and create the custom resource in the server.


C:\\ws\\sjsas90\\publish\\glassfish\\bin> asadmin start-domain
C:\\ws\\sjsas90\\publish\\glassfish\\bin> asadmin create-custom-resource --restype foo.Widget --factoryclass foo.WidgetFactory custom/widget
Command create-custom-resource executed successfully.
The above command creates a custom resource with JNDI name custom/widget. To list all custom resources:

C:\\ws\\sjsas90\\publish\\glassfish\\bin> asadmin list-custom-resources
custom/widget
Command list-custom-resources executed successfully.

To delete a custom resource:

C:\\ws\\sjsas90\\publish\\glassfish\\bin> asadmin delete-custom-resource custom/widget
Command delete-custom-resource executed successfully.

For a full explanation of these asadmin commands and parameters, please run help commands:

asadmin help create-custom-resource
asadmin help list-custom-resources
asadmin help delete-custom-resource

or, 
asadmin create-custom-resource --help
asadmin list-custom-resources --help
asadmin delete-custom-resource --help


Comments:

Hello,

I have a few questions about custom resources registered with JNDI.

1. Where should the class files for Widget and Widget files be placed?

2. If a stand alone java client looks up the custom/widget name over JNDI, will JNDI create a new instance of widget object and hand over the reference to the client? This java client is in a different JVM compared to the application server.

3. Does the widget class need to implement the Remote interface?

Thanks,

Shreyas

Posted by Shreyas on June 05, 2008 at 07:52 AM EDT #

Your classes should be made available to the server. You can put the jar file at $glassfish.home/domains/domain1/lib/xxx.jar, or put classes at $glassfish.home/domains/domain1/lib/classes/com/xxx/xxx

In general, a resource managed by appserver is intended to be used by components deployed on that server like EJB, any web component, application client. It may work for stand alone client as well.

Lookup usually returns a new instance. Of course if the instance is guaranteed to be immutable, there can be shared instances. Custom resources are typically small and easy to create and dispose of, why not just return new instance every time.

I don't see the need for a Remote interface. If you need to invoke business methods on it, make it an EJB 3 bean.

Posted by Cheng Fang on June 05, 2008 at 08:32 AM EDT #

Cheng,

I have copied the jar file with the implementation (Widget, WidgetFactory and WidgetInterface) to the lib directory of the app server.

Now from a stand alone java client outside of the application server that has access to only the WidgetInterface I use JNDI to look up the Widget object (as custom/widget).

I get a javax.naming.Reference object that throws an ClassCastException when I try to cast it to the WidgetInterface. I am not sure what JNDI is doing here...its giving me the Reference object to the name I had asked for but i cannot cast it to the interface...(so i cannot call methods on it)

There is another little strange quirk, when I call the getClassName on the javax.naming.Reference object from the lookup it prints out foo.Widget.

Does this make sense?

Posted by Shreyas Shinde on June 05, 2008 at 10:48 AM EDT #

getClassName() is a method on javax.naming.Reference. You meant to use getClass().getName()?

As for the ClassCaseException, you cannot cast a Reference to Widget. But the look up should really return an object of type Widget, not a Reference. I will try a standalone client later when I have time.

Posted by Cheng Fang on June 19, 2008 at 05:45 AM EDT #

Just tried looking up custom jndi resource from a standalone java application:

package customjndi;

public class Main {
public static void main(String[] args) throws Exception {
Context ic = new InitialContext();
Object obj = ic.lookup("custom/widget");
System.out.println("Lookup returned " + obj);
Widget widget = (Widget) obj;
System.out.println("Widget after casting " + widget);
}
}

To run the standalone client, the key part is to get the classpath right:

cd $HOME/NetBeansProjects/custom-jndi/build/classes
java -cp $glassfish/lib/javaee.jar:$glassfish/lib/appserv-rt.jar:$glassfish/domains/domain1/lib/widget.jar:.
customjndi.Main

At a minimum, you need javaee.jar and appserv-rt.jar. And depending on the nature of your resource, you may also need other jars from $glassfish/lib (appserv-admin.jar, appserv-deployment-client.jar, etc). For more details see GlassFish EJB FAQ.

Also note that getInstance method in your factory class should return your resource type (like Widget), not javax.naming.Reference. If you do that, the result from lookup should always be an Object that is castable to your resource type.

Posted by Cheng Fang on June 19, 2008 at 06:51 AM EDT #

Cheng,

I have 2 jar files widget-api.jar and widget-impl.jar. The widget-api.jar only contains the Widget interface. The client gets this jar and not the impl.

The widget-impl.jar contains classes that implement the Widget interface and the WidgetFactory.

I put both the jar files into $glassfish/domains/domain1/lib.

However, I continue to get only the Reference object which I cannot cast to Widget.

In your stand alone case, are you sure that the Widget that you are getting is not coming from the widget.jar which is present in the client's classpath? Can you try separating the interface and the implementation?

When I put the widget-impl.jar in my client's classpath, I do get a Widget class but its not coming from the WidgetFactory.

Posted by Shreyas Shinde on June 19, 2008 at 01:45 PM EDT #

By adding a remote interface to your custom resource, you are really stretching the limit of jndi, which is best used for storing and retrieving read-only data structures. JNDI is not designed for handling business logic. There are better alternatives such as EJB 3.

Back to specifics, why do you think only interface is needed on client VM? Your resource is known to the client as WidgetInterface, but its type is still WidgetImpl. Without a WidgetImpl, or a stub thereof, there is no way for the client VM to deserialize the object from lookup.

My standalone client does have access to all Widget classes. But I'm reasonably sure the object is from glassfish server, not locally from the client side. The naming manager connects to localhost:3700 (default host and port number, which glassfish is listening on). When I changing logging level for corba and naming to FINE, I can see server messages like these:

[#|2008-06-20T09:19:20.591-0400|FINE|sun-appserver9.1|javax.enterprise.system.core.naming|_ThreadID=21;_ThreadName=p: thread-pool-1; w: 5;ClassName=com.sun.enterprise.naming.SerialContextProviderImpl;MethodName=lookup;_RequestID=f7205ac1-9ed4-48b0-84b6-23af2be92561;| SerialContextProviderImpl :: lookup custom/widget|#]

Posted by Cheng Fang on June 20, 2008 at 02:27 AM EDT #

Cheng,

I really appreciate you taking time out to answer my questions.

But I am not sure I completely understand your last comment.

Is there anyway that I could call you to discuss this? If yes, kindly let me know a number that I could reach you. If you cannot, I'll try and further elucidate my use case.

You can mail me at shreyasshinde@gmail.com

Thanks,

Shreyas

Posted by Shreyas Shinde on June 20, 2008 at 11:37 AM EDT #

Hi Shreyas,

If your company is a glassfish customer or prospect, I and maybe other glassfish engineers can directly work with you.

-cheng

Posted by Cheng Fang on June 21, 2008 at 12:40 AM EDT #

Hello Cheng,

Yes, we do have a direct relation with Sun. I was able to speak to a few engineers on the Glassfish project a couple of days back on some other issues.

I will ask my Sun contact to get in touch with you.

Thanks a lot!

Shreyas

Posted by Shreyas Shinde on June 21, 2008 at 09:28 AM EDT #

I'm having similar problem, but with injection in servlet. I'm trying to learn how to inject custom resource (using GlassFish server), found your article and started by copying and pasting your code into my "sandbox" EJB3 application. Following code placed in servlet class:

public class Zasup extends HttpServlet {

@Resource(name="Widget")
Widget widg;

...

}

causes exception which root cause says:

Caused by: java.lang.IllegalArgumentException: Can not set modrzew.Widget field modrzew.web.zasup.widg to javax.naming.Reference

It looks like GlassFish has looked up object as Reference type and trying to set that field.

When changed to this:

@Resource(name="Widget")
Reference widg;

it started working.

Note that injection to stateless bean works. May it be a limitation of GlassFish v2? Or am I missing something?

Posted by heretique on September 05, 2008 at 02:27 AM EDT #

I'm curious as to what the best practice is for a situation where you need to share an expensive-to-construct resource between stateless session beans. At present, I'm creating the resource in a @PostConstruct-annotated method; the problem is that since multiple session beans get created in my pool, this expensive resource (in terms of memory) gets created multiple times.

I have considered using a custom JNDI resource to solve this problem, but my resource (a Drools RuleBase constructed via runtime compilation and class generation) depends upon my entity classes. If I take the approach of using a custom JNDI provider, I will have to take down the entire application server and all of my cluster nodes in order to redeploy, which is not really acceptable. So how can I construct this resource and use it only once? I've been told that singletons can be problematic when used in the context of an appserver due to classloader issues; can you clarify?

Posted by nuttycom on November 26, 2008 at 10:44 AM EST #

Since your resource depends on some entity classes (application domain objects), it is more like a part of application logic than an independent resource. So from a high-level view, custom resource is not the best solution. A common workaround is to package your ejb-jar and a WAR in the same EAR, and do the one-time initialization in a ServletContextListener, which is invoked when the app is loaded.

Another variant is to do the one-time initialization with an ejb timer. Have your ServletContextListener invokes an ejb method, which creates a timer. At expiration, the time-out method creates the resource. The timer is created to expire only once and is persistent. So it should also work well in a clustered environment.

There will be a Singleton bean type in EJB 3.1 (still work in progress as of now -- 12/2008). It can do the same as a ServletContextListener without using a WAR. EJB 3.1 will also introduces an automatic timer, which is created at deployment time, to eliminate the need to invoke a business method to create the timer.

Posted by Cheng Fang on December 01, 2008 at 02:35 AM EST #

Thanks, Cheng. One thing I'm not quite clear on with respect to either approach that relies upon the ServletContextListener is where to store the reference to my resource. Is it acceptable to store the reference in a regular old Singleton? Or is this what the Timer is for - to provide a place to store that reference, since the timer itself is persistent as you say?

Thanks again.

Posted by nuttycom on December 01, 2008 at 04:19 AM EST #

No, timer is not for storing any reference.

You can store it in http session and retrieve it from any web components. If it's serializable, it can even be replicated across a cluster.

To retrieve it from ejb, there is currently no good place. Plain singleton is not a good place since it interferes with the thread management of the container. And the singleton class may be loaded by different classloaders (as in hot deployment). This may cause various versions of the singleton class data, and each may have corresponding singleton instance. Basically you have a jvm-level singleton, rather than an appserver-level singleton. The latter is what we need since the app is managed by appserver. The new singleton bean type in EJB 3.1 is designed to solve this problem.

Timer can construct your resource once but itself is not a reference store. JNDI tree is, but it's pretty much read-only inside appserver. So you cannot add resources to JNDI dynamically.

You may want to try a combination of custom resource and ServletContextListener/Timer: design a place holder custom resource, which holds a typeless property, the real resource you want to store inside this custom resource. Since its content is typeless, you should be able to create it in appserver before deploying your app. You can then look up this custom resource and call its set method to store your resource reference.

Posted by Cheng Fang on December 01, 2008 at 06:56 AM EST #

Very nice tutorial

Posted by pranav on October 25, 2011 at 06:53 AM EDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Cheng Fang

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today