views:

428

answers:

4
+1  Q: 

EJB Factory Class

Hi all

I'm trying to create an EJB factory class, which works like this: You have a method which takes as argument a class of an EJB, then it checks whether the EJB has a remote interface (if not throw an exception) and if it does, it returns the concerning EJB.

The code below does exactly this. However the object it returns is of the type of the remote interface of the concerning bean and not of the bean itself. How can I change this? Is there a way to tell Java that the generic type T is of the same type as the class passed to the methods.

import java.util.Properties;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.*;


public class EJBFactory
{

    private InitialContext ctx;

    public EJBFactory() throws NamingException {
        ctx = new InitialContext();
    }

    public EJBFactory(String host, String port) throws NamingException {
        Properties props = new Properties();
        props.setProperty("org.omg.CORBA.ORBInitialHost", host);
        props.setProperty("org.omg.CORBA.ORBInitialPort", port);
        ctx = new InitialContext(props);
    }
.
    // To improve: The object returned should be of the type ejbClass
    // instead of the remote interface, which it implements
    public <T> T createEJB(Class ejbClass) throws NamingException
    {
        Class remoteInterface = null;
        for(Class interface_: ejbClass.getInterfaces()) {
            if(interface_.isAnnotationPresent(Remote.class))
                remoteInterface = interface_;
        }

        if(remoteInterface == null)
            throw new  IllegalArgumentException(
                "EJB Requires a remote interface");

        // Get the stateless annotation, then get the jndiName
        Stateless stateless =
            (Stateless)ejbClass.getAnnotation(Stateless.class);
        String jndiName = stateless.mappedName();
        T ejbObj = (T) ctx.lookup(jndiName);
        return ejbObj;
    }

}

Example of a unit test which uses the Factory.

import junit.framework.TestCase;


public class SimpleEJBTest extends TestCase
{
    TestRemote testBean;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        EJBFactory ejbFactory = new EJBFactory();
        testBean = ejbFactory.createEJB(TestBean.class);
    }

    public void testSayHello() {
        assertEquals("Hello", testBean.sayHello());
    }
}

Note: The example works with Glassfish, I didn't test it with any other app server.

A: 

try replacing

public <T> T createEJB(Class ejbClass) throws NamingException

with

public <T> T createEJB(Class<T> ejbClass) throws NamingException
zesc
I already tried, but got a cast exception: Testcase: testSayHello(SimpleEJBTest):Caused an ERROR _TestRemote_Wrapper cannot be cast to TestBeanjava.lang.ClassCastException: _TestRemote_Wrapper cannot be cast to TestBean at SimpleEJBTest.setUp(SimpleEJBTest.java:16)
Nils
may be the declaration on the test of testBean is wrong. Should it be type TestBean and not TestRemote?
zesc
yes of course I also changed that. Then I got a cast exception. Sry forgot to mention above.
Nils
I think you will always get the Remote and never the Bean. I mean you will always get the interface (I guess that in your case TestRemote). So I guess that the declaration is ok, but the call to createEJB should be with TestRemote.class
zesc
Sorry about that I should have read better your question, I think that's not possible to get the object itself. You will always get the interface.
zesc
> I think that's not possible to get the object itself. You will always get the interface. Yes, that's what the lookup method gives you. But you should be able to cast that to the obejct of the bean and return this, right?
Nils
+1  A: 

I do not think that you can get the EJB object. You can only get the interface. The createEJB should be called with the interface and it returns the interface.

zesc
ok, but if the annotation is in the actual bean class and not in the interace, how do you get it then?
Nils
I used the following annotation on the bean class: @Stateless(name="TestBean", mappedName="ejb/TestBean")
Nils
A: 

Can you try this?

Create a interface. Make it have @Remote. Your ejb that is annotated with @Stateless should implement the above created interface. Now try to do the same thing that you are doing I think it should give you the desired result. Typing it down here without copying from an ide so excuse any errors. But you should get the drift I guess.

@Remote
public interface Example{
   String some();
}

@stateless
public class ExampleBean implements Example{

}
OpenSource
Did u read my question?? I already did this..
Nils
Ahh.. for some reason I thought when you look for the remote it wasnt there. Will think more regarding this one.
OpenSource
ok creating a new answer. Lets see if it works.
OpenSource
+2  A: 

Clients of EJBs interact with them through the local/ remote interface that the EJBs implement. Client applications never have direct access to an actual session bean class instance. This is done to make instance pooling possible, where the container can reuse EJB instances to service different requests.

I'm not sure why you need to access the actual bean's object (since obviously I dont know your requirement). But if you still need to create an instance of that you can do it as follows using reflection Class.forName(className).newInstance(); Again the instance that you create like this is not an EJB. It is just a POJO thats all.

EDIT - after your comment regarding junit testing: When you access business methods from a JavaSE as follows, you are actually calling the methods in the EJB - just that you interact thru the interface. So if you want to test any business methods you can still do it from an object got thru a JNDI lookup in a Junit test.

//MyGreatBean implements MyGreat. MyGreat has @Remote, MyGreatBean has @Stateless
ref = jndiContext.lookup("MyGreatBean/remote");
MyGreat bean = (MyGreat) ref; 
String retValue = bean.businessMethod();
assertEquals("Success", retValue);

From an earlier comment, I get a feeling you want to check what kind of annotations have been added to the actual EJB class - if you want to do that kind of checking without actually running the business methods, you can create an instance using Class.forName... like I mentioned above. When you create an instance like this you can only call methods that don't do any "JEE" stuff. For example you can call a method in the EJB class that is as follows

public String someMethod(){
       return "I am a POJO but I look like an EJB";
}
OpenSource
> I'm not sure why you need to access the actual bean's object (since > obviously I dont know your requirement).For example to test it. Once you get the EJB object into a JavaSE app, you can use junit.
Nils
Ok. Added more info.
OpenSource