blob: 9804de117bc18ee6ba4ec2914bd682c495580250 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>Using Extensible MOXy | EclipseLink 2.7 EclipseLink MOXy</title>
<meta name="generator" content="Oracle DARB XHTML Converter (Mode = document) - Version 1.0.22 Build 1" />
<meta name="date" content="2014-06-10T10:25:32Z" />
<meta name="robots" content="noarchive" />
<meta name="doctitle" content="Using Extensible MOXy" />
<meta name="relnum" content="Release 2.7" />
<meta name="partnum" content="" />
<link rel="stylesheet" type="text/css" href="../../dcommon/style.css" media="screen" />
<link rel="copyright" href="../../dcommon/html/cpyr.htm" title="Copyright" type="text/html" />
<link rel="start" href="../../index.htm" title="Home" type="text/html" />
<link rel="contents" href="toc.htm" title="Contents" type="text/html" />
<link rel="prev" href="advanced_concepts003.htm" title="Previous" type="text/html" />
<link rel="next" href="advanced_concepts005.htm" title="Next" type="text/html" />
<!-- START: Disqus --><script type="text/javascript"> var disqus_developer = 0; </script><!-- END: Disqus --><!-- START: Sharethis --><script type="text/javascript">var switchTo5x=true;</script><script type="text/javascript" src="http://w.sharethis.com/button/buttons.js"></script><script type="text/javascript" src="http://s.sharethis.com/loader.js"></script> <!-- END: Sharethis --></head>
<body bgcolor="#FFFFFF"><iframe id="docheader" frameborder="0" framemargin="0" scrolling="no" src="../../dcommon/header.html"></iframe><script src="http://www.google.com/jsapi" type="text/javascript"></script><script type="text/javascript"> google.load('search', '1', {language : 'en'}); google.setOnLoadCallback(function() { var customSearchOptions = {}; var googleAnalyticsOptions = {}; googleAnalyticsOptions['queryParameter'] = 'q'; googleAnalyticsOptions['categoryParameter'] = ''; customSearchOptions['googleAnalyticsOptions'] = googleAnalyticsOptions; var customSearchControl = new google.search.CustomSearchControl( '016171230611334810008:1hjujor5_fg', customSearchOptions); customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET); var options = new google.search.DrawOptions(); options.setSearchFormRoot('cse-search-form'); customSearchControl.draw('cse', options); }, true);</script><link rel="stylesheet" href="http://www.google.com/cse/style/look/default.css" type="text/css" /><div id="cse" style="width:100%;"></div>
<div class="header"><a id="top" name="top"></a>
<table class="simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td align="left" valign="top"><div class="booktitle">Developing JAXB Applications Using EclipseLink MOXy,
<b>Release 2.7</b><br /></font></td>
<td valign="bottom" align="right" width="144">
<table class="simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td>&nbsp;</td>
<td align="center" valign="top"><a href="toc.htm"><img src="../../dcommon/images/contents.png" alt="Go To Table Of Contents" border="0" height="16" width="16" /><br />
</td><td>&nbsp;</td><td align="center"><a href="../../" target="_top" class="external text" title="Search" rel="nofollow"><img src="../../dcommon/images/search.png" alt="Search" style="border:0;" /><br /><span class="mini"></span></a></td><td>&nbsp;</td><td align="center"><a href="../eclipselink_moxy.pdf" title="PDF" target="_blank"><img src="../../dcommon/images/pdf_icon.png" style="padding-right:5px;border:0" alt="PDF"></a></td>
</tr>
</table>
</td>
</tr>
</table>
<hr />
<table class="navigation simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="100" align="center">
<tr>
<td align="center"><a href="advanced_concepts003.htm"><img src="../../dcommon/images/larrow.png" alt="Previous" border="0" height="16" width="16" /></a></td>
<td align="center"><a href="advanced_concepts005.htm"><img src="../../dcommon/images/rarrow.png" alt="Next" border="0" height="16" width="16" /></a></td>
<td>&nbsp;</td>
</tr>
</table>
</div>
<!-- class="header" -->
<div class="ind"><!-- End Header --><a id="CHDIHFEE" name="CHDIHFEE"></a><a id="TLJAX393" name="TLJAX393"></a>
<div class="sect1"><!-- infolevel="all" infotype="General" -->
<h1 class="sect1"><font face="arial, helvetica, sans-serif" color="#330099">Using Extensible MOXy</font></h1>
<p>In a multi-tenant architecture, a single application runs on a server serving multiple client organizations (tenants). Good multi-tenant applications allow per-tenant customizations. When these customizations are made to data, it can be difficult for the binding layer to handle them.</p>
<p>JAXB is designed to work with domain models that have <em>real</em> fields and properties. EclipseLink MOXy virtual properties provide a way to extend a class without modifying the source.</p>
<a id="TLJAX394" name="TLJAX394"></a>
<div class="sect2"><a id="sthref158" name="sthref158"></a>
<h2 class="sect2"><font face="arial, helvetica, sans-serif" color="#330099">Using the @XmlVirtualAccessMethods Annotation</font></h2>
<p>The <code>@XmlVirtualAccessMethods</code> annotation is used to specify that a class is extensible. An extensible class is required to have a <code>get</code> method that returns a value by property name, and a <code>set</code> method that stores a value by property name. The default names for these methods are <code>get</code> and <code>set</code>, and can be overridden with the <code>@XmlVirtualAccessMethods</code> annotation.</p>
<p>Since we will have multiple extensible classes in this example, we'll configure a base class for this behavior that extensible classes can extend. We will use the <code>@XmlTransient</code> annotation to prevent <code>ExtensibleBase</code> from being mapped as an inheritance relationship. The <em>real</em> properties represent the parts of the model that will be common to all tenants. The per-tenant extensions will be represented as <em>virtual</em> properties.</p>
<div class="example"><a id="TLJAX395" name="TLJAX395"></a><a id="sthref159" name="sthref159"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-16 Sample ExtensibleBase</font></em></strong></p>
<pre xml:space="preserve" class="oac_no_warn">
package examples.virtual;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlTransient;
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
@XmlTransient
<strong>@XmlVirtualAccessMethods(setMethod="put")</strong>
public class ExtensibleBase {
private Map&lt;String, Object&gt; extensions = new HashMap&lt;String, Object&gt;();
public &lt;T&gt; T get(String property) {
return (T) extensions.get(property);
}
public void put(String property, Object value) {
extensions.put(property, value);
}
}
</pre></div>
<!-- class="example" -->
<div class="example"><a id="TLJAX396" name="TLJAX396"></a><a id="sthref160" name="sthref160"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-17 Customer</font></em></strong></p>
<p>The <strong>Customer</strong> class will be extensible since it inherits from a domain class that has been annotated with <code>@XmlVirtualAccessMethods</code>.</p>
<pre xml:space="preserve" class="oac_no_warn">
package examples.virtual;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
<strong>public class Customer extends ExtensibleBase</strong> {
private String firstName;
private String lastName;
private Address billingAddress;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Address getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(Address billingAddress) {
this.billingAddress = billingAddress;
}
}
</pre></div>
<!-- class="example" -->
<div class="example"><a id="TLJAX397" name="TLJAX397"></a><a id="sthref161" name="sthref161"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-18 Address</font></em></strong></p>
<p>It is not necessary to have every class in your model be extensible. In this example the <strong>Address</strong> class will not have any virtual properties.</p>
<pre xml:space="preserve" class="oac_no_warn">
package examples.virtual;
public class <strong>Address</strong> {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
</pre></div>
<!-- class="example" -->
<div class="example"><a id="TLJAX398" name="TLJAX398"></a><a id="sthref162" name="sthref162"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-19 PhoneNumber</font></em></strong></p>
<p>Like <strong>Customer</strong>, <strong>PhoneNumber</strong> will be an extensible class.</p>
<pre xml:space="preserve" class="oac_no_warn">
package examples.virtual;
import javax.xml.bind.annotation.XmlValue;
<strong>public class PhoneNumber extends ExtensibleBase</strong> {
private String number;
@XmlValue
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
</pre></div>
<!-- class="example" --></div>
<!-- class="sect2" -->
<a id="TLJAX399" name="TLJAX399"></a>
<div class="sect2"><a id="sthref163" name="sthref163"></a>
<h2 class="sect2"><font face="arial, helvetica, sans-serif" color="#330099">Creating Tenant 1</font></h2>
<p>The first tenant is an online sporting goods store that requires the following extensions to the model:</p>
<ul>
<li>
<p>Customer ID</p>
</li>
<li>
<p>Customer's middle name</p>
</li>
<li>
<p>Shipping address</p>
</li>
<li>
<p>A collection of contact phone numbers</p>
</li>
<li>
<p>Type of phone number (i.e. home, work, or cell)</p>
</li>
</ul>
<p>The metadata for the virtual properties is supplied through MOXy's XML mapping file. Virtual properties are mapped in the same way as real properties. Some additional information is required including type (since this cannot be determined via reflection), and for collection properties a container type.</p>
<p>The virtual properties defined in <a href="#CHDFBEGH">Example 8-20</a> for <strong>Customer</strong> are: <strong>middleName</strong>, <strong>shippingAddress</strong>, and <strong>phoneNumbers</strong>. For <strong>PhoneNumber</strong> the virtual property is the <code>type</code> property.</p>
<div class="example"><a id="CHDFBEGH" name="CHDFBEGH"></a><a id="TLJAX400" name="TLJAX400"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-20 binding-tenant1.xml</font></em></strong></p>
<pre xml:space="preserve" class="oac_no_warn">
&lt;?xml version="1.0"?&gt;
&lt;xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="examples.virtual"&gt;
&lt;java-types&gt;
&lt;java-type name="Customer"&gt;
&lt;xml-type prop-order="firstName middleName lastName billingAddress shippingAddress phoneNumbers"/&gt;
&lt;java-attributes&gt;
&lt;xml-attribute
java-attribute="id"
type="java.lang.Integer"/&gt;
&lt;xml-element
java-attribute="middleName"
type="java.lang.String"/&gt;
&lt;xml-element
java-attribute="shippingAddress"
type="examples.virtual.Address"/&gt;
&lt;xml-element
java-attribute="phoneNumbers"
name="phoneNumber"
type="examples.virtual.PhoneNumber"
container-type="java.util.List"/&gt;
&lt;/java-attributes&gt;
&lt;/java-type&gt;
&lt;java-type name="PhoneNumber"&gt;
&lt;java-attributes&gt;
&lt;xml-attribute
java-attribute="type"
type="java.lang.String"/&gt;
&lt;/java-attributes&gt;
&lt;/java-type&gt;
&lt;/java-types&gt;
&lt;/xml-bindings&gt;
</pre></div>
<!-- class="example" -->
<p>The <code>get</code>/<code>set</code> methods are used on the domain model to interact with the <em>real</em> properties and the accessors defined on the <code>@XmlVirtualAccessMethods</code> annotation are used to interact with the <em>virtual</em> properties. The normal JAXB mechanisms are used for marshal and unmarshall operations:</p>
<pre xml:space="preserve" class="oac_no_warn">
Customer customer = new Customer();
//Set Customer's real properties
customer.setFirstName("Jane");
customer.setLastName("Doe");
Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
//Set Customer's virtual 'middleName' property
customer.put("middleName", "Anne");
//Set Customer's virtual 'shippingAddress' property
Address shippingAddress = new Address();
shippingAddress.setStreet("2 Shipping Road");
customer.put("shippingAddress", shippingAddress);
List&lt;PhoneNumber&gt; phoneNumbers = new ArrayList&lt;PhoneNumber&gt;();
customer.put("phoneNumbers", phoneNumbers);
PhoneNumber workPhoneNumber = new PhoneNumber();
workPhoneNumber.setNumber("555-WORK");
//Set the PhoneNumber's virtual 'type' property
workPhoneNumber.put("type", "WORK");
phoneNumbers.add(workPhoneNumber);
PhoneNumber homePhoneNumber = new PhoneNumber();
homePhoneNumber.setNumber("555-HOME");
//Set the PhoneNumber's virtual 'type' property
homePhoneNumber.put("type", "HOME");
phoneNumbers.add(homePhoneNumber);
Map&lt;String, Object&gt; properties = new HashMap&lt;String, Object&gt;();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "examples/virtual/binding-tenant1.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
</pre>
<div class="example"><a id="TLJAX401" name="TLJAX401"></a><a id="sthref164" name="sthref164"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-21 Output</font></em></strong></p>
<pre xml:space="preserve" class="oac_no_warn">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;customer&gt;
&lt;firstName&gt;Jane&lt;/firstName&gt;
&lt;middleName&gt;Anne&lt;/middleName&gt;
&lt;lastName&gt;Doe&lt;/lastName&gt;
&lt;billingAddress&gt;
&lt;street&gt;1 Billing Street&lt;/street&gt;
&lt;/billingAddress&gt;
&lt;shippingAddress&gt;
&lt;street&gt;2 Shipping Road&lt;/street&gt;
&lt;/shippingAddress&gt;
&lt;phoneNumber type="WORK"&gt;555-WORK&lt;/phoneNumber&gt;
&lt;phoneNumber type="HOME"&gt;555-HOME&lt;/phoneNumber&gt;
&lt;/customer&gt;
</pre></div>
<!-- class="example" --></div>
<!-- class="sect2" -->
<a id="TLJAX402" name="TLJAX402"></a>
<div class="sect2"><a id="sthref165" name="sthref165"></a>
<h2 class="sect2"><font face="arial, helvetica, sans-serif" color="#330099">Creating Tenant 2</font></h2>
<p>The second tenant is a streaming media provider that offers on-demand movies and music to it's subscribers. It requires a different set of extensions to the core model: a single contact phone number</p>
<p>For this tenant we will also leverage the mapping file to customize the mapping of the real properties, as shown in <a href="#CHDIFJGA">Example 8-22</a>:</p>
<div class="example"><a id="CHDIFJGA" name="CHDIFJGA"></a><a id="TLJAX403" name="TLJAX403"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-22 binding-tenant2.xml</font></em></strong></p>
<pre xml:space="preserve" class="oac_no_warn">
&lt;?xml version="1.0"?&gt;
&lt;xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="examples.virtual"&gt;
&lt;xml-schema namespace="urn:tenant1" element-form-default="QUALIFIED"/&gt;
&lt;java-types&gt;
&lt;java-type name="Customer"&gt;
&lt;xml-type prop-order="firstName lastName billingAddress phoneNumber"/&gt;
&lt;java-attributes&gt;
&lt;xml-attribute java-attribute="firstName"/&gt;
&lt;xml-attribute java-attribute="lastName"/&gt;
&lt;xml-element java-attribute="billingAddress" name="address"/&gt;
&lt;xml-element
java-attribute="phoneNumber"
type="examples.virtual.PhoneNumber"/&gt;
&lt;/java-attributes&gt;
&lt;/java-type&gt;
&lt;/java-types&gt;
&lt;/xml-bindings&gt;
</pre></div>
<!-- class="example" -->
<pre xml:space="preserve" class="oac_no_warn">
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
PhoneNumber phoneNumber = new PhoneNumber();
phoneNumber.setNumber("555-WORK");
customer.put("phoneNumber", phoneNumber);
Map&lt;String, Object&gt; properties = new HashMap&lt;String, Object&gt;();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "examples/virtual/binding-tenant2.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
</pre>
<div class="example"><a id="TLJAX404" name="TLJAX404"></a><a id="sthref166" name="sthref166"></a>
<p><strong><em><font face="arial, helvetica, sans-serif">Example 8-23 Output</font></em></strong></p>
<p>Note that even though both tenants share several real properties, the corresponding XML representation can be quite different due to virtual properties:</p>
<pre xml:space="preserve" class="oac_no_warn">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;customer xmlns="urn:tenant1" firstName="Jane" lastName="Doe"&gt;
&lt;address&gt;
&lt;street&gt;1 Billing Street&lt;/street&gt;
&lt;/address&gt;
&lt;phoneNumber&gt;555-WORK&lt;/phoneNumber&gt;
&lt;/customer&gt;
</pre></div>
<!-- class="example" --></div>
<!-- class="sect2" --></div>
<!-- class="sect1" --></div>
<!-- class="ind" -->
<!-- Start Footer -->
<div class="footer">
<hr />
<table class="simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="100%">
<col width="33%" />
<col width="*" />
<col width="33%" />
<tr>
<td valign="bottom">
<table class="navigation simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="100" align="center">
<col width="*" />
<col width="48%" />
<col width="48%" />
<tr>
<td>&nbsp;</td>
<td align="center"><a href="advanced_concepts003.htm"><img src="../../dcommon/images/larrow.png" alt="Previous" border="0" height="16" width="16" /></a></td>
<td align="center"><a href="advanced_concepts005.htm"><img src="../../dcommon/images/rarrow.png" alt="Next" border="0" height="16" width="16" /></a></td>
</tr>
</table>
</td>
<td align="center" width="34%"><a href="http://www.eclipse.org/eclipselink/" title="EclipseLink home"><img src="../../dcommon/images/ellogo.png" alt="EclipseLink" width="150" border="0" /></a><br />
<td valign="bottom" align="right">
<table class="simple oac_no_warn" summary="" cellspacing="0" cellpadding="0" width="225">
<tr>
<td>&nbsp;</td>
<td align="center" valign="top"><a href="toc.htm"><img src="../../dcommon/images/contents.png" alt="Go To Table Of Contents" border="0" height="16" width="16" /><br />
</td><td>&nbsp;</td><td align="center"><a href="../../" target="_top" class="external text" title="Search" rel="nofollow"><img src="../../dcommon/images/search.png" alt="Search" style="border:0;" /><br /><span class="mini"></span></a></td><td>&nbsp;</td><td align="center"><a href="../eclipselink_moxy.pdf" title="PDF" target="_blank"><img src="../../dcommon/images/pdf_icon.png" style="padding-right:5px;border:0" alt="PDF"></a></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<!-- class="footer" -->
<div id="copyright">Copyright &copy; 2013 by The Eclipse Foundation under the <a href="http://www.eclipse.org/org/documents/epl-v10.php">Eclipse Public License (EPL)</a><br /> <script type="text/javascript">var LastUpdated = document.lastModified;document.writeln ("Updated: " + LastUpdated);</script> </div><!-- START: Analytics --><script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1608008-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script><!-- END: Analytics --><!-- START: Sharethis --><script>var options={ "publisher": "e2fe9e07-fab6-4f84-83ea-0991b429842c", "position": "right", "ad": { "visible": false, "openDelay": 5, "closeDelay": 0}};var st_hover_widget = new sharethis.widgets.hoverbuttons(options);</script><!-- END: Sharethis --></body>
</html>