Equinox Jetty:尝试使用JDBCSessionManager时未找到类

Equinox Jetty:尝试使用JDBCSessionManager时未找到类,jetty,osgi,embedded-jetty,equinox,Jetty,Osgi,Embedded Jetty,Equinox,在尝试使用JDBCSessionManager和JDBCSessiondManager时,我从Jetty(Equininox embedded)收到一个ClassNotFoundException 例外情况: 2017-01-06 10:37:02.620:WARN:oejss.JDBCSessionManager:qtp1215746443-29: Unable to load session 192168178229yf02ln7ut25phh97b49003w java.lang.Clas

在尝试使用JDBCSessionManager和JDBCSessiondManager时,我从Jetty(Equininox embedded)收到一个ClassNotFoundException

例外情况:

2017-01-06 10:37:02.620:WARN:oejss.JDBCSessionManager:qtp1215746443-29: Unable to load session 192168178229yf02ln7ut25phh97b49003w
java.lang.ClassNotFoundException: org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener cannot be found by org.eclipse.jetty.util_9.3.9.v20160517
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:439)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:352)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:344)
    at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:160)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:628)
    at org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass(ClassLoadingObjectInputStream.java:59)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1620)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at java.util.HashMap.readObject(HashMap.java:1404)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1909)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at org.eclipse.jetty.server.session.JDBCSessionManager$1.run(JDBCSessionManager.java:970)
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1262)
    at org.eclipse.jetty.server.session.JDBCSessionManager.loadSession(JDBCSessionManager.java:992)
    at org.eclipse.jetty.server.session.JDBCSessionManager.getSession(JDBCSessionManager.java:502)
    at org.eclipse.jetty.server.session.JDBCSessionManager.getSession(JDBCSessionManager.java:75)
    at org.eclipse.jetty.server.session.AbstractSessionManager.getHttpSession(AbstractSessionManager.java:331)
    at org.eclipse.jetty.server.session.SessionHandler.checkRequestedSessionId(SessionHandler.java:275)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:151)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:524)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:319)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)
我正在使用JettyCustomizer连接到Jetty启动中,以使用JDBCSessionManager更改默认的HashSessionManager。JettyCustomizer位于属于的片段束中

Fragment-Host: org.eclipse.equinox.http.jetty
这个主意是我从

这个设置工作正常,JDBCSessionManager在数据库中放置一个会话。会话被序列化为字节BLOB并存储在数据库中。我可以在那里看到它

但序列化似乎是由org.equinox.http完成的,它将类引用如
org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener
放入BLOB中

请注意,
internal.servlet.HttpSessionAdaptor
是一个内部类,不会导出到其他bundle

现在,当再次从数据库读取会话信息时(例如,当我稍后再次使用相同的会话访问网页时),我在
org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass(ClassLoadingObjectInputStream.java:59)时遇到了这个问题
尝试加载类
HttpSessionAdaptor$ParentSessionListener
,但无法在另一个捆绑包中看到它(因为它是a)内部和/或b)

org.eclipse.jetty.util.ClassLoadingObjectInputStream
存在于bundle
org.eclipse.jetty.util
中,但
org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener
存在于bundle
org.eclipse.equinox.http.servlet

org.eclipse.jetty.util.ClassLoadingObjectInputStream似乎执行以下操作:

@Override
    public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
    {
        try
        {
            return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
        }
        catch (ClassNotFoundException e)
        {
            return super.resolveClass(cl);
        }
    }
@覆盖
公共类resolveClass(java.io.ObjectStreamClass cl)抛出IOException、ClassNotFoundException
{
尝试
{
返回类.forName(cl.getName(),false,Thread.currentThread().getContextClassLoader());
}
catch(classnotfounde异常)
{
返回super.resolveClass(cl);
}
}
有来自OSGI专家的人有想法吗

我将描述这个问题,因为会话字节BLOB包含对内部类的类引用,
org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass


这看起来像虫子吗?或者使用碎片包的方法是错误的方法?(在我看来,这是我找到的唯一交换SessionManager的方法)

问题可能是因为
ClassLoadingObjectInputStream
使用TCCL进行类解析,在Equinox中,默认情况下是
org.eclipse.osgi.internal.framework.ContextFinder
。它正在调用堆栈上查找第一个包。这可能是Jetty捆绑包,它没有看到任何Equinox类

就EquinoxHTTP服务而言,片段方法是连接Jetty的正确方法。如果我读对了代码路径,您可以尝试以下方法

(1)在
ContextHandler

在JettyCustomizer.customizeContext中,您应该检查上下文。它应该是一个
ServletContextHandler
。使用它的
setClassLoader
方法为它提供一个类加载器,该加载器知道Equinox类(org.eclipse.Equinox.http.jetty的任何片段都应该知道)和您自己自定义代码的任何其他类

(2)Fork/patch
JDBCSessionManager

如果方法1不起作用,那么您可能需要创建自己的
JDBCSessionManager分支。由于可见性问题(某些方法是私有的),扩展可能无法工作。您需要重写/修补/重新实现
JDBCSessionManager.loadSession
方法,以使用正确的类加载器进行加载。在最初的实现中,您可以看到为什么方法1会起作用(理论上)。不过,实现的代码可以简单得多


如果您的片段还导入了代码包,那么简单地使用片段类加载器。否则,您可以创建一个自定义包,将其委托给正确的捆绑包进行解析。

这是否相关?“HttpSessionAdapter属性不返回字符串,但返回对象名称字符串”选项(1)的第一次尝试似乎让我更进一步。我使用了((ServletContextHandler)context).setClassLoader(org.eclipse.equinox.http.servlet.HttpServiceServlet.class.getClassLoader()),但现在我得到了一个新的ClassNotFoundException,用于我们自己存储在会话中的一个类。自己的类是可序列化的,但是在我们的包中定义了,因此我想不会再出现了。同样的错误。2017-01-06 14:32:34.164:警告:JDBCSessionManager:qtp1335669291-42:无法加载会话。。。java.lang.ClassNotFoundException:com.synesty.servlet.AuthHolder在org.eclipse.jetty.util中找不到…我使用了自己的自定义类加载器,该加载器委托给其他两个不同的类加载器,以便从Equinox以及我们自己的代码中加载类。但是现在我在Equinox中遇到了另一个Bug,当我最终找到一个有效的解决方案后,我会在这里发布它。很高兴看到类装入器方法能够工作。是的,类加载器需要“查看”将内容放入会话的所有捆绑包。我遇到了一个类似的问题,这是由于类路径上存在多个Jetty版本造成的。一个来自org.eclipse.help feature.xml,另一个来自嵌入式Spring jetty。如果它不能决定使用哪个类,它也会以ClassNotFoundException响应。这个问题可以通过明确指定要在我们自己的feature.xml中使用的版本来解决。