1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.meta;
20
21 import java.io.Serializable;
22 import java.security.AccessController;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.TreeSet;
35
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.openjpa.conf.OpenJPAConfiguration;
38 import org.apache.openjpa.enhance.DynamicPersistenceCapable;
39 import org.apache.openjpa.enhance.PCRegistry;
40 import org.apache.openjpa.enhance.PCRegistry.RegisterClassListener;
41 import org.apache.openjpa.enhance.PersistenceCapable;
42 import org.apache.openjpa.event.LifecycleEventManager;
43 import org.apache.openjpa.lib.conf.Configurable;
44 import org.apache.openjpa.lib.conf.Configuration;
45 import org.apache.openjpa.lib.log.Log;
46 import org.apache.openjpa.lib.util.Closeable;
47 import org.apache.openjpa.lib.util.J2DoPrivHelper;
48 import org.apache.openjpa.lib.util.Localizer;
49 import org.apache.openjpa.lib.util.StringDistance;
50 import org.apache.openjpa.util.ImplHelper;
51 import org.apache.openjpa.util.InternalException;
52 import org.apache.openjpa.util.MetaDataException;
53 import org.apache.openjpa.util.OpenJPAId;
54 import serp.util.Strings;
55
56 /**
57 * Repository of and factory for persistent metadata.
58 *
59 * @since 0.3.0
60 * @author Abe White
61 * @author Steve Kim (query metadata)
62 */
63 public class MetaDataRepository
64 implements PCRegistry.RegisterClassListener, Configurable, Closeable,
65 MetaDataModes, Serializable {
66
67 /**
68 * Constant to not validate any metadata.
69 */
70 public static final int VALIDATE_NONE = 0;
71
72 /**
73 * Bit flag to validate metadata.
74 */
75 public static final int VALIDATE_META = 1;
76
77 /**
78 * Bit flag to validate mappings.
79 */
80 public static final int VALIDATE_MAPPING = 2;
81
82 /**
83 * Bit flag to validate unenhanced metadata only.
84 */
85 public static final int VALIDATE_UNENHANCED = 4;
86
87 /**
88 * Bit flag for runtime validation. Requires that all classes are
89 * enhanced, and performs extra field resolution steps.
90 */
91 public static final int VALIDATE_RUNTIME = 8;
92
93 protected static final Class[] EMPTY_CLASSES = new Class[0];
94 protected static final NonPersistentMetaData[] EMPTY_NON_PERSISTENT =
95 new NonPersistentMetaData[0];
96 protected final ClassMetaData[] EMPTY_METAS;
97 protected final FieldMetaData[] EMPTY_FIELDS;
98 protected final Order[] EMPTY_ORDERS;
99
100 private static final Localizer _loc = Localizer.forPackage
101 (MetaDataRepository.class);
102
103 // system sequence
104 private SequenceMetaData _sysSeq = null;
105
106 // cache of parsed metadata, oid class to class, and interface class
107 // to metadatas
108 private final Map _metas = new HashMap();
109 private final Map _oids = Collections.synchronizedMap(new HashMap());
110 private final Map _impls = Collections.synchronizedMap(new HashMap());
111 private final Map _ifaces = Collections.synchronizedMap(new HashMap());
112 private final Map _queries = new HashMap();
113 private final Map _seqs = new HashMap();
114 private final Map _aliases = Collections.synchronizedMap(new HashMap());
115 private final Map _pawares = Collections.synchronizedMap(new HashMap());
116 private final Map _nonMapped = Collections.synchronizedMap(new HashMap());
117
118 // map of classes to lists of their subclasses
119 private final Map _subs = Collections.synchronizedMap(new HashMap());
120
121 // xml mapping
122 protected final XMLMetaData[] EMPTY_XMLMETAS;
123 private final Map _xmlmetas = new HashMap();
124
125 private transient OpenJPAConfiguration _conf = null;
126 private transient Log _log = null;
127 private transient InterfaceImplGenerator _implGen = null;
128 private transient MetaDataFactory _factory = null;
129
130 private int _resMode = MODE_META | MODE_MAPPING;
131 private int _sourceMode = MODE_META | MODE_MAPPING | MODE_QUERY;
132 private int _validate = VALIDATE_META | VALIDATE_UNENHANCED;
133
134 // we buffer up any classes that register themselves to prevent
135 // reentrancy errors if classes register during a current parse (common)
136 private final Collection _registered = new HashSet();
137
138 // set of metadatas we're in the process of resolving
139 private final InheritanceOrderedMetaDataList _resolving =
140 new InheritanceOrderedMetaDataList();
141 private final InheritanceOrderedMetaDataList _mapping =
142 new InheritanceOrderedMetaDataList();
143 private final List _errs = new LinkedList();
144
145 // system listeners
146 private LifecycleEventManager.ListenerList _listeners =
147 new LifecycleEventManager.ListenerList(3);
148
149 /**
150 * Default constructor. Configure via {@link Configurable}.
151 */
152 public MetaDataRepository() {
153 EMPTY_METAS = newClassMetaDataArray(0);
154 EMPTY_FIELDS = newFieldMetaDataArray(0);
155 EMPTY_ORDERS = newOrderArray(0);
156 EMPTY_XMLMETAS = newXMLClassMetaDataArray(0);
157 }
158
159 /**
160 * Return the configuration for the repository.
161 */
162 public OpenJPAConfiguration getConfiguration() {
163 return _conf;
164 }
165
166 /**
167 * Return the metadata log.
168 */
169 public Log getLog() {
170 return _log;
171 }
172
173 /**
174 * The I/O used to load metadata.
175 */
176 public MetaDataFactory getMetaDataFactory() {
177 return _factory;
178 }
179
180 /**
181 * The I/O used to load metadata.
182 */
183 public void setMetaDataFactory(MetaDataFactory factory) {
184 factory.setRepository(this);
185 _factory = factory;
186 }
187
188 /**
189 * The metadata validation level. Defaults to
190 * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
191 */
192 public int getValidate() {
193 return _validate;
194 }
195
196 /**
197 * The metadata validation level. Defaults to
198 * <code>VALIDATE_META | VALIDATE_UNENHANCED</code>.
199 */
200 public void setValidate(int validate) {
201 _validate = validate;
202 }
203
204 /**
205 * The metadata validation level. Defaults to
206 * <code>VALIDATE_META | VALIDATE_MAPPING | VALIDATE_UNENHANCED</code>.
207 */
208 public void setValidate(int validate, boolean on) {
209 if (validate == VALIDATE_NONE)
210 _validate = validate;
211 else if (on)
212 _validate |= validate;
213 else
214 _validate &= ~validate;
215 }
216
217 /**
218 * The metadata resolution mode. Defaults to
219 * <code>MODE_META | MODE_MAPPING</code>.
220 */
221 public int getResolve() {
222 return _resMode;
223 }
224
225 /**
226 * The metadata resolution mode. Defaults to
227 * <code>MODE_META | MODE_MAPPING</code>.
228 */
229 public void setResolve(int mode) {
230 _resMode = mode;
231 }
232
233 /**
234 * The metadata resolution mode. Defaults to
235 * <code>MODE_META | MODE_MAPPING</code>.
236 */
237 public void setResolve(int mode, boolean on) {
238 if (mode == MODE_NONE)
239 _resMode = mode;
240 else if (on)
241 _resMode |= mode;
242 else
243 _resMode &= ~mode;
244 }
245
246 /**
247 * The source mode determining what metadata to load. Defaults to
248 * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
249 */
250 public int getSourceMode() {
251 return _sourceMode;
252 }
253
254 /**
255 * The source mode determining what metadata to load. Defaults to
256 * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
257 */
258 public void setSourceMode(int mode) {
259 _sourceMode = mode;
260 }
261
262 /**
263 * The source mode determining what metadata to load. Defaults to
264 * <code>MODE_META | MODE_MAPPING | MODE_QUERY</code>.
265 */
266 public void setSourceMode(int mode, boolean on) {
267 if (mode == MODE_NONE)
268 _sourceMode = mode;
269 else if (on)
270 _sourceMode |= mode;
271 else
272 _sourceMode &= ~mode;
273 }
274
275 /**
276 * Return the metadata for the given class.
277 *
278 * @param cls the class to retrieve metadata for
279 * @param envLoader the environmental class loader, if any
280 * @param mustExist if true, throws a {@link MetaDataException}
281 * if no metadata is found
282 */
283 public synchronized ClassMetaData getMetaData(Class cls,
284 ClassLoader envLoader, boolean mustExist) {
285 if (cls != null &&
286 DynamicPersistenceCapable.class.isAssignableFrom(cls))
287 cls = cls.getSuperclass();
288
289 // if cls is a generated interface, use the user interface
290 // to locate metadata
291 if (cls != null && _implGen.isImplType(cls))
292 cls = _implGen.toManagedInterface(cls);
293
294 ClassMetaData meta = getMetaDataInternal(cls, envLoader);
295 if (meta == null && mustExist) {
296 if (cls != null &&
297 !ImplHelper.isManagedType(_conf, cls))
298 throw new MetaDataException(_loc.get("no-meta-notpc", cls)).
299 setFatal(false);
300
301 Set pcNames = getPersistentTypeNames(false, envLoader);
302 if (pcNames != null && pcNames.size() > 0)
303 throw new MetaDataException(_loc.get("no-meta-types",
304 cls, pcNames));
305
306 throw new MetaDataException(_loc.get("no-meta", cls));
307 }
308 resolve(meta);
309 return meta;
310 }
311
312 /**
313 * Return the metadata for the given alias name.
314 *
315 * @param alias the alias to class to retrieve metadata for
316 * @param envLoader the environmental class loader, if any
317 * @param mustExist if true, throws a {@link MetaDataException}
318 * if no metadata is found
319 * @see ClassMetaData#getTypeAlias
320 */
321 public ClassMetaData getMetaData(String alias, ClassLoader envLoader,
322 boolean mustExist) {
323 if (alias == null && mustExist)
324 throw new MetaDataException(_loc.get("no-alias-meta", alias,
325 _aliases));
326 if (alias == null)
327 return null;
328
329 // check cache
330 processRegisteredClasses(envLoader);
331 List classList = (List) _aliases.get(alias);
332
333 // multiple classes may have been defined with the same alias: we
334 // will filter by checking against the current list of the
335 // persistent types and filter based on which classes are loadable
336 // via the current environment's ClassLoader
337 Set pcNames = getPersistentTypeNames(false, envLoader);
338 Class cls = null;
339 for (int i = 0; classList != null && i < classList.size(); i++) {
340 Class c = (Class) classList.get(i);
341 try {
342 // re-load the class in the current environment loader so
343 // that we can handle redeployment of the same class name
344 Class nc = Class.forName(c.getName(), false, envLoader);
345
346 // if we have specified a list of persistent clases,
347 // also check to ensure that the class is in that list
348 if (pcNames == null || pcNames.size() == 0
349 || pcNames.contains(nc.getName())) {
350 cls = nc;
351 if (!classList.contains(cls))
352 classList.add(cls);
353 break;
354 }
355 } catch (Throwable t) {
356 // this happens when the class is not loadable by
357 // the environment class loader, so it was probably
358 // listed elsewhere; also ignore linkage failures and
359 // other class loading problems
360 }
361 }
362 if (cls != null)
363 return getMetaData(cls, envLoader, mustExist);
364
365 // maybe this is some type we've seen but just isn't valid
366 if (_aliases.containsKey(alias)) {
367 if (mustExist)
368 throwNoRegisteredAlias(alias);
369 return null;
370 }
371
372 // record that this is an invalid type
373 _aliases.put(alias, null);
374
375 if (!mustExist)
376 return null;
377 return throwNoRegisteredAlias(alias);
378 }
379
380 private ClassMetaData throwNoRegisteredAlias(String alias) {
381 String close = getClosestAliasName(alias);
382 if (close != null)
383 throw new MetaDataException(
384 _loc.get("no-alias-meta-hint", alias, _aliases, close));
385 else
386 throw new MetaDataException(
387 _loc.get("no-alias-meta", alias, _aliases));
388 }
389
390 /**
391 * @return the nearest match to the specified alias name
392 * @since 1.1.0
393 */
394 public String getClosestAliasName(String alias) {
395 Collection aliases = getAliasNames();
396 return StringDistance.getClosestLevenshteinDistance(alias, aliases);
397 }
398
399 /**
400 * @return the registered alias names
401 * @since 1.1.0
402 */
403 public Collection getAliasNames() {
404 Collection aliases = new HashSet();
405 synchronized (_aliases) {
406 for (Iterator iter = _aliases.entrySet().iterator();
407 iter.hasNext(); ) {
408 Map.Entry e = (Map.Entry) iter.next();
409 if (e.getValue() != null)
410 aliases.add(e.getKey());
411 }
412 }
413 return aliases;
414 }
415
416 /**
417 * Internal method to get the metadata for the given class, without
418 * resolving it.
419 */
420 private ClassMetaData getMetaDataInternal(Class cls,
421 ClassLoader envLoader) {
422 if (cls == null)
423 return null;
424
425 // check cache for existing metadata, or give up if no metadata and
426 // our list of configured persistent types doesn't include the class
427 ClassMetaData meta = (ClassMetaData) _metas.get(cls);
428 if (meta != null && ((meta.getSourceMode() & MODE_META) != 0
429 || (_sourceMode & MODE_META) == 0))
430 return meta;
431
432 // if runtime, cut off search if not in pc list. we don't do this at
433 // dev time so that user can manipulate persistent classes he's writing
434 // before adding them to the list
435 if ((_validate & VALIDATE_RUNTIME) != 0) {
436 Set pcNames = getPersistentTypeNames(false, envLoader);
437 if (pcNames != null && !pcNames.contains(cls.getName()))
438 return meta;
439 }
440
441 if (meta == null) {
442 // check to see if maybe we know this class has no metadata
443 if (_metas.containsKey(cls))
444 return null;
445
446 // make sure this isn't an obviously bad class
447 if (cls.isPrimitive() || cls.getName().startsWith("java.")
448 || cls == PersistenceCapable.class)
449 return null;
450
451 // designed to get around jikes 1.17 / JDK1.5 issue where static
452 // initializers are not invoked when a class is referenced, so the
453 // class never registers itself with the system
454 if ((_validate & VALIDATE_RUNTIME) != 0) {
455 try {
456 Class.forName(cls.getName(), true,
457 (ClassLoader) AccessController.doPrivileged(
458 J2DoPrivHelper.getClassLoaderAction(cls)));
459 } catch (Throwable t) {
460 }
461 }
462 }
463
464 // not in cache: load metadata or mappings depending on source mode.
465 // loading metadata might also load mappings, but doesn't have to
466 int mode = 0;
467 if ((_sourceMode & MODE_META) != 0)
468 mode = _sourceMode & ~MODE_MAPPING;
469 else if ((_sourceMode & MODE_MAPPING) == 0)
470 mode = _sourceMode;
471 if (mode != MODE_NONE) {
472 if (_log.isTraceEnabled())
473 _log.trace(_loc.get("load-cls", cls, toModeString(mode)));
474 _factory.load(cls, mode, envLoader);
475 }
476
477 // check cache again
478 if (meta == null)
479 meta = (ClassMetaData) _metas.get(cls);
480 if (meta != null && ((meta.getSourceMode() & MODE_META) != 0
481 || (_sourceMode & MODE_META) == 0))
482 return meta;
483
484 // record that this class has no metadata; checking for this later
485 // speeds things up in environments with slow class loading
486 // like appservers
487 if (meta != null)
488 removeMetaData(meta);
489 _metas.put(cls, null);
490 return null;
491 }
492
493 /**
494 * Return a string representation of the given mode flags.
495 */
496 private static String toModeString(int mode) {
497 StringBuffer buf = new StringBuffer(31);
498 if ((mode & MODE_META) != 0)
499 buf.append("[META]");
500 if ((mode & MODE_QUERY) != 0)
501 buf.append("[QUERY]");
502 if ((mode & MODE_MAPPING) != 0)
503 buf.append("[MAPPING]");
504 if ((mode & MODE_MAPPING_INIT) != 0)
505 buf.append("[MAPPING_INIT]");
506 return buf.toString();
507 }
508
509 /**
510 * Prepare metadata for mapping resolution. This method might map parts
511 * of the metadata that don't rely on other classes being mapped, but that
512 * other classes might rely on during their own mapping (for example,
513 * primary key fields). By default, this method only calls
514 * {@link ClassMetaData#defineSuperclassFields}.
515 */
516 protected void prepareMapping(ClassMetaData meta) {
517 meta.defineSuperclassFields(false);
518 }
519
520 /**
521 * Resolve the given metadata if needed. There are three goals:
522 * <ol>
523 * <li>Make sure no unresolved metadata gets back to the client.</li>
524 * <li>Avoid infinite reentrant calls for mutually-dependent metadatas by
525 * allowing unresolved metadata to be returned to other metadatas.</li>
526 * <li>Always make sure the superclass metadata is resolved before the
527 * subclass metadata so that the subclass can access the super's list
528 * of fields.</li>
529 * </ol> Note that the code calling this method is synchronized, so this
530 * method doesn't have to be.
531 */
532 private void resolve(ClassMetaData meta) {
533 // return anything that has its metadata resolved, because that means
534 // it is either fully resolved or must at least be in the process of
535 // resolving mapping, etc since we do that right after meta resolve
536 if (meta == null || _resMode == MODE_NONE
537 || (meta.getResolve() & MODE_META) != 0)
538 return;
539
540 // resolve metadata
541 List resolved = resolveMeta(meta);
542 if (resolved == null)
543 return;
544
545 // load mapping data
546 for (int i = 0; i < resolved.size(); i++)
547 loadMapping((ClassMetaData) resolved.get(i));
548 for (int i = 0; i < resolved.size(); i++)
549 preMapping((ClassMetaData) resolved.get(i));
550
551 // resolve mappings
552 boolean err = true;
553 if ((_resMode & MODE_MAPPING) != 0)
554 for (int i = 0; i < resolved.size(); i++)
555 err &= resolveMapping((ClassMetaData) resolved.get(i));
556
557 // throw errors encountered
558 if (err && !_errs.isEmpty()) {
559 RuntimeException re;
560 if (_errs.size() == 1)
561 re = (RuntimeException) _errs.get(0);
562 else
563 re = new MetaDataException(_loc.get("resolve-errs")).
564 setNestedThrowables((Throwable[]) _errs.toArray
565 (new Exception[_errs.size()]));
566 _errs.clear();
567 throw re;
568 }
569 }
570
571 /**
572 * Resolve metadata mode, returning list of processed metadadatas, or null
573 * if we're still in the process of resolving other metadatas.
574 */
575 private List resolveMeta(ClassMetaData meta) {
576 if (meta.getPCSuperclass() == null) {
577 // set superclass
578 Class sup = meta.getDescribedType().getSuperclass();
579 ClassMetaData supMeta;
580 while (sup != null && sup != Object.class) {
581 supMeta = getMetaData(sup, meta.getEnvClassLoader(), false);
582 if (supMeta != null) {
583 meta.setPCSuperclass(sup);
584 meta.setPCSuperclassMetaData(supMeta);
585 break;
586 } else
587 sup = sup.getSuperclass();
588 }
589 if (meta.getDescribedType().isInterface()) {
590 Class[] sups = meta.getDescribedType().getInterfaces();
591 for (int i = 0; i < sups.length; i++) {
592 supMeta = getMetaData(sups[i], meta.getEnvClassLoader(),
593 false);
594 if (supMeta != null) {
595 meta.setPCSuperclass(sup);
596 meta.setPCSuperclassMetaData(supMeta);
597 break;
598 }
599 }
600 }
601 if (_log.isTraceEnabled())
602 _log.trace(_loc.get("assigned-sup", meta,
603 meta.getPCSuperclass()));
604 }
605
606 // resolve relation primary key fields for mapping dependencies
607 FieldMetaData[] fmds = meta.getDeclaredFields();
608 for (int i = 0; i < fmds.length; i++)
609 if (fmds[i].isPrimaryKey())
610 getMetaData(fmds[i].getDeclaredType(),
611 meta.getEnvClassLoader(), false);
612
613 // resolve metadata; if we're not in the process of resolving
614 // others, this will return the set of interrelated metas that
615 // resolved
616 return processBuffer(meta, _resolving, MODE_META);
617 }
618
619 /**
620 * Load mapping information for the given metadata.
621 */
622 private void loadMapping(ClassMetaData meta) {
623 if ((meta.getResolve() & MODE_MAPPING) != 0)
624 return;
625
626 // load mapping information
627 if ((meta.getSourceMode() & MODE_MAPPING) == 0
628 && (_sourceMode & MODE_MAPPING) != 0) {
629 // embedded-only metadata doesn't have mapping, so always loaded
630 if (meta.isEmbeddedOnly())
631 meta.setSourceMode(MODE_MAPPING, true);
632 else {
633 // load mapping data
634 int mode = _sourceMode & ~MODE_META;
635 if (_log.isTraceEnabled())
636 _log.trace(_loc.get("load-mapping", meta,
637 toModeString(mode)));
638 try {
639 _factory.load(meta.getDescribedType(), mode,
640 meta.getEnvClassLoader());
641 } catch (RuntimeException re) {
642 removeMetaData(meta);
643 _errs.add(re);
644 }
645 }
646 }
647 }
648
649 /**
650 * Pre-mapping preparation.
651 */
652 private void preMapping(ClassMetaData meta) {
653 if ((meta.getResolve() & MODE_MAPPING) != 0)
654 return;
655
656 // prepare mappings for resolve; if not resolving mappings, then
657 // make sure any superclass fields defined in metadata are resolved
658 try {
659 if ((_resMode & MODE_MAPPING) != 0) {
660 if (_log.isTraceEnabled())
661 _log.trace(_loc.get("prep-mapping", meta));
662 prepareMapping(meta);
663 } else
664 meta.defineSuperclassFields(false);
665 } catch (RuntimeException re) {
666 removeMetaData(meta);
667 _errs.add(re);
668 }
669 }
670
671 /**
672 * Resolve and initialize mapping.
673 *
674 * @return false if we're still in the process of resolving mappings
675 */
676 private boolean resolveMapping(ClassMetaData meta) {
677 List mapped = processBuffer(meta, _mapping, MODE_MAPPING);
678 if (mapped == null)
679 return false;
680
681 // initialize mapping for runtime use
682 if ((_resMode & MODE_MAPPING_INIT) != 0) {
683 for (int i = 0; i < mapped.size(); i++) {
684 meta = (ClassMetaData) mapped.get(i);
685 try {
686 meta.resolve(MODE_MAPPING_INIT);
687 } catch (RuntimeException re) {
688 removeMetaData(meta);
689 _errs.add(re);
690 }
691 }
692 }
693 return true;
694 }
695
696 /**
697 * Process the given metadata and the associated buffer.
698 */
699 private List processBuffer(ClassMetaData meta,
700 InheritanceOrderedMetaDataList buffer, int mode) {
701 // if we're already processing a metadata, just buffer this one; when
702 // the initial metadata finishes processing, we traverse the buffer
703 // and process all the others that were introduced during reentrant
704 // calls
705 if (!buffer.add(meta) || buffer.size() != 1)
706 return null;
707
708 // continually pop a metadata and process it until we run out; note
709 // that each processing call might place more metas in the buffer as
710 // one class tries to access metadata for another; also note that the
711 // buffer orders itself from least to most derived
712 ClassMetaData buffered;
713 List processed = new ArrayList(5);
714 while (!buffer.isEmpty()) {
715 buffered = buffer.peek();
716 try {
717 buffered.resolve(mode);
718 processed.add(buffered);
719 buffer.remove(buffered);
720 } catch (RuntimeException re) {
721 _errs.add(re);
722
723 // any exception during resolution of one type means we can't
724 // resolve any of the related types, so clear buffer. this also
725 // ensures that if two types relate to each other and one
726 // dies, we don't get into infinite cycles
727 for (Iterator itr = buffer.iterator(); itr.hasNext();) {
728 meta = (ClassMetaData) itr.next();
729 removeMetaData(meta);
730 if (meta != buffered) {
731 _errs.add(new MetaDataException(_loc.get
732 ("prev-errs", meta, buffered)));
733 }
734 }
735 buffer.clear();
736 }
737 }
738 return processed;
739 }
740
741 /**
742 * Return all the metadata instances currently in the repository.
743 */
744 public synchronized ClassMetaData[] getMetaDatas() {
745 // prevent concurrent mod errors when resolving one metadata
746 // introduces others
747 ClassMetaData[] metas = (ClassMetaData[]) _metas.values().
748 toArray(new ClassMetaData[_metas.size()]);
749 for (int i = 0; i < metas.length; i++)
750 if (metas[i] != null)
751 getMetaData(metas[i].getDescribedType(),
752 metas[i].getEnvClassLoader(), true);
753
754 List resolved = new ArrayList(_metas.size());
755 ClassMetaData meta;
756 for (Iterator itr = _metas.values().iterator(); itr.hasNext();) {
757 meta = (ClassMetaData) itr.next();
758 if (meta != null)
759 resolved.add(meta);
760 }
761 metas = (ClassMetaData[]) resolved.toArray
762 (newClassMetaDataArray(resolved.size()));
763 Arrays.sort(metas);
764 return metas;
765 }
766
767 /**
768 * Return the cached metadata for the given class, without any resolution.
769 * Return null if none.
770 */
771 public ClassMetaData getCachedMetaData(Class cls) {
772 return (ClassMetaData) _metas.get(cls);
773 }
774
775 /**
776 * Create a new metadata, populate it with default information, add it to
777 * the repository, and return it. Use the default access type.
778 */
779 public ClassMetaData addMetaData(Class cls) {
780 return addMetaData(cls, ClassMetaData.ACCESS_UNKNOWN);
781 }
782
783 /**
784 * Create a new metadata, populate it with default information, add it to
785 * the repository, and return it.
786 *
787 * @param access the access type to use in populating metadata
788 */
789 public ClassMetaData addMetaData(Class cls, int access) {
790 if (cls == null || cls.isPrimitive())
791 return null;
792
793 ClassMetaData meta = newClassMetaData(cls);
794 _factory.getDefaults().populate(meta, access);
795
796 // synchronize on this rather than the map, because all other methods
797 // that access _metas are synchronized on this
798 synchronized (this) {
799 if (_pawares.containsKey(cls))
800 throw new MetaDataException(_loc.get("pc-and-aware", cls));
801 _metas.put(cls, meta);
802 }
803 return meta;
804 }
805
806 /**
807 * Create a new class metadata instance.
808 */
809 protected ClassMetaData newClassMetaData(Class type) {
810 return new ClassMetaData(type, this);
811 }
812
813 /**
814 * Create a new array of the proper class metadata subclass.
815 */
816 protected ClassMetaData[] newClassMetaDataArray(int length) {
817 return new ClassMetaData[length];
818 }
819
820 /**
821 * Create a new field metadata instance.
822 */
823 protected FieldMetaData newFieldMetaData(String name, Class type,
824 ClassMetaData owner) {
825 return new FieldMetaData(name, type, owner);
826 }
827
828 /**
829 * Create a new array of the proper field metadata subclass.
830 */
831 protected FieldMetaData[] newFieldMetaDataArray(int length) {
832 return new FieldMetaData[length];
833 }
834
835 /**
836 * Create a new array of the proper xml class metadata subclass.
837 */
838 protected XMLMetaData[] newXMLClassMetaDataArray(int length) {
839 return new XMLClassMetaData[length];
840 }
841
842 /**
843 * Create a new embedded class metadata instance.
844 */
845 protected ClassMetaData newEmbeddedClassMetaData(ValueMetaData owner) {
846 return new ClassMetaData(owner);
847 }
848
849 /**
850 * Create a new value metadata instance.
851 */
852 protected ValueMetaData newValueMetaData(FieldMetaData owner) {
853 return new ValueMetaDataImpl(owner);
854 }
855
856 /**
857 * Create an {@link Order} for the given field and declaration. This
858 * method delegates to {@link #newRelatedFieldOrder} and
859 * {@link #newValueFieldOrder} by default.
860 */
861 protected Order newOrder(FieldMetaData owner, String name, boolean asc) {
862 // paths can start with (or equal) '#element'
863 if (name.startsWith(Order.ELEMENT))
864 name = name.substring(Order.ELEMENT.length());
865 if (name.length() == 0)
866 return newValueOrder(owner, asc);
867
868 // next token should be '.'
869 if (name.charAt(0) == '.')
870 name = name.substring(1);
871
872 // related field
873 ClassMetaData meta = owner.getElement().getTypeMetaData();
874 if (meta == null)
875 throw new MetaDataException(_loc.get("nonpc-field-orderable",
876 owner, name));
877 FieldMetaData rel = meta.getField(name);
878 if (rel == null)
879 throw new MetaDataException(_loc.get("bad-field-orderable",
880 owner, name));
881 return newRelatedFieldOrder(owner, rel, asc);
882 }
883
884 /**
885 * Order by the field value.
886 */
887 protected Order newValueOrder(FieldMetaData owner, boolean asc) {
888 return new InMemoryValueOrder(asc, getConfiguration());
889 }
890
891 /**
892 * Order by a field of the related type.
893 */
894 protected Order newRelatedFieldOrder(FieldMetaData owner,
895 FieldMetaData rel, boolean asc) {
896 return new InMemoryRelatedFieldOrder(rel, asc, getConfiguration());
897 }
898
899 /**
900 * Create an array of orders of the given size.
901 */
902 protected Order[] newOrderArray(int size) {
903 return new Order[size];
904 }
905
906 /**
907 * Remove a metadata instance from the repository.
908 *
909 * @return true if removed, false if not in this repository
910 */
911 public boolean removeMetaData(ClassMetaData meta) {
912 if (meta == null)
913 return false;
914 return removeMetaData(meta.getDescribedType());
915 }
916
917 /**
918 * Remove a metadata instance from the repository.
919 *
920 * @return true if removed, false if not in this repository
921 */
922 public synchronized boolean removeMetaData(Class cls) {
923 if (cls == null)
924 return false;
925 if (_metas.remove(cls) != null) {
926 Class impl = (Class) _ifaces.remove(cls);
927 if (impl != null)
928 _metas.remove(impl);
929 return true;
930 }
931 return false;
932 }
933
934 /**
935 * Add the given metadata as declared interface implementation.
936 */
937 void addDeclaredInterfaceImpl(ClassMetaData meta, Class iface) {
938 synchronized (_impls) {
939 Collection vals = (Collection) _impls.get(iface);
940
941 // check to see if the superclass already declares to avoid dups
942 if (vals != null) {
943 ClassMetaData sup = meta.getPCSuperclassMetaData();
944 for (; sup != null; sup = sup.getPCSuperclassMetaData())
945 if (vals.contains(sup.getDescribedType()))
946 return;
947 }
948 addToCollection(_impls, iface, meta.getDescribedType(), false);
949 }
950 }
951
952 /**
953 * Set the implementation for the given managed interface.
954 */
955 synchronized void setInterfaceImpl(ClassMetaData meta, Class impl) {
956 if (!meta.isManagedInterface())
957 throw new MetaDataException(_loc.get("not-managed-interface",
958 meta, impl));
959 _ifaces.put(meta.getDescribedType(), impl);
960 addDeclaredInterfaceImpl(meta, meta.getDescribedType());
961 ClassMetaData sup = meta.getPCSuperclassMetaData();
962 while (sup != null) {
963 // record superclass interface info while we can as well as we
964 // will only register concrete superclass in PCRegistry
965 sup.clearSubclassCache();
966 addToCollection(_subs, sup.getDescribedType(), impl, true);
967 sup = (ClassMetaData) sup.getPCSuperclassMetaData();
968 }
969 }
970
971 InterfaceImplGenerator getImplGenerator() {
972 return _implGen;
973 }
974
975 /**
976 * Return the least-derived class metadata for the given application
977 * identity object.
978 *
979 * @param oid the oid to get the metadata for
980 * @param envLoader the environmental class loader, if any
981 * @param mustExist if true, throws a {@link MetaDataException}
982 * if no metadata is found
983 */
984 public ClassMetaData getMetaData(Object oid, ClassLoader envLoader,
985 boolean mustExist) {
986 if (oid == null && mustExist)
987 throw new MetaDataException(_loc.get("no-oid-meta", oid, "?",
988 _oids.toString()));
989 if (oid == null)
990 return null;
991
992 if (oid instanceof OpenJPAId) {
993 Class cls = ((OpenJPAId) oid).getType();
994 return getMetaData(cls, envLoader, mustExist);
995 }
996
997 // check cache
998 processRegisteredClasses(envLoader);
999 Class cls = (Class) _oids.get(oid.getClass());
1000 if (cls != null)
1001 return getMetaData(cls, envLoader, mustExist);
1002
1003 // maybe this is some type we've seen but just isn't valid
1004 if (_oids.containsKey(oid.getClass())) {
1005 if (mustExist)
1006 throw new MetaDataException(_loc.get("no-oid-meta", oid,
1007 oid.getClass(), _oids));
1008 return null;
1009 }
1010
1011 // if still not match, register any classes that look similar to the
1012 // oid class and check again
1013 resolveIdentityClass(oid);
1014 if (processRegisteredClasses(envLoader).length > 0) {
1015 cls = (Class) _oids.get(oid.getClass());
1016 if (cls != null)
1017 return getMetaData(cls, envLoader, mustExist);
1018 }
1019
1020 // record that this is an invalid type
1021 _oids.put(oid.getClass(), null);
1022
1023 if (!mustExist)
1024 return null;
1025 throw new MetaDataException(_loc.get("no-oid-meta", oid,
1026 oid.getClass(), _oids)).setFailedObject(oid);
1027 }
1028
1029 /**
1030 * Make some guesses about the name of a target class for an
1031 * unknown application identity class.
1032 */
1033 private void resolveIdentityClass(Object oid) {
1034 if (oid == null)
1035 return;
1036
1037 Class oidClass = oid.getClass();
1038 if (_log.isTraceEnabled())
1039 _log.trace(_loc.get("resolve-identity", oidClass));
1040
1041 ClassLoader cl = (ClassLoader) AccessController.doPrivileged(
1042 J2DoPrivHelper.getClassLoaderAction(oidClass));
1043 String className;
1044 while (oidClass != null && oidClass != Object.class) {
1045 className = oidClass.getName();
1046
1047 // we take a brute-force approach: try to load all the class'
1048 // substrings. this will handle the following common naming cases:
1049 //
1050 // com.company.MyClass$ID -> com.company.MyClass
1051 // com.company.MyClassId -> com.company.MyClass
1052 // com.company.MyClassOid -> com.company.MyClass
1053 // com.company.MyClassPK -> com.company.MyClass
1054 //
1055 // this isn't the fastest thing possible, but this method will
1056 // only be called once per JVM per unknown app id class
1057 for (int i = className.length(); i > 1; i--) {
1058 if (className.charAt(i - 1) == '.')
1059 break;
1060
1061 try {
1062 Class.forName(className.substring(0, i), true, cl);
1063 } catch (Exception e) {
1064 } // consume all exceptions
1065 }
1066
1067 // move up the OID hierarchy
1068 oidClass = oidClass.getSuperclass();
1069 }
1070 }
1071
1072 /**
1073 * Return all least-derived metadatas with some mapped assignable type that
1074 * implement the given class.
1075 *
1076 * @param cls the class or interface to retrieve implementors for
1077 * @param envLoader the environmental class loader, if any
1078 * @param mustExist if true, throws a {@link MetaDataException}
1079 * if no metadata is found
1080 */
1081 public ClassMetaData[] getImplementorMetaDatas(Class cls,
1082 ClassLoader envLoader, boolean mustExist) {
1083 if (cls == null && mustExist)
1084 throw new MetaDataException(_loc.get("no-meta", cls));
1085 if (cls == null)
1086 return EMPTY_METAS;
1087
1088 // get impls of given interface / abstract class
1089 loadRegisteredClassMetaData(envLoader);
1090 Collection vals = (Collection) _impls.get(cls);
1091 ClassMetaData meta;
1092 Collection mapped = null;
1093 if (vals != null) {
1094 synchronized (vals) {
1095 for (Iterator itr = vals.iterator(); itr.hasNext();) {
1096 meta = getMetaData((Class) itr.next(), envLoader, true);
1097 if (meta.isMapped()
1098 || meta.getMappedPCSubclassMetaDatas().length > 0) {
1099 if (mapped == null)
1100 mapped = new ArrayList(vals.size());
1101 mapped.add(meta);
1102 }
1103 }
1104 }
1105 }
1106
1107 if (mapped == null && mustExist)
1108 throw new MetaDataException(_loc.get("no-meta", cls));
1109 if (mapped == null)
1110 return EMPTY_METAS;
1111 return (ClassMetaData[]) mapped.toArray(newClassMetaDataArray
1112 (mapped.size()));
1113 }
1114
1115 /**
1116 * Gets the metadata corresponding to the given persistence-aware class.
1117 * Returns null, if the given class is not registered as
1118 * persistence-aware.
1119 */
1120 public NonPersistentMetaData getPersistenceAware(Class cls) {
1121 return (NonPersistentMetaData)_pawares.get(cls);
1122 }
1123
1124 /**
1125 * Gets all the metadatas for persistence-aware classes
1126 *
1127 * @return empty array if no class has been registered as pers-aware
1128 */
1129 public NonPersistentMetaData[] getPersistenceAwares() {
1130 synchronized (_pawares) {
1131 if (_pawares.isEmpty())
1132 return EMPTY_NON_PERSISTENT;
1133 return (NonPersistentMetaData[])_pawares.values().toArray
1134 (new NonPersistentMetaData[_pawares.size()]);
1135 }
1136 }
1137
1138 /**
1139 * Add the given class as persistence-aware.
1140 *
1141 * @param cls non-null and must not alreaddy be added as persitence-capable
1142 */
1143 public NonPersistentMetaData addPersistenceAware(Class cls) {
1144 if (cls == null)
1145 return null;
1146 synchronized(this) {
1147 if (_pawares.containsKey(cls))
1148 return (NonPersistentMetaData)_pawares.get(cls);
1149 if (getCachedMetaData(cls) != null)
1150 throw new MetaDataException(_loc.get("pc-and-aware", cls));
1151 NonPersistentMetaData meta = new NonPersistentMetaData(cls, this,
1152 NonPersistentMetaData.TYPE_PERSISTENCE_AWARE);
1153 _pawares.put(cls, meta);
1154 return meta;
1155 }
1156 }
1157
1158 /**
1159 * Remove a persitence-aware class from the repository
1160 *
1161 * @return true if removed
1162 */
1163 public boolean removePersistenceAware(Class cls) {
1164 return _pawares.remove(cls) != null;
1165 }
1166
1167 /**
1168 * Gets the metadata corresponding to the given non-mapped interface.
1169 * Returns null, if the given interface is not registered as
1170 * persistence-aware.
1171 */
1172 public NonPersistentMetaData getNonMappedInterface(Class iface) {
1173 return (NonPersistentMetaData)_nonMapped.get(iface);
1174 }
1175
1176 /**
1177 * Gets the corresponding metadatas for all registered, non-mapped
1178 * interfaces
1179 *
1180 * @return empty array if no non-mapped interface has been registered.
1181 */
1182 public NonPersistentMetaData[] getNonMappedInterfaces() {
1183 synchronized (_nonMapped) {
1184 if (_nonMapped.isEmpty())
1185 return EMPTY_NON_PERSISTENT;
1186 return (NonPersistentMetaData[])_nonMapped.values().toArray
1187 (new NonPersistentMetaData[_nonMapped.size()]);
1188 }
1189 }
1190
1191 /**
1192 * Add the given non-mapped interface to the repository.
1193 *
1194 * @param iface the non-mapped interface
1195 */
1196 public NonPersistentMetaData addNonMappedInterface(Class iface) {
1197 if (iface == null)
1198 return null;
1199 if (!iface.isInterface())
1200 throw new MetaDataException(_loc.get("not-non-mapped", iface));
1201 synchronized(this) {
1202 if (_nonMapped.containsKey(iface))
1203 return (NonPersistentMetaData)_nonMapped.get(iface);
1204 if (getCachedMetaData(iface) != null)
1205 throw new MetaDataException(_loc.get("non-mapped-pc", iface));
1206 NonPersistentMetaData meta = new NonPersistentMetaData(iface, this,
1207 NonPersistentMetaData.TYPE_NON_MAPPED_INTERFACE);
1208 _nonMapped.put(iface, meta);
1209 return meta;
1210 }
1211 }
1212
1213 /**
1214 * Remove a non-mapped interface from the repository
1215 *
1216 * @return true if removed
1217 */
1218 public boolean removeNonMappedInterface(Class iface) {
1219 return _nonMapped.remove(iface) != null;
1220 }
1221
1222 /**
1223 * Clear the cache of parsed metadata. This method also clears the
1224 * internal {@link MetaDataFactory MetaDataFactory}'s cache.
1225 */
1226 public synchronized void clear() {
1227 if (_log.isTraceEnabled())
1228 _log.trace(_loc.get("clear-repos", this));
1229
1230 _metas.clear();
1231 _oids.clear();
1232 _subs.clear();
1233 _impls.clear();
1234 _queries.clear();
1235 _seqs.clear();
1236 _registered.clear();
1237 _factory.clear();
1238 _aliases.clear();
1239 _pawares.clear();
1240 _nonMapped.clear();
1241 }
1242
1243 /**
1244 * Return the set of configured persistent classes, or null if the user
1245 * did not configure any.
1246 *
1247 * @param devpath if true, search for metadata files in directories
1248 * in the classpath if no classes are configured explicitly
1249 * @param envLoader the class loader to use, or null for default
1250 */
1251 public synchronized Set getPersistentTypeNames(boolean devpath,
1252 ClassLoader envLoader) {
1253 return _factory.getPersistentTypeNames(devpath, envLoader);
1254 }
1255
1256 /**
1257 * Load the persistent classes named in configuration.
1258 * This ensures that all subclasses and application identity classes of
1259 * each type are known in advance, without having to rely on the
1260 * application loading the classes before performing operations that
1261 * might involve them.
1262 *
1263 * @param devpath if true, search for metadata files in directories
1264 * in the classpath if the no classes are configured explicitly
1265 * @param envLoader the class loader to use, or null for default
1266 * @return the loaded classes, or empty collection if none
1267 */
1268 public synchronized Collection loadPersistentTypes(boolean devpath,
1269 ClassLoader envLoader) {
1270 Set names = getPersistentTypeNames(devpath, envLoader);
1271 if (names == null || names.isEmpty())
1272 return Collections.EMPTY_LIST;
1273
1274 // attempt to load classes so that they get processed
1275 ClassLoader clsLoader = _conf.getClassResolverInstance().
1276 getClassLoader(getClass(), envLoader);
1277 List classes = new ArrayList(names.size());
1278 Class cls;
1279 for (Iterator itr = names.iterator(); itr.hasNext();) {
1280 cls = classForName((String) itr.next(), clsLoader);
1281 if (cls != null) {
1282 classes.add(cls);
1283
1284 // if the class is an interface, load its metadata to kick
1285 // off the impl generator
1286 if (cls.isInterface())
1287 getMetaData(cls, clsLoader, false);
1288 }
1289 }
1290 return classes;
1291 }
1292
1293 /**
1294 * Return the class for the given name, or null if not loadable.
1295 */
1296 private Class classForName(String name, ClassLoader loader) {
1297 try {
1298 return Class.forName(name, true, loader);
1299 } catch (Exception e) {
1300 if ((_validate & VALIDATE_RUNTIME) != 0) {
1301 if (_log.isWarnEnabled())
1302 _log.warn(_loc.get("bad-discover-class", name));
1303 } else if (_log.isInfoEnabled())
1304 _log.info(_loc.get("bad-discover-class", name));
1305 if (_log.isTraceEnabled())
1306 _log.trace(e);
1307 } catch (NoSuchMethodError nsme) {
1308 if (nsme.getMessage().indexOf(".pc") == -1)
1309 throw nsme;
1310
1311 // if the error is about a method that uses the PersistenceCapable
1312 // 'pc' method prefix, perform some logging and continue. This
1313 // probably just means that the class is not yet enhanced.
1314 if ((_validate & VALIDATE_RUNTIME) != 0) {
1315 if (_log.isWarnEnabled())
1316 _log.warn(_loc.get("bad-discover-class", name));
1317 } else if (_log.isInfoEnabled())
1318 _log.info(_loc.get("bad-discover-class", name));
1319 if (_log.isTraceEnabled())
1320 _log.trace(nsme);
1321 }
1322 return null;
1323 }
1324
1325 /**
1326 * Return all known subclasses for the given class mapping. Note
1327 * that this method only works during runtime when the repository is
1328 * registered as a {@link RegisterClassListener}.
1329 */
1330 Collection getPCSubclasses(Class cls) {
1331 Collection subs = (Collection) _subs.get(cls);
1332 if (subs == null)
1333 return Collections.EMPTY_LIST;
1334 return subs;
1335 }
1336
1337 ////////////////////////////////////////
1338 // RegisterClassListener implementation
1339 ////////////////////////////////////////
1340
1341 public void register(Class cls) {
1342 // buffer registered classes until an oid metadata request is made,
1343 // at which point we'll parse everything in the buffer
1344 synchronized (_registered) {
1345 _registered.add(cls);
1346 }
1347 }
1348
1349 /**
1350 * Parses the metadata for all registered classes.
1351 */
1352 private void loadRegisteredClassMetaData(ClassLoader envLoader) {
1353 Class[] reg = processRegisteredClasses(envLoader);
1354 for (int i = 0; i < reg.length; i++) {
1355 try {
1356 getMetaData(reg[i], envLoader, false);
1357 } catch (MetaDataException me) {
1358 if (_log.isWarnEnabled())
1359 _log.warn(me);
1360 }
1361 }
1362 }
1363
1364 /**
1365 * Updates our datastructures with the latest registered classes.
1366 */
1367 Class[] processRegisteredClasses(ClassLoader envLoader) {
1368 if (_registered.isEmpty())
1369 return EMPTY_CLASSES;
1370
1371 // copy into new collection to avoid concurrent mod errors on reentrant
1372 // registrations
1373 Class[] reg;
1374 synchronized (_registered) {
1375 reg = (Class[]) _registered.toArray(new Class[_registered.size()]);
1376 _registered.clear();
1377 }
1378
1379 Collection pcNames = getPersistentTypeNames(false, envLoader);
1380 Collection failed = null;
1381 for (int i = 0; i < reg.length; i++) {
1382 // don't process types that aren't listed by the user; may belong
1383 // to a different persistence unit
1384 if (pcNames != null && !pcNames.isEmpty()
1385 && !pcNames.contains(reg[i].getName()))
1386 continue;
1387
1388 try {
1389 processRegisteredClass(reg[i]);
1390 } catch (Throwable t) {
1391 if (!_conf.getRetryClassRegistration())
1392 throw new MetaDataException(_loc.get("error-registered",
1393 reg[i]), t);
1394
1395 if (_log.isWarnEnabled())
1396 _log.warn(_loc.get("failed-registered", reg[i]), t);
1397 if (failed == null)
1398 failed = new ArrayList();
1399 failed.add(reg[i]);
1400 }
1401 }
1402 if (failed != null) {
1403 synchronized (_registered) {
1404 _registered.addAll(failed);
1405 }
1406 }
1407 return reg;
1408 }
1409
1410 /**
1411 * Updates our datastructures with the given registered class.
1412 * Relies on the fact that a child class cannot register itself without
1413 * also registering its parent class by specifying its persistence
1414 * capable superclass in the registration event.
1415 */
1416 private void processRegisteredClass(Class cls) {
1417 if (_log.isTraceEnabled())
1418 _log.trace(_loc.get("process-registered", cls));
1419
1420 // update subclass lists; synchronize on this because accessing _metas
1421 // requires it
1422 Class leastDerived = cls;
1423 synchronized (this) {
1424 ClassMetaData meta;
1425 for (Class anc = cls;
1426 (anc = PCRegistry.getPersistentSuperclass(anc)) != null;) {
1427 addToCollection(_subs, anc, cls, true);
1428 meta = (ClassMetaData) _metas.get(anc);
1429 if (meta != null)
1430 meta.clearSubclassCache();
1431 leastDerived = anc;
1432 }
1433 }
1434
1435 // update oid mappings if this is a base concrete class
1436 Object oid = null;
1437 try {
1438 oid = PCRegistry.newObjectId(cls);
1439 } catch (InternalException ie) {
1440 // thrown for single field identity with null pk field value
1441 }
1442 if (oid != null) {
1443 Class existing = (Class) _oids.get(oid.getClass());
1444 if (existing != null) {
1445 // if there is already a class for this OID, then we know
1446 // that multiple classes are using the same OID: therefore,
1447 // put the least derived PC superclass into the map. This
1448 // gets around the problem of an abstract PC superclass
1449 // using application identity (since newObjectId
1450 // will return null for abstract classes).
1451 Class sup = cls;
1452 while (PCRegistry.getPersistentSuperclass(sup) != null)
1453 sup = PCRegistry.getPersistentSuperclass(sup);
1454
1455 _oids.put(oid.getClass(), sup);
1456 } else if (existing == null || cls.isAssignableFrom(existing))
1457 _oids.put(oid.getClass(), cls);
1458 }
1459
1460 // update mappings from interfaces and non-pc superclasses to
1461 // pc implementing types
1462 synchronized (_impls) {
1463 updateImpls(cls, leastDerived, cls);
1464 }
1465
1466 // set alias for class
1467 String alias = PCRegistry.getTypeAlias(cls);
1468 if (alias != null) {
1469 synchronized (_aliases) {
1470 List classList = (List) _aliases.get(alias);
1471 if (classList == null) {
1472 classList = new ArrayList(3);
1473 _aliases.put(alias, classList);
1474 }
1475 if (!classList.contains(cls))
1476 classList.add(cls);
1477 }
1478 }
1479 }
1480
1481 /**
1482 * Update the list of implementations of base classes and interfaces.
1483 */
1484 private void updateImpls(Class cls, Class leastDerived, Class check) {
1485 // allow users to query on common non-pc superclasses
1486 Class sup = check.getSuperclass();
1487 if (leastDerived == cls && sup != null && sup != Object.class) {
1488 addToCollection(_impls, sup, cls, false);
1489 updateImpls(cls, leastDerived, sup);
1490 }
1491
1492 // allow users to query on any implemented interfaces unless defaults
1493 // say the user must implement persistent interfaces explicitly in meta
1494 if (!_factory.getDefaults().isDeclaredInterfacePersistent())
1495 return;
1496 Class[] ints = check.getInterfaces();
1497 for (int i = 0; i < ints.length; i++) {
1498 // don't map java-standard interfaces
1499 if (ints[i].getName().startsWith("java."))
1500 continue;
1501
1502 // only map least-derived interface implementors
1503 if (leastDerived == cls || isLeastDerivedImpl(ints[i], cls)) {
1504 addToCollection(_impls, ints[i], cls, false);
1505 updateImpls(cls, leastDerived, ints[i]);
1506 }
1507 }
1508 }
1509
1510 /**
1511 * Return true if the given class is the least-derived persistent
1512 * implementor of the given interface, false otherwise.
1513 */
1514 private boolean isLeastDerivedImpl(Class inter, Class cls) {
1515 Class parent = PCRegistry.getPersistentSuperclass(cls);
1516 while (parent != null) {
1517 if (Arrays.asList(parent.getInterfaces()).contains(inter))
1518 return false;
1519 parent = PCRegistry.getPersistentSuperclass(parent);
1520 }
1521 return true;
1522 }
1523
1524 /**
1525 * Add the given value to the collection cached in the given map under
1526 * the given key.
1527 */
1528 private void addToCollection(Map map, Class key, Class value,
1529 boolean inheritance) {
1530 synchronized (map) {
1531 Collection coll = (Collection) map.get(key);
1532 if (coll == null) {
1533 if (inheritance) {
1534 InheritanceComparator comp = new InheritanceComparator();
1535 comp.setBase(key);
1536 coll = new TreeSet(comp);
1537 } else
1538 coll = new LinkedList();
1539 map.put(key, coll);
1540 }
1541 coll.add(value);
1542 }
1543 }
1544
1545 ///////////////////////////////
1546 // Configurable implementation
1547 ///////////////////////////////
1548
1549 public void setConfiguration(Configuration conf) {
1550 _conf = (OpenJPAConfiguration) conf;
1551 _log = _conf.getLog(OpenJPAConfiguration.LOG_METADATA);
1552 }
1553
1554 public void startConfiguration() {
1555 }
1556
1557 public void endConfiguration() {
1558 initializeMetaDataFactory();
1559 if (_implGen == null)
1560 _implGen = new InterfaceImplGenerator(this);
1561 }
1562
1563 private void initializeMetaDataFactory() {
1564 if (_factory == null) {
1565 MetaDataFactory mdf = _conf.newMetaDataFactoryInstance();
1566 if (mdf == null)
1567 throw new MetaDataException(_loc.get("no-metadatafactory"));
1568 setMetaDataFactory(mdf);
1569 }
1570 }
1571
1572 //////////////////
1573 // Query metadata
1574 //////////////////
1575
1576 /**
1577 * Return query metadata for the given class, name, and classloader.
1578 */
1579 public synchronized QueryMetaData getQueryMetaData(Class cls, String name,
1580 ClassLoader envLoader, boolean mustExist) {
1581 QueryMetaData meta = getQueryMetaDataInternal(cls, name, envLoader);
1582 if (meta == null) {
1583 // load all the metadatas for all the known classes so that
1584 // query names are seen and registered
1585 resolveAll(envLoader);
1586 meta = getQueryMetaDataInternal(cls, name, envLoader);
1587 }
1588
1589 if (meta == null && mustExist) {
1590 if (cls == null) {
1591 throw new MetaDataException(_loc.get
1592 ("no-named-query-null-class",
1593 getPersistentTypeNames(false, envLoader), name));
1594 } else {
1595 throw new MetaDataException(_loc.get("no-named-query",
1596 cls, name));
1597 }
1598 }
1599
1600 return meta;
1601 }
1602
1603 /**
1604 * Resolve all known metadata classes.
1605 */
1606 private void resolveAll(ClassLoader envLoader) {
1607 Collection types = loadPersistentTypes(false, envLoader);
1608 for (Iterator i = types.iterator(); i.hasNext(); ) {
1609 Class c = (Class) i.next();
1610 getMetaData(c, envLoader, false);
1611 }
1612 }
1613
1614 /**
1615 * Return query metadata for the given class, name, and classloader.
1616 */
1617 private QueryMetaData getQueryMetaDataInternal(Class cls, String name,
1618 ClassLoader envLoader) {
1619 if (name == null)
1620 return null;
1621
1622 // check cache
1623 Object key = getQueryKey(cls, name);
1624 QueryMetaData qm = (QueryMetaData) _queries.get(key);
1625 if (qm != null)
1626 return qm;
1627
1628 // get metadata for class, which will find queries in metadata file
1629 if (cls != null && getMetaData(cls, envLoader, false) != null) {
1630 qm = (QueryMetaData) _queries.get(key);
1631 if (qm != null)
1632 return qm;
1633 }
1634 if ((_sourceMode & MODE_QUERY) == 0)
1635 return null;
1636
1637 // see if factory can figure out a scope for this query
1638 if (cls == null)
1639 cls = _factory.getQueryScope(name, envLoader);
1640
1641 // not in cache; load
1642 _factory.load(cls, MODE_QUERY, envLoader);
1643 return (QueryMetaData) _queries.get(key);
1644 }
1645
1646 /**
1647 * Return the cached query metadata.
1648 */
1649 public synchronized QueryMetaData[] getQueryMetaDatas() {
1650 return (QueryMetaData[]) _queries.values().toArray
1651 (new QueryMetaData[_queries.size()]);
1652 }
1653
1654 /**
1655 * Return the cached query metadata for the given name.
1656 */
1657 public synchronized QueryMetaData getCachedQueryMetaData(Class cls,
1658 String name) {
1659 return (QueryMetaData) _queries.get(getQueryKey(cls, name));
1660 }
1661
1662 /**
1663 * Add a new query metadata to the repository and return it.
1664 */
1665 public synchronized QueryMetaData addQueryMetaData(Class cls, String name) {
1666 QueryMetaData meta = newQueryMetaData(cls, name);
1667 _queries.put(getQueryKey(meta), meta);
1668 return meta;
1669 }
1670
1671 /**
1672 * Create a new query metadata instance.
1673 */
1674 protected QueryMetaData newQueryMetaData(Class cls, String name) {
1675 QueryMetaData meta = new QueryMetaData(name);
1676 meta.setDefiningType(cls);
1677 return meta;
1678 }
1679
1680 /**
1681 * Remove the given query metadata from the repository.
1682 */
1683 public synchronized boolean removeQueryMetaData(QueryMetaData meta) {
1684 if (meta == null)
1685 return false;
1686 return _queries.remove(getQueryKey(meta)) != null;
1687 }
1688
1689 /**
1690 * Remove query metadata for the given class name if in the repository.
1691 */
1692 public synchronized boolean removeQueryMetaData(Class cls, String name) {
1693 if (name == null)
1694 return false;
1695 return _queries.remove(getQueryKey(cls, name)) != null;
1696 }
1697
1698 /**
1699 * Return a unique key for a given QueryMetaData.
1700 */
1701 private static Object getQueryKey(QueryMetaData meta) {
1702 if (meta == null)
1703 return null;
1704 return getQueryKey(meta.getDefiningType(), meta.getName());
1705 }
1706
1707 /**
1708 * Return a unique key for a given class / name. The class
1709 * argument can be null.
1710 */
1711 protected static Object getQueryKey(Class cls, String name) {
1712 if (cls == null)
1713 return name;
1714 QueryKey key = new QueryKey();
1715 key.clsName = cls.getName();
1716 key.name = name;
1717 return key;
1718 }
1719
1720 /////////////////////
1721 // Sequence metadata
1722 /////////////////////
1723
1724 /**
1725 * Return sequence metadata for the given name and classloader.
1726 */
1727 public synchronized SequenceMetaData getSequenceMetaData(String name,
1728 ClassLoader envLoader, boolean mustExist) {
1729 SequenceMetaData meta = getSequenceMetaDataInternal(name, envLoader);
1730 if (meta == null && SequenceMetaData.NAME_SYSTEM.equals(name)) {
1731 if (_sysSeq == null)
1732 _sysSeq = newSequenceMetaData(name);
1733 return _sysSeq;
1734 }
1735 if (meta == null && mustExist)
1736 throw new MetaDataException(_loc.get("no-named-sequence", name));
1737 return meta;
1738 }
1739
1740 /**
1741 * Used internally by metadata to retrieve sequence metadatas based on
1742 * possibly-unqualified sequence name.
1743 */
1744 SequenceMetaData getSequenceMetaData(ClassMetaData context, String name,
1745 boolean mustExist) {
1746 // try with given name
1747 MetaDataException e = null;
1748 try {
1749 SequenceMetaData seq = getSequenceMetaData(name,
1750 context.getEnvClassLoader(), mustExist);
1751 if (seq != null)
1752 return seq;
1753 } catch (MetaDataException mde) {
1754 e = mde;
1755 }
1756
1757 // if given name already fully qualified, give up
1758 if (name.indexOf('.') != -1) {
1759 if (e != null)
1760 throw e;
1761 return null;
1762 }
1763
1764 // try with qualified name
1765 name = Strings.getPackageName(context.getDescribedType())
1766 + "." + name;
1767 try {
1768 return getSequenceMetaData(name, context.getEnvClassLoader(),
1769 mustExist);
1770 } catch (MetaDataException mde) {
1771 // throw original exception
1772 if (e != null)
1773 throw e;
1774 throw mde;
1775 }
1776 }
1777
1778 /**
1779 * Return sequence metadata for the given name and classloader.
1780 */
1781 private SequenceMetaData getSequenceMetaDataInternal(String name,
1782 ClassLoader envLoader) {
1783 if (name == null)
1784 return null;
1785
1786 // check cache
1787 SequenceMetaData meta = (SequenceMetaData) _seqs.get(name);
1788 if (meta == null) {
1789 // load metadata for registered classes to hopefully find sequence
1790 // definition
1791 loadRegisteredClassMetaData(envLoader);
1792 meta = (SequenceMetaData) _seqs.get(name);
1793 }
1794 return meta;
1795 }
1796
1797 /**
1798 * Return the cached sequence metadata.
1799 */
1800 public synchronized SequenceMetaData[] getSequenceMetaDatas() {
1801 return (SequenceMetaData[]) _seqs.values().toArray
1802 (new SequenceMetaData[_seqs.size()]);
1803 }
1804
1805 /**
1806 * Return the cached a sequence metadata for the given name.
1807 */
1808 public synchronized SequenceMetaData getCachedSequenceMetaData(
1809 String name) {
1810 return (SequenceMetaData) _seqs.get(name);
1811 }
1812
1813 /**
1814 * Add a new sequence metadata to the repository and return it.
1815 */
1816 public synchronized SequenceMetaData addSequenceMetaData(String name) {
1817 SequenceMetaData meta = newSequenceMetaData(name);
1818 _seqs.put(name, meta);
1819 return meta;
1820 }
1821
1822 /**
1823 * Create a new sequence metadata instance.
1824 */
1825 protected SequenceMetaData newSequenceMetaData(String name) {
1826 return new SequenceMetaData(name, this);
1827 }
1828
1829 /**
1830 * Remove the given sequence metadata from the repository.
1831 */
1832 public synchronized boolean removeSequenceMetaData(SequenceMetaData meta) {
1833 if (meta == null)
1834 return false;
1835 return _seqs.remove(meta.getName()) != null;
1836 }
1837
1838 /**
1839 * Remove sequence metadata for the name if in the repository.
1840 */
1841 public synchronized boolean removeSequenceMetaData(String name) {
1842 if (name == null)
1843 return false;
1844 return _seqs.remove(name) != null;
1845 }
1846
1847 /**
1848 * Add the given system lifecycle listener.
1849 */
1850 public synchronized void addSystemListener(Object listener) {
1851 // copy to avoid issues with ListenerList and avoid unncessary
1852 // locking on the list during runtime
1853 LifecycleEventManager.ListenerList listeners = new
1854 LifecycleEventManager.ListenerList(_listeners);
1855 listeners.add(listener);
1856 _listeners = listeners;
1857 }
1858
1859 /**
1860 * Remove the given system lifecycle listener.
1861 */
1862 public synchronized boolean removeSystemListener(Object listener) {
1863 if (!_listeners.contains(listener))
1864 return false;
1865
1866 // copy to avoid issues with ListenerList and avoid unncessary
1867 // locking on the list during runtime
1868 LifecycleEventManager.ListenerList listeners = new
1869 LifecycleEventManager.ListenerList(_listeners);
1870 listeners.remove(listener);
1871 _listeners = listeners;
1872 return true;
1873 }
1874
1875 /**
1876 * Return the system lifecycle listeners
1877 */
1878 public LifecycleEventManager.ListenerList getSystemListeners() {
1879 return _listeners;
1880 }
1881
1882 /**
1883 * Free the resources used by this repository. Closes all user sequences.
1884 */
1885 public synchronized void close() {
1886 SequenceMetaData[] smds = getSequenceMetaDatas();
1887 for (int i = 0; i < smds.length; i++)
1888 smds[i].close();
1889 clear();
1890 }
1891
1892 /**
1893 * Query key struct.
1894 */
1895 private static class QueryKey
1896 implements Serializable {
1897
1898 public String clsName;
1899 public String name;
1900
1901 public int hashCode() {
1902 int clsHash = (clsName == null) ? 0 : clsName.hashCode();
1903 int nameHash = (name == null) ? 0 : name.hashCode();
1904 return clsHash + nameHash;
1905 }
1906
1907 public boolean equals(Object obj)
1908 {
1909 if (obj == this)
1910 return true;
1911 if (!(obj instanceof QueryKey))
1912 return false;
1913
1914 QueryKey qk = (QueryKey) obj;
1915 return StringUtils.equals (clsName, qk.clsName)
1916 && StringUtils.equals (name, qk.name);
1917 }
1918 }
1919
1920 /**
1921 * Return XML metadata for a given field metadata
1922 * @param fmd
1923 * @return XML metadata
1924 */
1925 public synchronized XMLMetaData getXMLMetaData(FieldMetaData fmd) {
1926 Class cls = fmd.getDeclaredType();
1927 // check if cached before
1928 XMLMetaData xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
1929 if (xmlmeta != null)
1930 return xmlmeta;
1931
1932 // load JAXB XML metadata
1933 _factory.loadXMLMetaData(fmd);
1934
1935 xmlmeta = (XMLClassMetaData) _xmlmetas.get(cls);
1936
1937 return xmlmeta;
1938 }
1939
1940 /**
1941 * Create a new metadata, populate it with default information, add it to
1942 * the repository, and return it.
1943 *
1944 * @param access the access type to use in populating metadata
1945 */
1946 public XMLClassMetaData addXMLMetaData(Class type, String name) {
1947 XMLClassMetaData meta = newXMLClassMetaData(type, name);
1948
1949 // synchronize on this rather than the map, because all other methods
1950 // that access _xmlmetas are synchronized on this
1951 synchronized (this) {
1952 _xmlmetas.put(type, meta);
1953 }
1954 return meta;
1955 }
1956
1957 /**
1958 * Return the cached XMLClassMetaData for the given class
1959 * Return null if none.
1960 */
1961 public XMLMetaData getCachedXMLMetaData(Class cls) {
1962 return (XMLMetaData) _xmlmetas.get(cls);
1963 }
1964
1965 /**
1966 * Create a new xml class metadata
1967 * @param type
1968 * @param name
1969 * @return a XMLClassMetaData
1970 */
1971 protected XMLClassMetaData newXMLClassMetaData(Class type, String name) {
1972 return new XMLClassMetaData(type, name);
1973 }
1974
1975 /**
1976 * Create a new xml field meta, add it to the fieldMap in the given
1977 * xml class metadata
1978 * @param type
1979 * @param name
1980 * @param meta
1981 * @return a XMLFieldMetaData
1982 */
1983 public XMLFieldMetaData newXMLFieldMetaData(Class type, String name) {
1984 return new XMLFieldMetaData(type, name);
1985 }
1986 }