View Javadoc

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