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