MPS plugin classpath issues
Hi everybody,
I have written a language that generates a small java program from a concept. This program uses Apache Jena to perform a SPARQL query on a Web Ontology Language (OWL) file. So far so good. When I generate the Java source everything is fine and when I run the generated source my query is executed and the result returned.
So far so good.
Now I tried to embed this functionality into a MPS plugin since I would like to have a window that can browse a OWL file and display information from it in MPS.
I have created a plugin solution for this with the following files:
What I want to achive:
The SPARQL query should be performed when I click on the button and the result should get displayed in the label of my Panel.
What actually happens:
The plugin is added to MPS at runtime and I can open my custom window with my label and button. But when I hit the button to perform the SPARQL query I get the following exception:
DVFactoryException DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.: DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.
Here is the Stacktrace:
My current guess is that the apache xerces lib provided with MPS is loaded in the classpath before the xerces version provided by apache jena is loaded. Therefore a wrong xerces class is used when executing the SPARQL query.
Does somebody have a hint how this can be solved?
I have written a language that generates a small java program from a concept. This program uses Apache Jena to perform a SPARQL query on a Web Ontology Language (OWL) file. So far so good. When I generate the Java source everything is fine and when I run the generated source my query is executed and the result returned.
So far so good.
Now I tried to embed this functionality into a MPS plugin since I would like to have a window that can browse a OWL file and display information from it in MPS.
I have created a plugin solution for this with the following files:
- standalone plugin descriptor
- baselanguage class subclassing JPanel (it's just a JPanel containing a label and a button)
- action file to open my tool
- group file that adds my action to the IDEAMainMenu bar
- tool file for instancing my JPanel subclass in MPS
What I want to achive:
The SPARQL query should be performed when I click on the button and the result should get displayed in the label of my Panel.
What actually happens:
The plugin is added to MPS at runtime and I can open my custom window with my label and button. But when I hit the button to perform the SPARQL query I get the following exception:
DVFactoryException DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.: DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.
Here is the Stacktrace:
org.apache.xerces.impl.dv.DVFactoryException: DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.…
org.apache.xerces.impl.dv.DVFactoryException: DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory.
at org.apache.xerces.impl.dv.DTDDVFactory.getInstance(Unknown Source)
at org.apache.xerces.impl.dv.DTDDVFactory.getInstance(Unknown Source)
at org.apache.xerces.parsers.DTDConfiguration.createDatatypeValidatorFactory(Unknown Source)
at org.apache.xerces.parsers.DTDConfiguration.<init>(Unknown Source)
at org.apache.xerces.parsers.StandardParserConfiguration.<init>(Unknown Source)
at org.apache.xerces.parsers.StandardParserConfiguration.<init>(Unknown Source)
at com.hp.hpl.jena.rdfxml.xmlinput.impl.RDFXMLParser.create(RDFXMLParser.java:126)
at com.hp.hpl.jena.rdfxml.xmlinput.ARP.<init>(ARP.java:77)
at org.apache.jena.riot.lang.LangRDFXML.<init>(LangRDFXML.java:51)
at org.apache.jena.riot.lang.LangRDFXML.create(LangRDFXML.java:72)
at org.apache.jena.riot.RiotReader.createParser(RiotReader.java:134)
at org.apache.jena.riot.RDFParserRegistry$ReaderRIOTLang.read(RDFParserRegistry.java:177)
at org.apache.jena.riot.RDFDataMgr.process(RDFDataMgr.java:906)
at org.apache.jena.riot.RDFDataMgr.parse(RDFDataMgr.java:687)
at org.apache.jena.riot.RDFDataMgr.read(RDFDataMgr.java:210)
at org.apache.jena.riot.RDFDataMgr.read(RDFDataMgr.java:143)
at org.apache.jena.riot.RDFDataMgr.read(RDFDataMgr.java:132)
at org.apache.jena.riot.adapters.AdapterFileManager.readModelWorker(AdapterFileManager.java:283)
at com.hp.hpl.jena.util.FileManager.readModel(FileManager.java:344)
at com.hp.hpl.jena.util.FileManager.readModel(FileManager.java:328)
at PluginSolutionSPARQL.plugin.BaasSparqlBrowser_Tool.queryDataPointOWL(BaasSparqlBrowser_Tool.java:62)
at PluginSolutionSPARQL.plugin.BaasSparqlBrowser_Tool$1.actionPerformed(BaasSparqlBrowser_Tool.java:42)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6516)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3311)
at java.awt.Component.processEvent(Component.java:6281)
at java.awt.Container.processEvent(Container.java:2229)
at java.awt.Component.dispatchEventImpl(Component.java:4872)
at java.awt.Container.dispatchEventImpl(Container.java:2287)
at java.awt.Component.dispatchEvent(Component.java:4698)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
at java.awt.Container.dispatchEventImpl(Container.java:2273)
at java.awt.Window.dispatchEventImpl(Window.java:2719)
at java.awt.Component.dispatchEvent(Component.java:4698)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue$4.run(EventQueue.java:708)
at java.awt.EventQueue$4.run(EventQueue.java:706)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:697)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:520)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:335)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
My current guess is that the apache xerces lib provided with MPS is loaded in the classpath before the xerces version provided by apache jena is loaded. Therefore a wrong xerces class is used when executing the SPARQL query.
Does somebody have a hint how this can be solved?
Please sign in to leave a comment.
The same error also occurs when I build the plugin with a build solution and install it into a new/fresh/clean MPS instance.
I read about the IntelliJ plugin behavior that each plugin has it's own classpath. I assume it is the same with MPS plugins? I just cannot figure out how to fix this problem.
You are right. I think that your issue is that the same class is loaded by two different plugins class loader.
According to IDEA documentation:
"By default, the main IDEA class loader loads classes that were not found in the plugin class loader. However, in the plugin.xml file, one can use the <depends> element to specify that a plugin depends on one or more other plugins. In this case, the class loaders of those plugins will be used for classes not found in the current plugin. This allows a plugin to reference classes from other plugins."
I suppose you can use that <depends> in your plugin.xml so that your plugin can use class loader from other plugins.
the past days I have tried to dig deeper into the process of building a plugin with external jars.
I have looked into the sources of mbedder and tried to figure out how they build their plugins. One thing I noticed is that they unjar the dependent jars and then package these class files into the plugin zip. But I have not yet figured out how to achieve this with the build-language.
I'm feeling a little bit lost right now...
I have used MPS 3.1.5 to create the project.
The project can be downloaded here: https://www.dropbox.com/s/xfi4p5aznjozubd/ExternalLibProblem.tar.gz?dl=0
To reproduce my problem try the following:
I will look into that issue on this week,
https://youtrack.jetbrains.com/issue/MPS-21758.
Right now I do not understand how it is possible that packing to MPS plugin did not help.
Have you also tried hacking it with putting xerces jars from Jena to the MPS lib folder instead of the MPS xerces.jar? Surely it must work
I have managed to investigate this issue.
MPS uses separate class loaders (as well as the Idea platform) for different plugins. But the thing is that the xerces method ObjectFactory#findProviderClass
uses the context classloader to find the DTDDVFactoryImpl class. Context classloader is set by idea platform (which MPS is based on) to the URLClassLoader which loads the /lib directory (MPS bundled xerces.jar goes in that folder).
MPS [plugin] ModuleClassLoader behaves like this: it looks through its own 'urls', after that he addresses the parent classloader (a bit unusual behaviour).
Here you have your jena xerces libs loaded by the MPS ModuleClassLoader.
Note that in your case the context URLClassLoader is the parent of the MPS ModuleClassLoader.
Idea PluginClassLoader exploits a similar algorithm of seeking classes to the MPS ModuleClassLoader.
Xerces DTDDVFactory class gets loaded by the MPS ModuleClassLoader, after that xerces has such a code:
It uses the context class loader to load the implementation class by default.
If the class is not found with the context class loader (!), then it has a fallback solution to load from the current (!) class loader (which is exactly the MPS ModuleClassLoader with jena xerces libs in its urls).
So in your project you finally get DTDDVFactory class loaded by MPS ModuleClassLoader, and after that you get the implementation class DTDDVFactoryImpl loaded by the context class loader. That leads to ClassCastException.
Here I see three solutions:
1. You can try to reset the context class loader in your code so that both Factory interface and implementation classes are loaded by the same ModuleClassLoader (that is what you want). After initialization you can reset the context classloader as it was.
2. You can move newer xerces jars from jena lib folder to the MPS lib folder and remove the older xerces.jar from there.
3. You can set java.endorsed.dirs property to your newer xerces jars and remove the jena xerces libs from the library paths in the MPS solution properties dialog (thus removing them from the MPS ModuleClassLoader scope).
In the second and third cases ModuleClassLoader always delegates the loading to its ancestor – the context classloader
As far as I know this particular library gives a lot of pain to developers and this issue is a good example.
I was able to get past this class initialization, but afterwards I get a NPE from your plugin:
[ 76140] ERROR - llij.ide.plugins.PluginManager - null
[ 76145] ERROR - llij.ide.plugins.PluginManager - JDK: 1.8.0_40
[ 76145] ERROR - llij.ide.plugins.PluginManager - VM: Java HotSpot(TM) 64-Bit Server VM
[ 76145] ERROR - llij.ide.plugins.PluginManager - Vendor: Oracle Corporation
[ 76145] ERROR - llij.ide.plugins.PluginManager - OS: Mac OS X
[ 76145] ERROR - llij.ide.plugins.PluginManager - Last Action: SPARQL_Test.plugin.SPARQLAction_Action
Do you assume it is an MPS problem?
Also a simple removing of xerces.jar from the MPS lib folder does work for me (leads to the same NPE).
I did not dig deeply into the latter NPE, but it seems to be unrelated to MPS.
Alex
For the sake of completeness I would like to post the code snippet that works for me:
Model model = ModelFactory.createDefaultModel().newInstance(); // Apache Jena specific class ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); FileManager.get().readModel(model, filepath); // this caused the class cast exception } catch (Exception e) { e.printStackTrace(); } finally { Thread.currentThread().setContextClassLoader(cl); }