2

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.

Nils
  • 13,319
  • 19
  • 86
  • 108
  • What will be the use of having the object of the actual bean itself? - you will always be interacting thru the interface right? basically like InterfaceType ref = (InterfaceType) jndiLookup; After that you call methods on ref. Trying to understand the need for this. – OpenSource Aug 15 '09 at 01:08

4 Answers4

3

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 "Java EE" 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";
}
Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
OpenSource
  • 2,217
  • 2
  • 21
  • 22
  • > 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 Aug 19 '09 at 04:18
1

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.

fgui
  • 1,564
  • 1
  • 13
  • 14
  • ok, but if the annotation is in the actual bean class and not in the interace, how do you get it then? – Nils Aug 06 '09 at 15:17
  • I used the following annotation on the bean class: @Stateless(name="TestBean", mappedName="ejb/TestBean") – Nils Aug 06 '09 at 15:29
0

try replacing

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

with

public <T> T createEJB(Class<T> ejbClass) throws NamingException
fgui
  • 1,564
  • 1
  • 13
  • 14
  • I already tried, but got a cast exception: Testcase: testSayHello(SimpleEJBTest): Caused an ERROR _TestRemote_Wrapper cannot be cast to TestBean java.lang.ClassCastException: _TestRemote_Wrapper cannot be cast to TestBean at SimpleEJBTest.setUp(SimpleEJBTest.java:16) – Nils Aug 06 '09 at 14:28
  • may be the declaration on the test of testBean is wrong. Should it be type TestBean and not TestRemote? – fgui Aug 06 '09 at 14:35
  • yes of course I also changed that. Then I got a cast exception. Sry forgot to mention above. – Nils Aug 06 '09 at 14:36
  • 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 – fgui Aug 06 '09 at 14:38
  • 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. – fgui Aug 06 '09 at 14:39
  • > 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 Aug 06 '09 at 14:52
0

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
  • 2,217
  • 2
  • 21
  • 22
  • Ahh.. for some reason I thought when you look for the remote it wasnt there. Will think more regarding this one. – OpenSource Aug 14 '09 at 21:17