|
|
Line 2: |
Line 2: |
| ===== Dynamic Persistence ===== | | ===== Dynamic Persistence ===== |
| Dynamic Persistence is defined as the ability to create a persistent entity class and use it within an application without <i>a-priori</i> the Java class existing (no <tt>.class</tt> file on the classpath or in the relevant <tt>.jar/.war</tt> archive). | | Dynamic Persistence is defined as the ability to create a persistent entity class and use it within an application without <i>a-priori</i> the Java class existing (no <tt>.class</tt> file on the classpath or in the relevant <tt>.jar/.war</tt> archive). |
− |
| |
− | ===== Mechanism in Java5/Java6 =====
| |
− | 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]).
| |
− |
| |
− | Java classloaders form an instance-hierarchy at run-time, with the system (<b>Bootstrap</b>, <b>Extension</b> and <b>System</b>) class loaders
| |
− | strictly controlled by the JVM. Once an application is launched (via an Application loader), a new loader <code>MyCustomClassLoader</code> can
| |
− | be added to the chain.
| |
− |
| |
− | <br />
| |
− | [[Image:custom_classloader.gif]]
| |
− | <br />
| |
− | The basic implementation pattern is as follows - in the constructor, the new instance of <code>MyCustomClassLoader</code> is added to the
| |
− | runtime instance-hierarchy by calling <code>super</code> with the parent loader.
| |
− | <source lang=java5>
| |
− | public class MyCustomClassLoader extends ClassLoader {
| |
− |
| |
− | public MyCustomClassLoader (ClassLoader parent) {
| |
− | super(parent);
| |
− | }
| |
− |
| |
− | @Override
| |
− | protected Class<?> findClass(String className) throws ClassNotFoundException {
| |
− | if (some_condition) {
| |
− | 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);
| |
− | }
| |
− |
| |
− | }
| |
− | </source>
| |
− |
| |
− | 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
| |
− | for <code>className</code>. 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 <u>much</u> more complicated, but 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.
| |
Dynamic Persistence is defined as the ability to create a persistent entity class and use it within an application without a-priori the Java class existing (no .class file on the classpath or in the relevant .jar/.war archive).