View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package org.apache.jdo.impl.model.java.reflection;
18  
19  import java.security.AccessController;
20  import java.security.PrivilegedAction;
21  import java.security.PrivilegedActionException;
22  import java.security.PrivilegedExceptionAction;
23  
24  import org.apache.jdo.model.ModelException;
25  import org.apache.jdo.model.ModelFatalException;
26  import org.apache.jdo.model.java.JavaModel;
27  import org.apache.jdo.model.java.JavaType;
28  import org.apache.jdo.impl.model.java.AbstractJavaModelFactory;
29  import org.apache.jdo.impl.model.java.BaseReflectionJavaType;
30  import org.apache.jdo.util.I18NHelper;
31  
32  /***
33   * A reflection based JavaModelFactory implementation. 
34   * The implementation takes <code>java.lang.Class</code> and
35   * <code>java.lang.reflect.Field</code> instances to get Java related
36   * metadata about types and fields. This implementation caches JavaModel
37   * instances per ClassLoader.
38   * 
39   * @since 1.1
40   */
41  public abstract class ReflectionJavaModelFactory
42      extends AbstractJavaModelFactory
43  {    
44      /*** I18N support */
45      private final static I18NHelper msg =  
46          I18NHelper.getInstance("org.apache.jdo.impl.model.java.Bundle"); //NOI18N
47  
48      /***
49       * Creates a new empty JavaModel instance. A factory implementation may
50       * use the specified key when caching the new JavaModel instance. 
51       * <p>
52       * This implementation only accepts <code>java.lang.ClassLoader</code>
53       * instances as key. A ModelException indicates an invalid key.
54       * <p>
55       * The method automatically sets the parent/child relationship for the
56       * created JavaModel according to the parent/child relationship of the 
57       * ClassLoader passed as key. 
58       * @param key the key that may be used to cache the returned JavaModel
59       * instance. 
60       * @return a new JavaModel instance.
61       * @exception ModelException if impossible; the key is of an
62       * inappropriate type.
63       */
64      public JavaModel createJavaModel(Object key)
65          throws ModelException
66      {
67          if ((key != null) && (!(key instanceof ClassLoader)))
68              throw new ModelException(msg.msg("EXC_InvalidJavaModelKey", //NOI18N
69                                               key.getClass().getName()));
70          
71          ClassLoader classLoader = (ClassLoader)key;
72          JavaModel javaModel = newJavaModelInstance(classLoader);
73  
74          // check parent <-> child relationship
75          if (classLoader != null) {
76              // if the specified classLoader is not null,
77              // try to get the parent class loader and update the parent property
78              try {
79                  ClassLoader parentClassLoader = classLoader.getParent();
80                  if (parentClassLoader != null) {
81                      javaModel.setParent(getJavaModel(parentClassLoader));
82                  }
83              }
84              catch (SecurityException ex) {
85                  // ignore => parentClassLoader and parent JavaModel are null
86              }
87          }
88  
89          return javaModel;
90      }
91  
92      /***
93       * Returns a JavaType instance for the specified type description
94       * (optional operation). This method is a convenience method and a
95       * short cut for <code>getJavaModel(key).getJavaType(typeName)</code>.
96       * <p>
97       * The ReflectionJavaModelFactory supports this short cut and accepts
98       * <code>java.lang.Class</code> instances as valid arguments for this
99       * method. The method throws a 
100      * {@link org.apache.jdo.model.ModelFatalException}, if the specified
101      * type descriptor is not a <code>java.lang.Class</code> instance. 
102      * @param typeDesc the type description
103      * @return a JavaType instance for the specified type.
104      * @exception ModelFatalException the specified type description is not
105      * a <code>java.lang.Class</code> instance.
106      */
107     public JavaType getJavaType(Object typeDesc)
108     {
109         if (typeDesc == null)
110             return null;
111 
112         try {
113             Class clazz = (Class)typeDesc;
114             ClassLoader classLoader = getClassLoaderPrivileged(clazz);
115             return getJavaModel(classLoader).getJavaType(clazz);
116         }
117         catch (ClassCastException ex) {
118             throw new ModelFatalException(msg.msg("EXC_InvalidTypeDesc", //NOI18N
119                 typeDesc.getClass().getName()));
120         }
121     }
122 
123     // ===== Methods not defined in JavaModelFactory =====
124 
125     /***
126      * Calls getClassLoader on the specified Class instance in a
127      * doPrivileged block. Any SecurityException is wrapped into a
128      * ModelFatalException. 
129      * @param clazz the class to get the ClassLoader from.
130      * @return the class loader that loaded the specified Class instance.
131      * @exception ModelFatalException wraps the SecurityException thrown by
132      * getClassLoader.
133      */
134     public static ClassLoader getClassLoaderPrivileged(final Class clazz)
135     {
136         if (clazz == null)
137             return null;
138 
139         try { 
140             return (ClassLoader) AccessController.doPrivileged(
141                 new PrivilegedAction () {
142                     public Object run () {
143                         return clazz.getClassLoader();
144                     }
145                 }
146                 );
147         }
148         catch (SecurityException ex) {
149             throw new ModelFatalException(
150                 msg.msg("EXC_CannotGetClassLoader", clazz), ex); //NOI18N
151         }
152     }
153 
154     /***
155      * Calls Class.forName in a doPrivileged block. Any SecurityException is
156      * wrapped into a ModelFatalException.
157      * @param name fully qualified name of the desired class
158      * @param initialize whether the class must be initialized
159      * @param loader class loader from which the class must be loaded
160      * @return class object representing the desired class.
161      * @exception ModelFatalException wraps the SecurityException thrown by
162      * getClassLoader.
163      * @exception ClassNotFoundException if the class cannot be located by the
164      * specified class loader.
165      */
166     public static Class forNamePrivileged(final String name, 
167                                           final boolean initialize, 
168                                           final ClassLoader loader)
169         throws ClassNotFoundException
170     {
171         try { 
172             return (Class) AccessController.doPrivileged(
173                 new PrivilegedExceptionAction () {
174                     public Object run () throws ClassNotFoundException {
175                         return Class.forName(name, initialize, loader);
176                     }
177                 }
178                 );
179         }
180         catch (PrivilegedActionException pae) {
181             throw (ClassNotFoundException) pae.getException();
182         }
183         catch (SecurityException ex) {
184             throw new ModelFatalException(
185                 msg.msg("EXC_CannotGetClassInstance", name, loader), ex);
186         }
187     }
188 
189     /***
190      * Returns the <code>java.lang.Class</code> wrapped in the specified 
191      * JavaType. 
192      * @return the <code>java.lang.Class</code> for the specified
193      * JavaType. 
194      * @exception ModelFatalException the specified JavaType does
195      * not wrap a <code>java.lang.Class</code> instance.
196      */
197     public Class getJavaClass(JavaType javaType) 
198     {
199         if (javaType == null)
200             return null;
201         
202         try {
203             return ((BaseReflectionJavaType)javaType).getJavaClass();
204         }
205         catch (ClassCastException ex) {
206             throw new ModelFatalException(msg.msg(
207                 "EXC_InvalidJavaType", javaType.getClass())); //NOI18N
208         }
209     }
210 
211     //========= Internal helper methods ==========
212     
213     /*** 
214      * Creates a new instance of the JavaModel implementation class.
215      * <p>
216      * This implementation returns a <code>ReflectionJavaModel</code>
217      * instance.
218      * @return a new JavaModel instance.
219      */
220     protected JavaModel newJavaModelInstance(ClassLoader classLoader) {
221         return new ReflectionJavaModel(classLoader, this);
222     }
223 
224 }