0001: /**********************************************************************
0002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: ...
0018: **********************************************************************/package org.jpox.metadata;
0019:
0020: import java.io.IOException;
0021: import java.lang.reflect.Constructor;
0022: import java.net.MalformedURLException;
0023: import java.net.URL;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.Collections;
0027: import java.util.Comparator;
0028: import java.util.Enumeration;
0029: import java.util.HashMap;
0030: import java.util.HashSet;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Set;
0035: import java.util.TreeSet;
0036:
0037: import javax.jdo.spi.JDOImplHelper;
0038: import javax.jdo.spi.RegisterClassEvent;
0039: import javax.jdo.spi.RegisterClassListener;
0040:
0041: import org.jpox.ClassLoaderResolver;
0042: import org.jpox.OMFContext;
0043: import org.jpox.api.ApiAdapter;
0044: import org.jpox.exceptions.ClassNotResolvedException;
0045: import org.jpox.exceptions.JPOXException;
0046: import org.jpox.exceptions.JPOXUserException;
0047: import org.jpox.metadata.annotations.AnnotationManager;
0048: import org.jpox.metadata.xml.MetaDataParser;
0049: import org.jpox.util.ClassUtils;
0050: import org.jpox.util.JPOXLogger;
0051: import org.jpox.util.JavaUtils;
0052: import org.jpox.util.Localiser;
0053: import org.jpox.util.StringUtils;
0054:
0055: /**
0056: * Manager of MetaData information in JPOX having scope of an ObjectManagerFactory.
0057: * Each PMF/EMF will effectively have a single MetaDataManager handling all XML/Annotations MetaData.
0058: * <p>
0059: * A MetaDataManager can be "initialised" to start with particular MetaData. This is typically performed
0060: * when the operation has a "persistence-unit", which defines the classes/jars/mapping-files to use.
0061: * Alternatively, it can be initialised using a set of classes and mapping-files (for use by SchemaTool
0062: * and the Enhancer when specifying the files as input).
0063: * <P>
0064: * Acts as a registry of metadata so that metadata files don't need to be
0065: * parsed multiple times. MetaData is stored as a FileMetaData, which contains
0066: * PackageMetaData, which contains ClassMetaData, and so on. This maps exactly
0067: * to the users model of their metadata. The users access point is
0068: * <B>getMetaDataForClass()</B> which will check the known classes without metadata,
0069: * then check the existing registered metdata, then check the valid locations for
0070: * metdata files. This way, the metadata is managed from this single point.
0071: * </P>
0072: * <P>
0073: * Maintains a list of all classes that have been checked for MetaData and
0074: * don't have any available. This avoids the needs to look up MetaData multiple
0075: * times finding the same result. Currently this list is for all ClassMetaData
0076: * objects keyed by the class name.
0077: * </P>
0078: * TODO Update MetaDataManager to be able to share ClassMetaData with other MetaDataManager
0079: * so we need a central (singleton/static) registry that allows such communication.
0080: *
0081: * @version $Revision: 1.170 $
0082: */
0083: public abstract class MetaDataManager {
0084: /** Localiser for messages. */
0085: protected static final Localiser LOCALISER = Localiser
0086: .getInstance("org.jpox.metadata.Localisation");
0087:
0088: /** The ObjectManagerFactory Context that this metadata manager is operating in. */
0089: protected OMFContext omfContext = null;
0090:
0091: /** Manager for annotations. */
0092: protected AnnotationManager annotationManager = null;
0093:
0094: /** Parser for MetaData. */
0095: protected MetaDataParser metaDataParser = null;
0096:
0097: /** Flag whether we should validate the metadata files when parsing. */
0098: protected boolean validateMetaData = true;
0099:
0100: /** Cache of class names that are known to not have MetaData/annotations. */
0101: protected Collection classesWithoutPersistenceInfo = new HashSet();
0102:
0103: /** Map of FileMetaData for the parsed files, keyed by the URL string. */
0104: protected Map fileMetaDataByURLString = new HashMap();
0105:
0106: /** Map of ClassMetaData, keyed by the class name. */
0107: protected Map classMetaDataByClass = new HashMap();
0108:
0109: /** Map of ClassMetaData, keyed by the JPA "entity name". */
0110: protected Map classMetaDataByEntityName = new HashMap();
0111:
0112: /** Map of QueryMetaData, keyed by the (class name + query name). */
0113: protected Map queryMetaDataByName = new HashMap();
0114:
0115: /** Map of FetchPlanMetaData, keyed by the fetch plan name. */
0116: protected Map fetchPlanMetaDataByName = new HashMap();
0117:
0118: /** Map of SequenceMetaData, keyed by the package name and sequence name. */
0119: protected Map sequenceMetaDataByPackageSequence = new HashMap();
0120:
0121: /** Map of TableGeneratorMetaData, keyed by the package name and generator name. */
0122: protected Map tableGeneratorMetaDataByPackageSequence = new HashMap();
0123:
0124: /** Map of QueryResultMetaData keyed by the name. */
0125: protected Map queryResultMetaDataByName = new HashMap();
0126:
0127: /** Indicator for whether this manager is managing the enhancement process, else it's runtime. */
0128: protected boolean enhancing = false;
0129:
0130: /** Flag for initialisation state when starting using a "persistence-unit". Used to prevent double initialise. */
0131: protected boolean initialised = false;
0132:
0133: /** EventListeners. Use a list to preserve ordering. */
0134: protected List listeners = new ArrayList();
0135:
0136: /** Factory for MetaData objects (class, field, property, interface). */
0137: private MetaDataFactory metaDataFactory;
0138:
0139: /**
0140: * Whether the MetaData manager supports ORM concepts and metadata.
0141: * When using an object datastore this will be false.
0142: */
0143: protected boolean supportsORM = true;
0144:
0145: /**
0146: * Constructor, specifying the OMFContext used.
0147: * @param ctxt OMF Context that this metadata manager operates in
0148: */
0149: public MetaDataManager(OMFContext ctxt) {
0150: this .omfContext = ctxt;
0151: JDOImplHelper.getInstance().removeRegisterClassListener(
0152: new MetaDataRegisterClassListener());
0153: this .setValidate(ctxt.getPersistenceConfiguration()
0154: .getMetaDataValidate());
0155:
0156: if (JavaUtils.isJRE1_5OrAbove()) {
0157: try {
0158: ClassLoaderResolver clr = ctxt
0159: .getClassLoaderResolver(null);
0160: Class annotationReaderClass = clr.classForName(ctxt
0161: .getPersistenceConfiguration()
0162: .getMetaDataAnnotationsManager());
0163:
0164: Class[] ctrArgs = new Class[] { MetaDataManager.class };
0165: Object[] ctrParams = new Object[] { this };
0166: Constructor ctor = annotationReaderClass
0167: .getConstructor(ctrArgs);
0168: annotationManager = (AnnotationManager) ctor
0169: .newInstance(ctrParams);
0170: } catch (Exception e) {
0171: JPOXLogger.METADATA.info(LOCALISER.msg("044045"));
0172: }
0173: }
0174:
0175: // Register all of the types managed by the TypeManager as not needing MetaData/Annotations.
0176: Set supportedClasses = getOMFContext().getTypeManager()
0177: .getSupportedTypes();
0178: Iterator iter = supportedClasses.iterator();
0179: while (iter.hasNext()) {
0180: classesWithoutPersistenceInfo.add(iter.next());
0181: }
0182:
0183: if (omfContext.getStoreManager() != null) {
0184: // Object datastores dont "map" for persistence so dont need ORM
0185: supportsORM = omfContext.getStoreManager()
0186: .usesDatastoreClass();
0187: }
0188: }
0189:
0190: /**
0191: * Accessor for whether the MetaData manager supports ORM concepts and metadata.
0192: * With object datastores this will return false.
0193: * @return Whether we support ORM
0194: */
0195: public boolean supportsORM() {
0196: return supportsORM;
0197: }
0198:
0199: /**
0200: * Method to log the configuration of this manager.
0201: */
0202: protected void logConfiguration() {
0203: // Log the configuration
0204: String inputTypes = null;
0205: if (annotationManager != null) {
0206: inputTypes = "XML, Annotations";
0207: } else {
0208: inputTypes = "XML";
0209: }
0210:
0211: JPOXLogger.METADATA.debug("MetaDataManager : " + " Input=("
0212: + inputTypes + ")" + " XML-Validation="
0213: + validateMetaData);
0214: }
0215:
0216: /**
0217: * Initialisation method to define the initial metadata files and class names being handled by this MetaDataManager.
0218: * @param metadataFiles The metadata files
0219: * @param classNames The class names
0220: * @param clr ClassLoader Resolver
0221: * @return Array of the FileMetaData that is managed
0222: * @throws JPOXUserException (with nested exceptions) if an error occurs parsing the files
0223: */
0224: public FileMetaData[] initialise(String[] metadataFiles,
0225: String[] classNames, ClassLoaderResolver clr) {
0226: if (initialised) {
0227: // TODO Throw exception
0228: return null;
0229: }
0230: if (JPOXLogger.METADATA.isDebugEnabled()) {
0231: JPOXLogger.METADATA.debug(LOCALISER.msg("044013"));
0232: }
0233:
0234: HashSet exceptions = new HashSet();
0235: ArrayList fileMetaData = new ArrayList();
0236:
0237: // Load MetaData files first
0238: for (int i = 0; i < metadataFiles.length; i++) {
0239: try {
0240: URL fileURL = null;
0241: try {
0242: fileURL = new URL("file:" + metadataFiles[i]);
0243: } catch (MalformedURLException mue) {
0244: fileURL = clr.getResource(metadataFiles[i], null);
0245: }
0246: if (fileURL != null
0247: && fileMetaDataByURLString.get(fileURL
0248: .toString()) == null) {
0249: FileMetaData filemd = parseFile(fileURL);
0250: if (filemd != null) {
0251: registerFile(fileURL.toString(), filemd, clr);
0252: fileMetaData.add(filemd);
0253: } else {
0254: throw new JPOXUserException(LOCALISER.msg(
0255: "044015", metadataFiles[i]));
0256: }
0257: }
0258: } catch (Exception e) {
0259: exceptions.add(e);
0260: }
0261: }
0262:
0263: // Load classes
0264: for (int i = 0; i < classNames.length; i++) {
0265: try {
0266: Class cls = clr.classForName(classNames[i]);
0267: // Check for MetaData for this class (take precedence over annotations if they exist)
0268: AbstractClassMetaData cmd = (AbstractClassMetaData) classMetaDataByClass
0269: .get(classNames[i]);
0270: if (cmd == null) {
0271: // No MetaData so try annotations
0272: FileMetaData filemd = loadAnnotationsForClass(cls,
0273: clr, true, false);
0274: if (filemd != null) {
0275: // Store file against an annotations specific "URL"
0276: registerFile("annotations:" + classNames[i],
0277: filemd, clr);
0278: fileMetaData.add(filemd);
0279: } else {
0280: // Class has no metadata or annotations so warn the user
0281: JPOXLogger.METADATA.info(LOCALISER.msg(
0282: "044017", classNames[i]));
0283: }
0284: } else {
0285: // We have MetaData, and any annotations will be merged in during the populate process
0286: }
0287: } catch (ClassNotResolvedException e) {
0288: // log and ignore this exception
0289: JPOXLogger.METADATA.error(StringUtils
0290: .getStringFromStackTrace(e));
0291: } catch (Exception e) {
0292: exceptions.add(e);
0293: }
0294: }
0295:
0296: if (exceptions.size() > 0) {
0297: // Exceptions while loading MetaData/annotations
0298: throw new JPOXUserException(LOCALISER.msg("044016"),
0299: (Throwable[]) exceptions
0300: .toArray(new Throwable[exceptions.size()]),
0301: null);
0302: }
0303:
0304: if (fileMetaData.size() > 0) {
0305: // MetaData has been loaded for the unit
0306: // a). Populate MetaData
0307: if (JPOXLogger.METADATA.isDebugEnabled()) {
0308: JPOXLogger.METADATA.debug(LOCALISER.msg("044018"));
0309: }
0310: Iterator iter = fileMetaData.iterator();
0311: while (iter.hasNext()) {
0312: FileMetaData filemd = (FileMetaData) iter.next();
0313: populateFileMetaData(filemd, clr, null);
0314: }
0315:
0316: // b). Initialise MetaData
0317: if (JPOXLogger.METADATA.isDebugEnabled()) {
0318: JPOXLogger.METADATA.debug(LOCALISER.msg("044019"));
0319: }
0320: iter = fileMetaData.iterator();
0321: while (iter.hasNext()) {
0322: FileMetaData filemd = (FileMetaData) iter.next();
0323: try {
0324: initialiseFileMetaData(filemd, clr, null);
0325: } catch (Exception e) {
0326: exceptions.add(e);
0327: }
0328: }
0329: if (exceptions.size() > 0) {
0330: throw new JPOXUserException(LOCALISER.msg("044020"),
0331: (Throwable[]) exceptions
0332: .toArray(new Throwable[exceptions
0333: .size()]));
0334: }
0335: }
0336:
0337: if (JPOXLogger.METADATA.isDebugEnabled()) {
0338: JPOXLogger.METADATA.debug(LOCALISER.msg("044014"));
0339: }
0340: initialised = true;
0341: return (FileMetaData[]) fileMetaData
0342: .toArray(new FileMetaData[fileMetaData.size()]);
0343: }
0344:
0345: /**
0346: * Initialisation method to define the "persistence-unit" being handled by this MetaDataManager.
0347: * @param persistenceUnitName name of the "persistence-unit"
0348: * @param clr ClassLoader Resolver
0349: * @return Array of the FileMetaData that is managed
0350: * @throws JPOXUserException if an error occurs parsing the persistence-unit info
0351: */
0352: public FileMetaData[] initialise(String persistenceUnitName,
0353: ClassLoaderResolver clr) {
0354: PersistenceUnitMetaData pumd = null;
0355: pumd = getMetaDataForPersistenceUnit(persistenceUnitName);
0356: if (pumd == null) {
0357: throw new JPOXUserException(LOCALISER.msg("044047",
0358: persistenceUnitName));
0359: } else {
0360: return initialise(pumd, clr);
0361: }
0362: }
0363:
0364: /**
0365: * Initialisation method to define the "persistence-unit" being handled by this MetaDataManager.
0366: * @param pumd The MetaData for this "persistence-unit"
0367: * @param clr ClassLoader Resolver
0368: * @return Array of the FileMetaData that is managed
0369: * @throws JPOXUserException if an error occurs parsing the persistence-unit info
0370: */
0371: public FileMetaData[] initialise(PersistenceUnitMetaData pumd,
0372: ClassLoaderResolver clr) {
0373: if (initialised) {
0374: // TODO Throw exception
0375: return null;
0376: }
0377: if (JPOXLogger.METADATA.isDebugEnabled()) {
0378: JPOXLogger.METADATA.debug(LOCALISER.msg("044021", pumd
0379: .getName()));
0380: }
0381:
0382: HashSet exceptions = new HashSet();
0383: ArrayList fileMetaData = new ArrayList();
0384:
0385: // Load XML metadata for all <mapping-file> specifications
0386: HashSet mappingFiles = new HashSet();
0387: mappingFiles.add("META-INF/orm.xml"); // Default location
0388: if (pumd.getMappingFiles() != null) {
0389: mappingFiles.addAll(pumd.getMappingFiles());
0390: }
0391: if (mappingFiles != null && mappingFiles.size() > 0) {
0392: Iterator iter = mappingFiles.iterator();
0393: while (iter.hasNext()) {
0394: String mappingFileName = (String) iter.next();
0395: try {
0396: Enumeration files = clr.getResources(
0397: mappingFileName, Thread.currentThread()
0398: .getContextClassLoader());
0399: while (files.hasMoreElements()) {
0400: URL url = (URL) files.nextElement();
0401: if (url != null
0402: && fileMetaDataByURLString.get(url
0403: .toString()) == null) {
0404: FileMetaData filemd = parseFile(url);
0405: if (filemd != null) {
0406: // Register the file
0407: registerFile(url.toString(), filemd,
0408: clr);
0409: fileMetaData.add(filemd);
0410: }
0411: }
0412: }
0413: } catch (InvalidMetaDataException imde) {
0414: // Error in the metadata for this file
0415: JPOXLogger.METADATA.error(StringUtils
0416: .getStringFromStackTrace(imde));
0417: exceptions.add(imde);
0418: } catch (IOException ioe) {
0419: JPOXLogger.METADATA.error(LOCALISER.msg("044027",
0420: pumd.getName(), mappingFileName, ioe
0421: .getMessage()), ioe);
0422: }
0423: }
0424: }
0425:
0426: // Load annotation metadata for all <class>, <jar-file> specifications
0427: Set classNames = new HashSet();
0428: if (!enhancing) {
0429: HashSet jarFileNames = pumd.getJarFiles();
0430: if (jarFileNames != null) {
0431: Iterator iter = jarFileNames.iterator();
0432: while (iter.hasNext()) {
0433: Object jarFile = iter.next();
0434: if (jarFile instanceof String) {
0435: String[] jarClassNames = ClassUtils
0436: .getClassNamesForJarFile((String) jarFile);
0437: if (jarClassNames != null) {
0438: for (int i = 0; i < jarClassNames.length; i++) {
0439: classNames.add(jarClassNames[i]);
0440: }
0441: }
0442: } else if (jarFile instanceof URL) {
0443: String[] jarClassNames = ClassUtils
0444: .getClassNamesForJarFile((URL) jarFile);
0445: if (jarClassNames != null) {
0446: for (int i = 0; i < jarClassNames.length; i++) {
0447: classNames.add(jarClassNames[i]);
0448: }
0449: }
0450: }
0451: }
0452: }
0453: }
0454: if (pumd.getClassNames() != null) {
0455: classNames.addAll(pumd.getClassNames());
0456: }
0457: if (classNames.size() > 0 && annotationManager != null) {
0458: // Process all classes
0459: Iterator iter = classNames.iterator();
0460: while (iter.hasNext()) {
0461: // Check for MetaData for this class (take precedence over annotations if they exist)
0462: String className = (String) iter.next();
0463: AbstractClassMetaData cmd = (AbstractClassMetaData) classMetaDataByClass
0464: .get(className);
0465: if (cmd == null) {
0466: // No MetaData so try annotations
0467: try {
0468: Class cls = clr.classForName(className);
0469: FileMetaData filemd = loadAnnotationsForClass(
0470: cls, clr, true, false);
0471: if (filemd != null) {
0472: fileMetaData.add(filemd);
0473: }
0474: } catch (Exception e) {
0475: exceptions.add(e);
0476: }
0477: } else {
0478: // We have MetaData, and any annotations will be merged in during the populate process
0479: }
0480: }
0481: }
0482:
0483: if (exceptions.size() > 0) {
0484: throw new JPOXUserException(LOCALISER.msg("044026", pumd
0485: .getName()), (Throwable[]) exceptions
0486: .toArray(new Throwable[exceptions.size()]));
0487: }
0488:
0489: if (fileMetaData.size() > 0) {
0490: // MetaData has been loaded for the unit
0491: // a). Populate MetaData
0492: if (JPOXLogger.METADATA.isDebugEnabled()) {
0493: JPOXLogger.METADATA.debug(LOCALISER.msg("044024", pumd
0494: .getName()));
0495: }
0496: Iterator iter = fileMetaData.iterator();
0497: while (iter.hasNext()) {
0498: FileMetaData filemd = (FileMetaData) iter.next();
0499: populateFileMetaData(filemd, clr, null);
0500: }
0501:
0502: // b). Initialise MetaData
0503: if (JPOXLogger.METADATA.isDebugEnabled()) {
0504: JPOXLogger.METADATA.debug(LOCALISER.msg("044025", pumd
0505: .getName()));
0506: }
0507: iter = fileMetaData.iterator();
0508: while (iter.hasNext()) {
0509: FileMetaData filemd = (FileMetaData) iter.next();
0510: try {
0511: initialiseFileMetaData(filemd, clr, null);
0512: } catch (Exception e) {
0513: JPOXLogger.METADATA.error(StringUtils
0514: .getStringFromStackTrace(e));
0515: exceptions.add(e);
0516: }
0517: }
0518: if (exceptions.size() > 0) {
0519: throw new JPOXUserException(LOCALISER.msg("044026",
0520: pumd.getName()), (Throwable[]) exceptions
0521: .toArray(new Throwable[exceptions.size()]));
0522: }
0523: }
0524:
0525: if (JPOXLogger.METADATA.isDebugEnabled()) {
0526: JPOXLogger.METADATA.debug(LOCALISER.msg("044022", pumd
0527: .getName()));
0528: }
0529: initialised = true;
0530: return (FileMetaData[]) fileMetaData
0531: .toArray(new FileMetaData[fileMetaData.size()]);
0532: }
0533:
0534: /**
0535: * Mutator for whether to validate the MetaData files for XML compliance.
0536: * @param validate Whether to validate
0537: */
0538: public void setValidate(boolean validate) {
0539: validateMetaData = validate;
0540: }
0541:
0542: /**
0543: * Accessor for the ObjectManagerFactory Context that this manager is running in.
0544: * @return The ObjectManagerFactory Context
0545: */
0546: public OMFContext getOMFContext() {
0547: return omfContext;
0548: }
0549:
0550: /**
0551: * Accessor for the API adapter being used by this MetaDataManager.
0552: * @return API adapter.
0553: */
0554: public ApiAdapter getApiAdapter() {
0555: return omfContext.getApiAdapter();
0556: }
0557:
0558: /**
0559: * Clear resources
0560: */
0561: public void close() {
0562: classMetaDataByClass.clear();
0563: queryMetaDataByName.clear();
0564: fetchPlanMetaDataByName.clear();
0565: sequenceMetaDataByPackageSequence.clear();
0566: tableGeneratorMetaDataByPackageSequence.clear();
0567: queryResultMetaDataByName.clear();
0568: fileMetaDataByURLString.clear();
0569: classesWithoutPersistenceInfo.clear();
0570: classMetaDataByClass = null;
0571: queryMetaDataByName = null;
0572: fetchPlanMetaDataByName = null;
0573: sequenceMetaDataByPackageSequence = null;
0574: tableGeneratorMetaDataByPackageSequence = null;
0575: fileMetaDataByURLString = null;
0576: classesWithoutPersistenceInfo = null;
0577: }
0578:
0579: /**
0580: * Accessor for whether we are managing the enhancement process.
0581: * @return Whether we are enhancing
0582: */
0583: public boolean isEnhancing() {
0584: return enhancing;
0585: }
0586:
0587: /**
0588: * Convenience method to return if the specified class is a known persistable class.
0589: * @param className Name of the class
0590: * @return Whether it is persistable
0591: */
0592: public boolean isClassPersistable(String className) {
0593: AbstractClassMetaData acmd = readMetaDataForClass(className);
0594: if (acmd == null) {
0595: return false;
0596: }
0597: return (acmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE);
0598: }
0599:
0600: /**
0601: * Accessor for all FileMetaData currently managed here.
0602: * @return FileMetaData managed here currently
0603: */
0604: public FileMetaData[] getFileMetaData() {
0605: Collection filemds = fileMetaDataByURLString.values();
0606: return (FileMetaData[]) filemds
0607: .toArray(new FileMetaData[filemds.size()]);
0608: }
0609:
0610: /**
0611: * Accessor for the names of the classes with MetaData currently registered with this manager.
0612: * @return Names of classes with MetaData
0613: */
0614: public Collection getClassesWithMetaData() {
0615: return Collections.unmodifiableCollection(classMetaDataByClass
0616: .keySet());
0617: }
0618:
0619: /**
0620: * Convenience method to check if we have metadata present for the specified class.
0621: * @param className The name of the class to check
0622: * @return Whether the metadata is already registered for this class
0623: */
0624: public boolean hasMetaDataForClass(String className) {
0625: if (className == null) {
0626: return false;
0627: }
0628:
0629: // Check if this class has no MetaData before instantiating its class
0630: if (isClassWithoutPersistenceInfo(className)) {
0631: return false;
0632: }
0633:
0634: return (classMetaDataByClass.get(className) != null);
0635: }
0636:
0637: /**
0638: * Accessor for whether a class doesn't have MetaData or annotations.
0639: * @param className Name of the class
0640: * @return Whether it has no metadata and annotations
0641: */
0642: protected boolean isClassWithoutPersistenceInfo(String className) {
0643: if (className == null) {
0644: return true;
0645: }
0646:
0647: // Standard Java classes have no MetaData
0648: if (className.startsWith("java.")
0649: || className.startsWith("javax.")) {
0650: return true;
0651: }
0652:
0653: // Use the cache to determine if it has metadata
0654: return classesWithoutPersistenceInfo.contains(className);
0655: }
0656:
0657: /**
0658: * Accessor for the MetaData for a class given the name and a loader.
0659: * All MetaData returned from this method will be initialised and ready for full use.
0660: * If the class can't be loaded, null will be returned.
0661: * @param className Name of the class to find MetaData for
0662: * @param clr ClassLoaderResolver resolver for use in loading the class.
0663: * @return The ClassMetaData for this class (or null if not found)
0664: **/
0665: public synchronized AbstractClassMetaData getMetaDataForClass(
0666: String className, ClassLoaderResolver clr) {
0667: if (className == null) {
0668: return null;
0669: }
0670:
0671: // Check if this class has no MetaData/annotations before instantiating its class
0672: if (isClassWithoutPersistenceInfo(className)) {
0673: return null;
0674: }
0675:
0676: // Resolve the class
0677: Class c = null;
0678: try {
0679: if (clr == null) {
0680: c = Class.forName(className);
0681: } else {
0682: c = clr.classForName(className, null, false);
0683: }
0684: } catch (ClassNotFoundException cnfe) {
0685: } catch (ClassNotResolvedException cnre) {
0686: }
0687: if (c == null) {
0688: return null;
0689: }
0690:
0691: return getMetaDataForClass(c, clr);
0692: }
0693:
0694: /** Temporary list of the FileMetaData objects utilised in this call for metadata. */
0695: protected ArrayList utilisedFileMetaData = new ArrayList();
0696:
0697: /**
0698: * Main accessor for the MetaData for a class.
0699: * All MetaData returned from this method will be initialised and ready for full use.
0700: * @param c The class to find MetaData for
0701: * @param clr the ClassLoaderResolver
0702: * @return The ClassMetaData for this class (or null if not found)
0703: */
0704: public synchronized AbstractClassMetaData getMetaDataForClass(
0705: Class c, ClassLoaderResolver clr) {
0706: if (c == null) {
0707: return null;
0708: }
0709:
0710: if (isClassWithoutPersistenceInfo(c.getName())) {
0711: return null;
0712: }
0713:
0714: AbstractClassMetaData cmd = null;
0715: if (c.isInterface()) {
0716: // "persistent-interface" - check if it has class built at runtime and return the MetaData for it
0717: cmd = getClassMetaDataForImplementationOfPersistentInterface(c
0718: .getName());
0719: } else {
0720: // "persistent-class"
0721: cmd = getMetaDataForClassInternal(c, clr);
0722: }
0723:
0724: if (cmd != null) {
0725: // Make sure that anything returned is initialised
0726: if (!cmd.isPopulated()) {
0727: // Initialise the class in question
0728: cmd.populate();
0729: }
0730: if (!cmd.isInitialised()) {
0731: // Initialise the class in question
0732: cmd.initialise();
0733: }
0734:
0735: if (utilisedFileMetaData.size() > 0) {
0736: // Initialise all FileMetaData that were processed in this call
0737: Iterator iter = utilisedFileMetaData.iterator();
0738: while (iter.hasNext()) {
0739: FileMetaData filemd = (FileMetaData) iter.next();
0740: initialiseFileMetaData(filemd, clr, c
0741: .getClassLoader());
0742: }
0743: }
0744: } else {
0745: if (!c.isInterface()) {
0746: classesWithoutPersistenceInfo.add(c.getName());
0747: }
0748: }
0749: utilisedFileMetaData.clear();
0750: return cmd;
0751: }
0752:
0753: /**
0754: * Accessor for the MetaData for a class given the "entity-name".
0755: * @param entityName The entity name to find MetaData for
0756: * @return The ClassMetaData for this entity name (or null if not found)
0757: */
0758: public synchronized AbstractClassMetaData getMetaDataForEntityName(
0759: String entityName) {
0760: return (AbstractClassMetaData) classMetaDataByEntityName
0761: .get(entityName);
0762: }
0763:
0764: /**
0765: * Method to access the (already known) metadata for the specified class.
0766: * If the class is not yet known about it returns null.
0767: * @param className Name of the class
0768: * @return MetaData for the class
0769: */
0770: public AbstractClassMetaData readMetaDataForClass(String className) {
0771: return (AbstractClassMetaData) classMetaDataByClass
0772: .get(className);
0773: }
0774:
0775: /**
0776: * Method to access the (already known) metadata for the field/property of the specified class.
0777: * If the class (or this field/property) is not yet known about it returns null.
0778: * @param className Name of the class
0779: * @param memberName Name of the field/property
0780: * @return MetaData for the field/property
0781: */
0782: public AbstractMemberMetaData readMetaDataForMember(
0783: String className, String memberName) {
0784: AbstractClassMetaData cmd = readMetaDataForClass(className);
0785: return (cmd != null ? cmd.getMetaDataForMember(memberName)
0786: : null);
0787: }
0788:
0789: /**
0790: * Internal convenience method for accessing the MetaData for a class.
0791: * MetaData returned by this method may be uninitialised so should only really
0792: * be used in initialisation processes.
0793: * To be implemented by the implementing class.
0794: * @param c The class to find MetaData for
0795: * @return The ClassMetaData for this class (or null if not found)
0796: **/
0797: public abstract AbstractClassMetaData getMetaDataForClassInternal(
0798: Class c, ClassLoaderResolver clr);
0799:
0800: /**
0801: * Accessor for the subclasses of a particular class
0802: * @param className Name of the class that we want the known subclasses for.
0803: * @param includeDescendents Whether to include subclasses of subclasses etc
0804: * @return Names of the subclasses. return null if there are no subclasses
0805: */
0806: public String[] getSubclassesForClass(String className,
0807: boolean includeDescendents) {
0808: Collection subclassNames = new HashSet();
0809: provideSubclassesForClass(className, includeDescendents,
0810: subclassNames);
0811: if (subclassNames.size() > 0) {
0812: return (String[]) subclassNames
0813: .toArray(new String[subclassNames.size()]);
0814: }
0815:
0816: return null;
0817: }
0818:
0819: /**
0820: * Provide the subclasses of a particular class to a given <code>consumer</code>
0821: * @param className Name of the class that we want the known subclasses for.
0822: * @param includeDescendents Whether to include subclasses of subclasses etc
0823: * @param consumer the Collection (Set) where discovered subclasses are added
0824: */
0825: private void provideSubclassesForClass(String className,
0826: boolean includeDescendents, Collection consumer) {
0827: Collection cmds = classMetaDataByClass.values();
0828: Iterator cmdIter = cmds.iterator();
0829: while (cmdIter.hasNext()) {
0830: AbstractClassMetaData acmd = (AbstractClassMetaData) cmdIter
0831: .next();
0832: if (acmd instanceof ClassMetaData) {
0833: ClassMetaData cmd = (ClassMetaData) acmd;
0834: if (cmd.getPersistenceCapableSuperclass() != null
0835: && cmd.getPersistenceCapableSuperclass()
0836: .equals(className)) {
0837: if (consumer.add(cmd.getFullClassName())
0838: && includeDescendents) {
0839: //go deeper in subclasses
0840: provideSubclassesForClass(cmd
0841: .getFullClassName(),
0842: includeDescendents, consumer);
0843: }
0844: }
0845: }
0846: }
0847: }
0848:
0849: /**
0850: * Accessor for the list of names of classes that are declared to implement the specified interface
0851: * (using <implements> in the MetaData). This will include subclasses of declared classes. Ignore abstract classes.
0852: * The array of implementation class names will have the initial implementations first followed by
0853: * the subclass implementations etc. So for example if we look for all implementations of I and A implements I
0854: * and B extends A, then it will return [A, B] in that order.
0855: * @param interfaceName Name of the interface
0856: * @param clr The ClassLoaderResolver
0857: * @return The names of the classes declared as implementing that interface. return null if no classes
0858: */
0859: public String[] getClassesImplementingInterface(
0860: String interfaceName, ClassLoaderResolver clr) {
0861: Collection classes = new HashSet();
0862: Class intfClass = clr.classForName(interfaceName);
0863:
0864: // Loop through all known classes and find the implementations
0865: // TODO This completely ignores the <implements> keyword info and should use it
0866: Collection cmds = classMetaDataByClass.values();
0867: Iterator cmdIter = cmds.iterator();
0868: boolean isPersistentInterface = false;
0869: while (cmdIter.hasNext()) {
0870: AbstractClassMetaData acmd = (AbstractClassMetaData) cmdIter
0871: .next();
0872: Class implClass = clr.classForName(acmd.getFullClassName());
0873: if (acmd instanceof ClassMetaData) {
0874: if (!acmd.isInitialised()) {
0875: // Make sure that we are initialised since implementsMetaData wont be set
0876: acmd.initialise();
0877: }
0878: if (intfClass.isAssignableFrom(implClass)) {
0879: if (!((ClassMetaData) acmd)
0880: .isAbstractPersistenceCapable()) {
0881: classes.add(implClass);
0882: }
0883: }
0884: } else if (acmd instanceof InterfaceMetaData) {
0885: if (intfClass.isAssignableFrom(implClass)) {
0886: isPersistentInterface = true;
0887: }
0888: }
0889: }
0890:
0891: if (isPersistentInterface) {
0892: // JDO2 "persistent interfaces"
0893: // This deliberately kept separate from normal persistence since it is largely undocumented and best left alone
0894: //TODO this is very time consuming. got to do some cache
0895: classes.add(omfContext.getImplementationCreator()
0896: .newInstance(intfClass, this , clr).getClass());
0897:
0898: String[] classNames = new String[classes.size()];
0899: Iterator iter = classes.iterator();
0900: int i = 0;
0901: while (iter.hasNext()) {
0902: classNames[i++] = ((Class) iter.next()).getName();
0903: }
0904: return classNames;
0905: } else if (classes.size() > 0) {
0906: // Normal persistence
0907: // Put the classes into a sorter so we make sure we get the initial implementations first followed
0908: // by any subclasses of these implementations. This is needed because when generating the schema we require
0909: // the subclass implementations to already have their datastore column created
0910: Collection classesSorted = new TreeSet(
0911: new InterfaceClassComparator());
0912: Iterator classesIter = classes.iterator();
0913: while (classesIter.hasNext()) {
0914: classesSorted.add(classesIter.next());
0915: }
0916:
0917: // Return the class names (in the same order)
0918: String[] classNames = new String[classesSorted.size()];
0919: Iterator iter = classesSorted.iterator();
0920: int i = 0;
0921: while (iter.hasNext()) {
0922: classNames[i++] = ((Class) iter.next()).getName();
0923: }
0924: return classNames;
0925: }
0926: return null;
0927: }
0928:
0929: /**
0930: * Simple comparator that orders the implementations of an interface so that the initial implementations
0931: * are first, and the subclasses later.
0932: */
0933: private class InterfaceClassComparator implements Comparator {
0934: /**
0935: * Default constructor.
0936: */
0937: public InterfaceClassComparator() {
0938: // Nothing to do
0939: }
0940:
0941: /**
0942: * Method defining the ordering of objects.
0943: * Places all nulls at the end.
0944: * @param o1 First object
0945: * @param o2 Second object
0946: * @return The comparison result
0947: */
0948: public int compare(Object o1, Object o2) {
0949: if (o1 == null && o2 == null) {
0950: return 0;
0951: } else if (o1 == null || o2 == null) {
0952: return Integer.MIN_VALUE;
0953: }
0954:
0955: // Just order based on hashcode
0956: Class cls1 = (Class) o1;
0957: Class cls2 = (Class) o2;
0958: return cls1.hashCode() - cls2.hashCode();
0959: }
0960: }
0961:
0962: /**
0963: * Load up and add any O/R mapping info for the specified class to the stored ClassMetaData (if supported).
0964: * This implementation does nothing so if ORM files are supported then this should be overridden by subclasses.
0965: * Is package-access so that is only accessable by MetaData classes
0966: * @param c The class
0967: * @param clr ClassLoader resolver
0968: */
0969: protected void addORMDataToClass(Class c, ClassLoaderResolver clr) {
0970: // Default to doing nothing. Specified in subclasses if they support it
0971: return;
0972: }
0973:
0974: /**
0975: * Load up and add any annotations mapping info for the specified class to the stored ClassMetaData.
0976: * Is package-access so that is only accessable by MetaData classes
0977: * @param c The class
0978: * @param cmd the metadata to add annotation to
0979: * @param clr ClassLoader resolver
0980: */
0981: void addAnnotationsDataToClass(Class c, AbstractClassMetaData cmd,
0982: ClassLoaderResolver clr) {
0983: // Get the MetaData for this class/interface
0984: if (cmd.getPackageMetaData() != null
0985: && cmd.getPackageMetaData().getFileMetaData() != null
0986: && cmd.getPackageMetaData().getFileMetaData().getType() == FileMetaData.ANNOTATIONS) {
0987: // Our MetaData is derived from the Annotations so nothing to merge!
0988: return;
0989: }
0990:
0991: // Find if there is any annotations metadata available
0992: FileMetaData filemd = loadAnnotationsForClass(c, clr, false,
0993: false);
0994: if (filemd != null) {
0995: AbstractClassMetaData annotCmd = filemd.getPackage(0)
0996: .getClass(0);
0997: if (annotCmd != null) {
0998: // Merge the annotations MetaData into the class MetaData
0999: MetaDataMerger.mergeClassAnnotationsData(cmd, annotCmd);
1000: }
1001: }
1002: }
1003:
1004: /**
1005: * Accessor for the MetaData for an implementation of a reference type.
1006: * Finds the metadata for the implementation of this reference.
1007: * @param referenceClass The reference class to find MetaData for
1008: * @param implValue Object of an implementation class, to return if possible (null=ignore)
1009: * @param clr ClassLoader resolver
1010: * @return The ClassMetaData for an implementation of a reference type
1011: */
1012: public ClassMetaData getMetaDataForImplementationOfReference(
1013: Class referenceClass, Object implValue,
1014: ClassLoaderResolver clr) {
1015: if (referenceClass == null
1016: || (!referenceClass.isInterface() && referenceClass != java.lang.Object.class)) {
1017: return null;
1018: }
1019:
1020: // Check if this is a "persistent interface"
1021: Object intfMetaData = getClassMetaDataForImplementationOfPersistentInterface(referenceClass
1022: .getName());
1023: if (intfMetaData != null) {
1024: return (ClassMetaData) intfMetaData;
1025: }
1026:
1027: ClassMetaData cmd = null;
1028:
1029: // Search for the class required
1030: Set classMetaDataClasses = classMetaDataByClass.keySet();
1031: Iterator classMetaDataClassesIter = classMetaDataClasses
1032: .iterator();
1033: while (classMetaDataClassesIter.hasNext()) {
1034: String class_name = (String) classMetaDataClassesIter
1035: .next();
1036: AbstractClassMetaData acmd_cls = (AbstractClassMetaData) classMetaDataByClass
1037: .get(class_name);
1038:
1039: if (acmd_cls instanceof ClassMetaData) {
1040: try {
1041: // Check if class is implementation of "implValue" (in the case of java.lang.Object, all will be!)
1042: Class cls = referenceClass.getClassLoader()
1043: .loadClass(class_name);
1044: if (referenceClass.isAssignableFrom(cls)) {
1045: // Find the base class that is an implementation
1046: cmd = (ClassMetaData) acmd_cls;
1047: if (implValue != null
1048: && cmd.getFullClassName().equals(
1049: implValue.getClass().getName())) {
1050: return cmd;
1051: }
1052:
1053: AbstractClassMetaData cmd_super class = cmd
1054: .getSuperAbstractClassMetaData();
1055: while (cmd_super class != null) {
1056: if (!referenceClass
1057: .isAssignableFrom(clr
1058: .classForName(((ClassMetaData) cmd_super class)
1059: .getFullClassName()))) {
1060: break;
1061: }
1062: // TODO Check if superclass is an implementation
1063: cmd = (ClassMetaData) cmd_super class;
1064: if (implValue != null
1065: && cmd.getFullClassName().equals(
1066: implValue.getClass()
1067: .getName())) {
1068: break;
1069: }
1070:
1071: // Go to next superclass
1072: cmd_super class = cmd_super class
1073: .getSuperAbstractClassMetaData();
1074: if (cmd_super class == null) {
1075: break;
1076: }
1077: }
1078: }
1079: } catch (Exception e) {
1080: }
1081: }
1082: }
1083:
1084: return cmd;
1085: }
1086:
1087: /**
1088: * Accessor for the MetaData for a field/property of a class.
1089: * Utilises getMetaDataForClass, and then finds the relevant field/property.
1090: * @param className The name of the class owning the field/property
1091: * @param memberName The name of the field to find MetaData for
1092: * @param clr ClassLoaderResolver resolver for any loading of classes
1093: * @return The metadata for this field/property (or null if not found)
1094: */
1095: public AbstractMemberMetaData getMetaDataForMember(
1096: String className, String memberName, ClassLoaderResolver clr) {
1097: if (className == null || memberName == null) {
1098: return null;
1099: }
1100:
1101: AbstractClassMetaData cmd = getMetaDataForClass(className, clr);
1102: return (cmd != null ? cmd.getMetaDataForMember(memberName)
1103: : null);
1104: }
1105:
1106: /**
1107: * Accessor for the MetaData for a field/property of a class.
1108: * Utilises getMetaDataForClass, and then finds the relevant field/property.
1109: * @param c The class owning the field/property
1110: * @param clr the ClassLoaderResolver
1111: * @param memberName The name of the field/property to find MetaData for
1112: * @return The metadata for this field/property (or null if not found)
1113: */
1114: public AbstractMemberMetaData getMetaDataForMember(Class c,
1115: ClassLoaderResolver clr, String memberName) {
1116: if (c == null || memberName == null) {
1117: return null;
1118: }
1119:
1120: AbstractClassMetaData cmd = getMetaDataForClass(c, clr);
1121: return (cmd != null ? cmd.getMetaDataForMember(memberName)
1122: : null);
1123: }
1124:
1125: /**
1126: * Accessor for the MetaData for a named query for a class.
1127: * If the class is not specified, searches for the query with this name for any class.
1128: * Will only return metadata for queries already registered in this implementation.
1129: * @param cls The class which has the query defined for it
1130: * @param clr the ClassLoaderResolver
1131: * @param queryName Name of the query
1132: * @return The QueryMetaData for the query for this class
1133: **/
1134: public QueryMetaData getMetaDataForQuery(Class cls,
1135: ClassLoaderResolver clr, String queryName) {
1136: if (queryName == null) {
1137: return null;
1138: }
1139:
1140: String query_key = queryName;
1141: if (cls != null) {
1142: query_key = cls.getName() + "_" + queryName;
1143: }
1144: return (QueryMetaData) queryMetaDataByName.get(query_key);
1145: }
1146:
1147: /**
1148: * Accessor for the MetaData for a named fetch plan.
1149: * @param name Name of the fetch plan
1150: * @return The FetchPlanMetaData for this name (if any)
1151: **/
1152: public FetchPlanMetaData getMetaDataForFetchPlan(String name) {
1153: if (name == null) {
1154: return null;
1155: }
1156:
1157: return (FetchPlanMetaData) fetchPlanMetaDataByName.get(name);
1158: }
1159:
1160: /**
1161: * Accessor for the MetaData for a Sequence in a package.
1162: * This implementation simply checks what is already loaded and returns if found
1163: * @param clr the ClassLoaderResolver
1164: * @param seqName Name of the package (fully qualified if necessary)
1165: * @return The SequenceMetaData for this named sequence
1166: **/
1167: public SequenceMetaData getMetaDataForSequence(
1168: ClassLoaderResolver clr, String seqName) {
1169: if (seqName == null) {
1170: return null;
1171: }
1172:
1173: return (SequenceMetaData) sequenceMetaDataByPackageSequence
1174: .get(seqName);
1175: }
1176:
1177: /**
1178: * Accessor for the MetaData for a TableGenerator in a package.
1179: * This implementation simply checks what is already loaded and returns if found
1180: * @param clr the ClassLoaderResolver
1181: * @param genName Name of the package (fully qualified if necessary)
1182: * @return The TableGenerator for this named generator
1183: **/
1184: public TableGeneratorMetaData getMetaDataForTableGenerator(
1185: ClassLoaderResolver clr, String genName) {
1186: if (genName == null) {
1187: return null;
1188: }
1189:
1190: return (TableGeneratorMetaData) tableGeneratorMetaDataByPackageSequence
1191: .get(genName);
1192: }
1193:
1194: /**
1195: * Accessor for the MetaData for a QueryResult.
1196: * @param name Name of the query result
1197: * @return The QueryResultMetaData under this name
1198: **/
1199: public QueryResultMetaData getMetaDataForQueryResult(String name) {
1200: if (name == null) {
1201: return null;
1202: }
1203:
1204: return (QueryResultMetaData) queryResultMetaDataByName
1205: .get(name);
1206: }
1207:
1208: // ------------------------------- Persistent Interfaces ---------------------------------------
1209:
1210: /**
1211: * Accessor for the MetaData for an interface.
1212: * Part of the support for "persistent-interface".
1213: * This defaults to returning null since interfaces are only supported by JDO.
1214: * @param c The interface to find MetaData for
1215: * @param clr the ClassLoaderResolver
1216: * @return The InterfaceMetaData for this interface (or null if not found)
1217: */
1218: public InterfaceMetaData getMetaDataForInterface(Class c,
1219: ClassLoaderResolver clr) {
1220: return null;
1221: }
1222:
1223: /**
1224: * Convenience method to return if the passed class name is a "persistent-interface".
1225: * @param name Name if the interface
1226: * @return Whether it is a "persistent-interface"
1227: */
1228: public boolean isPersistentInterface(String name) {
1229: // Default to not supporting "persistent-interface"s
1230: return false;
1231: }
1232:
1233: /**
1234: * Convenience method to return if the passed class name is an implementation of the passed "persistent-interface".
1235: * @param interfaceName Name of the persistent interface
1236: * @param implName The implementation name
1237: * @return Whether it is a (JPOX-generated) impl of the persistent interface
1238: */
1239: public boolean isPersistentInterfaceImplementation(
1240: String interfaceName, String implName) {
1241: // Default to not supporting "persistent-interface"s
1242: return false;
1243: }
1244:
1245: /**
1246: * Convenience method to return if the passed class name is an implementation of a "persistent definition".
1247: * @param implName The implementation name
1248: * @return Whether it is a (JPOX-generated) impl of the persistent interface or abstract class
1249: */
1250: public boolean isPersistentDefinitionImplementation(String implName) {
1251: return false;
1252: }
1253:
1254: /**
1255: * Accessor for the implementation name for the specified "persistent-interface".
1256: * @param interfaceName The name of the persistent interface
1257: * @return The name of the implementation class
1258: */
1259: public String getImplementationNameForPersistentInterface(
1260: String interfaceName) {
1261: // Default to not supporting "persistent-interface"s
1262: return null;
1263: }
1264:
1265: /**
1266: * Accessor for the metadata for the implementation of the specified "persistent-interface".
1267: * @param interfaceName The name of the persistent interface
1268: * @return The ClassMetaData of the implementation class
1269: */
1270: public ClassMetaData getClassMetaDataForImplementationOfPersistentInterface(
1271: String interfaceName) {
1272: // Default to not supporting "persistent-interface"s
1273: return null;
1274: }
1275:
1276: /**
1277: * Method to register a persistent interface and its implementation with the MetaData system.
1278: * @param imd MetaData for the interface
1279: * @param implClass The implementation class
1280: * @param clr ClassLoader Resolver to use
1281: */
1282: public void registerPersistentInterface(InterfaceMetaData imd,
1283: Class implClass, ClassLoaderResolver clr) {
1284: // Default to not supporting "persistent-interface"s
1285: return;
1286: }
1287:
1288: /**
1289: * Method to register the metadata for an implementation of a persistent abstract class.
1290: * @param cmd MetaData for the abstract class
1291: * @param implClass The implementation class
1292: * @param clr ClassLoader resolver
1293: */
1294: public void registerImplementationOfAbstractClass(
1295: ClassMetaData cmd, Class implClass, ClassLoaderResolver clr) {
1296: // Default to not supporting "persistent-abstract-classes"
1297: return;
1298: }
1299:
1300: // ------------------------------- Utilities -------------------------------
1301:
1302: /**
1303: * Method to parse all available "persistence.xml" files and return the metadata
1304: * for the persistence unit with the specified name.
1305: * @param unitName Name of the persistence-unit
1306: * @return MetaData for the persistence-unit of the specified name (or null if not found)
1307: * @throws JPOXUserException if no "persistence.xml" files are found
1308: */
1309: public PersistenceUnitMetaData getMetaDataForPersistenceUnit(
1310: String unitName) {
1311: PersistenceFileMetaData[] files = parsePersistenceFiles();
1312: if (files == null) {
1313: // No "persistence.xml" files found
1314: throw new JPOXUserException(LOCALISER.msg("044046"));
1315: } else {
1316: for (int i = 0; i < files.length; i++) {
1317: PersistenceUnitMetaData[] unitmds = files[i]
1318: .getPersistenceUnits();
1319: for (int j = 0; j < unitmds.length; j++) {
1320: if (unitmds[j].getName().equals(unitName)) {
1321: // Found the required unit
1322: return unitmds[j];
1323: }
1324: }
1325: }
1326: }
1327: return null;
1328: }
1329:
1330: /**
1331: * Method to parse the available "persistence.xml" files returning the metadata for all found.
1332: * Searches for all files "META-INF/persistence.xml" in the CLASSPATH of the current thread.
1333: * @return The metadata for all "persistence.xml" files
1334: */
1335: public PersistenceFileMetaData[] parsePersistenceFiles() {
1336: HashSet metadata = new HashSet();
1337: try {
1338: // Find all "META-INF/persistence.xml" files in the CLASSPATH of the current thread
1339: // TODO Use ClassLoaderResolver
1340: Enumeration files = Thread.currentThread()
1341: .getContextClassLoader().getResources(
1342: "META-INF/persistence.xml");
1343: if (!files.hasMoreElements()) {
1344: return null;
1345: }
1346:
1347: metaDataParser = new MetaDataParser(this , validateMetaData);
1348: for (; files.hasMoreElements();) {
1349: // Parse the "persistence.xml"
1350: URL fileURL = (URL) files.nextElement();
1351: MetaData permd = metaDataParser.parseMetaDataURL(
1352: fileURL, "persistence");
1353: metadata.add(permd);
1354: }
1355: } catch (IOException ioe) {
1356: // Do nothing
1357: JPOXLogger.METADATA.warn(StringUtils
1358: .getStringFromStackTrace(ioe));
1359: }
1360:
1361: return (PersistenceFileMetaData[]) metadata
1362: .toArray(new PersistenceFileMetaData[metadata.size()]);
1363: }
1364:
1365: /**
1366: * Utility to parse a MetaData file.
1367: * @param file_url URL of the file
1368: * @return The FileMetaData for this file
1369: */
1370: protected abstract FileMetaData parseFile(URL file_url);
1371:
1372: /**
1373: * Method to take the FileMetaData and register the relevant parts of it with the assorted caches provided.
1374: * @param fileURLString URL of the metadata file
1375: * @param filemd The File MetaData
1376: */
1377: public abstract void registerFile(String fileURLString,
1378: FileMetaData filemd, ClassLoaderResolver clr);
1379:
1380: /**
1381: * Convenience method to register all sequences found in the passed file.
1382: * @param filemd MetaData for the file
1383: */
1384: protected void registerSequencesForFile(FileMetaData filemd) {
1385: // Register all sequences for the packages in this file
1386: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1387: PackageMetaData pmd = filemd.getPackage(i);
1388: SequenceMetaData[] seqmds = pmd.getSequences();
1389: if (seqmds != null) {
1390: for (int j = 0; j < seqmds.length; j++) {
1391: // Register using its fully qualified name (JDO)
1392: sequenceMetaDataByPackageSequence.put(seqmds[j]
1393: .getFullyQualifiedName(), seqmds[j]);
1394: }
1395: }
1396: }
1397: }
1398:
1399: /**
1400: * Convenience method to register all table generators found in the passed file.
1401: * @param filemd MetaData for the file
1402: */
1403: protected void registerTableGeneratorsForFile(FileMetaData filemd) {
1404: // Register all table generators for the packages in this file
1405: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1406: PackageMetaData pmd = filemd.getPackage(i);
1407: TableGeneratorMetaData[] tgmds = pmd.getTableGenerators();
1408: if (tgmds != null) {
1409: for (int j = 0; j < tgmds.length; j++) {
1410: // Register using its fully qualified name (JDO)
1411: tableGeneratorMetaDataByPackageSequence.put(
1412: tgmds[j].getFullyQualifiedName(), tgmds[j]);
1413: }
1414: }
1415: }
1416: }
1417:
1418: /**
1419: * Convenience method to register all table generators found in the passed file.
1420: * @param filemd MetaData for the file
1421: */
1422: protected void registerQueryResultMetaDataForFile(
1423: FileMetaData filemd) {
1424: // Register all query result mappings for the file
1425: QueryResultMetaData[] fqrmds = filemd.getQueryResultMetaData();
1426: if (fqrmds != null) {
1427: for (int i = 0; i < fqrmds.length; i++) {
1428: queryResultMetaDataByName.put(fqrmds[i].getName(),
1429: fqrmds[i]);
1430: }
1431: }
1432:
1433: // Register all query result mappings for the classes in the file
1434: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1435: PackageMetaData pmd = filemd.getPackage(i);
1436: for (int j = 0; j < pmd.getNoOfClasses(); j++) {
1437: AbstractClassMetaData cmd = pmd.getClass(j);
1438: QueryResultMetaData[] qrmds = cmd
1439: .getQueryResultMetaData();
1440: if (qrmds != null) {
1441: for (int k = 0; k < qrmds.length; k++) {
1442: queryResultMetaDataByName.put(qrmds[k]
1443: .getName(), qrmds[k]);
1444: }
1445: }
1446: }
1447: }
1448: }
1449:
1450: /**
1451: * Convenience method to register all queries found in the passed file.
1452: * @param filemd MetaData for the file
1453: */
1454: protected void registerQueriesForFile(FileMetaData filemd) {
1455: // Register all queries for this file
1456: // Store queries against "queryname"
1457: QueryMetaData[] queries = filemd.getQueries();
1458: if (queries != null) {
1459: for (int i = 0; i < queries.length; i++) {
1460: String scope = queries[i].getScope();
1461: String key = queries[i].getName();
1462: if (scope != null) {
1463: key = scope + "_" + key;
1464: }
1465: queryMetaDataByName.put(key, queries[i]);
1466: }
1467: }
1468:
1469: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1470: PackageMetaData pmd = filemd.getPackage(i);
1471:
1472: // Register all classes (and their queries) into the respective lookup maps
1473: for (int j = 0; j < pmd.getNoOfClasses(); j++) {
1474: // Store queries against "classname_queryname"
1475: ClassMetaData cmd = pmd.getClass(j);
1476: QueryMetaData[] classQueries = cmd.getQueries();
1477: if (classQueries != null) {
1478: for (int k = 0; k < classQueries.length; k++) {
1479: String scope = classQueries[k].getScope();
1480: String key = classQueries[k].getName();
1481: if (scope != null) {
1482: key = scope + "_" + key;
1483: }
1484: queryMetaDataByName.put(key, classQueries[k]);
1485: }
1486: }
1487: }
1488:
1489: // Register all interfaces (and their queries) into the respective lookup maps
1490: for (int j = 0; j < pmd.getNoOfInterfaces(); j++) {
1491: // Store queries against "classname_queryname"
1492: InterfaceMetaData intfmd = pmd.getInterface(j);
1493: QueryMetaData[] interfaceQueries = intfmd.getQueries();
1494: if (interfaceQueries != null) {
1495: for (int k = 0; k < interfaceQueries.length; k++) {
1496: String scope = interfaceQueries[k].getScope();
1497: String key = interfaceQueries[k].getName();
1498: if (scope != null) {
1499: key = scope + "_" + key;
1500: }
1501: queryMetaDataByName.put(key,
1502: interfaceQueries[k]);
1503: }
1504: }
1505: }
1506: }
1507: }
1508:
1509: /**
1510: * Convenience method to register all FetchPlans found in the passed file.
1511: * @param filemd MetaData for the file
1512: */
1513: protected void registerFetchPlansForFile(FileMetaData filemd) {
1514: // Register all queries for this file
1515: // Store queries against "queryname"
1516: FetchPlanMetaData[] fetchPlans = filemd.getFetchPlans();
1517: if (fetchPlans != null) {
1518: for (int i = 0; i < fetchPlans.length; i++) {
1519: fetchPlanMetaDataByName.put(fetchPlans[i].getName(),
1520: fetchPlans[i]);
1521: }
1522: }
1523: }
1524:
1525: /**
1526: * Convenience method to populate all classes/interfaces in a Meta-Data file.
1527: * @param filemd The MetaData file
1528: * @param clr Class Loader to use in population
1529: * @param primary the primary ClassLoader to use (or null)
1530: */
1531: protected void populateFileMetaData(FileMetaData filemd,
1532: ClassLoaderResolver clr, ClassLoader primary) {
1533: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1534: PackageMetaData pmd = filemd.getPackage(i);
1535: for (int j = 0; j < pmd.getNoOfClasses(); j++) {
1536: AbstractClassMetaData cmd = pmd.getClass(j);
1537: if (!cmd.isPopulated() && !cmd.isInitialised()) {
1538: cmd.populate(clr, primary);
1539: }
1540: }
1541: for (int j = 0; j < pmd.getNoOfInterfaces(); j++) {
1542: AbstractClassMetaData cmd = pmd.getInterface(j);
1543: if (!cmd.isPopulated() && !cmd.isInitialised()) {
1544: cmd.populate(clr, primary);
1545: }
1546: }
1547: }
1548: }
1549:
1550: /**
1551: * Initialise all classes/interfaces in a Meta-Data file.
1552: * @param filemd the FileMetaData
1553: * @param clr ClassLoader resolver to use
1554: * @param primary the primary ClassLoader to use (or null)
1555: */
1556: protected void initialiseFileMetaData(FileMetaData filemd,
1557: ClassLoaderResolver clr, ClassLoader primary) {
1558: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
1559: PackageMetaData pmd = filemd.getPackage(i);
1560: for (int j = 0; j < pmd.getNoOfClasses(); j++) {
1561: ClassMetaData cmd = pmd.getClass(j);
1562: try {
1563: initialiseClassMetaData(cmd, clr.classForName(cmd
1564: .getFullClassName(), primary), clr);
1565: } catch (JPOXException jpex) {
1566: throw jpex;
1567: } catch (RuntimeException re) {
1568: // Do nothing
1569: }
1570: }
1571:
1572: for (int j = 0; j < pmd.getNoOfInterfaces(); j++) {
1573: InterfaceMetaData imd = pmd.getInterface(j);
1574: try {
1575: initialiseInterfaceMetaData(imd, clr, primary);
1576: } catch (JPOXException jpex) {
1577: throw jpex;
1578: } catch (RuntimeException re) {
1579: // Do nothing
1580: }
1581: }
1582: }
1583: }
1584:
1585: /**
1586: * Utility to initialise the MetaData for a class, using the specified
1587: * class. This assigns defaults to tags that haven't been assigned.
1588: * If the class that is being used to populate the MetaData is not
1589: * enhanced, this will throw a JPOXUserException informing them of this.
1590: * @param cmd The classes metadata
1591: * @param cls The class to use as a basis for initialisation
1592: * @param clr ClassLoader resolver to use
1593: * @throws JPOXUserException if the class is not enhanced
1594: */
1595: protected void initialiseClassMetaData(ClassMetaData cmd,
1596: Class cls, ClassLoaderResolver clr) {
1597: synchronized (cmd) {
1598: if (!enhancing
1599: && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE
1600: && !getOMFContext().getApiAdapter().isPersistable(
1601: cls)) {
1602: throw new JPOXUserException(LOCALISER.msg("044059", cls
1603: .getName()));
1604: }
1605: if (!cmd.isPopulated() && !cmd.isInitialised()) {
1606: cmd.populate(clr, cls.getClassLoader());
1607: }
1608: if (!cmd.isInitialised()) {
1609: cmd.initialise();
1610: }
1611: }
1612: }
1613:
1614: /**
1615: * Utility to initialise the MetaData for a interface, using the specified
1616: * class. This assigns defaults to tags that haven't been assigned.
1617: * If the class that is being used to populate the MetaData is not
1618: * enhanced, this will throw a JPOXUserException informing them of this.
1619: * @param imd The interface metadata
1620: * @param clr The loader of the interface
1621: * @param primary the primary ClassLoader to use (or null)
1622: */
1623: protected void initialiseInterfaceMetaData(InterfaceMetaData imd,
1624: ClassLoaderResolver clr, ClassLoader primary) {
1625: synchronized (imd) {
1626: if (!imd.isPopulated() && !imd.isInitialised()) {
1627: imd.populate(clr, primary);
1628: }
1629: if (!imd.isInitialised()) {
1630: imd.initialise();
1631: }
1632: }
1633: }
1634:
1635: /**
1636: * Method to load the annotations for the specified class and return the FileMetaData containing
1637: * the class. The FileMetaData, PackageMetaData will be dummy records.
1638: * @param cls The class
1639: * @param clr ClassLoader resolver
1640: * @param register Whether to register the data
1641: * @param populate Whether to populate the data
1642: * @return The FileMetaData
1643: */
1644: protected FileMetaData loadAnnotationsForClass(Class cls,
1645: ClassLoaderResolver clr, boolean register, boolean populate) {
1646: if (annotationManager != null) {
1647: if (isClassWithoutPersistenceInfo(cls.getName())) {
1648: return null;
1649: }
1650:
1651: String clsPackageName = ClassUtils
1652: .getPackageNameForClass(cls);
1653: if (clsPackageName == null) {
1654: // Unknown type with no package, so perhaps so multiple array (int[][] or something)
1655: return null;
1656: }
1657:
1658: // Check for annotations (use dummy file/package so we have a place for it)
1659: FileMetaData filemd = new FileMetaData(null, this , null,
1660: null);
1661: filemd.setType(FileMetaData.ANNOTATIONS);
1662: PackageMetaData pmd = new PackageMetaData(filemd,
1663: clsPackageName, null, null);
1664: filemd.addPackage(pmd);
1665: AbstractClassMetaData cmd = annotationManager
1666: .getMetaDataForClass(cls, pmd, clr);
1667: if (cmd != null) {
1668: if (register) {
1669: // register before populating to avoid recursive loops when loading referenced classes
1670: registerFile("annotations:" + cls.getName(),
1671: filemd, clr);
1672:
1673: if (populate) {
1674: // Populate all classes in this file we've just parsed (i.e only 1!)
1675: populateFileMetaData(filemd, clr, cls
1676: .getClassLoader());
1677: }
1678: }
1679: return filemd;
1680: }
1681: }
1682: return null;
1683: }
1684:
1685: // ------------------------------ Utilities --------------------------------
1686:
1687: /**
1688: * Utility to return all ClassMetaData that is referenced from the supplier
1689: * class.
1690: * @param cmd The origin class's MetaData.
1691: * @param dba_vendor_id The Vendor id of the database adapter in use.
1692: * (Used in handling "views" support)
1693: * @param clr ClassLoaderResolver resolver for loading any classes.
1694: * @return List of ClassMetaData referenced by the origin
1695: */
1696: public List getReferencedClassMetaData(AbstractClassMetaData cmd,
1697: String dba_vendor_id, ClassLoaderResolver clr) {
1698: if (cmd == null) {
1699: return null;
1700: }
1701:
1702: List orderedCMDs = new ArrayList();
1703: Set referencedCMDs = new HashSet();
1704:
1705: // Use the ClassMetaData to tell us about its classes
1706: cmd.getReferencedClassMetaData(orderedCMDs, referencedCMDs,
1707: dba_vendor_id, clr);
1708:
1709: return orderedCMDs;
1710: }
1711:
1712: /**
1713: * Register to persistent class load
1714: */
1715: private class MetaDataRegisterClassListener implements
1716: RegisterClassListener {
1717: public void registerClass(RegisterClassEvent arg0) {
1718: // register the class / interface in metadata
1719: getMetaDataForClassInternal(arg0.getRegisteredClass(),
1720: omfContext.getClassLoaderResolver(arg0
1721: .getRegisteredClass().getClassLoader()));
1722: }
1723: }
1724:
1725: /**
1726: * Get the event listeners
1727: * @return the event listeners
1728: */
1729: public List getListeners() {
1730: return listeners;
1731: }
1732:
1733: /**
1734: * Method to set that the Manager is enhancing classes.
1735: * This implies using slightly weaker checks during read/populate/initialise of MetaData.
1736: */
1737: public void setEnhancing() {
1738: this .enhancing = true;
1739: }
1740:
1741: /**
1742: * Method to set that the Manager is not enhancing classes.
1743: * This implies reverting to the normal checks during read/populate/initialise of MetaData.
1744: */
1745: public void unsetEnhancing() {
1746: this .enhancing = false;
1747: }
1748:
1749: /**
1750: * Accessor for a factory for MetaData objects.
1751: * @return The factory of objects
1752: */
1753: public MetaDataFactory getMetaDataFactory() {
1754: if (metaDataFactory == null) {
1755: metaDataFactory = new DefaultMetaDataFactory();
1756: }
1757: return metaDataFactory;
1758: }
1759:
1760: /**
1761: * Method to set the Factory for MetaData objects.
1762: * This is used to create Class, Field, Property, Interface objects.
1763: * @param factory The factory
1764: */
1765: public void setMetaDataFactory(MetaDataFactory factory) {
1766: this.metaDataFactory = factory;
1767: }
1768: }
|