Hi Martin,

I have implemented the JavaModel changes as we discussed. Now the Class.forName is called in a doPrivileged block to avoid the SecurityException. The other change is that I moved all the code that checks for the right class loader to the RuntimeJavaModel. A JavaType lookup by name first gets the class instance using the class loader bound to the model instance. Then it gets the class loader from the class instance and delegates to JavaModel bound to this class loader. This makes sure that a JavaModel handles only classes that are loaded by th class loader bound to the JavaModel. There is one exception: all JavaModel instances know about all the PredefinedType instances (JavaType instances for primitive types, Java wrapper class, java.util classes etc.).

Attached find two patches created from the trunk(!) directory:
- JDO1-JavaModel.patch: ri11 changes. I tested this running the ri11 tests and tck11 in a jdk1.5 environment (I'm using the enhancer changes you send over). - JDO2-JavaModel.patch: the corresponding changes for JDO2. This changes classes in core20, enhancer20, runtime20 and fostore20. I run the fostore20 tests with jdk 1.4.

Please let me know what you think.

Regards Michael


Hi Michael,

unfortunately, the fix for the class registration problem, as we
discussed it and as it was checked in, causes most of the ri11
security manager junit tests to fail (138 out of 149 tests).

The symptom is an AccessControlException ("getClassLoader") within
method Class.forName(), called from ReflectionJavaModel.java:130.

The issue is exposed by an almost trivial change that we discussed
(see below): When calling Class.forName() to initialize a class, we
decided to use the class' classloader instead of the one stored in
the JavaModel instance.  As you pointed out, we then must fetch the
classloader in a doPrivileged block.  (This block succeeds, the
exception is really raised within Class.forName()).

Though the code assumes that the stored and the fetched classloader
are always the same here, we discussed that if they're not, this
would result in an internal error that is very hard to track down,
since the consequences might show up later and elsewhere depending
on the order in which classes are registered.  We considered to
guard against this case with an assertion or an explicit check.

It turns out the stored and the fetched classloader may differ for
two reasons, I think.

First, clazz.getClassLoader() returns null for a number of classes
like Date and ArrayList.  Craig has pointed out that this is the
specified behaviour for classes loaded by the bootstrap classloader.

The javadoc on Class.forName() says that if the loader argument is
null (and other conditions apply), the security manager is checked
for permission ("getClassLoader") to access the bootstrap class
loader.  This check fails.

When using the stored classloader, which is always non-null, instead
of the fetched one as argument to clazz.forName(), this method does
not issue a "getClassLoader" request, and all tests security manager
junit tests pass then.

But I'm not sure this is the right solution either.  Essentially,
we'd represent Date, ArrayList etc. a multiple times in the Java
model - in each model instance per application classloader - while
they're only represented once in the JVM, by the bootstrap loader.

There might be another case to consider why the stored and the
fetched classloader may differ:  An application that uses multiple
classloaders forming a parent-child hierarchy, it may happen that
a PC class is loaded by a child classloader while it's superclasses
or the persistent field types are loaded by a parent classloader.

In this case, we probably do not want to have the type universe be
duplicated in each java model instance (i.e. per classloader), but
rather represent a class in the java model only as many times as
it's loaded in the VM, that is, per "owning" classloader.

Now, I do not know if we're already doing this (class represented in
java model only for "owning" classloader), but it seems to me that
the implicit assumption in ReflectionJavaModel.getJavaType() that the
stored and the fetched classloader are always the same does not hold.

That's why I'm not sure that always using the stored classloader
(instead of the fetched one) would be a general solution either.

Any thoughts?

Sorry for the long email.  If you'd rather like to discuss details
over phone, I'd have some time on Thursday morning (until 9:30am) or
on Friday during or after our JDO phone con.  Craig and I discussed
this issue briefly today.

Thx,
Martin


--
Michael Bouschen                [EMAIL PROTECTED] Engineering GmbH
mailto:[EMAIL PROTECTED]        http://www.tech.spree.de/
Tel.:++49/30/235 520-33         Buelowstr. 66                   
Fax.:++49/30/2175 2012          D-10783 Berlin                  

Index: ri11/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties      
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties      
(Arbeitskopie)
@@ -43,3 +43,18 @@
 #NOI18N
 ERR_CannotSetJDOModel=Cannot set JDOModel for JavaModel instance.
 
+#
+# RuntimeJavaModel
+#
+
+# {0} - class name
+# {1} - detailed message of the cause
+EXC_ClassLoadingError=Error during loading of class ''{0}'': {1}.
+
+# {0} - class name
+# {1} - class loader of class instance
+# {2} - class loader of JavaModel
+#NOI18N
+ERR_UnexpectedClassLoader=Unexpected class loader. The specified class \
+instance of class ''{0}'' was loaded by class loader ''{1}'', but the \
+JavaModel instance is bound to a different class loader ''{2}''.
Index: 
ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java  
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java  
(Arbeitskopie)
@@ -16,11 +16,9 @@
 
 package org.apache.jdo.impl.model.java.runtime;
 
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.io.InputStream;
-
+import org.apache.jdo.impl.model.java.PredefinedType; // for javadoc
 import org.apache.jdo.impl.model.java.reflection.ReflectionJavaModel;
+import org.apache.jdo.impl.model.java.reflection.ReflectionJavaModelFactory;
 import org.apache.jdo.model.ModelFatalException;
 import org.apache.jdo.model.java.JavaType;
 import org.apache.jdo.model.jdo.JDOModel;
@@ -35,26 +33,126 @@
  * The RuntimeJavaModelFactory caches JavaModel instances per ClassLoader.
  * The RuntimeJavaModel implementation will use this ClassLoader to lookup
  * any type by name. This makes sure that the type name is unique.
+ * <p>
+ * Any JavaType instance bound to a RuntimeJavaModel instance wraps a class
+ * instacne that is loaded by the ClassLoader that corresponds to this
+ * RuntimeJavaModel. The only exception are PredefinedType instances 
+ * (see [EMAIL PROTECTED] PredefinedType.getPredefinedTypes()} which are 
present in every
+ * RuntimeJavaModel instance.
  *
- * @author Michael Bouschen
- * @since JDO 1.0.1
+ * @since 1.0.1
+ * @version 1.1
  */
 public class RuntimeJavaModel
     extends ReflectionJavaModel
 {
-    /** Constructor taking the ClassLoader. */
-    public RuntimeJavaModel(ClassLoader classLoader) 
+    /** I18N support */
+    private final static I18NHelper msg =  
+        
I18NHelper.getInstance("org.apache.jdo.impl.model.java.runtime.Bundle"); 
//NOI18N
+
+    /** Constructor. */
+    public RuntimeJavaModel(ClassLoader classLoader, 
+                            ReflectionJavaModelFactory 
declaringJavaModelFactory) 
     {
-        super(classLoader, true);
+        super(classLoader, declaringJavaModelFactory);
     }
     
-    /** */
-    protected RuntimeJavaModel(ClassLoader classLoader, boolean initialize)
+    /** 
+     * The method returns the JavaType instance for the specified type
+     * name. A type name is unique within one JavaModel instance. The
+     * method returns <code>null</code> if this model instance does not
+     * know a type with the specified name.
+     * <p>
+     * Note, this method calls Class.forName with the wrapped ClassLoader,
+     * if it cannot find a JavaType with the specified name in the cache.
+     * @param name the name of the type
+     * @return a JavaType instance for the specified name or
+     * <code>null</code> if not present in this model instance.
+     */
+    public JavaType getJavaType(String name) 
     {
-        super(classLoader, initialize);
+        synchronized (types) {
+            JavaType javaType = (JavaType)types.get(name);
+            if (javaType == null) {
+                try {
+                    // Note, if name denotes a pc class that has not been
+                    // loaded, Class.forName will load the class which will
+                    // register the runtime metadata at the JDOImplHelper.
+                    // This will create a new JavaType entry in the cache.
+                    Class clazz = RuntimeJavaModelFactory.forNamePrivileged(
+                        name, true, getClassLoader());
+                    // Get the class loader of the class instance
+                    ClassLoader loader = RuntimeJavaModelFactory.
+                        getClassLoaderPrivileged(clazz);
+                    // and get the JavaModel instacne for the class loader. 
+                    RuntimeJavaModel javaModel = (RuntimeJavaModel)
+                        declaringJavaModelFactory.getJavaModel(loader);
+                    // Delegate the JavaType lookup to the JavaModel instance.
+                    javaType = javaModel.getJavaTypeInternal(clazz);
+                }
+                catch (ClassNotFoundException ex) {
+                    // cannot find class => return null
+                }
+                catch (LinkageError ex) {
+                    throw new ModelFatalException(msg.msg(
+                        "EXC_ClassLoadingError", name, ex.toString())); 
//NOI18N
+                }
+            }
+            return javaType;
+        }
     }
 
     /** 
+     * The method returns the JavaType instance for the type name of the
+     * specified class object. This is a convenience method for 
+     * <code>getJavaType(clazz.getName())</code>. The major difference
+     * between this method and getJavaType taking a type name is that this 
+     * method is supposed to return a non-<code>null<code> value. The
+     * specified class object describes an existing type.
+     * <p>
+     * Note, this implementation does not call the overloaded getJavaType
+     * method taking a String, because this would retrieve the Class
+     * instance for the specified type again. Instead, it checks the cache 
+     * directly. If not available it creates a new ReflectionJavaType using
+     * the specified class instance.
+     * @param clazz the Class instance representing the type
+     * @return a JavaType instance for the name of the specified class
+     * object or <code>null</code> if not present in this model instance.
+     */
+    public JavaType getJavaType(Class clazz)
+    {
+        if (clazz == null)
+            return null;
+        
+        String name = clazz.getName();
+        synchronized (types) {
+            JavaType javaType = (JavaType)types.get(name);
+            if (javaType == null) {
+                ClassLoader loader = 
+                    RuntimeJavaModelFactory.getClassLoaderPrivileged(clazz);
+                // check whether the specified class object is loaded by the
+                // class loader bound to this JavaModel
+                if (loader != getClassLoader()) {
+                    throw new ModelFatalException(msg.msg(
+                        "ERR_UnexpectedClassLoader", //NOI18N
+                        clazz.getName(), loader, getClassLoader()));
+                }
+                try {
+                    // Make sure the class is initialized, because this will
+                    // register the runtime metadata at the JDOImplHelper. 
+                    RuntimeJavaModelFactory.forNamePrivileged(
+                        clazz.getName(), true, loader);
+                }
+                catch (ClassNotFoundException ex) {
+                    // ignore, since class has already been loaded 
+                }
+                javaType = getJavaTypeInternal(clazz);
+            }
+            return javaType;
+        }
+    }
+
+    /** 
      * Creates a new JavaType instance for the specified Class object.
      * This method provides a hook such that RuntimeJavaModel subclasses
      * can create instances of a different JavaType implementation. 
Index: 
ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
===================================================================
--- 
ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
   (Revision 327115)
+++ 
ri11/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
   (Arbeitskopie)
@@ -18,7 +18,6 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.lang.reflect.Field;
 
 import javax.jdo.spi.JDOImplHelper;
 import javax.jdo.spi.JDOPermission;
@@ -48,8 +47,8 @@
  * JDOImplHelper to handle the runtime metadata as generated by the
  * enhancer. 
  * 
- * @author Michael Bouschen
- * @since JDO 1.0.1
+ * @since 1.0.1
+ * @version 1.1
  */
 public class RuntimeJavaModelFactory
     extends ReflectionJavaModelFactory
@@ -60,7 +59,7 @@
 
     /** I18N support */
     private final static I18NHelper msg =  
-        
I18NHelper.getInstance("org.apache.jdo.impl.model.java.runtime.Bundle"); 
//NOI18N
+        I18NHelper.getInstance(RuntimeJavaModelFactory.class);
 
     /** */
     static
@@ -127,8 +126,7 @@
      * use the specified key when caching the new JavaModel instance. 
      * <p>
      * This implementation only accepts <code>java.lang.ClassLoader</code>
-     * instances as key and does not support <code>null</code> keys. A
-     * ModelException indicates an invalid key.
+     * instances as key. A ModelException indicates an invalid key.
      * <p>
      * The method automatically sets the parent/child relationship for the
      * created JavaModel according to the parent/child relationship of the 
@@ -137,28 +135,23 @@
      * instance. 
      * @return a new JavaModel instance.
      * @exception ModelException if impossible; the key is of an
-     * inappropriate type or the key is <code>null</code> and this
-     * JavaModelFactory does not support <code>null</code> keys.
+     * inappropriate type.
      */
     public JavaModel createJavaModel(Object key)
         throws ModelException
     {
-        if ((key == null) || (!(key instanceof ClassLoader)))
+        if ((key != null) && (!(key instanceof ClassLoader)))
             throw new ModelException(msg.msg("EXC_InvalidJavaModelKey", 
//NOI18N
-                (key==null?"null":key.getClass().getName())));
+                key.getClass().getName()));
         
         ClassLoader classLoader = (ClassLoader)key;
-        JavaModel javaModel = new RuntimeJavaModel(classLoader);
+        JavaModel javaModel = new RuntimeJavaModel(classLoader, this);
 
         // check parent <-> child relationship
-        if (classLoader != ClassLoader.getSystemClassLoader()) {
-            // if the specified classLoader is not the system class loader
-            // try to get the parent class loader and update the parent 
property
+        if (classLoader != null) {
             try {
                 ClassLoader parentClassLoader = classLoader.getParent();
-                if (parentClassLoader != null) {
-                    javaModel.setParent(getJavaModel(parentClassLoader));
-                }
+                javaModel.setParent(getJavaModel(parentClassLoader));
             }
             catch (SecurityException ex) {
                 // ignore => parentClassLoader and parent JavaModel are null
@@ -172,20 +165,6 @@
     }
 
     /**
-     * Returns the JavaModel instance for the specified key.
-     * @param key the key used to cache the returned JavaModel instance
-     */
-    public JavaModel getJavaModel(Object key)
-    {
-        if (key == null) {
-            // null classLoader might happen for classes loaded by the
-            // bootstrap class loder
-            key = ClassLoader.getSystemClassLoader();
-        }
-        return super.getJavaModel(key);
-    }
-    
-    /**
      * Returns a JavaType instance for the specified type description
      * (optional operation). This method is a convenience method and a
      * short cut for <code>getJavaModel(key).getJavaType(typeName)</code>.
Index: 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
===================================================================
--- 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
     (Revision 327115)
+++ 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
     (Arbeitskopie)
@@ -18,19 +18,17 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 
-import org.apache.jdo.model.ModelException;
 import org.apache.jdo.model.ModelFatalException;
-import org.apache.jdo.model.java.JavaModel;
-import org.apache.jdo.model.java.JavaType;
 import org.apache.jdo.impl.model.java.AbstractJavaModelFactory;
-import org.apache.jdo.impl.model.java.BaseReflectionJavaType;
 import org.apache.jdo.util.I18NHelper;
 
 /**
  * A reflection based JavaModelFactory implementation. 
  * 
- * @since JDO 1.1
+ * @since 1.1
  */
 public abstract class ReflectionJavaModelFactory
     extends AbstractJavaModelFactory
@@ -45,6 +43,7 @@
      * Calls getClassLoader on the specified class instance in a
      * doPrivileged block. Any SecurityException is wrapped into a
      * ModelFatalException. 
+     * @param clazz the class to get the ClassLoader from.
      * @return the class loader that loaded the specified class instance.
      * @exception ModelFatalException wraps the SecurityException thrown by
      * getClassLoader.
@@ -69,4 +68,39 @@
         }
     }
 
+    /**
+     * Calls Class.forName in a doPrivileged block. Any SecurityException is
+     * wrapped into a ModelFatalException.
+     * @param name fully qualified name of the desired class
+     * @param initialize whether the class must be initialized
+     * @param loader class loader from which the class must be loaded
+     * @return class object representing the desired class.
+     * @exception ModelFatalException wraps the SecurityException thrown by
+     * getClassLoader.
+     * @exception ClassNotFoundException if the class cannot be located by the
+     * specified class loader.
+     */
+    public static Class forNamePrivileged(final String name, 
+                                          final boolean initialize, 
+                                          final ClassLoader loader)
+        throws ClassNotFoundException
+    {
+        try { 
+            return (Class) AccessController.doPrivileged(
+                new PrivilegedExceptionAction () {
+                    public Object run () throws ClassNotFoundException {
+                        return Class.forName(name, initialize, loader);
+                    }
+                }
+                );
+        }
+        catch (PrivilegedActionException pae) {
+            throw (ClassNotFoundException) pae.getException();
+        }
+        catch (SecurityException ex) {
+            throw new ModelFatalException(
+                msg.msg("CannotGetClassInstance", name, loader), ex);
+        }
+    }
+
 }
Index: 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
===================================================================
--- 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
    (Revision 327115)
+++ 
ri11/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
    (Arbeitskopie)
@@ -21,10 +21,8 @@
 import java.io.InputStream;
 
 import org.apache.jdo.impl.model.java.AbstractJavaModel;
-import org.apache.jdo.model.ModelFatalException;
 import org.apache.jdo.model.java.JavaType;
 import org.apache.jdo.model.jdo.JDOModel;
-import org.apache.jdo.util.I18NHelper;
 
 /**
  * A reflection based JavaModel implementation used at runtime.  
@@ -35,8 +33,7 @@
  * The ReflectionJavaModel implementation will use this ClassLoader to lookup
  * any type by name. This makes sure that the type name is unique.
  *
- * @author Michael Bouschen
- * @since JDO 1.1
+ * @since 1.1
  */
 public abstract class ReflectionJavaModel
     extends AbstractJavaModel
@@ -44,25 +41,16 @@
     /** The ClassLoader instance used as key to cache this JavaModel. */
     private final ClassLoader classLoader;
 
-    /** Flag passed to the Class.forName call. */
-    private final boolean initialize;
+    /** The declaring JavaModelFactory. */
+    protected final ReflectionJavaModelFactory declaringJavaModelFactory;
 
-    /** I18N support */
-    private final static I18NHelper msg =  
-        I18NHelper.getInstance("org.apache.jdo.impl.model.java.Bundle"); 
//NOI18N
-
-    /** Constructor taking the ClassLoader. */
-    public ReflectionJavaModel(ClassLoader classLoader) 
+    /** Constructor. */
+    public ReflectionJavaModel(ClassLoader classLoader,
+        ReflectionJavaModelFactory declaringJavaModelFactory)
     {
-        this(classLoader, true);
-    }
-    
-    /** */
-    protected ReflectionJavaModel(ClassLoader classLoader, boolean initialize)
-    {
         super();
         this.classLoader = classLoader;
-        this.initialize = initialize;
+        this.declaringJavaModelFactory = declaringJavaModelFactory;
     }
 
     /** 
@@ -83,20 +71,13 @@
             JavaType javaType = (JavaType)types.get(name);
             if (javaType == null) {
                 try {
-                    // Note, if name denotes a pc class that has not been
-                    // loaded, Class.forName will load the class which
-                    // calls RegisterClassListener.registerClass.
-                    // This will create a new JavaType entry in the cache.
-                    javaType = getJavaTypeInternal(
-                        Class.forName(name, initialize, classLoader));
+                    Class clazz = ReflectionJavaModelFactory.forNamePrivileged(
+                        name, false, classLoader);
+                    javaType = getJavaTypeInternal(clazz);
                 }
                 catch (ClassNotFoundException ex) {
                     // cannot find class => return null
                 }
-                catch (LinkageError ex) {
-                    throw new ModelFatalException(msg.msg(
-                        "EXC_ClassLoadingError", name, ex.toString())); 
//NOI18N
-                }
             }
             return javaType;
         }
@@ -109,32 +90,15 @@
      * between this method and getJavaType taking a type name is that this 
      * method is supposed to return a non-<code>null<code> value. The
      * specified class object describes an existing type.
-     * <p>
-     * Note, this implementation does not call the overloaded getJavaType
-     * method taking a String, because this would retrieve the Class
-     * instance for the specified type again. Instead, it checks the cache 
-     * directly. If not available it creates a new ReflectionJavaType using
-     * the specified class instance.
      * @param clazz the Class instance representing the type
      * @return a JavaType instance for the name of the specified class
-     * object or <code>null</code> if not present in this model instance.
+     * object.
      */
     public JavaType getJavaType(Class clazz)
     {
         if (clazz == null)
             return null;
         
-        if (initialize) {
-            try {
-                // make sure the class is initialized
-                Class.forName(clazz.getName(), initialize, 
-                    
ReflectionJavaModelFactory.getClassLoaderPrivileged(clazz));
-            }
-            catch (ClassNotFoundException ex) {
-                // ignore, since class has already been loaded 
-            }
-        }
-
         return getJavaTypeInternal(clazz);
     }
 
@@ -159,7 +123,9 @@
         return (InputStream) AccessController.doPrivileged(
             new PrivilegedAction () {
                 public Object run () {
-                    return classLoader.getResourceAsStream(resourceName);
+                    ClassLoader loader = (classLoader == null) ?
+                        ClassLoader.getSystemClassLoader() : classLoader;
+                    return loader.getResourceAsStream(resourceName);
                 }
             }
             );
@@ -185,7 +151,7 @@
      * @return a JavaType instance for the name of the specified class
      * object or <code>null</code> if not present in this model instance.
      */
-    protected JavaType getJavaTypeInternal(Class clazz)
+    public JavaType getJavaTypeInternal(Class clazz)
     {
         String name = clazz.getName();
         synchronized (types) {
@@ -198,6 +164,16 @@
         }
     }
 
+    /**
+     * Returns the declaring ReflectionJavaModelFactory of this
+     * ReflectionJavaModel.
+     * @return the declaring ReflectionJavaModelFactory
+     */
+    public ReflectionJavaModelFactory getDeclaringJavaModelFactory()
+    {
+        return declaringJavaModelFactory;
+    }
+
     /** 
      * Creates a new JavaType instance for the specified Class object.
      * This method provides a hook such that ReflectionJavaModel subclasses
Index: 
ri11/src/java/org/apache/jdo/impl/model/java/AbstractJavaModelFactory.java
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/java/AbstractJavaModelFactory.java  
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/java/AbstractJavaModelFactory.java  
(Arbeitskopie)
@@ -32,9 +32,7 @@
 /**
  * Abstract super class for JavaModelFactory implementations. It provides a
  * JavaModel cache and implements the JavaModel lookup method 
- * [EMAIL PROTECTED] #getJavaModel(Object key)}. Any JavaModel instance 
returned by
- * this method is automatically connected to its corresponding JDOModel
- * instance.
+ * [EMAIL PROTECTED] #getJavaModel(Object key)}.
  * <p>
  * A non-abstract subclass must implement method 
  * [EMAIL PROTECTED] #createJavaModel(Object key)}. The lookup method calls 
this
@@ -46,7 +44,8 @@
  * ModelFatalException.
  * 
  * @author Michael Bouschen
- * @since JDO 1.0.1
+ * @since 1.0.1
+ * @version 1.1
  */
 abstract public class AbstractJavaModelFactory
     implements JavaModelFactory
@@ -97,7 +96,7 @@
      */
     public JavaModel getJavaModel(Object key)
     {
-        synchronized (this.modelCache) {
+        synchronized (modelCache) {
             JavaModel javaModel = (JavaModel)modelCache.get(key);
             if (javaModel == null) {
                 // create new model and store it using the specified key
@@ -107,7 +106,7 @@
                 }
                 catch (ModelException ex) {
                     throw new ModelFatalException(
-                        "EXC_CannotCreateJavaModel", ex); //NOI18N
+                        msg.msg("EXC_CannotCreateJavaModel"), ex); //NOI18N
                 }
             } 
             return javaModel;
Index: ri11/src/java/org/apache/jdo/impl/model/java/Bundle.properties
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/java/Bundle.properties      
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/java/Bundle.properties      
(Arbeitskopie)
@@ -22,10 +22,6 @@
 ERR_InvalidNullFieldInstance={0}: specified Field instance is null.
 
 
-# {0} - class name
-# {1} - detailed message of the cause
-EXC_ClassLoadingError=Error during loading of class ''{0}'': {1}.
-
 #
 # BaseReflectionJavaType
 #
@@ -64,6 +60,8 @@
 # {1} - method name
 EXC_MethodNotSupported=Class {0} does not support method {1}.
 
+EXC_CannotCreateJavaModel=Problems during JavaModel creation.
+
 #
 # ReflectionJavaModelFactory
 #
@@ -74,3 +72,11 @@
 must grant ReflectPermission("getClassLoader") to the codeBase containing the \
 JDO Reference Implementation (jdori.jar).
 
+# {0} - class name
+# {1} - class loader
+EXC_CannotGetClassInstance=A SecurityException was thrown when trying to call \
+Class.forName for class ''{0}'' and class loader ''{1}''. In order to get \
+runtime metadata, you must grant ReflectPermission("getClassLoader") to the \
+codeBase containing the JDO Reference Implementation (jdori.jar).
+
+
Index: ri11/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java   
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java   
(Arbeitskopie)
@@ -145,11 +145,6 @@
                     catch (NoSuchFieldException ex) {
                         return null; // do nothing, just return null
                     }
-                    catch (LinkageError ex) {
-                        throw new ModelFatalException(msg.msg(
-                           "EXC_ClassLoadingError", clazz.getName(), //NOI18N
-                           ex.toString()));
-                    }
                 }
             }
             );
Index: ri11/src/java/org/apache/jdo/impl/model/jdo/xml/XMLExists.java
===================================================================
--- ri11/src/java/org/apache/jdo/impl/model/jdo/xml/XMLExists.java      
(Revision 327115)
+++ ri11/src/java/org/apache/jdo/impl/model/jdo/xml/XMLExists.java      
(Arbeitskopie)
@@ -18,7 +18,7 @@
 
 import java.util.*;
 
-import org.apache.jdo.impl.model.java.AbstractJavaModelFactory;
+import org.apache.jdo.impl.model.java.reflection.ReflectionJavaModelFactory;
 import org.apache.jdo.impl.model.java.runtime.RuntimeJavaModel;
 import org.apache.jdo.impl.model.jdo.caching.JDOModelFactoryImplCaching;
 import org.apache.jdo.impl.model.jdo.util.PrintSupport;
@@ -268,18 +268,18 @@
      * generated metadata.
      */
     private static class XMLExistsJDOModelFactory 
-        extends AbstractJavaModelFactory {
+        extends ReflectionJavaModelFactory {
 
         /** */
         protected XMLExistsJDOModelFactory() {}
 
         /** */
         public JavaModel createJavaModel(Object key) throws ModelException {
-            if ((key == null) || (!(key instanceof ClassLoader)))
+            if ((key != null) && (!(key instanceof ClassLoader)))
                 throw new ModelException("Invalid key " + key + 
                                          " expected ClassLoader");
             ClassLoader classLoader = (ClassLoader)key;
-            JavaModel javaModel = new RuntimeJavaModel(classLoader); 
+            JavaModel javaModel = new RuntimeJavaModel(classLoader, this);
 
             // set the JDOModel property in JavaModel
             setJDOModelInternal(javaModel);
Index: 
ri11/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java
===================================================================
--- 
ri11/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java    
    (Revision 327115)
+++ 
ri11/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java    
    (Arbeitskopie)
@@ -43,7 +43,7 @@
     public EnhancerJavaModel(ClassLoader classLoader,
                              ResourceLocator locator)
     {
-        super(classLoader, false);
+        super(classLoader, null);
         this.locator = locator;
     }
     
Index: 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
===================================================================
--- 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
   (Revision 328149)
+++ 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModelFactory.java
   (Arbeitskopie)
@@ -18,6 +18,8 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 
 import org.apache.jdo.model.ModelException;
 import org.apache.jdo.model.ModelFatalException;
@@ -34,8 +36,7 @@
  * metadata about types and fields. This implementation caches JavaModel
  * instances per ClassLoader.
  * 
- * @author Michael Bouschen
- * @since JDO 1.1
+ * @since 1.1
  */
 public abstract class ReflectionJavaModelFactory
     extends AbstractJavaModelFactory
@@ -49,8 +50,7 @@
      * use the specified key when caching the new JavaModel instance. 
      * <p>
      * This implementation only accepts <code>java.lang.ClassLoader</code>
-     * instances as key and does not support <code>null</code> keys. A
-     * ModelException indicates an invalid key.
+     * instances as key. A ModelException indicates an invalid key.
      * <p>
      * The method automatically sets the parent/child relationship for the
      * created JavaModel according to the parent/child relationship of the 
@@ -59,22 +59,21 @@
      * instance. 
      * @return a new JavaModel instance.
      * @exception ModelException if impossible; the key is of an
-     * inappropriate type or the key is <code>null</code> and this
-     * JavaModelFactory does not support <code>null</code> keys.
+     * inappropriate type.
      */
     public JavaModel createJavaModel(Object key)
         throws ModelException
     {
-        if ((key == null) || (!(key instanceof ClassLoader)))
+        if ((key != null) && (!(key instanceof ClassLoader)))
             throw new ModelException(msg.msg("EXC_InvalidJavaModelKey", 
//NOI18N
-                (key==null?"null":key.getClass().getName()))); //NOI18N
+                                             key.getClass().getName()));
         
         ClassLoader classLoader = (ClassLoader)key;
         JavaModel javaModel = newJavaModelInstance(classLoader);
 
         // check parent <-> child relationship
-        if (classLoader != ClassLoader.getSystemClassLoader()) {
-            // if the specified classLoader is not the system class loader
+        if (classLoader != null) {
+            // if the specified classLoader is not null,
             // try to get the parent class loader and update the parent 
property
             try {
                 ClassLoader parentClassLoader = classLoader.getParent();
@@ -91,20 +90,6 @@
     }
 
     /**
-     * Returns the JavaModel instance for the specified key.
-     * @param key the key used to cache the returned JavaModel instance
-     */
-    public JavaModel getJavaModel(Object key)
-    {
-        if (key == null) {
-            // null classLoader might happen for classes loaded by the
-            // bootstrap class loder
-            key = ClassLoader.getSystemClassLoader();
-        }
-        return super.getJavaModel(key);
-    }
-    
-    /**
      * Returns a JavaType instance for the specified type description
      * (optional operation). This method is a convenience method and a
      * short cut for <code>getJavaModel(key).getJavaType(typeName)</code>.
@@ -141,6 +126,7 @@
      * Calls getClassLoader on the specified class instance in a
      * doPrivileged block. Any SecurityException is wrapped into a
      * ModelFatalException. 
+     * @param clazz the class to get the ClassLoader from.
      * @return the class loader that loaded the specified class instance.
      * @exception ModelFatalException wraps the SecurityException thrown by
      * getClassLoader.
@@ -166,6 +152,41 @@
     }
 
     /**
+     * Calls Class.forName in a doPrivileged block. Any SecurityException is
+     * wrapped into a ModelFatalException.
+     * @param name fully qualified name of the desired class
+     * @param initialize whether the class must be initialized
+     * @param loader class loader from which the class must be loaded
+     * @return class object representing the desired class.
+     * @exception ModelFatalException wraps the SecurityException thrown by
+     * getClassLoader.
+     * @exception ClassNotFoundException if the class cannot be located by the
+     * specified class loader.
+     */
+    public static Class forNamePrivileged(final String name, 
+                                          final boolean initialize, 
+                                          final ClassLoader loader)
+        throws ClassNotFoundException
+    {
+        try { 
+            return (Class) AccessController.doPrivileged(
+                new PrivilegedExceptionAction () {
+                    public Object run () throws ClassNotFoundException {
+                        return Class.forName(name, initialize, loader);
+                    }
+                }
+                );
+        }
+        catch (PrivilegedActionException pae) {
+            throw (ClassNotFoundException) pae.getException();
+        }
+        catch (SecurityException ex) {
+            throw new ModelFatalException(
+                msg.msg("EXC_CannotGetClassInstance", name, loader), ex);
+        }
+    }
+
+    /**
      * Returns the <code>java.lang.Class</code> wrapped in the specified 
      * JavaType. 
      * @return the <code>java.lang.Class</code> for the specified
Index: 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
===================================================================
--- 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
  (Revision 328149)
+++ 
core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaModel.java
  (Arbeitskopie)
@@ -22,11 +22,9 @@
 
 import org.apache.jdo.impl.model.java.AbstractJavaModel;
 import org.apache.jdo.impl.model.jdo.caching.JDOModelFactoryImplCaching;
-import org.apache.jdo.model.ModelFatalException;
 import org.apache.jdo.model.java.JavaType;
 import org.apache.jdo.model.jdo.JDOModel;
 import org.apache.jdo.model.jdo.JDOModelFactory;
-import org.apache.jdo.util.I18NHelper;
 
 /**
  * A reflection based JavaModel implementation used at runtime.  
@@ -37,9 +35,8 @@
  * The ReflectionJavaModel implementation will use this ClassLoader to lookup
  * any type by name. This makes sure that the type name is unique.
  *
- * @author Michael Bouschen
- * @since JDO 1.1
- * @version JDO 2.0
+ * @since 1.1
+ * @version 2.0
  */
 public class ReflectionJavaModel
     extends AbstractJavaModel
@@ -47,30 +44,15 @@
     /** The ClassLoader instance used as key to cache this JavaModel. */
     private final ClassLoader classLoader;
 
-    /** Flag passed to the Class.forName call. */
-    private final boolean initialize;
-
     /** The declaring JavaModelFactory. */
     protected final ReflectionJavaModelFactory declaringJavaModelFactory;
 
-    /** I18N support */
-    private final static I18NHelper msg =  
-        I18NHelper.getInstance("org.apache.jdo.impl.model.java.Bundle"); 
//NOI18N
-
     /** Constructor. */
-    public ReflectionJavaModel(ClassLoader classLoader,
+    protected ReflectionJavaModel(ClassLoader classLoader,
         ReflectionJavaModelFactory declaringJavaModelFactory)
     {
-        this(classLoader, true, declaringJavaModelFactory);
-    }
-    
-    /** Constructor. */
-    protected ReflectionJavaModel(ClassLoader classLoader, boolean initialize, 
-        ReflectionJavaModelFactory declaringJavaModelFactory)
-    {
         super();
         this.classLoader = classLoader;
-        this.initialize = initialize;
         this.declaringJavaModelFactory = declaringJavaModelFactory;
     }
 
@@ -92,20 +74,13 @@
             JavaType javaType = (JavaType)types.get(name);
             if (javaType == null) {
                 try {
-                    // Note, if name denotes a pc class that has not been
-                    // loaded, Class.forName will load the class which
-                    // calls RegisterClassListener.registerClass.
-                    // This will create a new JavaType entry in the cache.
-                    javaType = getJavaTypeInternal(
-                        Class.forName(name, initialize, classLoader));
+                    Class clazz = ReflectionJavaModelFactory.forNamePrivileged(
+                        name, false, classLoader);
+                    javaType = getJavaTypeInternal(clazz);
                 }
                 catch (ClassNotFoundException ex) {
                     // cannot find class => return null
                 }
-                catch (LinkageError ex) {
-                    throw new ModelFatalException(msg.msg(
-                        "EXC_ClassLoadingError", name, ex.toString())); 
//NOI18N
-                }
             }
             return javaType;
         }
@@ -118,32 +93,15 @@
      * between this method and getJavaType taking a type name is that this 
      * method is supposed to return a non-<code>null<code> value. The
      * specified class object describes an existing type.
-     * <p>
-     * Note, this implementation does not call the overloaded getJavaType
-     * method taking a String, because this would retrieve the Class
-     * instance for the specified type again. Instead, it checks the cache 
-     * directly. If not available it creates a new ReflectionJavaType using
-     * the specified class instance.
      * @param clazz the Class instance representing the type
      * @return a JavaType instance for the name of the specified class
-     * object or <code>null</code> if not present in this model instance.
+     * object.
      */
     public JavaType getJavaType(Class clazz)
     {
         if (clazz == null)
             return null;
         
-        if (initialize) {
-            try {
-                // make sure the class is initialized
-                Class.forName(clazz.getName(), initialize, 
-                    
ReflectionJavaModelFactory.getClassLoaderPrivileged(clazz));
-            }
-            catch (ClassNotFoundException ex) {
-                // ignore, since class has already been loaded 
-            }
-        }
-
         return getJavaTypeInternal(clazz);
     }
 
@@ -168,7 +126,9 @@
         return (InputStream) AccessController.doPrivileged(
             new PrivilegedAction () {
                 public Object run () {
-                    return classLoader.getResourceAsStream(resourceName);
+                    ClassLoader loader = (classLoader == null) ?
+                        ClassLoader.getSystemClassLoader() : classLoader;
+                    return loader.getResourceAsStream(resourceName);
                 }
             }
             );
@@ -207,7 +167,7 @@
      * @return a JavaType instance for the name of the specified class
      * object or <code>null</code> if not present in this model instance.
      */
-    protected JavaType getJavaTypeInternal(Class clazz)
+    public JavaType getJavaTypeInternal(Class clazz)
     {
         String name = clazz.getName();
         synchronized (types) {
@@ -219,8 +179,12 @@
             return javaType;
         }
     }
-    
-    /** */
+
+    /**
+     * Returns the declaring ReflectionJavaModelFactory of this
+     * ReflectionJavaModel.
+     * @return the declaring ReflectionJavaModelFactory
+     */
     public ReflectionJavaModelFactory getDeclaringJavaModelFactory()
     {
         return declaringJavaModelFactory;
Index: core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties
===================================================================
--- core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties    
(Revision 328149)
+++ core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties    
(Arbeitskopie)
@@ -21,11 +21,6 @@
 #NOI18N
 ERR_InvalidNullFieldInstance={0}: specified Field instance is null.
 
-
-# {0} - class name
-# {1} - detailed message of the cause
-EXC_ClassLoadingError=Error during loading of class ''{0}'': {1}.
-
 #
 # BaseReflectionJavaType
 #
@@ -75,6 +70,13 @@
 must grant ReflectPermission("getClassLoader") to the codeBase containing the \
 JavaModel and JDOModel implementation.
 
+# {0} - class name
+# {1} - class loader
+EXC_CannotGetClassInstance=A SecurityException was thrown when trying to call \
+Class.forName for class ''{0}'' and class loader ''{1}''. In order to get \
+runtime metadata, you must grant ReflectPermission("getClassLoader") to the \
+codeBase containing the JavaModel and JDOModel implementation.
+
 # {0} - key instance
 EXC_InvalidJavaModelKey=Invalid key for runtime JavaModel lookup: \
 expected ''java.lang.ClassLoader'' instance, found ''{0}'' instance.
@@ -89,6 +91,18 @@
 found ''{0}'' instance.
 
 #
+# ReflectionJavaModel
+#
+
+# {0} - class name
+# {1} - class loader of class instance
+# {2} - class loader of JavaModel
+#NOI18N
+ERR_UnexpectedClassLoader=Unexpected class loader. The specified class \
+instance of class ''{0}'' was loaded by class loader ''{1}'', but the \
+JavaModel instance is bound to a different class loader ''{2}''.
+
+#
 # ReflectionJavaTypeIntrospector
 #
 
Index: 
core20/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java
===================================================================
--- core20/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java 
(Revision 328149)
+++ core20/src/java/org/apache/jdo/impl/model/java/BaseReflectionJavaField.java 
(Arbeitskopie)
@@ -165,11 +165,6 @@
                     catch (NoSuchFieldException ex) {
                         return null; // do nothing, just return null
                     }
-                    catch (LinkageError ex) {
-                        throw new ModelFatalException(msg.msg(
-                           "EXC_ClassLoadingError", clazz.getName(), //NOI18N
-                           ex.toString()));
-                    }
                 }
             }
             );
@@ -198,11 +193,6 @@
                             msg.msg("EXC_CannotGetDeclaredFields", //NOI18N
                                     clazz.getName()), ex); 
                     }
-                    catch (LinkageError ex) {
-                        throw new ModelFatalException(msg.msg(
-                           "EXC_ClassLoadingError", clazz.getName(), //NOI18N
-                           ex.toString()));
-                    }
                 }
             }
             );
Index: 
enhancer20/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java
===================================================================
--- 
enhancer20/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java
  (Revision 328149)
+++ 
enhancer20/src/java/org/apache/jdo/impl/enhancer/meta/model/EnhancerJavaModel.java
  (Arbeitskopie)
@@ -43,7 +43,7 @@
     public EnhancerJavaModel(ClassLoader classLoader,
                              ResourceLocator locator)
     {
-        super(classLoader, false, null);
+        super(classLoader, null);
         this.locator = locator;
     }
     
Index: 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties
===================================================================
--- runtime20/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties 
(Revision 328149)
+++ runtime20/src/java/org/apache/jdo/impl/model/java/runtime/Bundle.properties 
(Arbeitskopie)
@@ -28,6 +28,22 @@
 codeBases containing the JDO API and the JavaModel and JDOModel implementation.
 
 #
+# RuntimeJavaModel
+#
+
+# {0} - class name
+# {1} - detailed message of the cause
+EXC_ClassLoadingError=Error during loading of class ''{0}'': {1}.
+
+# {0} - class name
+# {1} - class loader of class instance
+# {2} - class loader of JavaModel
+#NOI18N
+ERR_UnexpectedClassLoader=Unexpected class loader. The specified class \
+instance of class ''{0}'' was loaded by class loader ''{1}'', but the \
+JavaModel instance is bound to a different class loader ''{2}''.
+
+#
 # RuntimeJavaType
 #
 
@@ -36,3 +52,8 @@
 # {2} - field name
 #NOI18N
 ERR_MultipleJavaField={0}: multiple JavaField ''{1}'' for class ''{2}''.
+
+# {0} - class name
+# {1} - detailed message of the cause
+EXC_ClassLoadingError=Error during loading of class ''{0}'': {1}.
+
Index: 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java
===================================================================
--- 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java 
    (Revision 328149)
+++ 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModel.java 
    (Arbeitskopie)
@@ -16,8 +16,11 @@
 
 package org.apache.jdo.impl.model.java.runtime;
 
+import org.apache.jdo.impl.model.java.PredefinedType; // for javadoc
 import org.apache.jdo.impl.model.java.reflection.ReflectionJavaModel;
+import org.apache.jdo.model.ModelFatalException;
 import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.util.I18NHelper;
 
 /**
  * A reflection based JavaModel implementation used at runtime.  
@@ -28,22 +31,126 @@
  * The RuntimeJavaModelFactory caches JavaModel instances per ClassLoader.
  * The RuntimeJavaModel implementation will use this ClassLoader to lookup
  * any type by name. This makes sure that the type name is unique.
+ * <p>
+ * Any JavaType instance bound to a RuntimeJavaModel instance wraps a class
+ * instacne that is loaded by the ClassLoader that corresponds to this
+ * RuntimeJavaModel. The only exception are PredefinedType instances 
+ * (see [EMAIL PROTECTED] PredefinedType.getPredefinedTypes()} which are 
present in every
+ * RuntimeJavaModel instance.
  *
- * @author Michael Bouschen
- * @since JDO 1.0.1
- * @version JDO 2.0
+ * @since 1.0.1
+ * @version 2.0
  */
 public class RuntimeJavaModel
     extends ReflectionJavaModel
 {
+    /** I18N support */
+    private final static I18NHelper msg =  
+        
I18NHelper.getInstance("org.apache.jdo.impl.model.java.runtime.Bundle"); 
//NOI18N
+
     /** Constructor. */
     public RuntimeJavaModel(ClassLoader classLoader, 
                             RuntimeJavaModelFactory declaringJavaModelFactory) 
     {
         super(classLoader, declaringJavaModelFactory);
     }
-    
+
     /** 
+     * The method returns the JavaType instance for the specified type
+     * name. A type name is unique within one JavaModel instance. The
+     * method returns <code>null</code> if this model instance does not
+     * know a type with the specified name.
+     * <p>
+     * Note, this method calls Class.forName with the wrapped ClassLoader,
+     * if it cannot find a JavaType with the specified name in the cache.
+     * @param name the name of the type
+     * @return a JavaType instance for the specified name or
+     * <code>null</code> if not present in this model instance.
+     */
+    public JavaType getJavaType(String name) 
+    {
+        synchronized (types) {
+            JavaType javaType = (JavaType)types.get(name);
+            if (javaType == null) {
+                try {
+                    // Note, if name denotes a pc class that has not been
+                    // loaded, Class.forName will load the class which will
+                    // register the runtime metadata at the JDOImplHelper.
+                    // This will create a new JavaType entry in the cache.
+                    Class clazz = RuntimeJavaModelFactory.forNamePrivileged(
+                        name, true, getClassLoader());
+                    // Get the class loader of the class instance
+                    ClassLoader loader = RuntimeJavaModelFactory.
+                        getClassLoaderPrivileged(clazz);
+                    // and get the JavaModel instacne for the class loader. 
+                    RuntimeJavaModel javaModel = (RuntimeJavaModel)
+                        declaringJavaModelFactory.getJavaModel(loader);
+                    // Delegate the JavaType lookup to the JavaModel instance.
+                    javaType = javaModel.getJavaTypeInternal(clazz);
+                }
+                catch (ClassNotFoundException ex) {
+                    // cannot find class => return null
+                }
+                catch (LinkageError ex) {
+                    throw new ModelFatalException(msg.msg(
+                        "EXC_ClassLoadingError", name, ex.toString())); 
//NOI18N
+                }
+            }
+            return javaType;
+        }
+    }
+
+    /** 
+     * The method returns the JavaType instance for the type name of the
+     * specified class object. This is a convenience method for 
+     * <code>getJavaType(clazz.getName())</code>. The major difference
+     * between this method and getJavaType taking a type name is that this 
+     * method is supposed to return a non-<code>null<code> value. The
+     * specified class object describes an existing type.
+     * <p>
+     * Note, this implementation does not call the overloaded getJavaType
+     * method taking a String, because this would retrieve the Class
+     * instance for the specified type again. Instead, it checks the cache 
+     * directly. If not available it creates a new ReflectionJavaType using
+     * the specified class instance.
+     * @param clazz the Class instance representing the type
+     * @return a JavaType instance for the name of the specified class
+     * object or <code>null</code> if not present in this model instance.
+     */
+    public JavaType getJavaType(Class clazz)
+    {
+        if (clazz == null)
+            return null;
+        
+        String name = clazz.getName();
+        synchronized (types) {
+            JavaType javaType = (JavaType)types.get(name);
+            if (javaType == null) {
+                ClassLoader loader = 
+                    RuntimeJavaModelFactory.getClassLoaderPrivileged(clazz);
+                // check whether the specified class object is loaded by the
+                // class loader bound to this JavaModel
+                if (loader != getClassLoader()) {
+                    throw new ModelFatalException(msg.msg(
+                        "ERR_UnexpectedClassLoader", //NOI18N
+                        clazz.getName(), loader, getClassLoader()));
+                }
+                try {
+                    // Make sure the class is initialized, because this will
+                    // register the runtime metadata at the JDOImplHelper. 
+                    RuntimeJavaModelFactory.forNamePrivileged(
+                        clazz.getName(), true, loader);
+                }
+                catch (ClassNotFoundException ex) {
+                    // ignore, since class has already been loaded 
+                }
+                javaType = getJavaTypeInternal(clazz);
+            }
+            return javaType;
+        }
+    }
+
+    /** 
      * Creates a new instance of the JavaType implementation class.
      * <p>
      * This implementation returns a RuntimeJavaType instance.
Index: 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
===================================================================
--- 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
      (Revision 328149)
+++ 
runtime20/src/java/org/apache/jdo/impl/model/java/runtime/RuntimeJavaModelFactory.java
      (Arbeitskopie)
@@ -41,9 +41,8 @@
  * JDOImplHelper to handle the runtime metadata as generated by the
  * enhancer. 
  * 
- * @author Michael Bouschen
- * @since JDO 1.0.1
- * @version JDO 2.0
+ * @since 1.0.1
+ * @version 2.0
  */
 public class RuntimeJavaModelFactory
     extends ReflectionJavaModelFactory
Index: fostore20/test/conf/JDO20Policy
===================================================================
--- fostore20/test/conf/JDO20Policy     (Revision 328149)
+++ fostore20/test/conf/JDO20Policy     (Arbeitskopie)
@@ -33,6 +33,9 @@
        // Allow jdori classes to get metadata for persistence-capable classes.
        permission javax.jdo.spi.JDOPermission "getMetadata";
 
+       // Allow jdo model classes to get a class loader
+       permission java.lang.RuntimePermission "getClassLoader";
+
        // Allow jdori classes to access declared members of a class 
        // (e.g. a query accessing transient instances or non-managed fields).
        // The query component needs access to persistent fields.

Reply via email to