Equinox Jetty:尝试使用JDBCSessionManager时的ClassNotFound

时间:2017-01-06 10:15:00

标签: jetty osgi embedded-jetty equinox

我在尝试使用JDBCSessionManager和JDBCSessionIdManager时从Jetty(Equninox 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 Bundle中,属于

Fragment-Host: org.eclipse.equinox.http.jetty

我从https://wiki.eclipse.org/RAP/FAQ#How_can_I_use_Jetty_basic_authentication_in_my_application.3F

得到了这个想法

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

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

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

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

org.eclipse.jetty.util.ClassLoadingObjectInputStream位于捆绑org.eclipse.jetty.utilorg.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener生活在捆绑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);
        }
    }

OSGI专家是否有人有想法?

我会描述这个问题,因为Session byte-BLOB包含内部类的类引用,org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass无法看到

这看起来像个bug吗?或者FragmentBundle使用错误方法的方法是什么? (IMO是我发现交换SessionManager的唯一方法)

1 个答案:

答案 0 :(得分:1)

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

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

(1)在ContextHandler

上设置类加载器

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

(2)分叉/补丁JDBCSessionManager

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

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