Paul Hunnisett
Paul Hunnisett

Reputation: 908

ClassCastException when using embedded glassfish for unit tests

I'm running some unit tests on some EJBS via maven and an embedded glassfish container. One of my tests works, but all subsequent attempts to test a different EJB result in the same error:

java.lang.ClassCastException: $Proxy81 cannot be cast to 

Followed by whatever bean I'm attempting to test. I'm confident my setup is good since, as I say, one of my beans can be tested properly.

Examples of workiing code:

@Stateful
public class LayoutManagerBean implements LayoutManager {

    private final Log LOG = LogFactory.getLog(LayoutManagerBean.class);



    public List<Menu> getMenus(User currentUser) {
        ...
    }

}

@Local
public interface LayoutManager {

    public List<Menu> getMenus(User user);

}

And the test:

public class LayoutManagerTest {

    private static EJBContainer ejbContainer;
    private static Context ctx;

    @BeforeClass
    public static void setUp() {
        ejbContainer = EJBContainer.createEJBContainer();
        ctx = ejbContainer.getContext();
    }

    @AfterClass
    public static void tearDown() {
        ejbContainer.close();
    }

    @Test
    public void getMenus() {
        LayoutManager manager = null;
        try {
            manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
        } catch (NamingException e) {
            System.out.println("Failed to lookup the gosh darned bean!");
        }
        assertNotNull(manager);
        //Menu[] menus = manager.getMenus();
        //assertTrue(menus.length > 1);
    }

}

And an example of a failure:

@Singleton
public class OpenChurchPortalContext implements PortalContext {

    private Set<PortletMode> portletModes = Collections.emptySet();
    private Set<WindowState> windowStates = Collections.emptySet();

    private Properties portalProperties = new Properties();

    public OpenChurchPortalContext() {
        portletModes.add(PortletMode.VIEW);
        portletModes.add(PortletMode.HELP);
        portletModes.add(PortletMode.EDIT);
        portletModes.add(new PortletMode("ABOUT"));

        windowStates.add(WindowState.MAXIMIZED);
        windowStates.add(WindowState.MINIMIZED);
        windowStates.add(WindowState.NORMAL);
    }
...
}

And the test:

public class OpenChurchPortalContextTest {

    private static EJBContainer ejbContainer;
    private static Context ctx;

    @BeforeClass
    public static void setUp() {
        ejbContainer = EJBContainer.createEJBContainer();
        ctx = ejbContainer.getContext();
    }

    @AfterClass
    public static void tearDown() {
        ejbContainer.close();
    }

    @Test
    public void test() {
        OpenChurchPortalContext context = null;
        try {
            context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
        } catch (NamingException e) {
            System.out.println("Failed to find the bean in the emebedded jobber");
        }
        assertNotNull(context);
        Set<PortletMode> modes = (Set<PortletMode>) context.getSupportedPortletModes();
        assertTrue(modes.size() > 1);
        Set<WindowState> states = (Set<WindowState>) context.getSupportedWindowStates();
        assertTrue(states.size() > 1);
    }

}

Any ideas as to why this may not be working?

Upvotes: 2

Views: 595

Answers (2)

cheng
cheng

Reputation: 1138

Your singleton EJB has a default local business interface by means of implementing PortalContext interface. The test client should know it only by its business interface, and the actual bean class (OpenChurchPortalContext) should not be referenced directly by the client. So the fix is to look it up by its business interface PortalContext.

Upvotes: 1

Matthew Farwell
Matthew Farwell

Reputation: 61695

You often get this problem if you are proxying a class, not an interface. Assuming that it's this line which is failing:

context = (OpenChurchPortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");

OpenChurchPortalContext is a class, but it is being wrapped by a proxy class to implement the EJB specific functionality. This proxy class isn't a subclass of OpenChurchPortalContext, so you're getting a ClassCastException.

You aren't getting this with the first example, because the LayoutManager is an interface.

LayoutManager manager = null; // INTERFACE, so it works
try {
    manager = (LayoutManager) ctx.lookup("java:global/classes/LayoutManagerBean!uk.co.monkeypower.openchurch.core.layout.beans.LayoutManager");
} catch (NamingException e) {
    System.out.println("Failed to lookup the gosh darned bean!");
}

First, you can test to see if this is really your problem, change context to be a PortalContext not OpenChurchPortalContext:

PortalContext context = null;
try {
    context = (PortalContext) ctx.lookup("java:global/classes/OpenChurchPortalContext");
} catch (NamingException e) {
    System.out.println("Failed to find the bean in the emebedded jobber");
}

If your problem really is the Proxy, then the above code should work. If this is the case, you have two potential solutions:

  1. When you do the ctx.lookup, always use an interface. This can be a bit of a pain, because you need to define an interface specifically for each EJB.
  2. You may be able to configure your EJB container to proxy the classes instead of just the interfaces, similar to proxyTargetClass for Spring AOP. You'll need to check with the documentation for your container for that.

Upvotes: 1

Related Questions