2013-02-12 33 views
5

Tôi muốn tắt tính năng tuần tự hóa trong ứng dụng Wicket và lưu trữ tất cả thông tin về trang/phiên trong RAM. Ứng dụng của tôi có số lượng người dùng rất nhỏ (thường là 1); Tôi không cần triển khai cluster; và tôi cần lưu vào bộ nhớ cache một số dữ liệu không thể tuần tự giữa các yêu cầu.Làm cách nào để vô hiệu hoá việc tuần tự hóa trong Wicket 1.5?

Có cách nào để Wicket không tự động cố gắng sắp xếp thứ tự các trang/phiên của tôi không? Tôi đã thử đề xuất sử dụng HttpSessionDataStore tại https://cwiki.apache.org/confluence/display/WICKET/Page+Storage, nhưng nó không có hiệu lực. Tôi vẫn nhận được đống dấu vết như thế này:

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) 

Trả lời

5

Bạn có thể triển khai IPageStore của riêng bạn để giữ các trang trong bộ nhớ.

+0

Điều đó có hiệu quả, cảm ơn bạn –

+0

Ngoài ra còn có một ví dụ: http://maciej-miklas.blogspot.de/2013/09/wicket-6-disable-page-serialization.html –

1

Tôi không thể bình luận về bất cứ điều gì cụ thể để Wicket, nhưng nói chung toàn bộ điểm của một Http Session là để lưu trữ Serializable trạng thái giữa các yêu cầu (và trong môi trường nhóm, để cho phép trạng thái đó được nhân rộng thành nhiều nút trong cụm để cung cấp dự phòng trong trường hợp lỗi nút). Đặt một cái gì đó là không phảiSerializable vào trong đó thường được coi là lỗi, như được hiển thị theo dấu vết ngăn xếp của bạn. Tôi sẽ hơi ngạc nhiên nếu có bất kỳ loại tùy chọn cấu hình nào có thể thay đổi điều này (mặc dù có lẽ có; như tôi đã nói tôi không thể thực sự bình luận về phía Wicket của sự vật). Một thay thế đơn giản, nếu bạn không yêu cầu sự kiên trì thực sự và nếu dữ liệu không phải là đặc biệt lớn/phức tạp, là chỉ sử dụng các trường biểu mẫu ẩn trên trang của bạn để theo dõi trạng thái có liên quan.

Nhưng nếu những gì bạn muốn là bộ nhớ cache trong bộ nhớ, tại sao bạn không thực hiện bộ nhớ cache của riêng mình? Nó đủ đơn giản để làm:

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

Lưu ý rằng bạn sẽ muốn móc vào vòng đời của Wicket để các phiên cũ bị xóa khi hết hạn. Nếu không, bạn sẽ bị rò rỉ bộ nhớ dần dần trên tay. Từ tài liệu, có vẻ như bạn có thể thực hiện việc này bằng cách sử dụng registerUnboundListener() trên lớp HttpSessionStore.

+0

Tôi khá chắc chắn rằng đây là một vấn đề Wicket cụ thể . Theo dõi ngăn xếp cho thấy rằng điều này không được khởi tạo bởi thùng chứa servlet; đó là một phương pháp Wicket đang cố gắng lưu trữ trang trong Sơ đồ trang. –

+0

Để cụ thể hơn, các công cụ tuần tự hóa bắt đầu xảy ra ở đây: org.apache.wicket.Application $ 2.onDetach (Application.java:1598) –

+0

Ví dụ cụ thể về vấn đề này có thể do lớp Wicket gây ra, nhưng về mặt khái niệm thì giống nhau vấn đề tồn tại với Tomcat] (http://dev-answers.blogspot.com.au/2007/03/how-to-turn-off-tomcat-session.html) (và tôi nghi ngờ với bất kỳ container Servlet nào khác). – aroth

1

Đây là giải pháp mà tôi đã đưa ra, dựa trên câu trả lời của svenmeier. Tôi chắc chắn rằng điều này không chính xác 100%, nhưng nó hoạt động tốt trong thử nghiệm của tôi:

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

Tôi muốn cải thiện câu trả lời của Jesse. Dưới đây là một thread thực hiện an toàn của IPageStore với nội bộ nhớ cache "Gần đây Least chèn" (giữ nhiều nhất là 5 mới truy cập trang stateful mỗi phiên):

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; 
    } 
} 
} 
Các vấn đề liên quan