Jump to: navigation, search

Difference between revisions of "EclipseLink/Release/2.4.0/JAXB RI Extensions/ID Resolver"

Line 85: Line 85:
  
 
= Appendix A - Example IDResolver =
 
= Appendix A - Example IDResolver =
 
From [http://weblogs.java.net/blog/kohsuke/archive/2005/08/pluggable_ididr.html Pluggable ID/IDREF handling in JAXB 2.0]
 
  
 
<div style="width:800px">
 
<div style="width:800px">
 
<source lang="java">
 
<source lang="java">
import java.util.HashMap;
+
import java.util.LinkedHashMap;
 
import java.util.Map;
 
import java.util.Map;
 
import java.util.concurrent.Callable;
 
import java.util.concurrent.Callable;
  
import com.sun.xml.bind.IDResolver;
+
import javax.xml.bind.ValidationEventHandler;
  
class MyIDResolver extends IDResolver {
+
import org.eclipse.persistence.jaxb.IDResolver;
    Map<String, Apple> apples = new HashMap<String, Apple>();
+
    Map<String, Orange> oranges = new HashMap<String, Orange>();
+
  
     void startDocument() {
+
import org.xml.sax.SAXException;
 +
 
 +
public class MyIDResolver extends IDResolver {
 +
     Map<Map<String, Object>, Apple> apples = new LinkedHashMap();
 +
    Map<Map<String, Object>, Orange> oranges = new LinkedHashMap();
 +
 
 +
    @Override
 +
    public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
 
         apples.clear();
 
         apples.clear();
 
         oranges.clear();
 
         oranges.clear();
 
     }
 
     }
  
     public void bind(String id, Object obj) {
+
    @Override
        if (obj instanceof Apple)
+
     public void endDocument() throws SAXException {
            apples.put(id, (Apple) obj);
+
        else
+
            oranges.put(id, (Orange) obj);
+
 
     }
 
     }
  
     public Callable resolve(final String id, final Class targetType) {
+
    @Override
         return new Callable() {
+
    public void bind(Map<String, Object> idWrapper, Object obj) throws SAXException {
 +
        if (obj instanceof Apple) {
 +
            ((Apple) obj).processed = true;
 +
            apples.put(idWrapper, (Apple) obj);
 +
        } else {
 +
            ((Orange) obj).processed = true;
 +
            oranges.put(idWrapper, (Orange) obj);
 +
        }
 +
    }
 +
 
 +
    @Override
 +
     public Callable<Object> resolve(final Map<String, Object> idWrapper, final Class type) throws SAXException {
 +
         return new Callable<Object>() {
 
             public Object call() {
 
             public Object call() {
                 if (targetType == Apple.class)
+
                 if (type == Apple.class) {
                     return apples.get(id);
+
                     return apples.get(idWrapper);
                 else
+
                 } else {
                     return oranges.get(id);
+
                     return oranges.get(idWrapper);
 +
                }
 
             }
 
             }
 
         };
 
         };
 
     }
 
     }
 +
 +
    @Override
 +
    public void bind(Object id, Object obj) throws SAXException {
 +
        Map<String, Object> idMap = new LinkedHashMap<String, Object>(1);
 +
        idMap.put("stringId", id);
 +
        bind(idMap, obj);
 +
    }
 +
 +
    @Override
 +
    public Callable<?> resolve(Object id, Class type) throws SAXException {
 +
        Map<String, Object> idMap = new LinkedHashMap<String, Object>(1);
 +
        idMap.put("stringId", id);
 +
        return resolve(idMap, type);
 +
    }
 +
 
}
 
}
 
</source>
 
</source>
 
</div>
 
</div>

Revision as of 15:07, 5 June 2012

Design Documentation: ID Resolver

ER 360249

In the current JAXB RI, developed by Sun, there is a series of "proprietary" JAXB extensions which provide advanced functionality outside of the JAXB specification (these extension classes and properties reside in the com.sun.xml.bind package).

The abstract class IDResolver provided in the Sun JAXB implementation allows users to override the ID/IDREF processing of the JAXB runtime.

This document will outline the design for an EclipseLink equivalent to this extension.


Behaviour

If an IDResolver has been set on the Unmarshaller (via properties), then the following things must happen during unmarshal:

  • The IDResolver's startDocument() method must be called when unmarshalling starts.
  • When an ID value is encountered during unmarshal, the IDResolver's bind method must be called, to bind the object to the ID.
  • When an IDREF value is encountered during unmarshal, the IDResolver's resolve method must be used to obtain the object for the IDREF.
  • The IDResolver's endDocument() method must be called when unmarshalling completes.


Configuration

The user must extend the following abstract class:

package org.eclipse.persistence.jaxb;
 
import java.util.concurrent.Callable;
 
import javax.xml.bind.ValidationEventHandler;
 
import org.xml.sax.SAXException;
 
/**
 * IDResolver can be subclassed to allow customization of the ID/IDREF processing of
 * JAXBUnmarshaller.
 *
 * @see JAXBUnmarshaller
 */
public abstract class IDResolver {
 
    public abstract Callable<?> resolve(Object id, Class type) throws SAXException;
 
    public abstract Callable<?> resolve(Map<String, Object> id, Class type) throws SAXException;
 
    public abstract void bind(Object id, Object obj) throws SAXException;
 
    public abstract void bind(Map<String, Object> id, Object obj) throws SAXException;
 
    public void startDocument(ErrorHandler errorHandler) throws SAXException {}
 
    public void endDocument() throws SAXException {}
 
}

The user's IDResolver class can then be passed to the Unmarshaller through the setProperty() method:

...
JAXBContext ctx = ...
Unmarshaller u = ctx.createUnmarshaller();
u.setProperty(UnmarshallerProperties.ID_RESOLVER, new MyIDResolver());
...

Note: EclipseLink also supports Sun's IDResolver property names:

m.setProperty("com.sun.xml.bind.IDResolver", new MyResolver());
m.setProperty("com.sun.xml.internal.bind.IDResolver", new MyResolver());

Also note that if you are using a Sun IDResolver with EclipseLink, it will be unable to support EclipseLink's multiple XML IDs feature. In this case, you should re-implement your IDResolver as a subclass of org.eclipse.persistence.jaxb.IDResolver.


Appendix A - Example IDResolver

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
 
import javax.xml.bind.ValidationEventHandler;
 
import org.eclipse.persistence.jaxb.IDResolver;
 
import org.xml.sax.SAXException;
 
public class MyIDResolver extends IDResolver {
    Map<Map<String, Object>, Apple> apples = new LinkedHashMap();
    Map<Map<String, Object>, Orange> oranges = new LinkedHashMap();
 
    @Override
    public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
        apples.clear();
        oranges.clear();
    }
 
    @Override
    public void endDocument() throws SAXException {
    }
 
    @Override
    public void bind(Map<String, Object> idWrapper, Object obj) throws SAXException {
        if (obj instanceof Apple) {
            ((Apple) obj).processed = true;
            apples.put(idWrapper, (Apple) obj);
        } else {
            ((Orange) obj).processed = true;
            oranges.put(idWrapper, (Orange) obj);
        }
    }
 
    @Override
    public Callable<Object> resolve(final Map<String, Object> idWrapper, final Class type) throws SAXException {
        return new Callable<Object>() {
            public Object call() {
                if (type == Apple.class) {
                    return apples.get(idWrapper);
                } else {
                    return oranges.get(idWrapper);
                }
            }
        };
    }
 
    @Override
    public void bind(Object id, Object obj) throws SAXException {
        Map<String, Object> idMap = new LinkedHashMap<String, Object>(1);
        idMap.put("stringId", id);
        bind(idMap, obj);
    }
 
    @Override
    public Callable<?> resolve(Object id, Class type) throws SAXException {
        Map<String, Object> idMap = new LinkedHashMap<String, Object>(1);
        idMap.put("stringId", id);
        return resolve(idMap, type);
    }
 
}