使用嵌入式glassfish进行单元测试时出现ClassCastException

时间:2011-11-21 13:37:14

标签: maven junit glassfish ejb

我正在通过maven和嵌入式玻璃鱼容器在某些EJBS上运行一些单元测试。我的一个测试工作正常,但是后续尝试测试不同的EJB会导致同样的错误:

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

接下来是我试图测试的任何bean。我相信我的设置很好,因为正如我所说,我的一个豆子可以正常测试。

工作代码示例:

@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);

}

测试:

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);
    }

}

失败的一个例子:

@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);
    }
...
}

测试:

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);
    }

}

关于为什么这可能不起作用的任何想法?

2 个答案:

答案 0 :(得分:1)

如果您代理的是某个类,而不是接口,则通常会遇到此问题。假设这条线路失败了:

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

OpenChurchPortalContext是一个类,但它由代理类包装以实现EJB特定的功能。此代理类不是OpenChurchPortalContext的子类,因此您将获得ClassCastException。

你没有得到第一个例子,因为LayoutManager是界面

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!");
}

首先,您可以测试以确定这是否真的是您的问题,将上下文更改为PortalContext而不是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");
}

如果你的问题确实是代理,那么上面的代码应该可行。如果是这种情况,您有两种可能的解决方案:

  1. 当您执行ctx.lookup时,请始终使用接口。这可能有点痛苦,因为您需要专门为每个EJB定义一个接口。
  2. 您可以将EJB容器配置为代理类而不仅仅是接口,类似于proxyTargetClass for Spring AOP。您需要查看容器的文档。

答案 1 :(得分:1)

您的singleton EJB通过实现PortalContext接口具有默认的本地业务接口。测试客户端应仅通过其业务接口知道它,并且客户端不应直接引用实际的bean类(OpenChurchPortalContext)。因此,修复方法是通过其业务接口PortalContext查找它。