Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/Dynamic/Design DynamicClassCreation"

 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
<css>
 +
  .source-java5 {border-style: solid;}
 +
</css>
 
= Dynamic Persistence Design: Class Creation =
 
= Dynamic Persistence Design: Class Creation =
 
+
This page describes the design for the creation of dynamic persistent entity classes without having source.  
This page describes the design in dynamic persistence for the creation of persistent classes without having source.  
+
  
 
== Background ==
 
== Background ==
 
+
EclipseLink requires a Java class with the following capabilities to be a persistent entity:
EclipseLink requires a concrete Java class with the following capabilities to be a persistent entity:
+
* Ability to be uniquely identified by class: <code>entity.getClass()</code> must be unique (compared using <code>==</code>) for a ClassDescriptor in a given Project/Session
* Ability to uniquely identify by type: entity.getClass() must be unique for a ClassDescriptor in a given session
+
* Ability to create dynamic persistent entities using EclipseLink Instantiation policies - during reading, creating, cloning
* Ability to create instances of the persistent type during reading, cloning, and new instance creation (UnitOfWork.newInstance())
+
* Ability to retrieve values from and store values into dynamic persistent entities using a custom <code>AttributeAccessor</code>
* Ability to retrieve and store values from the persistent entity using the mapping's AttributeAccessor
+
* Ability to do type conversions to align the value coming from the data store w.r.t the value stored in the entity. It uses the mapping's attribute classification info for this purpose.
* Ability to do type conversions to align the value coming from a data store with the value stored in the object. It uses the AttributeAccessor's classification type for this purpose.
+
<br/>
 
+
EclipseLink requires only minor 'tweaks' to deal with the above requirements and in general treats dynamic persistent entity classes no differently than 'regular' classes.
Dynamic persistence allows an application to use EclipseLink's persistence capabilities without requiring a static java class definition (.class file) to exist on the classpath at runtime. It does this using ASM to generate the byte-codes for a 'dynamic' class at runtime and convert these byte-codes into a class that it uses as if it were a static class. The EclipseLink runtime requires some special configuration to deal with the above requirements but in general it treats these dynamic classes no differently then it would a static class.  
+
  
 
== Design ==
 
== Design ==
 
 
In order to create a Java class at runtime without Java source code, the use of a custom ClassLoader is required, along with a bytecode
 
In order to create a Java class at runtime without Java source code, the use of a custom ClassLoader is required, along with a bytecode
 
manipulation framework (such as [http://asm.objectweb.org ASM] or some other [http://www.java-source.net/open-source/bytecode-libraries library]).
 
manipulation framework (such as [http://asm.objectweb.org ASM] or some other [http://www.java-source.net/open-source/bytecode-libraries library]).
Line 36: Line 36:
 
     @Override
 
     @Override
 
     protected Class<?> findClass(String className) throws ClassNotFoundException {
 
     protected Class<?> findClass(String className) throws ClassNotFoundException {
         if (some_condition) {
+
         if (className_is_registered_for_dynamic_generation) {
 
             try {
 
             try {
 
                 byte[] bytes = use_framework_to_generate_bytecode();
 
                 byte[] bytes = use_framework_to_generate_bytecode();
Line 50: Line 50:
 
}
 
}
 
</source>
 
</source>
 
+
The <code>findClass</code> method is overridden so that if some condition is met - say for example the className has been previously registered for dynamice generation - the bytecode for the Class <code>className</code> is generated; otherwise, the call is delegated up the instance-hierarchy to search for the class. The implementation hierarchy is responsible for maintaining a cache of classes, as well as any resources that have been loaded (XML descriptor files, image files, etc). Thus, an instance of <code>MyCustomClassLoader</code> behaves as a 'proper' class loader in all cases, with the additional capability that non-existent classes can be built/found without their corresponding <tt>.class</tt> files being on the JVM's classpath.
The <code>findClass</code> method is overridden so that if some condition is met, the bytecode for the Class <code>className</code> is
+
generated; otherwise, the call is delegated up the instance-hierarchy to search for the class. The implementation hierarchy is responsible
+
for maintaining a cache of classes, as well as any resources that have been loaded (XML descriptor files, image files, etc). Thus, an instance
+
of <code>MyCustomClassLoader</code> behaves as a 'proper' class loader in all cases, with the additional capability that non-existent
+
classes can be built/found without their corresponding <tt>.class</tt> files being on the JVM's classpath.
+
  
 
<b><u>NB</u></b>: it is important to note that two separate instances of <code>MyCustomClassLoader</code> can generate two classes
 
<b><u>NB</u></b>: it is important to note that two separate instances of <code>MyCustomClassLoader</code> can generate two classes
 
for <code>className</code>. Even though the bytecode is identical, the classes are distinct. When running under a Java EE&trade; container
 
for <code>className</code>. Even though the bytecode is identical, the classes are distinct. When running under a Java EE&trade; container
(or an OSGi environment), the class loader hierarchy may be <u>much</u> more complicated, but the point remains - a custom classloader is
+
(or an OSGi environment), the class loader hierarchy may be <u>much</u> more complicated than the simple chain pictured above; however,
required to build classes at runtime; the custom classloader is part of a chain of loaders and each dynamic class is distinct.
+
the point remains - a custom classloader is required to build classes at runtime; the custom classloader is part of a chain of loaders and each dynamic class is distinct. It is thus incumbent upon the designer to ensure that the correct loader is used throughout the lifecycle of the application.
It is thus incumbent upon the designer to ensure that the correct loader is used throughout the lifecycle of the application.
+

Latest revision as of 14:49, 22 September 2009

Dynamic Persistence Design: Class Creation

This page describes the design for the creation of dynamic persistent entity classes without having source.

Background

EclipseLink requires a Java class with the following capabilities to be a persistent entity:

  • Ability to be uniquely identified by class: entity.getClass() must be unique (compared using ==) for a ClassDescriptor in a given Project/Session
  • Ability to create dynamic persistent entities using EclipseLink Instantiation policies - during reading, creating, cloning
  • Ability to retrieve values from and store values into dynamic persistent entities using a custom AttributeAccessor
  • Ability to do type conversions to align the value coming from the data store w.r.t the value stored in the entity. It uses the mapping's attribute classification info for this purpose.


EclipseLink requires only minor 'tweaks' to deal with the above requirements and in general treats dynamic persistent entity classes no differently than 'regular' classes.

Design

In order to create a Java class at runtime without Java source code, the use of a custom ClassLoader is required, along with a bytecode manipulation framework (such as ASM or some other library).

Java classloaders form an instance-hierarchy at run-time, with the system (Bootstrap, Extension and System) class loaders strictly controlled by the JVM. Once an application is launched (via an Application loader), a new loader MyCustomClassLoader can be added to the chain.


Custom classloader.gif
The basic implementation pattern is as follows - in the constructor, the new instance of MyCustomClassLoader is added to the runtime instance-hierarchy by calling super with the parent loader.

public class MyCustomClassLoader extends ClassLoader {
 
    public MyCustomClassLoader (ClassLoader parent) {
        super(parent);
    }
 
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        if (className_is_registered_for_dynamic_generation) {
            try {
                byte[] bytes = use_framework_to_generate_bytecode();
                return defineClass(className, bytes, 0, bytes.length);
            }
            catch (ClassFormatError cfe) {
                throw new ClassNotFoundException(className, cfe);
            }
        }
        return super.findClass(className);
    }
 
}

The findClass method is overridden so that if some condition is met - say for example the className has been previously registered for dynamice generation - the bytecode for the Class className is generated; otherwise, the call is delegated up the instance-hierarchy to search for the class. The implementation hierarchy is responsible for maintaining a cache of classes, as well as any resources that have been loaded (XML descriptor files, image files, etc). Thus, an instance of MyCustomClassLoader behaves as a 'proper' class loader in all cases, with the additional capability that non-existent classes can be built/found without their corresponding .class files being on the JVM's classpath.

NB: it is important to note that two separate instances of MyCustomClassLoader can generate two classes for className. Even though the bytecode is identical, the classes are distinct. When running under a Java EE™ container (or an OSGi environment), the class loader hierarchy may be much more complicated than the simple chain pictured above; however, the point remains - a custom classloader is required to build classes at runtime; the custom classloader is part of a chain of loaders and each dynamic class is distinct. It is thus incumbent upon the designer to ensure that the correct loader is used throughout the lifecycle of the application.

Back to the top