Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EclipseLink/Development/2.1/DynamicMOXy/296967

Design Specification: MOXy support for Dynamic Persistence

ER 296967

Document History

Date Author Version Description & Notes
091204 Rick Barkhouse 1.0
091207 Rick Barkhouse 1.1 - Added Bootstrapping from Project XML
091217 Rick Barkhouse 1.2 - Added Bootstrapping from XSD

Project overview

The goal of this feature is to enable users of EclipseLink JAXB to perform typical JAXB operations without having real .class files available for their domain objects. Users will pass in some form of metadata (EclipseLink Deployment XML or eclipselink-oxm.xml) to our JAXBContextFactory, and this metadata will be used to construct DynamicEntity objects representing the domain classes.

The ultimate purpose of this feature is to support an end-to-end JPA<->JAXB solution. EclipseLink JPA already has some support for Dynamic Persistence and this work will provide a path from Dynamic JPA to JAXB.

Goals:

  • Bootstrapping of JAXB context with metadata and no class files
  • Support adding arbitrary properties/mappings at runtime, after initialization has been performed


Some initial groundwork was included in EclipseLink 2.0, more information is available here.

Concepts

Dynamic Persistence

The core of this feature is the Dynamic Persistence support that was initially added for JPA (found in org.eclipse.persistence.dynamic). To obtain a "dynamic" project (i.e. one that maps to classes that were generated in-memory), a "dry" project (one that does not have Java Classes specified, only class names) is passed to the DynamicTypeBuilder, passing in an instance of DynamicClassLoader, which does the work of building the in-memory classes:

InputStream metadata = ...
Project dynamicProject = DynamicTypeBuilder.loadDynamicProject(inputStream, null, new DynamicClassLoader());

When this project is used to unmarshall XML documents, the objects that are returned by EclipseLink will be subclasses of DynamicEntity. DynamicEntities offer a simple get(propertyName) / set(propertyName, propertyValue) API to manipulate their data:

DynamicEntity dynamicEmployee = (DynamicEntity) jaxbContext.createUnmarshaller().unmarshal(instanceDoc);
String firstName = dynamicEmp.get("fname");
DynamicEntity dynamicAddress = dynamicEmp.get("address");
 
...
 
dynamicEmp.set("lname", "Duggar");
address.set("street", "1001 Duggar Ranch Way");

Requirements

The following sections will expand the goals of this project into more concrete requirements.

Design Constraints

Design / Functionality

Bootstrapping from Project XML

To avoid introducing proprietary API, we can use the Map of properties passed in to JAXBContextFactory to configure the use of Dynamic Persistence. In this example, two new property keys are introduced, one to indicate that Dynamic Persistence should be used, and the second to specify the location of an EclipseLink deployment XML file. There is already a property key to indicate the location of an eclipselink-oxm.xml file.

String contextPath = "example.dynamic";
InputStream iStream = sysClassLoader.getResourceAsStream("example/dynamic/DryProject.xml");
...
// Build properties map to pass in to JAXBConextFactory
// 
// [properties]
//		{K: JAXBContextFactory.ECLIPSELINK_DEPLOYMENT_XML }
//		{V: Map of JAXB context names -> InputStreams }
HashMap<String, InputStream> metadataSourceMap = new HashMap<String, InputStream>();
metadataSourceMap.put(contextPath, iStream);
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.USE_DYNAMIC_PERSISTENCE, Boolean.TRUE);
properties.put(JAXBContextFactory.ECLIPSELINK_DEPLOYMENT_XML, metadataSourceMap);
 
JAXBContext jaxbContext = JAXBContextFactory.createContext(contextPath, sysClassLoader, properties);
 
// Using eclipselink-oxm.xml:
//
// InputStream iStream = sysClassLoader.getResourceAsStream("example/dynamic/eclipselink-oxm.xml");
// HashMap<String, InputStream> metadataSourceMap = new HashMap<String, InputStream>();
// metadataSourceMap.put(contextPath, iStream);
//
// Map<String, Object> properties = new HashMap<String, Object>();
// properties.put(JAXBContextFactory.USE_DYNAMIC_PERSISTENCE, Boolean.TRUE);
// properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, metadataSourceMap);

Because JAXB supports a collection of context paths, we pass in a Map of InputStreams, one for each context path being used. Also note that we pass in a system class loader; the user does not need to know about DynamicClassLoader, this is handled under the covers.

In JAXBContextFactory, we then use DynamicTypeBuilder and DynamicClassLoader to obtain a dynamic project, create an XMLContext from that project, and then create a JAXBContext from the XMLContext:

if (properties.get(USE_DYNAMIC_PERSISTENCE).equals(Boolean.TRUE)) {
    StringTokenizer tokenizer = new StringTokenizer(contextPath, ":");
    DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(classLoader);
 
    HashMap<String, InputStream> metadataSourceMap = (HashMap<String, InputStream>) properties.get(ECLIPSELINK_DEPLOYMENT_XML);
 
    List<Project> projects = new ArrayList<Project>();
    try {
        while (tokenizer.hasMoreElements()) {
            String path = tokenizer.nextToken();
            InputStream inputStream = metadataSourceMap.get(path);
            Project p = DynamicTypeBuilder.loadDynamicProject(inputStream, null, dynamicClassLoader);
            projects.add(p);
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
 
    return new JAXBContext(new XMLContext(projects, dynamicClassLoader));
}

Bootstrapping from XML Schema

To facilitate bootstrapping from an XML Schema (XSD), we can use APIs from the Java XJC to parse a schema and create Java class definitions (XJC's JCodeModel) in memory, then pass these class definitions to an EclipseLink Generator (org.eclipse.persistence.jaxb.compiler.Generator) to generate an EclipseLink project. After we have created this project, we can use the DynamicTypeBuilder to create a dynamic project, generating Java classes in memory along the way.

First, we use XJC API to parse a

SchemaCompiler sc = XJC.createSchemaCompiler();
File schemaFile = new File("src/rick/dynamic/employee.xsd");
InputSource inputSource = new InputSource(schemaFile.toURI().toURL().toExternalForm());
sc.parseSchema(inputSource);
S2JJAXBModel model = sc.bind();
JCodeModel jCodeModel = model.generateCode(new Plugin[0], null);

Adding Properties/Mappings at Runtime

One of the main aspects of this feature is the ability to add new mappings to an existing running system (aligning with JPA's support for this scenario).

  • Mappings must be fully initialized before they are added to the Descriptor, for thread-safety.
  • Mapping customizations will be isolated to the current XMLContext, i.e. changes made to a Descriptor will only be visible in that Descriptor's XMLContext, even if multiple XMLContexts were created from the same initial metadata.

Testing

API

GUI

No GUI is required for this feature.

Config files

A new metadata file may be needed to specify and persist new mapping information. (See Open Issues below)

Documentation

Open Issues

This section lists the open issues that are still pending that must be decided prior to fully implementing this project's requirements.

Issue # Owner Description / Notes
001 Rick Barkhouse What naming conventions should we use for the new Property keys that can be passed in to JAXBContextFactory?
002 Rick Barkhouse How will we handle concurrency / threading issues when we allow modifying mappings at runtime? Do objects created BEFORE the Type was modified reflect the new mappings?
003 Rick Barkhouse How are new mappings specified on existing Descriptors? Is an additional metadata file required to hold the new mapping information?

Decisions

This section lists decisions made. These are intended to document the resolution of open issues or constraints added to the project that are important.

Issue # Description / Notes Decision

Future Considerations

During the research for this project the following items were identified as out of scope but are captured here as potential future enhancements. If agreed upon during the review process these should be logged in the bug system.

Back to the top