3

This program:

import foo.bar.baz.ClassSpecificallyIncluded;  
import javax.xml.bind.JAXBContext;  
public class A {  
    public static void main(String[] args) throws Exception {  
        System.out.println(JAXBContext.newInstance(ClassSpecificallyIncluded.class).toString());  
        System.out.println(JAXBContext.newInstance("foo.bar.baz").toString());  
    }  
}  

produces different output for the first and second JAXBContext:

jar:file:/C:/dev/trunk/rt/tomcat/shared/lib/webservices-rt-2.0.1.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class Build-Id: 1.0
Classes known to this context:
[B
boolean
byte
char
foo.bar.baz.Class1
foo.bar.baz.Class1$NestedClass
foo.bar.baz.Class2
foo.bar.baz.ClassSpecificallyIncluded
foo.bar.baz.AnotherClass
com.sun.xml.bind.api.CompositeStructure
double
float
int
java.awt.Image
java.io.File
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Class
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Object
java.lang.Short
java.lang.String
java.lang.Void
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.net.URL
java.util.Calendar
java.util.Date
java.util.GregorianCalendar
java.util.UUID
javax.activation.DataHandler
javax.xml.bind.JAXBElement
javax.xml.datatype.Duration
javax.xml.datatype.XMLGregorianCalendar
javax.xml.namespace.QName
javax.xml.transform.Source
long
short
void

jar:file:/C:/dev/trunk/rt/tomcat/shared/lib/webservices-rt-2.0.1.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class Build-Id: 1.0
Classes known to this context:
[B
boolean
byte
char
foo.bar.baz.Class1
foo.bar.baz.Class1$NestedClass
foo.bar.baz.Class2
<<< CLASS IS MISSING HERE>>>
foo.bar.baz.AnotherClass
com.sun.xml.bind.api.CompositeStructure
double
float
int
java.awt.Image
java.io.File
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Class
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Object
java.lang.Short
java.lang.String
java.lang.Void
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.net.URL
java.util.Calendar
java.util.Date
java.util.GregorianCalendar
java.util.UUID
javax.activation.DataHandler
javax.xml.bind.JAXBElement
javax.xml.datatype.Duration
javax.xml.datatype.XMLGregorianCalendar
javax.xml.namespace.QName
javax.xml.transform.Source
long
short
void

And yet, ClassSpecificallyIncluded is in the foo.bar.baz package:

//  
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6   
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>   
// Any modifications to this file will be lost upon recompilation of the source schema.   
// Generated on: 2012.05.14 at 10:47:17 PM IST   
//  

package foo.bar.baz;  

import javax.xml.bind.annotation.XmlAccessType;  
import javax.xml.bind.annotation.XmlAccessorType;  
import javax.xml.bind.annotation.XmlElement;  
import javax.xml.bind.annotation.XmlRootElement;  


@XmlAccessorType(XmlAccessType.FIELD)  
@XmlType(name = "", propOrder = {  
    "applicationArea",  
    "dataArea"  
})  
@XmlRootElement(name = "ClassSpecificallyIncluded")  
public class ClassSpecificallyIncluded {...  

The classpath and classloader are the same for both JAXBContexts.

So why doesn’t the second JAXBContext know about ClassSpecificallyIncluded?

Jim Showalter
  • 550
  • 3
  • 8
  • 20

2 Answers2

3

The answer to your question is effectively answered here:

Can you find all classes in a package using reflection?

Answer: No By design, java doesn't know and can't find every class in a package. Java is built on the concept of "Just in time". Which for this purpose means that java doesn't know what it's got until it's asked for something specifically.

So from that stand point, JAXB has no way to find what classes are in a package.

It would be inconvenient if JAXB had to be told precisely every class every time you needed context. So as a convenience method JAXB offers you the option of providing a package name.

In order to get round the limitations of java it attempts to load from that package an ObjectFactory and a jaxb.index file. It can do this because they are specific names. If one is found, then it uses them as a manifest for the package. If neither is found it has no option but to abort because it can't possibly know what's in the package.

Community
  • 1
  • 1
Philip Couling
  • 13,581
  • 5
  • 53
  • 85
1

Creating a JAXBContext on a Package Name

Assume I have the following classes in a directory called forum20273355:

  • Address
  • Customer (extends Person)
  • Mammal
  • Person (extends Mammal & has property of type Address)

If I create a JAXBContext using the following code:

JAXBContext jc = JAXBContext.newInstance("forum20273355");

Then I will get the following exception:

Exception in thread "main" javax.xml.bind.JAXBException: Provider com.sun.xml.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index
 - with linked exception:
[javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index]
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:146)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:334)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:431)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:394)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:298)
    at forum20273355.Demo.main(Demo.java:8)
Caused by: javax.xml.bind.JAXBException: "forum20273355" doesnt contain ObjectFactory.class or jaxb.index
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:197)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:172)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:132)
    ... 5 more

Specifying a jaxb.index or ObjectFactory

Below we will create a jaxb.index file and ObjectFactory that will cause the Person class to be processed to see what happens.

jaxb.index

jaxb.index is a text file that contains a carriage return separated list of short class names.

Person

ObjectFactory

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public class ObjectFactory {

    public Person createPerson() {
        return new Person();
    }

}

Processed Classes

Obviously Person was processed, but so was the super class Mammal and the reference class Address. The class that wasn't processed was the subclass Customer.

forum20273355.Address
forum20273355.Mammal
forum20273355.Person

Getting the Subclass Processed

Obviously we could have added the subclass Customer to the jaxb.index or ObjectFactory. We can also leverage the @XmlSeeAlso annotation to make this happen.

package forum20273355;

import javax.xml.bind.annotation.XmlSeeAlso;

@XmlSeeAlso({Customer.class})
public class Person extends Mammal {

Now Customer is processed as well.

forum20273355.Address
forum20273355.Customer
forum20273355.Mammal
forum20273355.Person

UPDATE

Everything you wrote in your comment is true. What is missing is that the jaxb.index file or ObjectFactory class is required to kick of the process. When a model is generated from an XML schema an ObjectFactory is generated containing all the necessary references to bootstrap the model. When you start from Java classes I recommend bootstrapping from Java classes instead of the package name, but the same rules apply.

But the Sun Oracle documentation says: JAXBContext.newInstance( "com.acme.foo:com.acme.bar" ) The JAXBContext instance is initialized from a list of colon separated Java package names.

True, the package names specify where metadata will be looked for.

Each java package contains JAXB mapped classes, schema-derived classes and/or user annotated classes.

True.

Additionally, the java package may contain JAXB package annotations that must be processed. (see JLS 3rd Edition, Section 7.4.1. Package Annotations).

True, the package annotations will definitely be applied.

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • But the Sun Oracle documentation says: JAXBContext.newInstance( "com.acme.foo:com.acme.bar" ) The JAXBContext instance is initialized from a list of colon separated Java package names. Each java package contains JAXB mapped classes, schema-derived classes and/or user annotated classes. Additionally, the java package may contain JAXB package annotations that must be processed. (see JLS 3rd Edition, Section 7.4.1. Package Annotations). Where does it say anything about subclasses, etc.? – Jim Showalter Nov 28 '13 at 21:24
  • @user1461450 - Your comment is true, and doesn't conflict with my answer. I have added an update to provide more information. – bdoughan Nov 28 '13 at 21:32
  • 1
    Hm. The ObjectFactory in this package does not have a QName for ClassSpecificallyIncluded. (It's not my package, just trying to use it.) Hadn't noticed that before. But ClassSpecificallyIncluded is not a subclass or superclass. It's by itself. So it would be unnatural to be required to add XmlSeeAlso. Anyway, it appears that the package was not generated correctly, and thus the problem. Thanks for your answer! – Jim Showalter Nov 28 '13 at 21:33