JAXB class 未找到 ForkJoinPool Java
JAXB class not found with ForkJoinPool Java
我使用 jaxb 进行解组,但是当我使用 ForkJoinPool execute() 方法时,我得到一个“java.log.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory”,但我确定在我的运行时类路径中的存在,因为当我不使用 ForkJoinPool 时它可以正常工作......你知道问题或解决方法吗?
我用Java11
我的代码:
ForkJoinPool commonPool = ForkJoinPool.commonPool();
commonPool.execute(() -> {
try {
String messageFileContent = Files.readString(Paths.get(
this.getClass().getClassLoader().getResource("xml-to-process.xml").getPath()));
JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
// avoiding schema validation for more performance
jaxbUnmarshaller.setSchema(null);
UpdateWorkOrder updateWorkOrder = (UpdateWorkOrder) jaxbUnmarshaller.unmarshal(new StringReader(messageFileContent));
} catch (Exception e) {
e.printStackTrace();
}
});
这很奇怪不...?在 ForkJoinPool 的 execute() 之外,正确执行了解析。
错误是:
javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:92)
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:125)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:230)
如果 ForkJoinPool 使用与您的应用程序不同的 ClassLoader,就会发生这种情况,尤其是当您使用 Tomcat.
这样的 Web 容器时
您可以尝试为您的 ForkJoinPool 设置线程工厂,并在该工厂内,将创建的线程的上下文类加载器设置为正确的应用程序类加载器。
虽然 NightShade 的回答完整地描述了问题,但让我添加一些细节。
Servlet API 世界和并发世界非常不兼容:前者假定所有计算都在同一个线程上完成(ServletRequest#startAsync
是相对较新的补充)。因此使用 Servlet API 的应用程序和库经常将对象附加到当前 Thread
,例如:
- 执行请求的线程将应用程序的类加载器附加为 "context classloader",
- Spring 将
RequestContextHolder
附加到当前线程(如 ThreadLocal
),
- Vaadin 通过
CurrentInstance
、 附加许多对象
- ...
这意味着每当你想在另一个线程上执行某些东西时,你需要将所有这些对象复制到目标线程上。一个简单的方法是为您的 Runnable
(和 Callable<T>
)编写一个包装器,它在开始执行之前设置环境:
public class WrappedRunnable implements Runnable {
private final ClassLoader ccl;
... // other objects
private final Runnable runnable;
public static Runnable wrap(final Runnable runnable) {
if (runnable instanceof WrappedRunnable) {
return runnable;
}
return new WrappedRunnable(runnable);
}
public WrappedRunnable(final Runnable runnable) {
this.ccl = Thread.currentThread().getContextClassLoader();
... // other objects
this.runnable = runnable;
}
@Override
public void run() {
final ClassLoader oldCcl = Thread.currentThread().getContextClassLoader();
... // save the value of other objects
try {
Thread.currentThread().setContextClassLoader(ccl);
... // set the value of other objects
runnable.run();
} finally {
Thread.currentThread().setContextClassLoader(oldCcl);
// restore the value of other objects
}
}
}
您也可以编写自己的 ExecutorService
(you are actually using the ForkJoinPool
as ExecutorService
) or ForkJoinPool
,它会自动换行 Runnable
。
我使用 jaxb 进行解组,但是当我使用 ForkJoinPool execute() 方法时,我得到一个“java.log.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory”,但我确定在我的运行时类路径中的存在,因为当我不使用 ForkJoinPool 时它可以正常工作......你知道问题或解决方法吗?
我用Java11
我的代码:
ForkJoinPool commonPool = ForkJoinPool.commonPool();
commonPool.execute(() -> {
try {
String messageFileContent = Files.readString(Paths.get(
this.getClass().getClassLoader().getResource("xml-to-process.xml").getPath()));
JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
// avoiding schema validation for more performance
jaxbUnmarshaller.setSchema(null);
UpdateWorkOrder updateWorkOrder = (UpdateWorkOrder) jaxbUnmarshaller.unmarshal(new StringReader(messageFileContent));
} catch (Exception e) {
e.printStackTrace();
}
});
这很奇怪不...?在 ForkJoinPool 的 execute() 之外,正确执行了解析。
错误是:
javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinTask.doExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.scan(Unknown Source)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(Unknown Source)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:92)
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:125)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:230)
如果 ForkJoinPool 使用与您的应用程序不同的 ClassLoader,就会发生这种情况,尤其是当您使用 Tomcat.
这样的 Web 容器时您可以尝试为您的 ForkJoinPool 设置线程工厂,并在该工厂内,将创建的线程的上下文类加载器设置为正确的应用程序类加载器。
虽然 NightShade 的回答完整地描述了问题,但让我添加一些细节。
Servlet API 世界和并发世界非常不兼容:前者假定所有计算都在同一个线程上完成(ServletRequest#startAsync
是相对较新的补充)。因此使用 Servlet API 的应用程序和库经常将对象附加到当前 Thread
,例如:
- 执行请求的线程将应用程序的类加载器附加为 "context classloader",
- Spring 将
RequestContextHolder
附加到当前线程(如ThreadLocal
), - Vaadin 通过
CurrentInstance
、 附加许多对象
- ...
这意味着每当你想在另一个线程上执行某些东西时,你需要将所有这些对象复制到目标线程上。一个简单的方法是为您的 Runnable
(和 Callable<T>
)编写一个包装器,它在开始执行之前设置环境:
public class WrappedRunnable implements Runnable {
private final ClassLoader ccl;
... // other objects
private final Runnable runnable;
public static Runnable wrap(final Runnable runnable) {
if (runnable instanceof WrappedRunnable) {
return runnable;
}
return new WrappedRunnable(runnable);
}
public WrappedRunnable(final Runnable runnable) {
this.ccl = Thread.currentThread().getContextClassLoader();
... // other objects
this.runnable = runnable;
}
@Override
public void run() {
final ClassLoader oldCcl = Thread.currentThread().getContextClassLoader();
... // save the value of other objects
try {
Thread.currentThread().setContextClassLoader(ccl);
... // set the value of other objects
runnable.run();
} finally {
Thread.currentThread().setContextClassLoader(oldCcl);
// restore the value of other objects
}
}
}
您也可以编写自己的 ExecutorService
(you are actually using the ForkJoinPool
as ExecutorService
) or ForkJoinPool
,它会自动换行 Runnable
。