@EJB в @ViewScoped @ManagedBean вызывает java.io.NotSerializableException

Я прочитал @EJB в управляемом @ViewScoped bean вызывает java.io.NotSerializableException, но моя настройка сохранения состояния server.

Вот что у меня есть:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>sispra</display-name>
    <welcome-file-list>
        <welcome-file>index.jsf</welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>

    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>facelets.BUILD_BEFORE_RESTORE</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.PRETTY_HTML</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name>
        <param-value>false</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.AUTO_SCROLL</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>glass-x</param-value>
    </context-param>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure Application</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>fileRealm</realm-name>
    </login-config>
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

customer.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui">      

    <h:head>
        <title>TODO supply a title</title>
    </h:head>

    <h:body>

        <p:growl id="messages"/>

        <h:form>

            <p:commandButton actionListener="#{customerController.create}" value="save" update="@form :messages"/>

            <p:panel>
                <f:facet name="header">
                    <h:outputText value="Details"/>
                </f:facet>

                <h:panelGrid columns="3">

                    <h:outputLabel for="name" value="#{bundle['person.name']}"/>
                    <p:inputText id="name" label="#{bundle['person.name']}" value="#{customerController.selected.name}"/>
                    <p:message for="name"/>

                    <h:outputLabel for="surname" value="#{bundle['person.surname']}"/>
                    <p:inputText id="surname" label="#{bundle['person.surname']}" value="#{customerController.selected.surname}"/>
                    <p:message for="surname"/>

                </h:panelGrid>
            </p:panel>

        </h:form>

    </h:body>
</html>

CustomerController.java:

package it.shape.sispra.controllers;

import it.shape.sispra.ejb.PersonFacade;
import it.shape.sispra.entities.Customer;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class CustomerController extends AbstractController<Customer>
{
    private static final long serialVersionUID = 134755304347034L;

    @EJB
    private PersonFacade facade;

    public CustomerController()
    {
        super(Customer.class);
    }

    @Override
    public PersonFacade getFacade()
    {
        return facade;
    }
}

PersonFacade.java:

package it.shape.sispra.ejb;

import it.shape.sispra.entities.Person;
import javax.ejb.Stateless;

@Stateless
public class PersonFacade extends AbstractFacade<Person>
{
    private static final long serialVersionUID = 4357823648345L;

    public PersonFacade()
    {
        super(Person.class);
    }

}

AbstractFacade.java:

package it.shape.sispra.ejb;

import it.shape.sispra.entities.AbstractEntity;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.queries.QueryByExamplePolicy;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;

public abstract class AbstractFacade<T extends AbstractEntity> implements Serializable
{
    private static final long serialVersionUID = 12467890452346123L;

    @PersistenceContext(unitName = "sispra")
    private EntityManager em;

    private final Class<T> entityClass;

    public AbstractFacade(Class<T> entityClass)
    {
        this.entityClass = entityClass;
    }

    public EntityManager getEntityManager()
    {
        return em;
    }

    public void create(T entity)
    {
        getEntityManager().persist(entity);
    }

    public void edit(T entity)
    {
        getEntityManager().merge(entity);
    }

    public void remove(T entity)
    {
        getEntityManager().remove(getEntityManager().merge(entity));
    }

    public void refresh(T entity)
    {
        getEntityManager().refresh(entity);
    }

    public T find(Object id)
    {
        return getEntityManager().find(entityClass, id);
    }

    public List<T> findAll()
    {
        CriteriaQuery<T> cq = getEntityManager().getCriteriaBuilder().createQuery(entityClass);
        cq.select(cq.from(entityClass));

        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<T> findRange(int first, int max)
    {
        CriteriaQuery<T> cq = getEntityManager().getCriteriaBuilder().createQuery(entityClass);
        cq.select(cq.from(entityClass));

        TypedQuery<T> q = getEntityManager().createQuery(cq);
        q.setMaxResults(max);
        q.setFirstResult(first);

        return q.getResultList();
    }

    public int count()
    {
        CriteriaQuery<Long> cq = getEntityManager().getCriteriaBuilder().createQuery(Long.class);
        Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        TypedQuery<Long> q = getEntityManager().createQuery(cq);
        return q.getSingleResult().intValue();
    }

    @SuppressWarnings("unchecked")
    public T findByExample(T entity)
    {
        // Create a native EclipseLink query using QBE policy
        QueryByExamplePolicy policy = new QueryByExamplePolicy();
        policy.addSpecialOperation(String.class, "like");

        ReadObjectQuery roq = new ReadObjectQuery(entity, policy);

        // Wrap the native query in a standard JPA Query and execute it
        Query query = JpaHelper.createQuery(roq, getEntityManager());

        return (T) query.getSingleResult();
    }

    @SuppressWarnings("unchecked")
    public List<T> findAllByExample(T entity)
    {
        // Create a native EclipseLink query using QBE policy
        QueryByExamplePolicy policy = new QueryByExamplePolicy();
        policy.addSpecialOperation(String.class, "like");

        ReadAllQuery raq = new ReadAllQuery(entity, policy);

        // Wrap the native query in a standard JPA Query and execute it
        Query query = JpaHelper.createQuery(raq, getEntityManager());

        return query.getResultList();
    }

    @SuppressWarnings("unchecked")
    public List<T> findByExample(T entity, int start, int max)
    {
        // Create a native EclipseLink query using QBE policy
        QueryByExamplePolicy policy = new QueryByExamplePolicy();
        policy.addSpecialOperation(String.class, "like");

        ReadAllQuery raq = new ReadAllQuery(entity, policy);

        // Wrap the native query in a standard JPA Query and execute it
        Query query = JpaHelper.createQuery(raq, getEntityManager());
        query.setFirstResult(start);
        query.setMaxResults(max);

        return query.getResultList();
    }

    public int countByExample(T entity)
    {
        //TODO find a better way...
        return findAllByExample(entity).size();
    }
}

и это stacktrace:

GRAVE: Exiting serializeView - Could not serialize state: com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate
java.io.NotSerializableException: com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
  at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
  at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
  at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
  at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
  at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
  at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
  at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
  at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
  at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
  at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
  at java.util.HashMap.writeObject(HashMap.java:1001)
  at sun.reflect.GeneratedMethodAccessor45.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
  at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
  at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
  at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1346)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1154)
  at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
  at java.util.HashMap.writeObject(HashMap.java:1001)
  at sun.reflect.GeneratedMethodAccessor45.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
  at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
  at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
  at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1346)
  at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1154)
  at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
  at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.serializeView(ServerSideStateCacheImpl.java:357)
  at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.saveSerializedViewInServletSession(ServerSideStateCacheImpl.java:220)
  at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.saveSerializedView(ServerSideStateCacheImpl.java:798)
  at org.apache.myfaces.renderkit.html.HtmlResponseStateManager.saveState(HtmlResponseStateManager.java:127)
  at org.apache.myfaces.application.StateManagerImpl.saveView(StateManagerImpl.java:166)
  at org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage.renderView(FaceletViewDeclarationLanguage.java:1554)
  at org.apache.myfaces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:281)
  at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:85)
  at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:239)
  at javax.faces.webapp.FacesServlet.service(FacesServlet.java:191)
  at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
  at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
  at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
  at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
  at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
  at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
  at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
  at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
  at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
  at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
  at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
  at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
  at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
  at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
  at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
  at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
  at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
  at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
  at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
  at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
  at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
  at java.lang.Thread.run(Thread.java:662)

Я знаю, что могу обойти это, объявив PersonFacade как transient и использовать JNDI для получения ссылки EJB после повторной обработки, но мне действительно не нравится этот подход.

Возможно ли, что Glassfish 3.1.1 предоставляет несериализуемые EJB? Есть ли способ использовать @EJB и @ViewScoped вместе?

обновление: я обнаружил, что это проблема, связанная с MyFaces, все работает отлично, используя mojarra

Ответ 1

:) У меня была та же проблема: https://issues.apache.org/jira/browse/MYFACES-3581

Дэвид Блевинс в Apache TomEE помог мне создать журнал ошибок и патчей для MyFaces. Проблема была сгенерирована. Прокси-классы не находятся на пути к загрузчику классов для десериализатора.

Обходной путь заключается в том, чтобы установить для этого параметра веб-приложения значение false: org.apache.myfaces.SERIALIZE_STATE_IN_SESSION

Ответ 2

Я тоже видел эту проблему. То, что я делал в прошлом, разделяет мой bean на StateBean, который @ViewScoped и ActionsBean, который является @RequestScoped. ActionBean вводится StateBean, а также любые EJB или несериализуемые объекты доступа к ресурсам. В интерфейсе вы используете StateBean для доступа к свойствам и ActionsBean для выполнения действий.

Мне бы очень хотелось услышать от кого-то другого, который бы устарел мой шаблон "bean расщепления".

Это пример того, что я делаю:

@ManagedBean
@ViewScoped
public class CustomControllerStateBean implements Serializable {

  private static final long serialVersionUID = 134755304347034L;

  private Person selected;

  public Person getSelected() {
    return selected;
  }

  public void setSelected(Person selected) {
    this.selected = selected;
  }
}

Примечание CustomControllerStateBean is @ViewScoped и Serializable содержит только Serializable объекты.

@ManagedBean
@RequestScoped
public class CustomControllerActionsBean {

  @EJB
  private PersonFacade facade;
  @Inject
  private CustomControllerStateBean state;

  public void create() {
    facade.create(state.getSelected());
  }
}

Примечание CustomControllerActionsBean is @RequestScoped и НЕ Serializable содержит не объекты Serializable.

Ваш интерфейс не будет выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui">      

    <h:head>
        <title>TODO supply a title</title>
    </h:head>

    <h:body>

        <p:growl id="messages"/>

        <h:form>

            <p:commandButton actionListener="#{customerControllerActionsBean.create}" value="save" update="@form :messages"/>

            <p:panel>
                <f:facet name="header">
                    <h:outputText value="Details"/>
                </f:facet>

                <h:panelGrid columns="3">

                    <h:outputLabel for="name" value="#{bundle['person.name']}"/>
                    <p:inputText id="name" label="#{bundle['person.name']}" value="#{customerControllerStateBean.selected.name}"/>
                    <p:message for="name"/>

                    <h:outputLabel for="surname" value="#{bundle['person.surname']}"/>
                    <p:inputText id="surname" label="#{bundle['person.surname']}" value="#{customerControllerStateBean.selected.surname}"/>
                    <p:message for="surname"/>

                </h:panelGrid>
            </p:panel>

        </h:form>

    </h:body>
</html>

Примечание CustomControllerActionsBean используется в верхней части в p:commandButton, а CustomControllerStateBean используется в p:inputText s.