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.enhancer.meta.model;
18  
19  import java.io.IOException;
20  import java.io.PrintWriter;
21  import java.io.File;
22  
23  import java.util.List;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  
27  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
28  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError;
29  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException;
30  import org.apache.jdo.impl.enhancer.meta.util.EnhancerMetaDataBaseModel;
31  import org.apache.jdo.impl.enhancer.util.CombinedResourceLocator;
32  import org.apache.jdo.impl.enhancer.util.ListResourceLocator;
33  import org.apache.jdo.impl.enhancer.util.PathResourceLocator;
34  import org.apache.jdo.impl.enhancer.util.ResourceLocator;
35  import org.apache.jdo.impl.model.jdo.caching.JDOModelFactoryImplCaching;
36  import org.apache.jdo.impl.model.jdo.util.TypeSupport;
37  import org.apache.jdo.model.ModelException;
38  import org.apache.jdo.model.ModelFatalException;
39  import org.apache.jdo.model.java.JavaField;
40  import org.apache.jdo.model.java.JavaModel;
41  import org.apache.jdo.model.java.JavaType;
42  import org.apache.jdo.model.jdo.JDOClass;
43  import org.apache.jdo.model.jdo.JDOField;
44  import org.apache.jdo.model.jdo.JDOModel;
45  import org.apache.jdo.model.jdo.JDOModelFactory;
46  import org.apache.jdo.model.jdo.PersistenceModifier;
47  
48  /***
49   * Provides the JDO meta information based on a JDO meta model.
50   */
51  public class EnhancerMetaDataJDOModelImpl
52      extends EnhancerMetaDataBaseModel
53      implements EnhancerMetaData
54  {
55      /***
56       * The jdoModel instance.
57       */
58      private final JDOModel jdoModel;
59  
60      /***
61       * The model instance.
62       */
63      private final EnhancerJavaModel javaModel;
64  
65      /***
66       * The JavaType representation for java.io.Serializable.
67       */
68      private final JavaType serializableJavaType;
69      
70      /***
71       * Creates an instance.
72       */
73      public EnhancerMetaDataJDOModelImpl(PrintWriter out,
74                                          boolean verbose,
75                                          List jdoFileNames,
76                                          List jarFileNames,
77                                          String sourcePath)
78          throws EnhancerMetaDataFatalError
79      {
80          super(out, verbose);
81  
82          try {
83              final List locators = new ArrayList();
84              ClassLoader classLoader = null;
85  
86              // create resource locator for specified jdo files
87              if (jdoFileNames != null && !jdoFileNames.isEmpty()) {
88                  final StringBuffer s = new StringBuffer();
89                  for (Iterator i = jdoFileNames.iterator(); i.hasNext();) {
90                      s.append(" " + i.next());
91                  }
92                  final ResourceLocator jdos
93                      = new ListResourceLocator(out, verbose, jdoFileNames);
94                  //printMessage(getI18N("enhancer.metadata.using_jdo_files",
95                  //                     s.toString()));
96                  locators.add(jdos);
97              }
98  
99              // create resource locator for specified jar files
100             if (jarFileNames != null && !jarFileNames.isEmpty()) {
101                 final StringBuffer s = new StringBuffer();
102                 final Iterator i = jarFileNames.iterator();
103                 s.append(i.next());
104                 while (i.hasNext()) {
105                     s.append(File.pathSeparator + i.next());
106                 }
107                 final PathResourceLocator jars
108                     = new PathResourceLocator(out, verbose, s.toString());
109                 //printMessage(getI18N("enhancer.metadata.using_jar_files",
110                 //                     s.toString()));
111                 locators.add(jars);
112                 classLoader = jars.getClassLoader();
113             }
114 
115             // create resource locator for specified source path
116             if (sourcePath != null && sourcePath.length() > 0) {
117                 final PathResourceLocator path
118                     = new PathResourceLocator(out, verbose, sourcePath);
119                 //printMessage(getI18N("enhancer.metadata.using_source_path",
120                 //                     sourcePath));
121                 locators.add(path);
122                 classLoader = path.getClassLoader();
123             }
124 
125             if (classLoader == null) {
126                 // use the current class loader as the default, if there is
127                 // no -s option and no archives specified.
128                 classLoader = EnhancerMetaDataJDOModelImpl.class.getClassLoader();
129             }
130 
131             // print warning if no meta-data source specified
132             if (locators.isEmpty()) {
133                 printWarning(getI18N("enhancer.metadata.using_no_metadata"));
134             }
135 
136             // create JavaModel with combined resource locators
137             final ResourceLocator locator
138                 = new CombinedResourceLocator(out, verbose, locators);
139             //^olsen: wrap with timing jdo file locator
140             //if (options.doTiming.value) {
141             //    classLocator = new ResourceLocatorTimer(classLocator);
142             //}
143             javaModel = new EnhancerJavaModel(classLoader, locator);
144             final JDOModelFactory factory = JDOModelFactoryImplCaching.getInstance();
145             affirm(factory != null);
146             jdoModel = factory.getJDOModel(javaModel);
147             affirm(jdoModel != null);
148             javaModel.setJDOModel(jdoModel);
149             serializableJavaType = javaModel.getJavaType("java.io.Serializable");
150         } catch (IOException ex) {
151             final String msg
152                 = getI18N("enhancer.metadata.io_error", ex.getMessage());
153             throw new EnhancerMetaDataFatalError(msg, ex);
154         } catch (ModelFatalException ex) {
155             final String msg
156                 = getI18N("enhancer.metadata.jdomodel_error", ex.getMessage());
157             throw new EnhancerMetaDataFatalError(msg, ex);
158         } catch (ModelException ex) {
159             final String msg
160                 = getI18N("enhancer.metadata.jdomodel_error", ex.getMessage());
161             throw new EnhancerMetaDataFatalError(msg, ex);
162         }        
163     }
164 
165     // ----------------------------------------------------------------------
166     
167     private JDOClass getJDOClass(String classPath)
168         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
169     {
170         final String className = classPath.replace('/', '.');
171         final JDOClass clazz = jdoModel.getJDOClass(className);
172         return clazz;
173     }
174     
175     private JDOField getJDOField(String classPath,
176                                  String fieldName)
177         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
178     {
179         final JDOClass clazz = getJDOClass(classPath);
180         if (clazz == null) {
181             return null;
182         }
183         final JDOField field = clazz.getDeclaredField(fieldName);
184         affirm(field == null || field.getDeclaringClass() == clazz,
185                "field not declared in class: " + classPath + "." + fieldName);
186         return field;
187     }
188     
189     private boolean hasFieldModifier(String classPath,
190                                      String fieldName,
191                                      int fieldModifier)
192         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
193     {
194         final JDOField field = getJDOField(classPath, fieldName);
195         if (field == null) {
196             return false;
197         }
198         final int pm = field.getPersistenceModifier();
199         affirm(pm != PersistenceModifier.UNSPECIFIED,
200                "field modifier 'UNSPECIFIED': " + classPath + "." + fieldName);
201         return (pm & fieldModifier) != 0;
202     }
203 
204     // ----------------------------------------------------------------------
205     
206 
207     /*** 
208      * Returns the JVM-qualified name of the specified field's declaring
209      * class. The method first checks whether the class of the specified
210      * classPath (the JVM-qualified name) declares such a field. If yes,
211      * classPath is returned. Otherwise, it checks its superclasses. The
212      * method returns <code>null</code> for an unkown field.
213      * @param classPath the non-null JVM-qualified name of the class
214      * @param fieldName the non-null name of the field
215      * @return the JVM-qualified name of the declararing class of the
216      * field, or <code>null</code> if there is no such field.
217      */
218     public String getDeclaringClass(String classPath, String fieldName)
219         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
220     {
221         affirm(classPath);
222         affirm(fieldName);
223         final String className = classPath.replace('/', '.');
224         try {
225             final JavaType javaType = javaModel.getJavaType(className);
226             final JavaField javaField = javaType.getJavaField(fieldName);
227             final JavaType declaringClass = javaField.getDeclaringClass();
228             return declaringClass.getName().replace('.', '/');
229         } catch (ModelFatalException ex) {
230             throw new EnhancerMetaDataUserException(ex);
231         }
232     }
233 
234     /***
235      * Declares a field to the JDO model passing its type information.
236      * @see declareField(String, String, String)
237      */
238 /*
239     public void declareField(String classPath,
240                              String fieldName,
241                              String fieldSig)
242         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
243     {        
244         affirm(classPath);
245         affirm(fieldName);
246         affirm(fieldSig);
247         final JDOClass clazz = getJDOClass(classPath);
248         affirm(clazz != null,
249                "class is not persistence-capable: " + classPath);
250         try {
251             final JDOField field = clazz.createJDOField(fieldName);
252             affirm(field != null,
253                    "cannot create JDO field: " + classPath + "." + fieldName);
254             field.setType(fieldSig);
255             affirm(fieldSig == field.getType());    
256 
257             //^olsen: cleanup debugging code
258             String s0 = model.getJavaModel().getTypeName(fieldSig);
259             String s1 = (String)model.getJavaModel().getTypeForName(s0);
260             //out.println("s0 = " + s0);
261             //out.println("s1 = " + s1);
262             affirm(fieldSig.equals(s1));
263         } catch (JDOModelException ex) {
264             throw new EnhancerMetaDataUserException(ex);
265         }
266     }
267 */
268     
269     /***
270      * Declares a field to the JDO model passing its type information.
271      */
272     public void declareField(String classPath,
273                              String fieldName,
274                              String fieldSig)
275         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
276     {        
277         affirm(classPath);
278         affirm(fieldName);
279         try {
280             final JDOClass clazz = getJDOClass(classPath);
281             JavaType javaClass = clazz.getJavaType();
282             affirm(javaClass != null,
283                    "cannot find class file for class: " + classPath);
284             JavaField javaField = javaClass.getJavaField(fieldName);
285             affirm(javaField != null,
286                    "cannot find java field " + classPath + "." + fieldName);
287             JavaType fieldType = javaField.getType();
288             JDOField field = clazz.getField(fieldName);
289             // if field not known by JDOClass (not specified in JDO XML),
290             // create the field only if the model's method of default
291             // calculation would yield a persistent field.  We must not
292             // change the models state by newly created fields with
293             // a persistence-modifier "none", because this would lead to
294             // in a different annotation by isKnownNonManagedField().
295             if (field == null
296                 && TypeSupport.isPersistenceFieldType(fieldType)) {
297                 field = clazz.createJDOField(fieldName);
298                 affirm(field != null,
299                        "cannot create JDO field: "
300                        + classPath + "." + fieldName);
301             }
302             field.setJavaField(javaField);
303             affirm(fieldType == field.getType());
304             affirm(field.getPersistenceModifier()
305                    != PersistenceModifier.UNSPECIFIED,
306                    "known, unspecified JDO field: " + classPath + "." + fieldName);
307         } catch (ModelFatalException ex) {
308             throw new EnhancerMetaDataUserException(ex);
309         } catch (ModelException ex) {
310             throw new EnhancerMetaDataUserException(ex);
311         }
312     }
313     
314     /***
315      * Tests whether a class is known to be persistence-capable.
316      */
317     public boolean isPersistenceCapableClass(String classPath)
318         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
319     {
320         final JDOClass clazz = getJDOClass(classPath);
321         return (clazz != null);
322     }
323 
324     /***
325      * Returns whether a class implements java.io.Serializable
326      * @param classPath the non-null JVM-qualified name of the class
327      * @return true if this class is serializable; otherwise false
328      */
329     public boolean isSerializableClass(String classPath)
330         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
331     {
332         final String className = classPath.replace('/', '.');
333         final JavaType javaType = javaModel.getJavaType(className);
334         return javaType.isCompatibleWith(serializableJavaType);
335     }
336 
337     /***
338      * Returns the name of the persistence-capable root class of a class.
339      */
340 /*
341     public String getPersistenceCapableRootClass(String classPath)
342         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
343     {
344         final JDOClass clazz = getJDOClass(classPath);
345         if (clazz == null) {
346             return null;
347         }
348         final JDOClass root = clazz.getPersistenceCapableRootClass();
349         if (root == null) {
350             return null;
351         }
352         final String name = root.getName();
353         affirm(name != null);
354         return name.replace('.', '/');
355     }
356 */
357 
358     /***
359      * Returns the name of the persistence-capable superclass of a class.
360      */
361     public String getPersistenceCapableSuperClass(String classPath)
362         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
363     {
364         final JDOClass clazz = getJDOClass(classPath);
365         if (clazz == null) {
366             return null;
367         }
368         final String name = clazz.getPersistenceCapableSuperclassName();
369         return (name != null ? name.replace('.', '/') : null);
370     }
371 
372     /***
373      * Returns the name of the key class of a persistence-capable class.
374      */
375     public String getKeyClass(String classPath)
376         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
377     {
378         final JDOClass clazz = getJDOClass(classPath);
379         if (clazz == null) {
380             return null;
381         }
382         final String name = clazz.getDeclaredObjectIdClassName();
383         return (name != null ? name.replace('.', '/') : null);
384     }
385 
386     /***
387      * Returns an array of field names of all declared persistent and
388      * transactional fields of a class.
389      */
390     public String[] getManagedFields(String classPath)
391         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
392     {
393         final JDOClass clazz = getJDOClass(classPath);
394         if (clazz == null) {
395             return new String[]{};
396         }
397 
398         final JDOField[] fields = clazz.getDeclaredManagedFields();
399         if (fields == null) {
400             return new String[]{};
401         }
402         affirm(fields.length == clazz.getDeclaredManagedFieldCount());
403         
404         final int n = fields.length;
405         final String[] names = new String[n];
406         for (int i = 0; i < n; i++) {
407             affirm(fields[i] != null);
408             affirm(fields[i].getRelativeFieldNumber() == i);
409             affirm(fields[i].isManaged());
410             names[i] = fields[i].getName();
411             affirm(names[i] != null);
412         }
413         return names;
414     }
415 
416     /***
417      * Returns whether a field of a class is known to be non-managed.
418      */
419     //^olsen: cleanup old code
420 /*
421     public boolean isKnownNonManagedField(String classPath, String fieldName)
422     {
423         final JDOClass clazz = getJDOClass(classPath);
424         if (clazz == null) {
425             // class not known to be persistence-capable
426             return true;
427         }
428         final JDOField field = clazz.getField(fieldName);
429         if (field == null) {
430             // field not known by JDOClass (thus, not specified in JDO XML)
431             return false;
432         }
433         return field.isKnownNonManaged();
434     }
435 */
436 
437     /***
438      * Returns whether a field of a class is known to be non-managed.
439      */
440     public boolean isKnownNonManagedField(String classPath,
441                                           String fieldName,
442                                           String fieldSig)
443         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
444     {
445         affirm(classPath);
446         affirm(fieldName);
447         affirm(fieldSig);
448         try {
449             final JDOClass clazz = getJDOClass(classPath);
450             if (clazz == null) {
451                 // class not known to be persistence-capable
452                 return true;
453             }
454             
455             // check whether field is managed only if field's
456             // persistence-modifier is known by the JDO model
457             final JDOField field = clazz.getField(fieldName);
458             if (field != null && (field.getPersistenceModifier()
459                                   != PersistenceModifier.UNSPECIFIED)) {
460                 // only field's persistence-modifier known by model
461                 return !field.isManaged();
462             }
463 
464             // field not known by JDOClass (not specified in JDO XML)
465             // apply model's method of default calculation without
466             // changing the model's state
467             JavaType fieldType = javaModel.getJavaType(javaModel.getTypeName(fieldSig));
468             affirm(fieldType != null, 
469                    "cannot get java type for: " + fieldSig);
470             return !TypeSupport.isPersistenceFieldType(fieldType);
471         } catch (ModelFatalException ex) {
472             throw new EnhancerMetaDataUserException(ex);
473         }
474     }
475 
476     /***
477      * Tests whether a field of a class is transient transactional or
478      * persistent.
479      */
480     public boolean isManagedField(String classPath, String fieldName)
481         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
482     {
483         return hasFieldModifier(classPath, fieldName,
484                                 (PersistenceModifier.PERSISTENT
485                                  | PersistenceModifier.POSSIBLY_PERSISTENT
486                                  | PersistenceModifier.TRANSACTIONAL));
487     }
488 
489     /***
490      * Tests whether a field of a class is persistent.
491      */
492     public boolean isPersistentField(String classPath, String fieldName)
493         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
494     {
495         return hasFieldModifier(classPath, fieldName,
496                                 (PersistenceModifier.PERSISTENT
497                                  | PersistenceModifier.POSSIBLY_PERSISTENT));
498     }
499 
500     /***
501      * Tests whether a field of a class is transient transactional.
502      */
503     public boolean isTransactionalField(String classPath, String fieldName)
504         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
505     {
506         return hasFieldModifier(classPath, fieldName,
507                                 PersistenceModifier.TRANSACTIONAL);
508     }
509 
510     /***
511      * Tests whether a field of a class is known to be Key.
512      */
513     public boolean isKeyField(String classPath, String fieldName)
514         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
515     {
516         final JDOField field = getJDOField(classPath, fieldName);
517         if (field == null) {
518             return false;
519         }
520         return field.isPrimaryKey();
521     }
522 
523     /***
524      * Tests whether a field of a class is known to be part of the
525      * Default Fetch Group.
526      */
527     public boolean isDefaultFetchGroupField(String classPath, String fieldName)
528         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
529     {
530         final JDOField field = getJDOField(classPath, fieldName);
531         if (field == null) {
532             return false;
533         }
534         return field.isDefaultFetchGroup();
535     }
536 
537     /***
538      * Returns the unique field index of a declared, persistent field of a
539      * class.
540      */
541     public int getFieldNumber(String classPath, String fieldName)
542         throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError
543     {
544         final JDOField field = getJDOField(classPath, fieldName);
545         if (field == null) {
546             return -1;
547         }
548         return field.getRelativeFieldNumber();
549     }
550 
551 }