Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > EJB3 Wrong target class


Автор: Alexis 10.2.2009, 10:54
Мучаю потихоньку EJB3. Чувствую, что ошибка совсем глупая, но все же разобраться самостоятельно не могу.

Если слегка упростить мое творчество, то получается, что имеется класс модели User, есть Stateless бин с методом добавления пользователя:

Код

public interface Users {    
    public void addUser(User user);
}


Код

@Stateless
public class UsersImpl implements Users {
    
    @PersistenceContext(unitName="myUnit")
    private EntityManager entityManager;

    public void addUser(User user) {
        entityManager.persist(user);
    }

}


И сервлет
Код

public class UserServlet extends HttpServlet {

    @EJB 
    private Users users;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        
        User user = new User();
        // ...
        users.addUser(user);
    }

}


Собираются два JAR: ejb, модель и war-файл с сервлетом. Пакуется EAR и деплоится на JBoss 5.0 GA. При вызове сервлета выкидывается ошибка:

Код

javax.ejb.EJBException: java.lang.IllegalArgumentException: Wrong target. class com.mycompany.project.ejb.UsersImpl for public void com.mycompany.project.ejb.UsersImpl.addUser(com.mycompany.project.model.User)
    org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:77)
    org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)
    org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:190)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:159)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:65)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
    org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
    org.jboss.ejb3.session.SessionSpecContainer.invoke(SessionSpecContainer.java:219)
    org.jboss.ejb3.proxy.handler.ProxyInvocationHandlerBase.invoke(ProxyInvocationHandlerBase.java:261)
    org.jboss.ejb3.proxy.handler.session.SessionSpecProxyInvocationHandlerBase.invoke(SessionSpecProxyInvocationHandlerBase.java:101)
    $Proxy196.addUser(Unknown Source)
    com.mycompany.project.webapp.UserServlet.doGet(UserServlet.java:40)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)

и так далее в том же духе.

Самое интересное, что так ругается он только на мою модель. Если, к примеру, передавать String с именем пользователя, а объект создавать в бине и сохранять, то все отлично работает. Чем ему не нравятся классы моей модели, когда их передают бинам, я ума ни приложу.

Автор: powerOn 10.2.2009, 12:43
А что  если интерфейс Users пометить как @Local ?

Автор: Alexis 11.2.2009, 10:05
Цитата(powerOn @ 10.2.2009,  12:43)
А что  если интерфейс Users пометить как @Local ?

Без изменений, ошибка та же.

Автор: chand0s 11.2.2009, 10:24
1) Насколько я помню EJB спеку - если пометок @Local, @Remote нет - интерфейс считается локальным. 
2) Если один интерфейс загружен двумя класслоадерами - это два разных интерфейса

По моему у вас здесь именно это и произошло - jboss грузит бин (и интерфейс Users) одним класслоадером, а tomcat - другим, поэтому у вас эти "имплементации" интерфейса несовместимы. Поковыряйтесь в доке jboss'а, поищите как можно заставить tomcat использовать класслоадер jboss-овского deployer'а

Автор: Alexis 19.2.2009, 10:41
Цитата(chand0s @  11.2.2009,  10:24 Найти цитируемый пост)
2) Если один интерфейс загружен двумя класслоадерами - это два разных интерфейса

Судя по всему дело не в этом. Пробовал менять загрузчик для web-приложений как описано в http://www.jboss.org/community/docs/DOC-9288 не помогло.

Как ни странно, все работает, если интерфейс пометить @Remote

Автор: chand0s 19.2.2009, 15:41
Цитата(Alexis @ 19.2.2009,  08:41)
Цитата(chand0s @  11.2.2009,  10:24 Найти цитируемый пост)
2) Если один интерфейс загружен двумя класслоадерами - это два разных интерфейса

Как ни странно, все работает, если интерфейс пометить @Remote

Вообще это не странно.
Подумайте сами, что происходит внутри сервера:
1) Интерфейс локальный. JBoss делает так:
  • создает экземпляр UsersImpl
  • пытается присвоить ссылку на этот экземпляр переменной Users (которая в сервлете), т.к. интерфейс локальный.
  • экземпляр UsersImpl реализует интерфейс Users, который загружен класслоадером JBoss'а, а присваивается этот экземпляр переменной, чей интерфейс Users загружен tomcat'ом. Эти интерфейсы несовместимы, присваивание невозможно, всё падает.

2) Интерфейс удаленный. Происходит примерно вот что:
  • JBoss создает экземпляр UsersImpl (на самом деле там пул Stateless-бинов, но это неважно)
  • Для переменной Users создается прокси, который знает куда и как отправлять вызовы методов (пусть даже в пределах одной JVM, но прокси все равно создается)
  • При вызове какого-нибудь метода этой переменной в сервлете, прокси сериализует параметры метода и пуляет это все туда, где это ждет JBoss. Метод выполняется, возвращает значение, это значение сериализуется и отправляется обратно.


Автор: ivg 19.2.2009, 17:26
Alexis, у вас классы модели находятся только в отдельном jar'е? В war'е их нету?
Обычно и классы модели и Enterprise бины упаковываются в один jar. В манифесте war'а (/META-INF/MANIFEST.MF) прописывается ссылка на <ejb>.jar (строка Class-Path: <ejb>.jar)
Ну и в application.xml ear'а должны быть определены оба модуля.
<ejb>.jar - имя jar файла с классами модели и Enterprise бинами.
Современные IDE делают это автоматом, либо при сборке проекта, либо при создании(настройке) проектов.
Если вы выносите классы модели в отдельный jar, то соответствующие ссылки должны быть прописаны в манифестах и <ejb>.jar и war файлов.

Автор: Alexis 19.2.2009, 18:46
Действительно, установил в pom.xml зависимостям ejb и модели scope равным provided. Как результат, JAR файлы не копируются в создаваемый WAR-архив. Т. е. фактически получается следующая структура:

Код

- myproject.ear
   - META-INF
      application.xml  # прописаны WEB и EJB модули
      jbos-app.xml     # прописан loader-repository
      manifest.mf      # в classpath model.jar и ejb.jar
   - ejb.jar
      manifest.mf      # в classpath model.jar
      persistence.xml
      + com.company.myproject
   + model.jar
   - webapp.war
      - META-INF
          manifest.mf      # в classpath пусто
      - WEB-INF
         + lib             # никаких ejb.jar, да и model.jar ни к чему :)


До этого EJB лежали в WAR-файле, что и вызавало проблему, которую подробно описал chand0s.

chand0s, спасибо за терпение и подробное объяснение!
ivg, спасибо за наводку на простой путь решения!
Поставил плюсы.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)