Equinox Jetty:尝试使用 JDBC 会话管理器时找不到类
Equinox Jetty: ClassNotFound when trying use JDBCSessionManager
我在尝试使用 JDBCSessionManager 和 JDBCSessionIdManager 时收到来自 Jetty(Equninox 嵌入式)的 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.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.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.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 在数据库中放置了一个会话。会话被序列化为 Byte-BLOB 并存储在数据库中。我可以在那里看到它。
但序列化似乎是由 org.equinox.http 完成的,它会将 class 引用(如 org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener
)放入 BLOB。
请注意,internal.servlet.HttpSessionAdaptor
是内部 class,不会导出到其他包。
现在,当再次从数据库中读取会话信息时(例如,当我稍后使用相同的 sessionCookie 再次访问网页时)我 运行 在 org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass(ClassLoadingObjectInputStream.java:59)
尝试加载 classHttpSessionAdaptor$ParentSessionListener
但看不到它(因为它是 a) 内部和/或 b) 在另一个包中。
org.eclipse.jetty.util.ClassLoadingObjectInputStream
生活在 org.eclipse.jetty.util
束中,但 org.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 专家有什么想法吗?
我会将问题描述为会话字节 BLOB 包含 Class 对 内部 classes 的引用 org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass
这看起来像个错误吗?或者 FragmentBundle 的方法是错误的方法? (IMO 这是我发现交换 SessionManager 的唯一方法)
问题可能是因为 ClassLoadingObjectInputStream
正在使用 TCCL 进行 class 解析,在 Equinox 中默认为 org.eclipse.osgi.internal.framework.ContextFinder
。它正在调用堆栈上找到第一个包。这可能是 Jetty 包,它 未见 任何 Equinox classes.
就 Equinox HTTP 服务而言,片段方法是连接到 Jetty 的正确方法。如果我没看错代码路径,您可以尝试以下操作。
(1) 在 ContextHandler
上设置 class 加载程序
在您的 JettyCustomizer.customizeContext
中,您应该检查上下文。它应该是 ServletContextHandler
。使用它的 setClassLoader
方法给它一个 class 加载器,它知道 Equinox classes(无论如何 org.eclipse.equinox.http.jetty
的任何片段都应该知道)和任何其他 classes 你自己的自定义代码。
(2) Fork/patch JDBCSessionManager
如果方法 1 不起作用,那么您可能需要创建自己的 JDBCSessionManager
分支。由于可见性问题,扩展可能不起作用(某些方法是私有的)。您需要 override/patch/reimplement JDBCSessionManager.loadSession
方法来使用正确的 class 加载程序进行加载。在最初的实现中,您可以看到为什么方法 1 应该有效(理论上)。不过,您的实现代码可以简单得多。
如果您的片段还导入了您的代码包,那么只需使用您的片段 class 加载器。否则,您可以创建一个自定义的委托给正确的包进行解析。
我在尝试使用 JDBCSessionManager 和 JDBCSessionIdManager 时收到来自 Jetty(Equninox 嵌入式)的 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.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.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.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 在数据库中放置了一个会话。会话被序列化为 Byte-BLOB 并存储在数据库中。我可以在那里看到它。
但序列化似乎是由 org.equinox.http 完成的,它会将 class 引用(如 org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor$ParentSessionListener
)放入 BLOB。
请注意,internal.servlet.HttpSessionAdaptor
是内部 class,不会导出到其他包。
现在,当再次从数据库中读取会话信息时(例如,当我稍后使用相同的 sessionCookie 再次访问网页时)我 运行 在 org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass(ClassLoadingObjectInputStream.java:59)
尝试加载 classHttpSessionAdaptor$ParentSessionListener
但看不到它(因为它是 a) 内部和/或 b) 在另一个包中。
org.eclipse.jetty.util.ClassLoadingObjectInputStream
生活在 org.eclipse.jetty.util
束中,但 org.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 专家有什么想法吗?
我会将问题描述为会话字节 BLOB 包含 Class 对 内部 classes 的引用 org.eclipse.jetty.util.ClassLoadingObjectInputStream.resolveClass
这看起来像个错误吗?或者 FragmentBundle 的方法是错误的方法? (IMO 这是我发现交换 SessionManager 的唯一方法)
问题可能是因为 ClassLoadingObjectInputStream
正在使用 TCCL 进行 class 解析,在 Equinox 中默认为 org.eclipse.osgi.internal.framework.ContextFinder
。它正在调用堆栈上找到第一个包。这可能是 Jetty 包,它 未见 任何 Equinox classes.
就 Equinox HTTP 服务而言,片段方法是连接到 Jetty 的正确方法。如果我没看错代码路径,您可以尝试以下操作。
(1) 在 ContextHandler
在您的 JettyCustomizer.customizeContext
中,您应该检查上下文。它应该是 ServletContextHandler
。使用它的 setClassLoader
方法给它一个 class 加载器,它知道 Equinox classes(无论如何 org.eclipse.equinox.http.jetty
的任何片段都应该知道)和任何其他 classes 你自己的自定义代码。
(2) Fork/patch JDBCSessionManager
如果方法 1 不起作用,那么您可能需要创建自己的 JDBCSessionManager
分支。由于可见性问题,扩展可能不起作用(某些方法是私有的)。您需要 override/patch/reimplement JDBCSessionManager.loadSession
方法来使用正确的 class 加载程序进行加载。在最初的实现中,您可以看到为什么方法 1 应该有效(理论上)。不过,您的实现代码可以简单得多。
如果您的片段还导入了您的代码包,那么只需使用您的片段 class 加载器。否则,您可以创建一个自定义的委托给正确的包进行解析。