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:
  • 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?
8 comments
Comment actions Permalink
Update: I can now confirm that this is an issue between the xerces provided by MPS and the one provided by Apache Jena. When I remove the xercex.jar from the MPS lib folder the SPARQL query gets executed but I get a NullPointer later on when using the "arratlist" item of the collection baselanguage.
0
Comment actions Permalink
2. Update:
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.
0
Comment actions Permalink
Hello,

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.
0
Comment actions Permalink
Hi Joao,

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...
0
Comment actions Permalink
I have now created a MPS demo project that displays my problem when Apache xerces. The only thing that must be adapted to test the project is the Filepath in the SPARQLQueryWindow Tool file. I just provide this path as a hard coded string for the sake of simplicity.

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:
  1. Open the project in MPS
  2. Change the filepath in the SPARQLQueryWindow Tool (marked with TODO). This path must point to the MPS_Demo.owl file that is included in the project archive.
  3. Rebuild the whole project
  4. In the MainMenu right next to the Help menu a "SPARQL Group" Menu entry should appear
  5. Open the the menu and select "Perform a SPARQL Query"
  6. A new panel is opened in MPS showing a label and a button. Press the button to execute the SPARQL Query and enjoy the DVFactroyException
0
Comment actions Permalink
I am sorry for such a long delay,
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
0
Comment actions Permalink
Hi,

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
java.lang.NullPointerException
java.lang.NullPointerException
 at SPARQL_Test.plugin.SPARQLQueryWindow_Tool.getStringFromOWLFile(SPARQLQueryWindow_Tool.java:84)
 at SPARQL_Test.plugin.SPARQLQueryWindow_Tool$1.actionPerformed(SPARQLQueryWindow_Tool.java:43)
 at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
 at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
 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:6525)
 at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
 at java.awt.Component.processEvent(Component.java:6290)
 at java.awt.Container.processEvent(Container.java:2234)
 at java.awt.Component.dispatchEventImpl(Component.java:4881)
 at java.awt.Container.dispatchEventImpl(Container.java:2292)
 at java.awt.Component.dispatchEvent(Component.java:4703)
 at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
 at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
 at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
 at java.awt.Container.dispatchEventImpl(Container.java:2278)
 at java.awt.Window.dispatchEventImpl(Window.java:2750)
 at java.awt.Component.dispatchEvent(Component.java:4703)
 at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:751)
 at java.awt.EventQueue.access$500(EventQueue.java:97)
 at java.awt.EventQueue$3.run(EventQueue.java:702)
 at java.awt.EventQueue$3.run(EventQueue.java:696)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
 at java.awt.EventQueue$4.run(EventQueue.java:724)
 at java.awt.EventQueue$4.run(EventQueue.java:722)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 at java.awt.EventQueue.dispatchEvent(EventQueue.java:721)
 at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:748)
 at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:573)
 at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:384)
 at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
 at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
 at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
 at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
[  76145]  ERROR - llij.ide.plugins.PluginManager - JetBrains MPS (MPS) IC-999.SNAPSHOT  Build #IC-999.SNAPSHOT
[  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
0
Comment actions Permalink
Hi Alex, thank you for your help! Your first solution suggestion works perfectly fine for me.
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); 
}
prettyPrint();
0

Please sign in to leave a comment.