Jaxb package-info ignored when using Java 10 - jaxb

I'm struggling with this, and any information would be greatly appreciated. I have a project that has been using JAXB for some time to construct a Java Model from an XML Schema and uses that model. This has been working in Java 8 for some tie now.
However, I've upgraded to Open JDK 10 and I get this error when I attempt to Unmarshall an XML file into the Java objects....
java.lang.IllegalArgumentException: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.minestar.cat.com/namespace/units",
local:"units"). Expected elements are <{}units>
at minestar.units.schema.parser.UnitsXmlParser.readXml(UnitsXmlParser.java:31)
at minestar.units.javagenerator.JavaGeneratorPlugin.execute(JavaGeneratorPlugin.java:41)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:154)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:146)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:956)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:290)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:194)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.minestar.cat.com/namespace/units", local:"units"). Expected
elements are <{}units>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:741)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:262)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:257)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:124)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1149)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:574)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:556)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:168)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScanner
Impl.java:613)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScan
nerImpl.java:3058)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:821)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:5
32)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:229)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:170)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:209)
at minestar.units.schema.parser.UnitsXmlParser.readXml(UnitsXmlParser.java:29)
... 23 more
I am using the maven-jaxb2-plugin to generate the sources, and they look fine. I have upgraded to the latest version of this plugin (at the time of this writing, 0.14.0). The classes are generated fine, and there is a package-info.java class generated and compiled into the resulting jar. The class in question looks like this at the top
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"dimensions",
"quantityTypes"
})
#XmlRootElement(name = "units")
public class Units {
#XmlElement(name = "dimension")
protected List<Dimension> dimensions;
#XmlElement(name = "quantityType")
protected List<QuantityType> quantityTypes;
And the package-info.java looks like this
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.minestar.cat.com/namespace/units", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package minestar.units.schema;
Because this is Java 10, I have added direct dependency entries to javax.xml.bind:jaxb-api:2.3.0, com.sun.xml.bind:jaxb-core:2.3.0 and com.sun.xml.bind:jaxb-impl:2.3.0.
I have tried changing JAXB implementation from the RI to Eclipse MOXy, but that made no difference.
As a test, I edited the generated Units.java class file above, adding the namespace attribute to the #XmlRootElement annotation...
#XmlRootElement(namespace = "http://www.minestar.cat.com/namespace/units", name = "units")
public class Units {
When this java file is compiled and used downstream, the XML file can be parsed. I do not receive the UnmarshalException. However, this source file is generated so I cannot rely on these changes staying put. Additionally, from everything that I have read from much more-informed people than myself, the package-info.class file (which is in the JAR file) should make the namespace value in the annotation unnecessary.
If there is something I have not set up correctly, I would be grateful for any assistance in getting this working in Java 10.
Thanks for any help,
Ed

I have a similar problem with namespaces, unmarshalling with JAXB in a home-made plugin.
I am using Java 11.
I resolved my problem upgrading maven to 3.6.2 (the problem occured with maven 3.5.2). Not sure to be able to explain why it works, but if it helps someone...

I'm actually currently facing the same problem on Java 9.
Adding com.sun.xml.bind.backupWithParentNamespace system property makes it work, but I feel this is working around the problem.
I looked at the Java 9 source(have not looked at the Java 10 source yet), and it's inside the java.lang.Package.getPackageInfo() method that it tries to load the package-info class using:
...
String cn = packageName() + ".package-info";
Module module = module();
...
c = loader.loadClass(module, cn);
...
If I substitute it with
...
c = loader.loadClass(cn);
...
it manages to load it fine and all is well.
To me it looks like a JDK bug.
The latest maven-jaxb2-plugin does have the following option to turn off package-level annotations:
<packageLevelAnnotations>false</packageLevelAnnotations>
This resolved my problem.
Other XJC plugins seems to use "-npa" argument to turn off package-level annotations

It's a Maven bug, Mojos are unable to load package annotations on Java >= 9, fixed in 3.6.2

I have found the following solution for the same issue in my project with JDK 9:
JAXBContext ctx = JAXBContext.newInstance(YOUR_CLASS.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false); // This line is important!
XMLReader reader = sax.newSAXParser().getXMLReader();
Source source = new SAXSource(reader, new InputSource(new StringReader(xml)));
return (YOUR_CLASS) unmarshaller.unmarshal(source);

Related

Using JDK's JAXB without ns2 prefix

After having read all posts about this on Oracle forums, Stackoverflow, java.net I'm finally posting here.
I'm using JAXB to create XML files but the problem is that it adds the famous ns2 prefix before my elements, I have tried all the solutions no one worked for me.
java -version gives "1.6.0_37"
Solution 1 : Using package-info.java
I created the file in my package containing my #Xml* annotated classes with the following content :
#XmlSchema(
namespace = "http://mynamespace",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(namespaceURI = "http://mynamespace", prefix = "")
}
)
package com.mypackage;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Solution 2 : NamespacePrefixMapper
I created the following class and set the mapper to the marshaller :
// Change mapper to avoid ns2 prefix on generated XML
class PreferredMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "";
}
}
NamespacePrefixMapper mapper = new PreferredMapper();
try {
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper);
}
catch (PropertyException e) {
logger.info("No property for com.sun.xml.bind.namespacePrefixMapper found : " + e.getMessage());
}
With com.sun.xml.bind.namespacePrefixMapper nothing happens, with com.sun.xml.internal.bind.namespacePrefixMapper, it throws the exception.
I've also addded the maven dependency in my pom, but it seems that JRE version has a higher priority :
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.4</version>
</dependency>
Can you help me on this ?
PS : I can't include directly the jar in my classpath for build reasons.
PS2 : I can't use JDK7.
Thanks in advance.
Without the implementation of MOXy is not possible. JAXB if the preferred prefix is "", it generates a new one.
I had the same problem in the past, and I configured each prefix for each package-info.java.
NamespacePrefixMapper says in JAVADOC
null if there's no prefered prefix for the namespace URI.
In this case, the system will generate a prefix for you.
Otherwise the system will try to use the returned prefix,
but generally there's no guarantee if the prefix will be
actually used or not.
return "" to map this namespace URI to the default namespace.
Again, there's no guarantee that this preference will be
honored.
If this method returns "" when requirePrefix=true, the return
value will be ignored and the system will generate one"
else if use package-info
we know we can't bind to "", but we don't have any possible name at hand.
generate it here to avoid this namespace to be bound to "".
I hope I've given you all the answers about your question.
I had the same problem today. The production machine has Java 6 and when I deployed my application, I was getting the ns2 prefix. This is how I resolved it. Production server has only Java 1.6 patch 21
I made sure that I have a package-info.java file in my package where all the classes were generated using Jaxb. I checked it and the #XmlSchema was all auto generated, so I didn't mess with any of that. Don't use namespacemapper, that just confused me.
In my pom.xml file, I added jaxb-impl dependency:
com.sun.xml.bind
jaxb-impl
2.2.5-b04
and specified source and target as 1.6. Did a maven clean install and package and deployed into production, everything looks good.
Next step is to make sure that production machine is upgraded to Java 7. Hope this helps :)

Jaxb Xjc: make generated classes subtype of Exception?

How is it possible to make some xjc generated classes subclasses of a custom Exception, such that you can actually throw them, and processable by the JAXBContext? Often webservices return various faults defined that really should be an exception, but since they aren't you need to wrap them unneccesarily.
Even if you could create a JAXB (JSR-222) model that extended from Exception you wouldn't be able to create a JAXBContext from it. I would recommend wrapping the Exception in a domain model that is compatible with JAXB.
Java Model (Foo)
Below is a simple Java class that extends Exception.
package forum12840627;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Foo extends Exception {
}
Demo
The demo code below attempts to creates a JAXBContext on the Java model.
package forum12840627;
import javax.xml.bind.JAXBContext;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
}
}
Output
Below is the exception returned from running the demo code. The problem is that Exception is not a valid JAXB class and JAXB implementations pull in the super classes as it processes the Java model. (Note: In your own domain model you can annotate super classes with #XmlTransient to prevent them from being processed: http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.lang.StackTraceElement does not have a no-arg default constructor.
this problem is related to the following location:
at java.lang.StackTraceElement
at public java.lang.StackTraceElement[] java.lang.Throwable.getStackTrace()
at java.lang.Throwable
at java.lang.Exception
at forum12840627.Foo
at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:102)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:472)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:302)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:363)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
at forum12840627.Demo.main(Demo.java:8)
UPDATE #1
If you are using EclipseLink JAXB (MOXy) as your JAXB provider then you will not see this exception as classes in the javax.* and java.* packages are not treated as domain classes. MOXy is the default JAXB provider in the WebLogic 12c environment or can be configured using a jaxb.properties file.
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/12/eclipselink-moxy-is-jaxb-provider-in.html
UPDATE #2
The latest versions of the JAXB reference implementation appear to handle this use case now as well as MOXy. My original portability concerns may not be so much of an issue.
Yeah, I finally found something! The Inheritance plugin is able to make the generated classes inherit from classes or implement additional interfaces.
You need to include something like
<bindings node="//xsd:complexType[#name='WhateverException']">
<inheritance:extends>foo.bar.WhateverException</inheritance:extends>
</bindings>
into the binding file and override getStackTrace() to return null such that it doesn't get marshalled.
Unfortunately you might run into trouble with some JAXB implementations (see Blaise Doughan's answer) - I haven't found a workaround for that yet. So you can either use a not quite nonportable solution, or wrap the JAXB objects into Exceptions.

XSD SchemaFactory error - Java 6

In the code below, I am trying to validate a XML. Everything works find when i run it from eclipse. When i deploy it on weblogic the code fails with error: "SAX Exception while processingsrc-resolve: Cannot resolve the name XX:sometag to a(n) 'type definition' component."
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
System.out.println("schemaFactory -> " + schemaFactory.getClass());
Schema schema = schemaFactory.newSchema(resourceHandler.getXSDStreams());
StreamSource xmlContent = new StreamSource(new StringReader(requestXML));
Validator validator = schema.newValidator();
validator.validate(xmlContent);
This is the output generated on eclipse for Schema Factory:
schemaFactory -> class com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory
On Weblogic the same code with the same JAR files gives the error mentioned above. The schemaFactory is exactly the same.
Config - 2
I tried setting the System property to load xerces files and added xerces to the classpath
String schemaFactoryProperty = "javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI;
System.setProperty(schemaFactoryProperty,"org.apache.xerces.jaxp.validation.XMLSchemaFactory");
Still the output for the schema factory remains the same: "schemaFactory -> class com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory" but this time the error changed to:
java.lang.IllegalArgumentException: Source parameter of type 'javax.xml.transform.stream.StreamSource' is not recognized this SchemaFactory.
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:203)
at com.bt.asi.task.AffectedServiceTask.execute(AffectedServiceTask.java:47)
at com.bt.asi.service.ASIJMSService.onMessage(ASIJMSService.java:30)
at...
I am on Java 6 and WLS 10.3.0 (Java 6) and using Spring framework.
Got it working (after 4 hrs !!!).
On local why it worked: The Schemas were loaded from a folder so the imports within the XSD like used to work.
On Server i had put in a jar file so the sourceLocation="SomeFile.xsd" never used to result in a file as the actual path was a URI.
What fixed it:
1) I changed the sequence in which i load the schemas (from no imports --> all imports) to make sure when the current xsd compiles all dependencies for it are already in the schemaFactory (It was possible as i did not have nexted imports so i had a clear order of what file to load first and so on)
2) What i could have done as well was - combine all XSDs to one file.
3) OR I could have kept the XSD in web-inf/ and then looked them up instead of a JAR file. Then the import sourceLocation="" would have found that file.
Hope this helps someone !!!
Cheers.
Vivek

No suitable classloader found for grab

I have this at the beginning of a class:
#Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2')
class MyClass{...
I'm trying to unit test this class, but whenever I try to run JUnit 4 tests, I get this error:
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:163)
at groovy.grape.GrapeIvy$chooseClassLoader.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:227)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSite.invoke(PogoMetaMethodSite.java:225)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:153)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:216)
at groovy.grape.Grape.grab(Grape.java:131)
at groovy.grape.Grape$grab.callStatic(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:173)
at ammoscanner.AmmoScanner.<clinit>(AmmoScanner.groovy)
... 30 more
Any ideas? I'm using groovy 1.7.5
The Problem
Looking at the source code, this exception is thrown whenever the supplied ClassLoader's name (or it's superclasses) is not groovy.lang.GroovyClassLoader or org.codehaus.groovy.tools.RootLoader. i.e. The target classloader must be an instance of the aforementioned classes (a bit restrictive IMHO).
A Solution
Currently I don't know how to configure a specific classloader using #Grape/#Grab/#GrabConfig annotations. The closest would be to use #GrabConfig(systemClassLoader=true), and ensure the System classloader is an instance of one of the above ClassLoader classes.
If anyone does know, please let me know (and I'll update this answer).
A Workaround
The following code will programmatically download your Grapes, and load them into the supplied GroovyClassLoader (admittedly, not quite what you want).
def loadGrapes(){
ClassLoader classLoader = new groovy.lang.GroovyClassLoader()
Map[] grapez = [[group : 'org.ccil.cowan.tagsoup', module : 'tagsoup', version : '1.2']]
Grape.grab(classLoader: classLoader, grapez)
println "Class: " + classLoader.loadClass('org.ccil.cowan.tagsoup.jaxp.SAXParserImpl')
}
Using #Grab makes code untestable, at least as of 01/26/2011.
Solution that worked for me (both for running tests for scripts using #Grab in IntelliJ and via Maven):
Reference the dependencies used via #Grab in your Maven pom.xml file (this is useful anyway for better coding experience):
E.g. I have the following #Grab in my Groovy script:
#Grab(group='info.picocli', module='picocli', version='4.6.1')
So I add the following Maven dependency:
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.6.1</version>
</dependency>
Add an optional dependency on ivy in your Maven pom.xml file (needed to handle #Grab properly in your IDE):
<dependency>
<groupId>org.apache.ivy</groupId>
<artifactId>ivy</artifactId>
<version>${ivy.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
In your test code, set groovy.grape.enable system property to false. This is the main and crucial part of the solution - it disables #Grab annotation processing for the script, but remember we already have those dependencies referenced in our Maven pom.xml file:
static {
System.setProperty("groovy.grape.enable", "false")
}
#Test
void test() {
MainScript.call()
}
The downside of the solution is that you have to duplicate your dependencies in #Grab and Maven pom.xml file but again, if you develop a Groovy script you oftentimes already do so to improve your coding experience (get better code highlights etc).
I assume you've tried adding
#GrabConfig(systemClassLoader=true)
like so:
#Grapes([
#Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2'),
#GrabConfig( systemClassLoader=true )
])
class MyClass{...
If you are not using systemClassLoader=true then it seems your IDE is not rrunning the code with a groovy compiler, you can check that with a simple groovy class that outputs the class name of its classloader. I would guess it tries to compile the groovy classes and run them with a non-groovy classloader.
See also this answer to General error during conversion: No suitable ClassLoader found for grab. Also this blog post explains more about running pre-compiled groovy classes with the stock classloader.
Add the plugin snapshot update site for Kepler.
This seems to solve the "..no suitable classloader problem". Unfortunately, I still had to add the grape repo to the classpath for the project after this.
There's one more solution for testing a class with #Grab annotation:
Extract an interface from this class.
Create another class which implements its interface. Move the #Grab annotation to this class. Then make this class a simple wrapper, which just passes all the messages to the original class.
Run the tests against your original class.
Whenever you need to have a version #Grab, use the wrapper.
There is a solution to this!
You can use Groovy's metaprogramming to override the methods responsible for determining if the class loader is an instance of groovy.lang.GroovyClassLoader or org.codehaus.groovy.tools.RootLoader.
Because of this Groovy bug, you cannot override the private methods using metaprogramming, otherwise you could go ahead and change the isValidTargetClassLoaderClass method by doing this:
GrapeIvy.metaClass.isValidTargetClassLoaderClass = { Class loaderClass ->
return (loaderClass != null)
}
However, isValidTargetClassLoaderClass is called by isValidTargetClassLoader (another private method), which is called by chooseClassLoader, which is a public method, which can be overridden using metaprogramming:
GrapeIvy.metaClass.chooseClassLoader = { Map args ->
def loader = args.classLoader
if (loader?.class == null) {
loader = (args.refObject?.class
?: ReflectionUtils.getCallingClass(args.calleeDepth?:1)
)?.classLoader
while (loader && loader?.class == null) {
loader = loader.parent
}
if (loader?.class == null) {
throw new RuntimeException("No suitable ClassLoader found for grab")
}
}
return loader
}
All I did was replace any calls to !isValidTargetClassLoader with loader?.class == null.
Now, I am using Spock, so I put this code in my setupSpec method in my test class. However if you are using JUnit, I would imagine it would want to go in the method annotated with #BeforeClass.
Here is an example of it working with Spock (notice IntelliJ showing it about to return a class loader that would normally throw an exception:

JAXB Unable To Handle Attribute with Colon (:) in name?

I am attempting to use JAXB to unmarshall an XML files whose schema is defined by a DTD (ugh!).
The external provider of the DTD has specified one of the element attributes as xml:lang:
<!ATTLIST langSet
id ID #IMPLIED
xml:lang CDATA #REQUIRED
>
This comes into the xjc-generated class (standard generation; no *.xjb magic) as:
#XmlAttribute(name = "xml:lang", required = true)
#XmlJavaTypeAdapter(NormalizedStringAdapter.class)
protected String xmlLang;
However, when unmarshalling valid XML files with JAXB, the xmlLang attribute is always null.
When I edited the XML file, replacing xml:lang with lang and changed the #XmlAttribute to match, unmarshalling was successful (i.e. attributes were non-null).
I did find this http://old.nabble.com/unmarshalling-ignores-element-attribute-%27xml%27-td22558466.html. But, the resolution there was to convert to XML Schema, etc. My strong preference is to go straight from an un-altered DTD (since it is externally provided and defined by an ISO standard).
Is this a JAXB bug? Am I missing something about "namespaces" in attribute names?
FWIW, java -version = "build 1.6.0_20-b02" and xjc -version = "xjc version "JAXB 2.1.10 in JDK 6""
Solved the issue by changing replacing xml: with a namespace declaration in the JAXB-generated class:
#XmlAttribute(name = "lang", namespace="http://www.w3.org/XML/1998/namespace", required = true)
Which makes sense, in a way.
Without this kind of guidance, how would JAXB know how to interpret the otherwise-undefined namespace xml:? Unless, of course, it implemented some special-case internal handling to xml: as done in http://java.sun.com/javase/6/docs/api/javax/xml/stream/XMLStreamReader.html#getNamespaceURI%28java.lang.String%29 (see the first NOTE:)
Whether it's a bug in xjc's generation of the annotated objects or a bug in the unmarhaller, or simply requires a mapping somewhere in the xjc process is still an open question in my mind.
For now, it's working and all it requires is a little xjc magic, so I'm reasonably happy.
Disclaimer: Although 8 years late, I am adding this answer for lost souls such as myself trying to understand auto generation of java files from a DTD.
You can set project wide namespaces for the unmarshaller to work with directly in the project-info.java file via the #XmlSchema option.
This file should be automatically generated by xjc when generating classes from a schema, however it appears xjc does not automatically generate the package-info.java file when generating from a DTD!
However, you can manually make this file, and add it to the same package as the files generated by xjc.
The file would look like the following:
package-info.java :
#XmlSchema(
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix="xlink", namespaceURI="http://www.w3c.org/1999/xlink"),
#XmlNs(prefix="namespace2", namespaceURI="http://www.w3c.org/1999/namespace2")
})
package your.generated.package.hierarchy;
import javax.xml.bind.annotation.*;
You can add as many namespaces as required, simply add a new line in the form:
#XmlNs(prefix="namespace", namespaceURI="http://www.uri.to.namespace.com")
The benefit of doing it this way, rather than compared to editing the generated #XmlAttribute is that you do not need to change each generated XmlAttribute, and you do not need to manually remove the namespaces from the XmlAttribute name variable.

Resources