我正在开发一个使用Wicket 1.5和Hibernate / JPA 2的大型Java应用程序.Wicket有一个标准规则,即存储在会话中的对象必须实现Serializable
。我们有一个额外的规则,即JPA管理对象不能存储在会话中。而是通过可拆卸模型在每个请求上加载JPA托管对象。
要使问题复杂化,在会话中存储Entity对象是合法的,前提是它尚未持久存在。因此,我们的一些课程已经实现了Serializable
。
如果我想扩展Wicket的序列化测试以检测JPA拥有的对象,我该怎么做呢?如果没有Wicket的本地分支,这可能吗?
答案 0 :(得分:5)
在我的$ dayjob中,我们使用您描述的内容,以及我拥有的presented at several meetups(参见幻灯片23及其后)。你不必为此分叉Wicket。
基本上你要做的就是复制序列化器检查器代码并修改它以包括你的检查以及检查序列化错误。然后在请求周期的最后阶段,您在受影响的页面上运行自己的序列化检查器。
我们创建的检查检查了我们的公共基类,并且实体已经持久化了。如果是这样,我们就失败了。另外,我们在基页中有一个Ajax回调,用于检查会话属性以查看是否存在序列化错误。如果是这样,我们会重定向到序列化失败的错误页面,以确保开发人员不会忽略页面层次结构中的实体。
这是我们检查器的核心(从Wicket的序列化程序检查重写的check
方法):
private void check(Object obj)
{
if (obj == null || obj.getClass().isAnnotationPresent(Deprecated.class)
|| obj.getClass().isAnnotationPresent(SkipClass.class))
{
return;
}
Class< ? > cls = obj.getClass();
nameStack.add(simpleName);
traceStack.add(new TraceSlot(obj, fieldDescription));
if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls)))
{
throw new WicketNotSerializableException(toPrettyPrintedStack(obj.getClass().getName())
.toString(), exception);
}
if (obj instanceof IdObject)
{
Serializable id = ((IdObject) obj).getIdAsSerializable();
if (id != null && !(id instanceof Long && ((Long) id) <= 0))
{
throw new WicketContainsEntityException(toPrettyPrintedStack(
obj.getClass().getName()).toString(), exception);
}
}
if (obj instanceof LoadableDetachableModel)
{
LoadableDetachableModel< ? > ldm = (LoadableDetachableModel< ? >) obj;
if (ldm.isAttached())
{
throw new WicketContainsAttachedLDMException(toPrettyPrintedStack(
obj.getClass().getName()).toString(), exception);
}
}
对于Wicket 1.5,我们创建了自己的PageStoreManager
来执行这些检查(以及许多其他事情,例如为我们的用户启用服务器端浏览历史记录)。我们通过覆盖RequestAdapter
并在适配器中进行序列化检查来提供我们自己的PageStoreManager#newRequestAdapter(IPageManagerContext context)
:
class DetachCheckingRequestAdapter extends RequestAdapter
{
public DetachCheckingRequestAdapter(IPageManagerContext context)
{
super(context);
}
@Override
protected void storeTouchedPages(List<IManageablePage> touchedPages)
{
super.storeTouchedPages(touchedPages);
if (Application.get().usesDevelopmentConfig())
{
for (IManageablePage curPage : touchedPages)
{
if (!((Page) curPage).isErrorPage())
testDetachedObjects(curPage);
}
}
}
private void testDetachedObjects(final IManageablePage page)
{
try
{
NotSerializableException exception = new NotSerializableException();
EntityAndSerializableChecker checker = new EntityAndSerializableChecker(exception);
checker.writeObject(page);
}
catch (Exception ex)
{
log.error("Couldn't test/serialize the Page: " + page + ", error: " + ex);
Session.get().setDetachException(ex);
}
}
}
答案 1 :(得分:3)
有关Wicket 6.3.0+中更简单的解决方案,请参阅https://cwiki.apache.org/confluence/display/WICKET/Serialization+Checker
答案 2 :(得分:0)
一种非常简单的方法是自定义实体的序列化:
public Object writeReplace() throws ObjectStreamException {
if (!isTransient()) {
throw new NotSerializableException("persistent objects must not be serialized");
}
return this;
}
我们已将此片段放入我们的所有实体(实际上,实际上是一个名为AbstractPersistentObject的公共基类)并且它的效果非常好。它唯一复杂的是编辑持久化实体或具有持久属性的瞬态实体:不允许您序列化脏/编辑/修改过的对象。