001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.net.URL;
024: import java.security.AccessController;
025: import java.util.Arrays;
026: import java.util.Collection;
027: import java.util.Collections;
028: import java.util.HashMap;
029: import java.util.HashSet;
030: import java.util.Map;
031: import java.util.Set;
032: import javax.persistence.Embeddable;
033: import javax.persistence.Entity;
034: import javax.persistence.MappedSuperclass;
035: import javax.persistence.NamedNativeQueries;
036: import javax.persistence.NamedNativeQuery;
037: import javax.persistence.NamedQueries;
038: import javax.persistence.NamedQuery;
039: import javax.persistence.SqlResultSetMapping;
040: import javax.persistence.SqlResultSetMappings;
041:
042: import org.apache.openjpa.lib.conf.Configurable;
043: import org.apache.openjpa.lib.conf.Configuration;
044: import org.apache.openjpa.lib.conf.GenericConfigurable;
045: import org.apache.openjpa.lib.meta.ClassAnnotationMetaDataFilter;
046: import org.apache.openjpa.lib.meta.ClassArgParser;
047: import org.apache.openjpa.lib.meta.MetaDataFilter;
048: import org.apache.openjpa.lib.meta.MetaDataParser;
049: import org.apache.openjpa.lib.util.J2DoPriv5Helper;
050: import org.apache.openjpa.lib.util.Localizer;
051: import org.apache.openjpa.lib.util.Options;
052: import org.apache.openjpa.meta.AbstractCFMetaDataFactory;
053: import org.apache.openjpa.meta.ClassMetaData;
054: import org.apache.openjpa.meta.FieldMetaData;
055: import org.apache.openjpa.meta.MetaDataDefaults;
056: import org.apache.openjpa.meta.MetaDataFactory;
057: import org.apache.openjpa.meta.QueryMetaData;
058: import org.apache.openjpa.meta.SequenceMetaData;
059: import org.apache.openjpa.util.GeneralException;
060: import org.apache.openjpa.util.MetaDataException;
061:
062: /**
063: * {@link MetaDataFactory} for JPA metadata.
064: *
065: * @author Steve Kim
066: * @since 0.4.0
067: * @nojavadoc
068: */
069: public class PersistenceMetaDataFactory extends
070: AbstractCFMetaDataFactory implements Configurable,
071: GenericConfigurable {
072:
073: private static final Localizer _loc = Localizer
074: .forPackage(PersistenceMetaDataFactory.class);
075:
076: private final PersistenceMetaDataDefaults _def = new PersistenceMetaDataDefaults();
077: private AnnotationPersistenceMetaDataParser _annoParser = null;
078: private AnnotationPersistenceXMLMetaDataParser _annoXMLParser = null;
079: private XMLPersistenceMetaDataParser _xmlParser = null;
080: private Map<URL, Set> _xml = null; // xml rsrc -> class names
081: private Set<URL> _unparsed = null; // xml rsrc
082: private boolean _fieldOverride = true;
083:
084: /**
085: * Whether to use field-level override or class-level override.
086: * Defaults to true.
087: */
088: public void setFieldOverride(boolean field) {
089: _fieldOverride = field;
090: }
091:
092: /**
093: * Whether to use field-level override or class-level override.
094: * Defaults to true.
095: */
096: public boolean getFieldOverride() {
097: return _fieldOverride;
098: }
099:
100: /**
101: * Return metadata parser, creating it if it does not already exist.
102: */
103: public AnnotationPersistenceMetaDataParser getAnnotationParser() {
104: if (_annoParser == null) {
105: _annoParser = newAnnotationParser();
106: _annoParser.setRepository(repos);
107: }
108: return _annoParser;
109: }
110:
111: /**
112: * Set the metadata parser.
113: */
114: public void setAnnotationParser(
115: AnnotationPersistenceMetaDataParser parser) {
116: if (_annoParser != null)
117: _annoParser.setRepository(null);
118: if (parser != null)
119: parser.setRepository(repos);
120: _annoParser = parser;
121: }
122:
123: /**
124: * Create a new metadata parser.
125: */
126: protected AnnotationPersistenceMetaDataParser newAnnotationParser() {
127: return new AnnotationPersistenceMetaDataParser(repos
128: .getConfiguration());
129: }
130:
131: /**
132: * Create a new annotation serializer.
133: */
134: protected AnnotationPersistenceMetaDataSerializer newAnnotationSerializer() {
135: return new AnnotationPersistenceMetaDataSerializer(repos
136: .getConfiguration());
137: }
138:
139: /**
140: * Return XML metadata parser, creating it if it does not already exist.
141: */
142: public XMLPersistenceMetaDataParser getXMLParser() {
143: if (_xmlParser == null) {
144: _xmlParser = newXMLParser(true);
145: _xmlParser.setRepository(repos);
146: if (_fieldOverride)
147: _xmlParser.setAnnotationParser(getAnnotationParser());
148: }
149: return _xmlParser;
150: }
151:
152: /**
153: * Set the metadata parser.
154: */
155: public void setXMLParser(XMLPersistenceMetaDataParser parser) {
156: if (_xmlParser != null)
157: _xmlParser.setRepository(null);
158: if (parser != null)
159: parser.setRepository(repos);
160: _xmlParser = parser;
161: }
162:
163: /**
164: * Create a new metadata parser.
165: */
166: protected XMLPersistenceMetaDataParser newXMLParser(boolean loading) {
167: return new XMLPersistenceMetaDataParser(repos
168: .getConfiguration());
169: }
170:
171: /**
172: * Create a new serializer
173: */
174: protected XMLPersistenceMetaDataSerializer newXMLSerializer() {
175: return new XMLPersistenceMetaDataSerializer(repos
176: .getConfiguration());
177: }
178:
179: public void load(Class cls, int mode, ClassLoader envLoader) {
180: if (mode == MODE_NONE)
181: return;
182: if (!strict && (mode & MODE_META) != 0)
183: mode |= MODE_MAPPING;
184:
185: // getting the list of persistent types runs callbacks to
186: // mapPersistentTypeNames if it hasn't been called already, which
187: // caches XML resources
188: getPersistentTypeNames(false, envLoader);
189: URL xml = findXML(cls);
190:
191: // we have to parse metadata up-front to register persistence unit
192: // defaults and system callbacks
193: ClassMetaData meta;
194: boolean parsedXML = false;
195: if (_unparsed != null && !_unparsed.isEmpty()
196: && (mode & MODE_META) != 0) {
197: for (URL url : _unparsed)
198: parseXML(url, cls, mode, envLoader);
199: parsedXML = _unparsed.contains(xml);
200: _unparsed.clear();
201:
202: // XML process check
203: meta = repos.getCachedMetaData(cls);
204: if (meta != null && (meta.getSourceMode() & mode) == mode) {
205: validateStrategies(meta);
206: return;
207: }
208: }
209:
210: // might have been looking for system-level query
211: if (cls == null)
212: return;
213:
214: // we may still need to parse XML if this is a redeploy of a class, or
215: // if we're in strict query-only mode
216: if (!parsedXML && xml != null) {
217: parseXML(xml, cls, mode, envLoader);
218: // XML process check
219: meta = repos.getCachedMetaData(cls);
220: if (meta != null && (meta.getSourceMode() & mode) == mode) {
221: validateStrategies(meta);
222: return;
223: }
224: }
225:
226: AnnotationPersistenceMetaDataParser parser = getAnnotationParser();
227: parser.setEnvClassLoader(envLoader);
228: parser.setMode(mode);
229: parser.parse(cls);
230:
231: meta = repos.getCachedMetaData(cls);
232: if (meta != null && (meta.getSourceMode() & mode) == mode)
233: validateStrategies(meta);
234: }
235:
236: /**
237: * Parse the given XML resource.
238: */
239: private void parseXML(URL xml, Class cls, int mode,
240: ClassLoader envLoader) {
241: ClassLoader loader = repos.getConfiguration()
242: .getClassResolverInstance().getClassLoader(cls,
243: envLoader);
244: XMLPersistenceMetaDataParser xmlParser = getXMLParser();
245: xmlParser
246: .setClassLoader(envLoader != null ? envLoader : loader);
247: xmlParser.setEnvClassLoader(envLoader);
248: xmlParser.setMode(mode);
249: try {
250: xmlParser.parse(xml);
251: } catch (IOException ioe) {
252: throw new GeneralException(ioe);
253: }
254: }
255:
256: /**
257: * Locate the XML resource for the given class.
258: */
259: private URL findXML(Class cls) {
260: if (_xml != null && cls != null)
261: for (Map.Entry<URL, Set> entry : _xml.entrySet())
262: if (entry.getValue().contains(cls.getName()))
263: return entry.getKey();
264: return null;
265: }
266:
267: @Override
268: protected void mapPersistentTypeNames(Object rsrc, String[] names) {
269: if (rsrc.toString().endsWith(".class")) {
270: if (log.isTraceEnabled())
271: log.trace(_loc.get(
272: "map-persistent-types-skipping-class", rsrc));
273: return;
274: } else if (!(rsrc instanceof URL)) {
275: if (log.isTraceEnabled())
276: log.trace(_loc.get(
277: "map-persistent-types-skipping-non-url", rsrc));
278: return;
279: }
280:
281: if (log.isTraceEnabled())
282: log.trace(_loc.get("map-persistent-type-names", rsrc,
283: Arrays.asList(names)));
284:
285: if (_xml == null)
286: _xml = new HashMap<URL, Set>();
287: _xml.put((URL) rsrc, new HashSet(Arrays.asList(names)));
288: if (_unparsed == null)
289: _unparsed = new HashSet<URL>();
290: _unparsed.add((URL) rsrc);
291: }
292:
293: @Override
294: public Class getQueryScope(String queryName, ClassLoader loader) {
295: if (queryName == null)
296: return null;
297: Collection classes = repos.loadPersistentTypes(false, loader);
298: for (Class cls : (Collection<Class>) classes) {
299: if (((Boolean) AccessController
300: .doPrivileged(J2DoPriv5Helper
301: .isAnnotationPresentAction(cls,
302: NamedQuery.class))).booleanValue()
303: && hasNamedQuery(queryName, (NamedQuery) cls
304: .getAnnotation(NamedQuery.class)))
305: return cls;
306: if (((Boolean) AccessController
307: .doPrivileged(J2DoPriv5Helper
308: .isAnnotationPresentAction(cls,
309: NamedQueries.class)))
310: .booleanValue()
311: && hasNamedQuery(queryName, ((NamedQueries) cls
312: .getAnnotation(NamedQueries.class)).value()))
313: return cls;
314: if (((Boolean) AccessController
315: .doPrivileged(J2DoPriv5Helper
316: .isAnnotationPresentAction(cls,
317: NamedNativeQuery.class)))
318: .booleanValue()
319: && hasNamedNativeQuery(
320: queryName,
321: (NamedNativeQuery) cls
322: .getAnnotation(NamedNativeQuery.class)))
323: return cls;
324: if (((Boolean) AccessController
325: .doPrivileged(J2DoPriv5Helper
326: .isAnnotationPresentAction(cls,
327: NamedNativeQueries.class)))
328: .booleanValue()
329: && hasNamedNativeQuery(
330: queryName,
331: ((NamedNativeQueries) cls
332: .getAnnotation(NamedNativeQueries.class))
333: .value()))
334: return cls;
335: }
336: return null;
337: }
338:
339: @Override
340: public Class getResultSetMappingScope(String rsMappingName,
341: ClassLoader loader) {
342: if (rsMappingName == null)
343: return null;
344:
345: Collection classes = repos.loadPersistentTypes(false, loader);
346: for (Class cls : (Collection<Class>) classes) {
347:
348: if (((Boolean) AccessController
349: .doPrivileged(J2DoPriv5Helper
350: .isAnnotationPresentAction(cls,
351: SqlResultSetMapping.class)))
352: .booleanValue()
353: && hasRSMapping(
354: rsMappingName,
355: (SqlResultSetMapping) cls
356: .getAnnotation(SqlResultSetMapping.class)))
357: return cls;
358:
359: if (((Boolean) AccessController
360: .doPrivileged(J2DoPriv5Helper
361: .isAnnotationPresentAction(cls,
362: SqlResultSetMappings.class)))
363: .booleanValue()
364: && hasRSMapping(
365: rsMappingName,
366: ((SqlResultSetMappings) cls
367: .getAnnotation(SqlResultSetMappings.class))
368: .value()))
369: return cls;
370: }
371: return null;
372: }
373:
374: private boolean hasNamedQuery(String query, NamedQuery... queries) {
375: for (NamedQuery q : queries) {
376: if (query.equals(q.name()))
377: return true;
378: }
379: return false;
380: }
381:
382: private boolean hasRSMapping(String rsMapping,
383: SqlResultSetMapping... mappings) {
384: for (SqlResultSetMapping m : mappings) {
385: if (rsMapping.equals(m.name()))
386: return true;
387: }
388: return false;
389: }
390:
391: private boolean hasNamedNativeQuery(String query,
392: NamedNativeQuery... queries) {
393: for (NamedNativeQuery q : queries) {
394: if (query.equals(q.name()))
395: return true;
396: }
397: return false;
398: }
399:
400: @Override
401: protected MetaDataFilter newMetaDataFilter() {
402: ClassAnnotationMetaDataFilter camdf = new ClassAnnotationMetaDataFilter(
403: new Class[] { Entity.class, Embeddable.class,
404: MappedSuperclass.class });
405: camdf.setLog(log);
406: return camdf;
407: }
408:
409: /**
410: * Ensure all fields have declared a strategy.
411: */
412: private void validateStrategies(ClassMetaData meta) {
413: StringBuffer buf = null;
414: for (FieldMetaData fmd : meta.getDeclaredFields()) {
415: if (!fmd.isExplicit()) {
416: if (buf == null)
417: buf = new StringBuffer();
418: else
419: buf.append(", ");
420: buf.append(fmd);
421: }
422: }
423: if (buf != null)
424: throw new MetaDataException(_loc.get("no-pers-strat", buf));
425: }
426:
427: public MetaDataDefaults getDefaults() {
428: return _def;
429: }
430:
431: @Override
432: public ClassArgParser newClassArgParser() {
433: ClassArgParser parser = new ClassArgParser();
434: parser.setMetaDataStructure("package", null, new String[] {
435: "entity", "embeddable", "mapped-superclass" }, "class");
436: return parser;
437: }
438:
439: @Override
440: public void clear() {
441: super .clear();
442: if (_annoParser != null)
443: _annoParser.clear();
444: if (_xmlParser != null)
445: _xmlParser.clear();
446: if (_xml != null)
447: _xml.clear();
448: }
449:
450: protected Parser newParser(boolean loading) {
451: return newXMLParser(loading);
452: }
453:
454: protected Serializer newSerializer() {
455: return newXMLSerializer();
456: }
457:
458: @Override
459: protected void parse(MetaDataParser parser, Class[] cls) {
460: parse(parser, Collections.singleton(defaultXMLFile()));
461: }
462:
463: protected File defaultSourceFile(ClassMetaData meta) {
464: return defaultXMLFile();
465: }
466:
467: protected File defaultSourceFile(QueryMetaData query, Map clsNames) {
468: ClassMetaData meta = getDefiningMetaData(query, clsNames);
469: File file = (meta == null) ? null : meta.getSourceFile();
470: if (file != null)
471: return file;
472: return defaultXMLFile();
473: }
474:
475: protected File defaultSourceFile(SequenceMetaData seq, Map clsNames) {
476: return defaultXMLFile();
477: }
478:
479: /**
480: * Look for META-INF/orm.xml, and if it doesn't exist, choose a default.
481: */
482: private File defaultXMLFile() {
483: ClassLoader loader = repos.getConfiguration()
484: .getClassResolverInstance().getClassLoader(getClass(),
485: null);
486: URL rsrc = (URL) AccessController.doPrivileged(J2DoPriv5Helper
487: .getResourceAction(loader, "META-INF/orm.xml"));
488: if (rsrc != null) {
489: File file = new File(rsrc.getFile());
490: if (((Boolean) AccessController
491: .doPrivileged(J2DoPriv5Helper.existsAction(file)))
492: .booleanValue())
493: return file;
494: }
495: return new File("orm.xml");
496: }
497:
498: public void setConfiguration(Configuration conf) {
499: }
500:
501: public void startConfiguration() {
502: }
503:
504: public void endConfiguration() {
505: if (rsrcs == null)
506: rsrcs = Collections.singleton("META-INF/orm.xml");
507: else
508: rsrcs.add("META-INF/orm.xml");
509: }
510:
511: public void setInto(Options opts) {
512: opts.keySet().retainAll(opts.setInto(_def).keySet());
513: }
514:
515: /**
516: * Return JAXB XML annotation parser,
517: * creating it if it does not already exist.
518: */
519: public AnnotationPersistenceXMLMetaDataParser getXMLAnnotationParser() {
520: if (_annoXMLParser == null) {
521: _annoXMLParser = newXMLAnnotationParser();
522: _annoXMLParser.setRepository(repos);
523: }
524: return _annoXMLParser;
525: }
526:
527: /**
528: * Set the JAXB XML annotation parser.
529: */
530: public void setXMLAnnotationParser(
531: AnnotationPersistenceXMLMetaDataParser parser) {
532: if (_annoXMLParser != null)
533: _annoXMLParser.setRepository(null);
534: if (parser != null)
535: parser.setRepository(repos);
536: _annoXMLParser = parser;
537: }
538:
539: /**
540: * Create a new JAXB XML annotation parser.
541: */
542: protected AnnotationPersistenceXMLMetaDataParser newXMLAnnotationParser() {
543: return new AnnotationPersistenceXMLMetaDataParser(repos
544: .getConfiguration());
545: }
546:
547: public void loadXMLMetaData(FieldMetaData fmd) {
548: AnnotationPersistenceXMLMetaDataParser parser = getXMLAnnotationParser();
549: parser.parse(fmd);
550: }
551: }
|