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.
Difference between revisions of "EclipseLink/Development/2.1/DynamicMOXy/296967"
(→Bootstrapping from Deployment XML) |
|||
Line 35: | Line 35: | ||
== Project Overview == | == 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 | + | 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 External Metadata) to our <code>JAXBContextFactory</code>, and this metadata will be used to construct <code>DynamicEntity</code> 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. | 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. | ||
Line 41: | Line 41: | ||
The progress of this feature can be broken up into the following parts: | The progress of this feature can be broken up into the following parts: | ||
* Bootstrapping JAXB Context with EclipseLink Deployment XML / Project object (no domain classes on classpath) | * Bootstrapping JAXB Context with EclipseLink Deployment XML / Project object (no domain classes on classpath) | ||
− | * Bootstrapping JAXB Context with new | + | * Bootstrapping JAXB Context with new External Metadata (no domain classes on classpath) |
* Bootstrapping JAXB Context with XML Schema (no domain classes on classpath) | * Bootstrapping JAXB Context with XML Schema (no domain classes on classpath) | ||
* New public API to bootstrap using the above methods | * New public API to bootstrap using the above methods | ||
Line 119: | Line 119: | ||
=== Bootstrapping from XML Schema === | === Bootstrapping from XML Schema === | ||
− | + | A user bootstrapping from an XML Schema can use the following code: | |
+ | |||
+ | <source lang="java5"> | ||
+ | File schemaFile = new File("resource/xsd/employee.xsd"); | ||
+ | ClassLoader loader = Thread.currentThread().getContextClassLoader(); | ||
+ | |||
+ | DynamicJAXBContext jaxbContext = new DynamicJAXBContext(schemaFile.toURI().toURL(), DynamicJAXBContext.XML_SCHEMA, loader); | ||
+ | JAXBMarshaller jaxbMarshaller = jaxbContext.createMarshaller(); | ||
+ | JAXBUnmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); | ||
+ | </source> | ||
+ | |||
+ | Internally, we can use APIs from the Java XJC to parse a schema and create Java class definitions (XJC's <code>JCodeModel</code>) in memory, then pass these class definitions to an EclipseLink <code>Generator</code> (<code>org.eclipse.persistence.jaxb.compiler.Generator</code>) to generate an EclipseLink project. After we have created this project, we can use the <code>DynamicTypeBuilder</code> to create a dynamic project, generating Java classes in memory along the way. | ||
First, we use XJC API to parse an XSD and generate a <code>JCodeModel</code>. Note that this code stops short of actually generating .java files, it only generates an in-memory representation of the Java files that would be generated. | First, we use XJC API to parse an XSD and generate a <code>JCodeModel</code>. Note that this code stops short of actually generating .java files, it only generates an in-memory representation of the Java files that would be generated. | ||
<source lang="java5"> | <source lang="java5"> | ||
+ | // Use XJC API to parse the schema and generate its JCodeModel | ||
SchemaCompiler sc = XJC.createSchemaCompiler(); | SchemaCompiler sc = XJC.createSchemaCompiler(); | ||
− | + | InputSource inputSource = new InputSource(metadataURL.toExternalForm()); | |
− | InputSource inputSource = new InputSource( | + | |
sc.parseSchema(inputSource); | sc.parseSchema(inputSource); | ||
S2JJAXBModel model = sc.bind(); | S2JJAXBModel model = sc.bind(); | ||
Line 170: | Line 181: | ||
} | } | ||
} | } | ||
− | JavaClass[] jotClasses = | + | JavaClass[] jotClasses = createClassModelFromXJC(classesToProcess, jCodeModel); |
</source> | </source> | ||
− | At this point, we can instantiate a Generator and obtain a "dry" EclipseLink project, which we can then turn into a Dynamic Project with generated in-memory classes. Finally, we | + | At this point, we can instantiate a Generator and obtain a "dry" EclipseLink project, which we can then turn into a Dynamic Project with generated in-memory classes. Finally, we create and store an <code>XMLContext</code> and a <code>DynamicHelper</code>: |
<source lang="java5"> | <source lang="java5"> | ||
Line 183: | Line 194: | ||
// Make a Dynamic Project from this project, because these classes do not exist on the classpath | // Make a Dynamic Project from this project, because these classes do not exist on the classpath | ||
− | + | DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(classLoader); | |
− | DynamicClassLoader dynamicClassLoader = new DynamicClassLoader( | + | Project dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader); |
− | Project dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader); | + | |
− | + | this.xmlContext = new XMLContext(dp); | |
− | + | this.dynamicHelper = new DynamicHelper(xmlContext.getSession(0)); | |
− | + | ||
</source> | </source> | ||
Revision as of 15:44, 7 January 2010
Contents
- 1 Design Specification: MOXy Support for Dynamic Persistence
Design Specification: MOXy Support for Dynamic Persistence
Document History
Date | Author | Version Description & Notes |
---|---|---|
091204 | Rick Barkhouse | 1.0 |
091207 | Rick Barkhouse | 1.1 - Added Bootstrapping from Deployment XML |
091217 | Rick Barkhouse | 1.2 - Added Bootstrapping from XSD |
100104 | Rick Barkhouse | 1.3 - Expanded Overview to identify feature milestones |
100107 | Rick Barkhouse | 1.4 - Introduced DynamicJAXBContext, reorganized Design |
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 External Metadata) 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.
The progress of this feature can be broken up into the following parts:
- Bootstrapping JAXB Context with EclipseLink Deployment XML / Project object (no domain classes on classpath)
- Bootstrapping JAXB Context with new External Metadata (no domain classes on classpath)
- Bootstrapping JAXB Context with XML Schema (no domain classes on classpath)
- New public API to bootstrap using the above methods
- Support for adding arbitrary properties/mappings at runtime, after initialization has been performed
- Thread Safety
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(Thread.currentThread().getContextClassLoader()));
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
DynamicJAXBContext
The entry point to this feature is a new class, DynamicJAXBContext
, which is a subclass of the existing EclipseLink JAXBContext
(which is itself a subclass of Sun's JAXBContext
). Because this functionality is proprietary and not part of the JAXB spec, we will not provide creation APIs on JAXBContextFactory
.
public DynamicJAXBContext(URL metadataURL, int metadataFormat, ClassLoader classLoader)
-
URL metadataURL
- A Java URL pointing to the metadata (XML Schema, EclipseLink Deployment XML, or EclipseLink External Metadata) -
int metadataFormat
- One ofDynamicJAXBContext.XML_SCHEMA
,DynamicJAXBContext.DEPLOYMENT_XML
, orDynamicJAXBContext.EXT_METADATA
-
ClassLoader classLoader
- The current class loader; aDynamicClassLoader
will be created with this loader as its parent
Bootstrapping from Deployment XML
A user bootstrapping from Deployment XML can use the following code:
File projectFile = new File("resource/eclipselink/project.xml"); ClassLoader loader = Thread.currentThread().getContextClassLoader(); DynamicJAXBContext jaxbContext = new DynamicJAXBContext(projectFile.toURI().toURL(), DynamicJAXBContext.DEPLOYMENT_XML, loader); JAXBMarshaller jaxbMarshaller = jaxbContext.createMarshaller(); JAXBUnmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Internally, a DynamicClassLoader
is created using the supplied ClassLoader
as its parent. An InputStream
is created from the metadata URL and used to create a Project
mapped to Dynamic Entities. Finally, XMLContext
and DynamicHelper
objects are instantiated and stored.
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(classLoader); InputStream inputStream = metadataURL.openStream(); Project p = DynamicTypeBuilder.loadDynamicProject(inputStream, null, dynamicClassLoader); this.xmlContext = new XMLContext(p); this.dynamicHelper = new DynamicHelper(xmlContext.getSession(0));
Bootstrapping from XML Schema
A user bootstrapping from an XML Schema can use the following code:
File schemaFile = new File("resource/xsd/employee.xsd"); ClassLoader loader = Thread.currentThread().getContextClassLoader(); DynamicJAXBContext jaxbContext = new DynamicJAXBContext(schemaFile.toURI().toURL(), DynamicJAXBContext.XML_SCHEMA, loader); JAXBMarshaller jaxbMarshaller = jaxbContext.createMarshaller(); JAXBUnmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Internally, 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 an XSD and generate a JCodeModel
. Note that this code stops short of actually generating .java files, it only generates an in-memory representation of the Java files that would be generated.
// Use XJC API to parse the schema and generate its JCodeModel SchemaCompiler sc = XJC.createSchemaCompiler(); InputSource inputSource = new InputSource(metadataURL.toExternalForm()); sc.parseSchema(inputSource); S2JJAXBModel model = sc.bind(); JCodeModel jCodeModel = model.generateCode(new Plugin[0], null);
We can then wrap these XJC classes in our own implentations of EclipseLink's JAXB JavaModel
classes. This will allow us to use the Generator
to create an EclipseLink project and mappings. The JavaModel
interfaces define "wrappers" for the various Java language constructs that make up the domain model (e.g. classes, methods, constructors, annotations, packages, etc). For example:
public class XJCJavaFieldImpl implements JavaField { // XJC's definition of a Field protected JFieldVar xjcField; ... public int getModifiers() { return xjcField.mods().getValue(); } public String getName() { return xjcField.name(); } ... }
Creating the JavaModel classes:
// Create EclipseLink JavaModel objects for each of XJC's JDefinedClasses ArrayList<JDefinedClass> classesToProcess = new ArrayList<JDefinedClass>(); Iterator<JPackage> packages = jCodeModel.packages(); while (packages.hasNext()) { JPackage pkg = packages.next(); Iterator<JDefinedClass> classes = pkg.classes(); while (classes.hasNext()) { JDefinedClass cls = classes.next(); if (!cls.name().equals("ObjectFactory")) { classesToProcess.add(cls); } } } JavaClass[] jotClasses = createClassModelFromXJC(classesToProcess, jCodeModel);
At this point, we can instantiate a Generator and obtain a "dry" EclipseLink project, which we can then turn into a Dynamic Project with generated in-memory classes. Finally, we create and store an XMLContext
and a DynamicHelper
:
// Use the JavaModel to setup a Generator to generate an EclipseLink project XJCJavaModelImpl javaModel = new XJCJavaModelImpl(Thread.currentThread().getContextClassLoader(), jCodeModel); XJCJavaModelInputImpl javaModelInput = new XJCJavaModelInputImpl(jotClasses, javaModel); Generator g = new Generator(javaModelInput); Project p = g.generateProject(); // Make a Dynamic Project from this project, because these classes do not exist on the classpath DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(classLoader); Project dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader); this.xmlContext = new XMLContext(dp); this.dynamicHelper = new DynamicHelper(xmlContext.getSession(0));
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.