Jump to: navigation, search

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

 
(41 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
 
<div style="margin:5px;float:right;border:1px solid #000000;padding:5px">__TOC__</div>
  
= Design Documentation: IDResolver =
+
= ID Resolver =
  
[http://bugs.eclipse.org/360249 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).
 
+
In the current JAXB RI, developed by Sun, there are a series of "proprietary" JAXB extensions that are available to provide advanced JAXB functionality outside of the JAXB spec (these extension classes 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.
 
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.
 
 
 
= Requirements =
 
 
* Deliver an abstract '''IDResolver''' class in the EclipseLink library that will provide the same functionality as the Sun extension.
 
** Given a String <tt>id</tt> and Object <tt>obj</tt>, allow the user to perform custom <tt>bind</tt> code
 
** Given a String <tt>id</tt> and Class <tt>type</tt>, allow the user to perform custom <tt>resolve</tt> code
 
** Provide the user a hook into <tt>startDocument()</tt> and <tt>endDocument()</tt> events
 
 
 
*Unlike Sun's JAXB implementation, EclipseLink MOXy is not restricted to String-only IDs.  Therefore, we will add additional methods to support complex IDs.
 
** Given a IDWrapper <tt>id</tt> and Object <tt>obj</tt>, allow the user to perform custom <tt>bind</tt> code
 
** Given a IDWrapper <tt>id</tt> and Class <tt>type</tt>, allow the user to perform custom <tt>resolve</tt>
 
  
 
= Behaviour =
 
= Behaviour =
Line 28: Line 12:
 
If an '''IDResolver''' has been set on the '''Unmarshaller''' (via properties), then the following things must happen during unmarshal:
 
If an '''IDResolver''' has been set on the '''Unmarshaller''' (via properties), then the following things must happen during unmarshal:
  
* The '''IDResolver''''s <tt>startDocument()</tt> method must be called when unmarshalling starts.
+
* The '''IDResolver's''' <tt>startDocument()</tt> method must be called when unmarshalling starts.
* When an '''ID''' value is encountered during unmarshal, the '''IDResolver''''s <tt>bind</tt> method must be called, to bind the object to the ID.
+
* When an '''ID''' value is encountered during unmarshal, the '''IDResolver's''' <tt>bind</tt> method must be called, to bind the object to the ID.
* When an '''IDREF''' value is encountered during unmarshal, the '''IDResolver''''s <tt>resolve</tt> method must be used to obtain the object for the IDREF.
+
* When an '''IDREF''' value is encountered during unmarshal, the '''IDResolver's''' <tt>resolve</tt> method must be used to obtain the object for the IDREF.
* The '''IDResolver''''s <tt>endDocument()</tt> method must be called when unmarshalling completes.
+
* The '''IDResolver's''' <tt>endDocument()</tt> method must be called when unmarshalling completes.
  
  
 
= Configuration =
 
= Configuration =
  
The user must extend the following abstract class:
+
The user must extend the '''org.eclipse.persistence.jaxb.IDResolver''' class and implement the following abstract methods (<tt>startDocument</tt> and <tt>endDocument</tt> are optional):
  
<div style="width:800px">
+
<div style="width:900px">
 
<source lang="java">
 
<source lang="java">
package org.eclipse.persistence.jaxb;
+
public abstract Callable<?> resolve(Object id, Class type) throws SAXException;
  
import java.util.concurrent.Callable;
+
public abstract Callable<?> resolve(Map<String, Object> id, final Class type) throws SAXException;
  
import javax.xml.bind.ValidationEventHandler;
+
public abstract void bind(Object id, Object obj) throws SAXException;
  
import org.xml.sax.SAXException;
+
public abstract void bind(Map<String, Object> id, Object obj) throws SAXException;
  
public abstract class IDResolver {
+
public void startDocument(ValidationEventHandler errorHandler) throws SAXException {}
  
    public abstract Callable<?> resolve(String id, Class type) throws SAXException;
+
public void endDocument() throws SAXException {}
 
+
    public abstract void bind(String id, Object obj) throws SAXException;
+
 
+
    public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
+
    }
+
 
+
    public void endDocument() throws SAXException {
+
    }
+
 
+
}
+
 
</source>
 
</source>
 
</div>
 
</div>
  
 +
The user's '''IDResolver''' class can then be passed to the '''Unmarshaller''' through the <tt>setProperty()</tt> method:
  
 
+
<div style="width:900px">
'''OPEN ISSUE:''' Should we include additional support for non-String, and composite keys?  If so, should we include additional bind()/resolve() methods, or force the user to change to a new method signature?
+
 
+
 
+
The user's '''IDResolver''' class can then be passed to the EclipseLink Unmarshaller through the <tt>addProperty()</tt> method:
+
 
+
<div style="width:800px">
+
 
<source lang="java">
 
<source lang="java">
 
...
 
...
 
JAXBContext ctx = ...
 
JAXBContext ctx = ...
 
Unmarshaller u = ctx.createUnmarshaller();
 
Unmarshaller u = ctx.createUnmarshaller();
u.setProperty(IDResolver.class.getName(), new MyIDResolver());
+
u.setProperty(UnmarshallerProperties.ID_RESOLVER, new MyIDResolver());
 
...
 
...
 
</source>
 
</source>
 
</div>
 
</div>
  
 +
'''Note:''' EclipseLink also supports Sun's IDResolver property names:
  
== XML Bindings ==
+
<div style="width:900px">
 +
<source lang="java">
 +
m.setProperty("com.sun.xml.bind.IDResolver", new MyResolver());
 +
m.setProperty("com.sun.xml.internal.bind.IDResolver", new MyResolver());
 +
</source>
 +
</div>
  
???
+
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'''.
  
  
= Examples =
+
= Appendix A - Example IDResolver =
  
 +
<div style="width:925px">
 +
<source lang="java">
 +
import java.util.LinkedHashMap;
 +
import java.util.Map;
 +
import java.util.concurrent.Callable;
  
= Design =
+
import javax.xml.bind.ValidationEventHandler;
  
 +
import org.eclipse.persistence.jaxb.IDResolver;
  
= Appendix A - Example IDResolver =
+
import org.xml.sax.SAXException;
  
From [http://weblogs.java.net/blog/kohsuke/archive/2005/08/pluggable_ididr.html Pluggable ID/IDREF handling in JAXB 2.0]
+
public class MyIDResolver extends IDResolver {
 +
  Map<Map<String, Object>, Apple> apples = new LinkedHashMap();
 +
  Map<Map<String, Object>, Orange> oranges = new LinkedHashMap();
  
<div style="width:800px">
+
  @Override
<source lang="java">
+
  public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
import java.util.HashMap;
+
      apples.clear();
import java.util.Map;
+
      oranges.clear();
import java.util.concurrent.Callable;
+
  }
  
import com.sun.xml.bind.IDResolver;
+
  @Override
 +
  public void endDocument() throws SAXException {
 +
  }
  
class MyIDResolver extends IDResolver {
+
  @Override
    Map<String, Apple> apples = new HashMap<String, Apple>();
+
  public void bind(Map<String, Object> idWrapper, Object obj) throws SAXException {
    Map<String, Orange> oranges = new HashMap<String, Orange>();
+
      if (obj instanceof Apple) {
 +
          ((Apple) obj).processed = true;
 +
          apples.put(idWrapper, (Apple) obj);
 +
      } else {
 +
          ((Orange) obj).processed = true;
 +
          oranges.put(idWrapper, (Orange) obj);
 +
      }
 +
  }
  
    void startDocument() {
+
  @Override
        apples.clear();
+
  public Callable<Object> resolve(final Map<String, Object> idWrapper, final Class type) throws SAXException {
        oranges.clear();
+
      return new Callable<Object>() {
    }
+
          public Object call() {
 +
              if (type == Apple.class) {
 +
                  return apples.get(idWrapper);
 +
              } else {
 +
                  return oranges.get(idWrapper);
 +
              }
 +
          }
 +
      };
 +
  }
  
    public void bind(String id, Object obj) {
+
  @Override
        if (obj instanceof Apple)
+
  public void bind(Object id, Object obj) throws SAXException {
            apples.put(id, (Apple) obj);
+
      Map<String, Object> idMap = new LinkedHashMap<String, Object>(1);
        else
+
      idMap.put("stringId", id);
            oranges.put(id, (Orange) obj);
+
      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);
 +
  }
  
    public Callable resolve(final String id, final Class targetType) {
 
        return new Callable() {
 
            public Object call() {
 
                if (targetType == Apple.class)
 
                    return apples.get(id);
 
                else
 
                    return oranges.get(id);
 
            }
 
        };
 
    }
 
 
}
 
}
 
</source>
 
</source>
 
</div>
 
</div>
 
= Document History =
 
{|{{BMTableStyle}}
 
|-{{BMTHStyle}}
 
! Date
 
! Author
 
! Version Description & Notes
 
|-
 
| 111007
 
| Rick Barkhouse
 
| 1.00 : First draft
 
|-
 
| 111017
 
| Rick Barkhouse
 
| 1.01 : Considering possibility of supporting complex PKs
 
|}<br>
 

Latest revision as of 11:36, 18 June 2012

ID Resolver

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.


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 org.eclipse.persistence.jaxb.IDResolver class and implement the following abstract methods (startDocument and endDocument are optional):

public abstract Callable<?> resolve(Object id, Class type) throws SAXException;
 
public abstract Callable<?> resolve(Map<String, Object> id, final 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(ValidationEventHandler 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);
   }
 
}