2013-02-12 16 views
5

Desidero disattivare la serializzazione nella mia app Wicket e memorizzare tutte le informazioni relative alla pagina/sessione nella RAM. La mia applicazione ha un numero molto piccolo di utenti (generalmente 1); Non ho bisogno di una distribuzione di cluster; e ho bisogno di memorizzare alcuni dati non serializzabili tra le richieste.Come disabilitare la serializzazione in Wicket 1.5?

C'è un modo in cui Wicket non tenterà automaticamente di serializzare le mie pagine/sessione? Ho provato il suggerimento di utilizzare HttpSessionDataStore a https://cwiki.apache.org/confluence/display/WICKET/Page+Storage, ma non ha avuto effetto. Ho ancora le tracce di stack in questo modo:

SEVERE: Error serializing object class com.prosc.safetynet.Administer [object=[Page class = com.prosc.safetynet.Administer, id = 0, render count = 1]] 
org.apache.wicket.util.io.SerializableChecker$WicketNotSerializableException: Unable to serialize class: com.prosc.safetynet.SafetyNetSession$1 
Field hierarchy is: 
    0 [class=com.prosc.safetynet.Administer, path=0] 
    java.lang.Object org.apache.wicket.Component.data [class=org.apache.wicket.model.CompoundPropertyModel] 
     private java.lang.Object org.apache.wicket.model.CompoundPropertyModel.target [class=com.prosc.safetynet.SafetyNetSession$2] 
     final com.prosc.safetynet.SafetyNetSession com.prosc.safetynet.SafetyNetSession$2.this$0 [class=com.prosc.safetynet.SafetyNetSession] 
      private java.lang.Object com.prosc.safetynet.SafetyNetSession.tryAndSerializeMeBitch [class=com.prosc.safetynet.SafetyNetSession$1] <----- field that is not serializable 
    at org.apache.wicket.util.io.SerializableChecker.internalCheck(SerializableChecker.java:395) 
    at org.apache.wicket.util.io.SerializableChecker.check(SerializableChecker.java:374) 
    at org.apache.wicket.util.io.SerializableChecker.checkFields(SerializableChecker.java:655) 
    at org.apache.wicket.util.io.SerializableChecker.internalCheck(SerializableChecker.java:578) 
    at org.apache.wicket.util.io.SerializableChecker.check(SerializableChecker.java:374) 
    at org.apache.wicket.util.io.SerializableChecker.checkFields(SerializableChecker.java:655) 
    at org.apache.wicket.util.io.SerializableChecker.internalCheck(SerializableChecker.java:578) 
    at org.apache.wicket.util.io.SerializableChecker.check(SerializableChecker.java:374) 
    at org.apache.wicket.util.io.SerializableChecker.checkFields(SerializableChecker.java:655) 
    at org.apache.wicket.util.io.SerializableChecker.internalCheck(SerializableChecker.java:578) 
    at org.apache.wicket.util.io.SerializableChecker.check(SerializableChecker.java:374) 
    at org.apache.wicket.util.io.SerializableChecker.checkFields(SerializableChecker.java:655) 
    at org.apache.wicket.util.io.SerializableChecker.internalCheck(SerializableChecker.java:578) 
    at org.apache.wicket.util.io.SerializableChecker.check(SerializableChecker.java:374) 
    at org.apache.wicket.util.io.SerializableChecker.writeObjectOverride(SerializableChecker.java:724) 
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326) 
    at org.apache.wicket.serialize.java.JavaSerializer$CheckerObjectOutputStream.writeObjectOverride(JavaSerializer.java:258) 
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326) 
    at org.apache.wicket.serialize.java.JavaSerializer.serialize(JavaSerializer.java:77) 
    at org.apache.wicket.pageStore.DefaultPageStore.serializePage(DefaultPageStore.java:368) 
    at org.apache.wicket.pageStore.DefaultPageStore.storePage(DefaultPageStore.java:146) 
    at org.apache.wicket.page.PageStoreManager$PersistentRequestAdapter.storeTouchedPages(PageStoreManager.java:383) 
    at org.apache.wicket.page.RequestAdapter.commitRequest(RequestAdapter.java:171) 
    at org.apache.wicket.page.AbstractPageManager.commitRequest(AbstractPageManager.java:94) 
    at org.apache.wicket.page.PageManagerDecorator.commitRequest(PageManagerDecorator.java:68) 
    at org.apache.wicket.page.PageAccessSynchronizer$2.commitRequest(PageAccessSynchronizer.java:281) 
    at org.apache.wicket.Application$2.onDetach(Application.java:1598) 
    at org.apache.wicket.request.cycle.RequestCycleListenerCollection$3.notify(RequestCycleListenerCollection.java:99) 
    at org.apache.wicket.request.cycle.RequestCycleListenerCollection$3.notify(RequestCycleListenerCollection.java:97) 
    at org.apache.wicket.util.listener.ListenerCollection$1.notify(ListenerCollection.java:119) 
    at org.apache.wicket.util.listener.ListenerCollection.reversedNotify(ListenerCollection.java:143) 
    at org.apache.wicket.util.listener.ListenerCollection.reversedNotifyIgnoringExceptions(ListenerCollection.java:113) 
    at org.apache.wicket.request.cycle.RequestCycleListenerCollection.onDetach(RequestCycleListenerCollection.java:95) 
    at org.apache.wicket.request.cycle.RequestCycle.onDetach(RequestCycle.java:603) 
    at org.apache.wicket.request.cycle.RequestCycle.detach(RequestCycle.java:542) 
    at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:287) 
    at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:188) 
    at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:244) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:210) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151) 
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:870) 
    at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665) 
    at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528) 
    at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81) 
    at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:685) 
    at java.lang.Thread.run(Thread.java:680) 

risposta

5

È possibile implementare il proprio IPageStore che mantiene le pagine in memoria.

+0

Che ha funzionato, grazie –

+0

C'è anche un esempio: http://maciej-miklas.blogspot.de/2013/09/wicket-6-disable-page-serialization.html –

1

Non posso commentare su qualcosa di specifico per Wicket, ma parlando in generale l'intero punto di un HTTP Session è quello di memorizzare Serializable stato tra le richieste (e in ambienti cluster, a consentire a tale stato di essere replicato su più nodi nel cluster per fornire ridondanza in caso di errore di un nodo). Mettere qualcosa che è nonSerializable in esso è generalmente considerato un errore, come mostrato dalla traccia dello stack. Sarei un po 'sorpreso se ci fosse qualche tipo di opzione di configurazione che cambierebbe questo (anche se forse c'è, come ho detto non posso commentare sul lato Wicket delle cose).

Un'alternativa semplice, se non si richiede la persistenza reale e se i dati non sono eccezionalmente grandi/complessi, è sufficiente utilizzare campi modulo nascosti nella pagina per tenere traccia dello stato pertinente.

Ma se ciò che si desidera è una cache in memoria, perché non implementare il proprio? E 'abbastanza semplice da fare:

public class SessionCache { 
    private static final Map<String, Map<String, Object>> CACHE = Collections.synchronizedMap(new HashMap<String, Map<String, Object>>()); 

    public static Object getAttribute(String sessionId, String attribName) { 
     Map<String, Object> attribs = CACHE.get(sessionId); 
     if (attribs != null) { 
      synchronized(attribs) { 
       return attribs.get(attribName); 
      } 
     } 

     return null; 
    } 

    public static void setAttribute(String sessionId, String attribName, Object attribValue) { 
     Map<String, Object> attribs = CACHE.get(sessionId); 
     if (attribs == null) { 
      attribs = new HashMap<String, Object>(); 
      CACHE.put(sessionId, attribs); 
     } 

     synchronized(attribs) { 
      attribs.put(attribName, attribValue); 
     } 
    } 

    public static void destroySession(String sessionId) { 
     CACHE.remove(sessionId); 
    } 

    public static void createSession(String sessionId, boolean force) { 
     if (force || ! CACHE.containsKey(sessionId)) { 
      CACHE.put(sessionId, new HashMap<String, Object>()); 
     } 
    } 
} 

noti che si vorrà agganciare che nel ciclo di vita della sessione di Wicket in modo che le vecchie sessioni vengono rimossi quando scadono. Altrimenti avrai una perdita di memoria graduale sulle tue mani. Dai documenti sembra che tu possa farlo usando registerUnboundListener() nella classe HttpSessionStore.

+0

Sono abbastanza sicuro che questo è un problema specifico wicket . La traccia dello stack mostra che questo non viene avviato dal contenitore del servlet; è un metodo di Wicket che sta tentando di memorizzare la pagina in una PageMap. –

+0

Per essere più specifici, le operazioni di serializzazione iniziano qui: org.apache.wicket.Application $ 2.onDetach (Application.java:1598) –

+0

Questa specifica istanza del problema potrebbe essere causata da una classe Wicket, ma concettualmente lo stesso [ esiste un problema con Tomcat] (http://dev-answers.blogspot.com.au/2007/03/how-to-turn-off-tomcat-session.html) (e ho il sospetto che con qualsiasi altro contenitore Servlet). – aroth

1

Ecco la soluzione che ho trovato, basata sulle risposte di svenmeier. Sono sicuro che questo è corretto non al 100%, ma sta funzionando bene nel mio test:

package com.prosc.wicket; 

import org.apache.wicket.Application; 
import org.apache.wicket.DefaultPageManagerProvider; 
import org.apache.wicket.page.IManageablePage; 
import org.apache.wicket.page.IPageManagerContext; 
import org.apache.wicket.pageStore.IDataStore; 
import org.apache.wicket.pageStore.IPageStore; 
import org.apache.wicket.pageStore.memory.HttpSessionDataStore; 
import org.apache.wicket.pageStore.memory.PageNumberEvictionStrategy; 

import java.io.Serializable; 
import java.util.HashMap; 
import java.util.Map; 

/** 
* This class disables Wicket's serialization behavior, while still retaining session and page data in memory (so back button will work). 
* This will run out of memory under heavy load; but it's very convenient for low volume web applications. 
* To disable serialization in your application, call this code: 
* <pre> 
*  setPageManagerProvider(new NoSerializePageManagerProvider(this, getPageManagerContext())); 
* </pre> 
*/ 
public class NoSerializePageManagerProvider extends DefaultPageManagerProvider { 
    private IPageManagerContext pageManagerContext; 

    public NoSerializePageManagerProvider(Application application, IPageManagerContext pageManagerContext) { 
     super(application); 
     this.pageManagerContext = pageManagerContext; 
    } 

    @Override 
    protected IDataStore newDataStore() { 
     return new HttpSessionDataStore(pageManagerContext, new PageNumberEvictionStrategy(20)); 
    } 

    @Override 
    protected IPageStore newPageStore(IDataStore dataStore) { 
     return new IPageStore() { 
      Map<String,Map<Integer,IManageablePage>> cache = new HashMap<String, Map<Integer, IManageablePage>>(); 

      public void destroy() { 
       cache = null; 
      } 

      public IManageablePage getPage(String sessionId, int pageId) { 
       Map<Integer, IManageablePage> sessionCache = getSessionCache(sessionId, false); 
       IManageablePage page = sessionCache.get(pageId); 
       if(page == null) { 
        throw new IllegalArgumentException("Found this session, but there is no page with id " + pageId); 
       } 
       return page; 
      } 

      public void removePage(String sessionId, int pageId) { 
       getSessionCache(sessionId, false).remove(pageId); 
      } 

      public void storePage(String sessionId, IManageablePage page) { 
       getSessionCache(sessionId, true).put(page.getPageId(), page); 
      } 

      public void unbind(String sessionId) { 
       cache.remove(sessionId); 
      } 

      public Serializable prepareForSerialization(String sessionId, Object page) { 
       return null; 
      } 

      public Object restoreAfterSerialization(Serializable serializable) { 
       return null; 
      } 

      public IManageablePage convertToPage(Object page) { 
       return (IManageablePage)page; 
      } 

      private Map<Integer, IManageablePage> getSessionCache(String sessionId, boolean create) { 
       Map<Integer, IManageablePage> sessionCache = cache.get(sessionId); 
       if(sessionCache == null) { 
        if(create) { 
         sessionCache = new HashMap<Integer, IManageablePage>(); 
         cache.put(sessionId, sessionCache); 
        } else { 
         throw new IllegalArgumentException("There are no pages stored for session id " + sessionId); 
        } 
       } 
       return sessionCache; 
      } 
     }; 
    } 
} 
1

voglio migliorare la risposta di Jesse. Qui di seguito è un'implementazione thread-safe di IPageStore con cache interna "Inserito Least Recently" (mantiene al massimo 5 pagine con stato di recente accesso a seduta):

public class CustomPageStore implements IPageStore { 

private static final Logger logger = LoggerFactory.getLogger(CustomPageStore.class); 

private static final int MEDIAN_OF_NUMBER_OF_SESSIONS = 6000; 

private ConcurrentMap<String, CustomLinkedHashMap<Integer, IManageablePage>> cache = new ConcurrentHashMap<>(MEDIAN_OF_NUMBER_OF_SESSIONS); 

@Override 
public void destroy() { 
    cache.clear(); 
} 

@Override 
public IManageablePage getPage(final String sessionId, int pageId) { 
    final Map<Integer, IManageablePage> sessionCache = getSessionCache(sessionId); 
    final RequestCycle requestCycle = RequestCycle.get(); 
    if (sessionCache == null) { 
     logger.warn("Missing cache. SessionId: {}, pageId: {}, URL: {}", sessionId, pageId, requestCycle == null ? StringUtils.EMPTY : requestCycle.getRequest().getUrl()); 
     return null; 
    } 

    final IManageablePage page; 
    //noinspection SynchronizationOnLocalVariableOrMethodParameter 
    synchronized (sessionCache) { 
     page = sessionCache.get(pageId); 
    } 

    if (page == null && logger.isDebugEnabled()) { 
     logger.debug("Missed page. SessionId: {}, pageId: {}, URL: {}", sessionId, pageId, requestCycle == null ? StringUtils.EMPTY : requestCycle.getRequest().getUrl()); 
    } 

    return page; 
} 

@Override 
public void removePage(final String sessionId, int pageId) { 
    final Map<Integer, IManageablePage> sessionCache = getSessionCache(sessionId); 
    if (sessionCache != null) { 
     //noinspection SynchronizationOnLocalVariableOrMethodParameter 
     synchronized (sessionCache) { 
      sessionCache.remove(pageId); 
     } 
    } 
} 

@Override 
public void storePage(final String sessionId, IManageablePage page) { 
    final LinkedHashMap<Integer, IManageablePage> sessionCache = getOrCreateSessionCache(sessionId); 
    final int pageId = page.getPageId(); 
    //noinspection SynchronizationOnLocalVariableOrMethodParameter 
    synchronized (sessionCache) { 
     if (sessionCache.containsKey(pageId)) { 
      // do this to change insertion order and update least inserted entry 
      sessionCache.remove(pageId); 
      sessionCache.put(pageId, page); 
     } else { 
      sessionCache.put(pageId, page); 
     } 
    } 
} 

@Override 
public void unbind(final String sessionId) { 
    cache.remove(sessionId); 
} 

@Override 
public Serializable prepareForSerialization(String sessionId, Object page) { 
    return null; 
} 

@Override 
public Object restoreAfterSerialization(Serializable serializable) { 
    return null; 
} 

@Override 
public IManageablePage convertToPage(final Object page) { 
    return (IManageablePage) page; 
} 

@Nullable 
private Map<Integer, IManageablePage> getSessionCache(final String sessionId) { 
    return cache.get(sessionId); 
} 

@Nonnull 
private CustomLinkedHashMap<Integer, IManageablePage> getOrCreateSessionCache(final String sessionId) { 
    return cache.computeIfAbsent(sessionId, s -> new CustomLinkedHashMap<>()); 
} 

/** Mimics "least recently inserted" cache */ 
private static class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> { 

    /** use this parameter to control memory consumption and frequency of appearance of PageExpiredException */ 
    private static final int MAX_PAGES_PER_SESSION = 5; 

    @Override 
    protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) { 
     return size() > MAX_PAGES_PER_SESSION; 
    } 
} 
} 
Problemi correlati