0001: /**********************************************************************
0002: Copyright (c) 2006 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: Contributors:
0016: ...
0017: **********************************************************************/package org.jpox.jdo.metadata;
0018:
0019: import java.io.IOException;
0020: import java.net.URL;
0021: import java.util.ArrayList;
0022: import java.util.Enumeration;
0023: import java.util.HashMap;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Map;
0027: import java.util.StringTokenizer;
0028:
0029: import org.jpox.ClassLoaderResolver;
0030: import org.jpox.OMFContext;
0031: import org.jpox.exceptions.JPOXException;
0032: import org.jpox.metadata.AbstractClassMetaData;
0033: import org.jpox.metadata.ClassMetaData;
0034: import org.jpox.metadata.FileMetaData;
0035: import org.jpox.metadata.ImplementsMetaData;
0036: import org.jpox.metadata.InterfaceMetaData;
0037: import org.jpox.metadata.MetaDataManager;
0038: import org.jpox.metadata.MetaDataMerger;
0039: import org.jpox.metadata.PackageMetaData;
0040: import org.jpox.metadata.QueryMetaData;
0041: import org.jpox.metadata.SequenceMetaData;
0042: import org.jpox.metadata.xml.MetaDataParser;
0043: import org.jpox.util.JPOXLogger;
0044: import org.jpox.util.StringUtils;
0045:
0046: /**
0047: * Manager of JDO MetaData information in JPOX.
0048: * <P>
0049: * Acts as a registry of JDO metadata so that metadata files don't need to be
0050: * parsed multiple times. MetaData is stored as a FileMetaData, which contains
0051: * PackageMetaData, which contains ClassMetaData, and so on. This maps exactly
0052: * to the users model of their metadata. The users access point is
0053: * <B>getMetaDataForClass()</B> which will check the known classes without metadata,
0054: * then check the existing registered metdata, then check the valid locations for
0055: * metdata files. This way, the metadata is managed from this single point.
0056: * </P>
0057: * <P>
0058: * When the MetaData is requested for a class, if it isnt already found, the valid
0059: * file locations are checked for that class and the file containing it will be
0060: * read. The MetaData for all classes, queries, sequences etc in that file are
0061: * loaded at that point. In addition, all classes will be "populated" (meaning that
0062: * their superclasses are assigned, and unspecified fields are added, and any related
0063: * objects are linked). The MetaData of these classes are only initialised when
0064: * they are absolutely needed - to avoid generating circular references in the
0065: * initialisation process.
0066: * </P>
0067: * <P>
0068: * Each PMFContext typically will have its own MetaDataManager so allowing
0069: * Meta-Data to be for different datastores. In addition, each PMF can allow
0070: * MetaData files to use a particular suffix, hence we allow the JDO/ORM file
0071: * suffices to be specifiable at construction.
0072: * </P>
0073: *
0074: * @version $Revision: 1.31 $
0075: */
0076: public class JDOMetaDataManager extends MetaDataManager {
0077: /** MetaData files will be searched in all possible locations defined in JDO 1.0, JDO 1.0.1, JDO 2.0 or later **/
0078: public static final int ALL_JDO_LOCATIONS = 1;
0079:
0080: /** MetaData files will be searched in all locations defined in JDO 1.0 **/
0081: public static final int JDO_1_0_0_LOCATIONS = 2;
0082:
0083: /** MetaData files will be searched in all locations defined in JDO 1.0.1 **/
0084: public static final int JDO_1_0_1_LOCATIONS = 3;
0085:
0086: /** Definition of which locations we accept for MetaData files. */
0087: protected int locationDefinition = ALL_JDO_LOCATIONS;
0088:
0089: /** Map of ClassMetaData from ORM files, keyed by the class name. */
0090: protected Map ormClassMetaDataByClass = new HashMap();
0091:
0092: /**
0093: * Map of ClassMetaData, keyed by the interface class name (for "persistent-interface"s).
0094: * Keyed by the persistent-interface name.
0095: */
0096: protected Map classMetaDataByInterface = new HashMap();
0097:
0098: /**
0099: * Constructor.
0100: * @param ctxt ObjectManagerFactory Context that this metadata manager operates in
0101: */
0102: public JDOMetaDataManager(OMFContext ctxt) {
0103: super (ctxt);
0104:
0105: locationDefinition = ALL_JDO_LOCATIONS;
0106:
0107: // Log the current configuration
0108: if (JPOXLogger.METADATA.isDebugEnabled()) {
0109: logConfiguration();
0110: }
0111: }
0112:
0113: /**
0114: * Convenience accessor for the mapping name.
0115: * @return ORM mapping name
0116: */
0117: protected String getORMMappingName() {
0118: String mappingName = omfContext.getPersistenceConfiguration()
0119: .getMapping();
0120: return (StringUtils.isWhitespace(mappingName) ? null
0121: : mappingName);
0122: }
0123:
0124: /**
0125: * Convenience accessor for the JDO file suffix.
0126: * @return JDO file suffix
0127: */
0128: public String getJDOFileSuffix() {
0129: String suffix = omfContext.getPersistenceConfiguration()
0130: .getJdoMetaDataFileExtension();
0131: return (StringUtils.isWhitespace(suffix) ? "jdo" : suffix);
0132: }
0133:
0134: /**
0135: * Convenience accessor for the ORM file suffix.
0136: * @return ORM file suffix
0137: */
0138: public String getORMFileSuffix() {
0139: String suffix = omfContext.getPersistenceConfiguration()
0140: .getOrmMetaDataFileExtension();
0141: return (StringUtils.isWhitespace(suffix) ? "orm" : suffix);
0142: }
0143:
0144: /**
0145: * Convenience accessor for the JDOQuery file suffix.
0146: * @return JDOQuery file suffix
0147: */
0148: public String getJDOQueryFileSuffix() {
0149: String suffix = omfContext.getPersistenceConfiguration()
0150: .getJdoqueryMetaDataFileExtension();
0151: return (StringUtils.isWhitespace(suffix) ? "jdoquery" : suffix);
0152: }
0153:
0154: /**
0155: * Method to log the configuration of this manager.
0156: */
0157: protected void logConfiguration() {
0158: // Log the configuration
0159: String inputTypes = null;
0160: if (annotationManager != null) {
0161: inputTypes = "XML, Annotations";
0162: } else {
0163: inputTypes = "XML";
0164: }
0165:
0166: if (JPOXLogger.METADATA.isDebugEnabled()) {
0167: if (supportsORM) {
0168: String mappingName = getORMMappingName();
0169: String jdoSuffix = getJDOFileSuffix();
0170: String ormSuffix = getORMFileSuffix();
0171: String jdoquerySuffix = getJDOQueryFileSuffix();
0172: JPOXLogger.METADATA
0173: .debug("MetaDataManager : "
0174: + " Input=("
0175: + inputTypes
0176: + ")"
0177: + " XML-Validation="
0178: + validateMetaData
0179: + " XML-Files=(persistence=*."
0180: + jdoSuffix
0181: + ", ORM=*."
0182: + ormSuffix
0183: + ", query=*."
0184: + jdoquerySuffix
0185: + ")"
0186: + (mappingName != null ? (" ORM-name=" + mappingName)
0187: : ""));
0188: } else {
0189: String jdoSuffix = getJDOFileSuffix();
0190: String jdoquerySuffix = getJDOQueryFileSuffix();
0191: JPOXLogger.METADATA.debug("MetaDataManager : "
0192: + " Input=(" + inputTypes + ")"
0193: + " XML-Validation=" + validateMetaData
0194: + " XML-Files=(persistence=*." + jdoSuffix
0195: + ", query=*." + jdoquerySuffix + ")");
0196: }
0197: }
0198: }
0199:
0200: /**
0201: * Clear resources
0202: */
0203: public void close() {
0204: super .close();
0205: ormClassMetaDataByClass.clear();
0206: ormClassMetaDataByClass = null;
0207: }
0208:
0209: /**
0210: * Utility to parse a file, using the "jdo" MetaData handler.
0211: * @param fileURL URL of the file
0212: * @return The FileMetaData for this file
0213: */
0214: protected FileMetaData parseFile(URL fileURL) {
0215: if (metaDataParser == null) {
0216: metaDataParser = new MetaDataParser(this , validateMetaData);
0217: }
0218: return (FileMetaData) metaDataParser.parseMetaDataURL(fileURL,
0219: "jdo");
0220: }
0221:
0222: /**
0223: * Method to take the FileMetaData and register the relevant parts of it with the assorted caches provided.
0224: * @param fileURLString URL of the metadata file
0225: * @param filemd The File MetaData
0226: */
0227: public void registerFile(String fileURLString, FileMetaData filemd,
0228: ClassLoaderResolver clr) {
0229: if (fileURLString == null) {
0230: // Null file
0231: return;
0232: }
0233: if (fileMetaDataByURLString.get(fileURLString) != null) {
0234: // Already registered!
0235: return;
0236: }
0237:
0238: fileMetaDataByURLString.put(fileURLString, filemd);
0239:
0240: registerQueriesForFile(filemd);
0241: registerFetchPlansForFile(filemd);
0242: registerSequencesForFile(filemd);
0243: registerTableGeneratorsForFile(filemd);
0244:
0245: // Register the classes and interfaces for later use
0246: if (filemd.getType() != FileMetaData.JDOQUERY_FILE) {
0247: for (int i = 0; i < filemd.getNoOfPackages(); i++) {
0248: PackageMetaData pmd = filemd.getPackage(i);
0249:
0250: // Register all classes into the respective lookup maps
0251: for (int j = 0; j < pmd.getNoOfClasses(); j++) {
0252: ClassMetaData cmd = pmd.getClass(j);
0253: if (filemd.getType() == FileMetaData.JDO_FILE
0254: || filemd.getType() == FileMetaData.ANNOTATIONS) {
0255: classMetaDataByClass.put(
0256: cmd.getFullClassName(), cmd);
0257: } else if (filemd.getType() == FileMetaData.ORM_FILE) {
0258: ormClassMetaDataByClass.put(cmd
0259: .getFullClassName(), cmd);
0260: }
0261: if (cmd.getEntityName() != null) {
0262: // Register the ClassMetaData against the "entity name"
0263: classMetaDataByEntityName.put(cmd
0264: .getEntityName(), cmd);
0265: }
0266: }
0267:
0268: // Register all interfaces into the respective lookup maps
0269: for (int j = 0; j < pmd.getNoOfInterfaces(); j++) {
0270: InterfaceMetaData intfmd = pmd.getInterface(j);
0271: if (filemd.getType() == FileMetaData.JDO_FILE
0272: || filemd.getType() == FileMetaData.ANNOTATIONS) {
0273: classMetaDataByClass.put(intfmd
0274: .getFullClassName(), intfmd);
0275: } else if (filemd.getType() == FileMetaData.ORM_FILE) {
0276: ormClassMetaDataByClass.put(intfmd
0277: .getFullClassName(), intfmd);
0278: }
0279: }
0280: }
0281: }
0282: }
0283:
0284: /**
0285: * Convenience method to check if we have metadata present for the specified class.
0286: * @param className The name of the class to check
0287: * @return Whether the metadata is already registered for this class
0288: */
0289: public boolean hasMetaDataForClass(String className) {
0290: if (className == null) {
0291: return false;
0292: }
0293:
0294: // Check if this class has no MetaData before instantiating its class
0295: if (isClassWithoutPersistenceInfo(className)) {
0296: return false;
0297: }
0298:
0299: return (classMetaDataByClass.get(className) != null);
0300: }
0301:
0302: /**
0303: * Internal method for accessing the MetaData for a class.
0304: * The MetaData returned can be uninitialised.
0305: * Runs through the following process
0306: * <OL>
0307: * <LI>Checks if the class is known not to have metata</LI>
0308: * <LI>Check if we have metadata for the class in one of the files that has
0309: * been parsed.</LI>
0310: * <LI>If we have metadata, check that it is initialised</LI>
0311: * <LI>If we don't have metadata, find the file for this class.</LI>
0312: * <LI>If we cant find a file for it, add it to the list of classes known
0313: * to have no metadata</LI>
0314: * </OL>
0315: * @param c The class to find MetaData for
0316: * @return The ClassMetaData for this class (or null if not found)
0317: **/
0318: public synchronized AbstractClassMetaData getMetaDataForClassInternal(
0319: Class c, ClassLoaderResolver clr) {
0320: String className = c.getName();
0321: // If we know that this class/interface has no MetaData/annotations don't bother searching
0322: if (isClassWithoutPersistenceInfo(className)) {
0323: return null;
0324: }
0325:
0326: // Check if we have the MetaData already
0327: AbstractClassMetaData the_md = (AbstractClassMetaData) classMetaDataByClass
0328: .get(className);
0329: if (the_md != null) {
0330: return the_md;
0331: }
0332:
0333: // No loaded MetaData so search valid location for a file for this class and load all in the process
0334: FileMetaData filemd = loadMetaDataForClass(c, clr, null,
0335: getJDOFileSuffix(), true);
0336: if (filemd != null) {
0337: // Class has had its metadata loaded
0338: utilisedFileMetaData.add(filemd);
0339:
0340: // If not MetaData complete will also merge in annotations at populate stage
0341:
0342: // Retrieve the MetaData for the requested class
0343: the_md = (AbstractClassMetaData) classMetaDataByClass
0344: .get(className);
0345:
0346: return the_md;
0347: }
0348:
0349: // No MetaData so check for annotations
0350: FileMetaData annFilemd = loadAnnotationsForClass(c, clr, true,
0351: true);
0352: if (annFilemd != null) {
0353: // No MetaData but annotations present so use that
0354: if (c.isInterface()) {
0355: return annFilemd.getPackage(0).getInterface(0);
0356: } else {
0357: return annFilemd.getPackage(0).getClass(0);
0358: }
0359: }
0360:
0361: // Not found, so add to known classes/interfaces without MetaData
0362: if (JPOXLogger.METADATA.isDebugEnabled()) {
0363: JPOXLogger.METADATA.debug(LOCALISER
0364: .msg("044043", className));
0365: }
0366: classesWithoutPersistenceInfo.add(className);
0367:
0368: return null;
0369: }
0370:
0371: /**
0372: * Accessor for the MetaData for a named query for a class.
0373: * If the query is not found, will check all valid JDO file locations and try to load it.
0374: * @param cls The class which has the query defined for it
0375: * @param clr the ClassLoaderResolver
0376: * @param queryName Name of the query
0377: * @return The QueryMetaData for the query for this class
0378: **/
0379: public QueryMetaData getMetaDataForQuery(Class cls,
0380: ClassLoaderResolver clr, String queryName) {
0381: QueryMetaData qmd = super .getMetaDataForQuery(cls, clr,
0382: queryName);
0383: if (qmd != null) {
0384: return qmd;
0385: }
0386:
0387: String query_key = queryName;
0388: if (cls != null) {
0389: query_key = cls.getName() + "_" + queryName;
0390: }
0391:
0392: // No query found, so try to load one from a valid JDO location
0393: if (cls != null) {
0394: // Query is scoped to a candidate class, so load the class as necessary
0395: AbstractClassMetaData cmd = getMetaDataForClass(cls, clr);
0396: if (cmd == null) {
0397: // No metadata for this class so no chance of finding the query for it
0398: return null;
0399: }
0400:
0401: Object obj = queryMetaDataByName.get(query_key);
0402: if (obj != null) {
0403: return (QueryMetaData) obj;
0404: }
0405:
0406: // Query not stored in JDO/ORM files so try JDOQUERY
0407: List locations = new ArrayList();
0408: locations.addAll(getValidMetaDataLocationsForClass(
0409: getJDOQueryFileSuffix(), null, cls.getName()));
0410:
0411: for (int i = 0; i < locations.size(); i++) {
0412: String location = (String) locations.get(i);
0413: // Process all resources for this location
0414: Enumeration resources;
0415: try {
0416: resources = clr.getResources(location,
0417: cls != null ? cls.getClassLoader() : null);
0418: } catch (IOException e) {
0419: throw new JPOXException("Error loading resource", e)
0420: .setFatal();
0421: }
0422: while (resources.hasMoreElements()) {
0423: URL fileURL = (URL) resources.nextElement();
0424: if (fileMetaDataByURLString.get(fileURL.toString()) == null) {
0425: // File hasn't been loaded so load it
0426: FileMetaData filemd = parseFile(fileURL);
0427: filemd.setType(FileMetaData.JDOQUERY_FILE); // TODO Remove this since set in the parser at <jdoquery>
0428: registerFile(fileURL.toString(), filemd, clr);
0429:
0430: // Populate all classes in this file we've just parsed
0431: // TODO Populate the classes found in this file
0432: }
0433: }
0434: cmd = getMetaDataForClass(cls, clr);
0435:
0436: qmd = (QueryMetaData) queryMetaDataByName
0437: .get(query_key);
0438: if (qmd != null) {
0439: if (JPOXLogger.METADATA.isDebugEnabled()) {
0440: JPOXLogger.METADATA.debug(LOCALISER.msg(
0441: "044053", query_key, location));
0442: }
0443: return qmd;
0444: }
0445: if (JPOXLogger.METADATA.isDebugEnabled()) {
0446: JPOXLogger.METADATA.debug(LOCALISER.msg("044050",
0447: query_key, location));
0448: }
0449: }
0450: return null;
0451: }
0452:
0453: // Query isn't scoped to a candidate class, so search the valid package-independent locations
0454: List locations = new ArrayList();
0455: locations.addAll(getValidMetaDataLocationsForItem(
0456: getJDOFileSuffix(), null, null, false));
0457: locations.addAll(getValidMetaDataLocationsForItem(
0458: getORMFileSuffix(), getORMMappingName(), null, false));
0459: locations.addAll(getValidMetaDataLocationsForItem(
0460: getJDOQueryFileSuffix(), null, null, false));
0461:
0462: for (int i = 0; i < locations.size(); i++) {
0463: String location = (String) locations.get(i);
0464: // Process all resources for this location
0465: Enumeration resources;
0466: try {
0467: resources = clr.getResources(location,
0468: cls != null ? cls.getClassLoader() : null);
0469: } catch (IOException e) {
0470: throw new JPOXException("Error loading resources", e)
0471: .setFatal();
0472: }
0473: while (resources.hasMoreElements()) {
0474: URL fileURL = (URL) resources.nextElement();
0475: if (fileMetaDataByURLString.get(fileURL.toString()) == null) {
0476: // File hasn't been loaded so load it
0477: FileMetaData filemd = parseFile(fileURL);
0478: registerFile(fileURL.toString(), filemd, clr);
0479:
0480: // Populate all classes in this file we've just parsed
0481: // TODO Populate the classes found in this file
0482: }
0483: }
0484:
0485: qmd = (QueryMetaData) queryMetaDataByName.get(query_key);
0486: if (qmd != null) {
0487: if (JPOXLogger.METADATA.isDebugEnabled()) {
0488: JPOXLogger.METADATA.debug(LOCALISER.msg("044053",
0489: query_key, location));
0490: }
0491: return qmd;
0492: }
0493: if (JPOXLogger.METADATA.isDebugEnabled()) {
0494: JPOXLogger.METADATA.debug(LOCALISER.msg("044050",
0495: query_key, location));
0496: }
0497: }
0498: return null;
0499: }
0500:
0501: /**
0502: * Accessor for the MetaData for a Sequence in a package.
0503: * If the sequence is not yet known will search the valid locations for the passed name.
0504: * @param clr the ClassLoaderResolver
0505: * @param packageSequenceName Fully qualified name of the sequence (inc package name)
0506: * @return The SequenceMetaData for this named sequence
0507: **/
0508: public SequenceMetaData getMetaDataForSequence(
0509: ClassLoaderResolver clr, String packageSequenceName) {
0510: SequenceMetaData seqmd = super .getMetaDataForSequence(clr,
0511: packageSequenceName);
0512: if (seqmd != null) {
0513: return seqmd;
0514: }
0515:
0516: // MetaData not found so maybe just not yet loaded
0517: String packageName = packageSequenceName;
0518: if (packageSequenceName.lastIndexOf('.') >= 0) {
0519: packageName = packageSequenceName.substring(0,
0520: packageSequenceName.lastIndexOf('.'));
0521: }
0522:
0523: // Search valid JDO file locations ("jdo" and "orm" files for the specified package)
0524: List locations = new ArrayList();
0525: locations.addAll(getValidMetaDataLocationsForItem(
0526: getJDOFileSuffix(), null, packageName, false));
0527: locations.addAll(getValidMetaDataLocationsForItem(
0528: getORMFileSuffix(), getORMMappingName(), packageName,
0529: false));
0530:
0531: for (int i = 0; i < locations.size(); i++) {
0532: String location = (String) locations.get(i);
0533: // Process all resources for this location
0534: Enumeration resources;
0535: try {
0536: resources = clr.getResources(location, null);
0537: } catch (IOException e) {
0538: throw new JPOXException("Error loading resource", e)
0539: .setFatal();
0540: }
0541: while (resources.hasMoreElements()) {
0542: URL fileURL = (URL) resources.nextElement();
0543: if (fileMetaDataByURLString.get(fileURL.toString()) == null) {
0544: // File hasn't been loaded so load it
0545: FileMetaData filemd = parseFile(fileURL);
0546: registerFile(fileURL.toString(), filemd, clr);
0547:
0548: // Populate all classes in this file we've just parsed
0549: // TODO Populate the classes found in this file
0550: }
0551: }
0552:
0553: seqmd = (SequenceMetaData) sequenceMetaDataByPackageSequence
0554: .get(packageSequenceName);
0555: if (seqmd != null) {
0556: if (JPOXLogger.METADATA.isDebugEnabled()) {
0557: JPOXLogger.METADATA.debug(LOCALISER.msg("044053",
0558: packageSequenceName, location));
0559: }
0560: return seqmd;
0561: }
0562: if (JPOXLogger.METADATA.isDebugEnabled()) {
0563: JPOXLogger.METADATA.debug(LOCALISER.msg("044051",
0564: packageSequenceName, location));
0565: }
0566: }
0567: return null;
0568: }
0569:
0570: /**
0571: * Load up and add the O/R mapping info for the specified class to the stored JDO ClassMetaData.
0572: * @param c The class
0573: * @param clr the ClassLoaderResolver
0574: */
0575: public void addORMDataToClass(Class c, ClassLoaderResolver clr) {
0576: if (enhancing) {
0577: // We don't need ORM data when enhancing
0578: return;
0579: }
0580: if (!supportsORM) {
0581: // StoreManager doesnt "map" to the datastore so don't use ORM info
0582: return;
0583: }
0584:
0585: // Get the JDO MetaData for this class/interface
0586: AbstractClassMetaData cmd = (AbstractClassMetaData) classMetaDataByClass
0587: .get(c.getName());
0588:
0589: // See if we already have a file registered with the ORM metadata for this class
0590: AbstractClassMetaData ormCmd = (AbstractClassMetaData) ormClassMetaDataByClass
0591: .get(c.getName());
0592: if (ormCmd != null) {
0593: // Merge the ORM class into the JDO class
0594: MetaDataMerger.mergeClassORMData(cmd, ormCmd);
0595:
0596: // Remove it from the map since no longer needed
0597: ormClassMetaDataByClass.remove(c.getName());
0598:
0599: return;
0600: }
0601:
0602: // No ORM loaded for this class, so find if there is any ORM metadata available
0603: FileMetaData filemdORM = loadMetaDataForClass(c, clr,
0604: getORMMappingName(), getORMFileSuffix(), false);
0605: if (filemdORM != null) {
0606: // The ORM file has now been registered, so find the class and merge it into the JDO definition
0607: ormCmd = (AbstractClassMetaData) ormClassMetaDataByClass
0608: .get(c.getName());
0609: if (ormCmd != null) {
0610: // Merge the ORM file into the JDO file
0611: MetaDataMerger.mergeFileORMData((FileMetaData) cmd
0612: .getPackageMetaData().getParent(),
0613: (FileMetaData) ormCmd.getPackageMetaData()
0614: .getParent());
0615:
0616: // Merge the ORM class into the JDO class
0617: MetaDataMerger.mergeClassORMData(cmd, ormCmd);
0618:
0619: // Remove it from the map since no longer needed
0620: ormClassMetaDataByClass.remove(c.getName());
0621: }
0622: }
0623: }
0624:
0625: /**
0626: * Method to find the Meta-Data file for a specified class.
0627: * Checks the locations one-by-one, and checks for existence of the
0628: * specified class in the file. If a valid file is found it is loaded no matter
0629: * if the file contains the actual class. When a file is found containing the class
0630: * the process stops and the FileMetaData for that file (containing the class) returned.
0631: * <P>
0632: * Allows 2 variations on the naming above. The first is a modifier which
0633: * caters for a JDO 2.0 requirement whereby the user can specify a modifier
0634: * such as "mysql", which would mean that this should search for filenames
0635: * "package-mysql.jdo". The second variation is the suffix of the file.
0636: * This is "jdo" by default, but JDO 2.0 has situations where "orm", or
0637: * "jdoquery" are required as a suffix.
0638: * </P>
0639: * @param pc_class The class/interface to retrieve the Meta-Data file for
0640: * @param clr the ClassLoaderResolver
0641: * @param metadata_file_modifier Any modifier for the filename
0642: * @param metadata_file_extension File extension of MetaData files (e.g "jdo")
0643: * @param populate Whether to populate any loaded MetaData classes
0644: * @return FileMetaData for the file containing the class
0645: **/
0646: protected FileMetaData loadMetaDataForClass(Class pc_class,
0647: ClassLoaderResolver clr, String metadata_file_modifier,
0648: String metadata_file_extension, boolean populate) {
0649: // MetaData file locations
0650: List validLocations = getValidMetaDataLocationsForClass(
0651: metadata_file_extension, metadata_file_modifier,
0652: pc_class.getName());
0653: Iterator locationsIter = validLocations.iterator();
0654: while (locationsIter.hasNext()) {
0655: String location = (String) locationsIter.next();
0656: Enumeration resources;
0657: try {
0658: resources = clr.getResources(location, pc_class
0659: .getClassLoader());
0660: } catch (IOException e) {
0661: throw new JPOXException("Error loading resource", e)
0662: .setFatal();
0663: }
0664: if (!resources.hasMoreElements()
0665: && JPOXLogger.METADATA.isDebugEnabled()) {
0666: JPOXLogger.METADATA.debug(LOCALISER.msg("044049",
0667: metadata_file_extension, pc_class.getName(),
0668: location));
0669: }
0670: while (resources.hasMoreElements()) {
0671: URL url = (URL) resources.nextElement();
0672: if (url != null) {
0673: // File exists (valid URL), so check if we already have this file registered
0674: FileMetaData filemd = (FileMetaData) fileMetaDataByURLString
0675: .get(url.toString());
0676: if (filemd == null) {
0677: // Not registered so load the file from the URL
0678: filemd = parseFile(url);
0679: registerFile(url.toString(), filemd, clr);
0680:
0681: if (populate) {
0682: // Populate all classes in this file we've just parsed
0683: populateFileMetaData(filemd, clr, pc_class
0684: .getClassLoader());
0685: }
0686: }
0687:
0688: if ((filemd.getType() == FileMetaData.JDO_FILE && classMetaDataByClass
0689: .get(pc_class.getName()) != null)
0690: || (filemd.getType() == FileMetaData.ORM_FILE && ormClassMetaDataByClass
0691: .get(pc_class.getName()) != null)) {
0692: // We now have the class, so it must have been in this file
0693: if (JPOXLogger.METADATA.isDebugEnabled()) {
0694: JPOXLogger.METADATA.debug(LOCALISER.msg(
0695: "044052", metadata_file_extension,
0696: pc_class.getName(), url));
0697: }
0698: return filemd;
0699: }
0700: }
0701: }
0702: }
0703:
0704: if (JPOXLogger.METADATA.isDebugEnabled()) {
0705: JPOXLogger.METADATA.debug(LOCALISER.msg("044048",
0706: metadata_file_extension, pc_class.getName()));
0707: }
0708: return null;
0709: }
0710:
0711: /**
0712: * Method to return the valid metadata locations to contain a particular package.
0713: * @param fileExtension File extension (e.g "jdo")
0714: * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
0715: * @param packageName The package name to look for
0716: * @return The list of valid locations
0717: */
0718: public List getValidMetaDataLocationsForPackage(
0719: String fileExtension, String fileModifier,
0720: String packageName) {
0721: return getValidMetaDataLocationsForItem(fileExtension,
0722: fileModifier, packageName, false);
0723: }
0724:
0725: /**
0726: * Method to return the valid metadata locations to contain a particular class.
0727: * @param fileExtension File extension (e.g "jdo")
0728: * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
0729: * @param className The class name to look for
0730: * @return The list of valid locations
0731: */
0732: public List getValidMetaDataLocationsForClass(String fileExtension,
0733: String fileModifier, String className) {
0734: return getValidMetaDataLocationsForItem(fileExtension,
0735: fileModifier, className, true);
0736: }
0737:
0738: // Parameters used in the definition of MetaData file location
0739: private static final char CLASS_SEPARATOR = '.';
0740: private static final char PATH_SEPARATOR = '/';
0741: private static final char EXTENSION_SEPARATOR = '.';
0742: private static final String METADATA_PACKAGE = "package";
0743: private static final String METADATA_LOCATION_METAINF = "/META-INF/"
0744: + METADATA_PACKAGE;
0745: private static final String METADATA_LOCATION_WEBINF = "/WEB-INF/"
0746: + METADATA_PACKAGE;
0747:
0748: /**
0749: * Method to return the valid metadata locations to contain a particular item. The
0750: * "item" can be a package or a class. Will look in the locations appropriate for the
0751: * setting of "locationDefintion".
0752: * @param fileExtension File extension (e.g "jdo") accepts comma separated list
0753: * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
0754: * @param itemName The name of the item (package or class)
0755: * @param isClass Whether this is a class
0756: * @return The list of valid locations
0757: */
0758: List getValidMetaDataLocationsForItem(String fileExtension,
0759: String fileModifier, String itemName, boolean isClass) {
0760: // Build up a list of valid locations
0761: List locations = new ArrayList();
0762:
0763: if (fileExtension == null) {
0764: fileExtension = "jdo";
0765: }
0766: StringTokenizer tokens = new StringTokenizer(fileExtension, ",");
0767: while (tokens.hasMoreTokens()) {
0768: locations
0769: .addAll(getValidMetaDataLocationsForSingleExtension(
0770: tokens.nextToken(), fileModifier, itemName,
0771: isClass));
0772: }
0773: return locations;
0774: }
0775:
0776: /**
0777: * Method to return the valid metadata locations to contain a particular item. The
0778: * "item" can be a package or a class. Will look in the locations appropriate for the
0779: * setting of "locationDefintion".
0780: * @param fileExtension File extension (e.g "jdo")
0781: * @param fileModifier Any modifier (for use when using ORM files package-mysql.orm, this is the "mysql" part)
0782: * @param itemName The name of the item (package or class)
0783: * @param isClass Whether this is a class
0784: * @return The list of valid locations
0785: */
0786: private List getValidMetaDataLocationsForSingleExtension(
0787: String fileExtension, String fileModifier, String itemName,
0788: boolean isClass) {
0789: // Build up a list of valid locations
0790: List locations = new ArrayList();
0791:
0792: String suffix = null;
0793: if (fileExtension == null) {
0794: fileExtension = "jdo";
0795: }
0796: if (fileModifier != null) {
0797: // This will be something like "-mysql.orm" (suffix for ORM files)
0798: suffix = "-" + fileModifier + EXTENSION_SEPARATOR
0799: + fileExtension;
0800: } else {
0801: suffix = EXTENSION_SEPARATOR + fileExtension;
0802: }
0803:
0804: if (locationDefinition == ALL_JDO_LOCATIONS
0805: || locationDefinition == JDO_1_0_1_LOCATIONS) {
0806: locations.add((METADATA_LOCATION_METAINF + suffix)); // "/META-INF/package.jdo" (JDO 1.0.1)
0807: locations.add((METADATA_LOCATION_WEBINF + suffix)); // "/WEB-INF/package.jdo" (JDO 1.0.1)
0808: locations.add(PATH_SEPARATOR + METADATA_PACKAGE + suffix); // "/package.jdo" (JDO 1.0.1)
0809: }
0810: if (itemName != null && itemName.length() > 0) {
0811: int separatorPosition = itemName.indexOf('.');
0812: if (separatorPosition < 0) {
0813: if (locationDefinition == ALL_JDO_LOCATIONS
0814: || locationDefinition == JDO_1_0_1_LOCATIONS) {
0815: // "/com/package.jdo" (JDO 1.0.1)
0816: locations.add(PATH_SEPARATOR + itemName
0817: + PATH_SEPARATOR + METADATA_PACKAGE
0818: + suffix);
0819: }
0820: if (locationDefinition == ALL_JDO_LOCATIONS
0821: || locationDefinition == JDO_1_0_0_LOCATIONS) {
0822: // "/com.jdo" (JDO 1.0.0)
0823: locations.add(PATH_SEPARATOR + itemName + suffix);
0824: }
0825: } else {
0826: while (separatorPosition >= 0) {
0827: String name = itemName.substring(0,
0828: separatorPosition);
0829:
0830: if (locationDefinition == ALL_JDO_LOCATIONS
0831: || locationDefinition == JDO_1_0_1_LOCATIONS) {
0832: // "/com/xyz/package.jdo" (JDO 1.0.1)
0833: locations.add(PATH_SEPARATOR
0834: + name.replace(CLASS_SEPARATOR,
0835: PATH_SEPARATOR)
0836: + PATH_SEPARATOR + METADATA_PACKAGE
0837: + suffix);
0838: }
0839: if (locationDefinition == ALL_JDO_LOCATIONS
0840: || locationDefinition == JDO_1_0_0_LOCATIONS) {
0841: // "/com/xyz.jdo" (JDO 1.0.0)
0842: locations.add(PATH_SEPARATOR
0843: + name.replace(CLASS_SEPARATOR,
0844: PATH_SEPARATOR) + suffix);
0845: }
0846:
0847: separatorPosition = itemName.indexOf('.',
0848: separatorPosition + 1);
0849: if (separatorPosition < 0) {
0850: if (!isClass) {
0851: if (locationDefinition == ALL_JDO_LOCATIONS
0852: || locationDefinition == JDO_1_0_1_LOCATIONS) {
0853: // "/com/xyz/uvw/package.jdo" (JDO 1.0.1)
0854: locations.add(PATH_SEPARATOR
0855: + itemName.replace(
0856: CLASS_SEPARATOR,
0857: PATH_SEPARATOR)
0858: + PATH_SEPARATOR
0859: + METADATA_PACKAGE + suffix);
0860: }
0861: }
0862:
0863: if (locationDefinition == ALL_JDO_LOCATIONS
0864: || locationDefinition == JDO_1_0_0_LOCATIONS) {
0865: // "/com/xyz/uvw.jdo" (JDO 1.0.0)
0866: locations.add(PATH_SEPARATOR
0867: + itemName.replace(CLASS_SEPARATOR,
0868: PATH_SEPARATOR) + suffix);
0869: }
0870: }
0871: }
0872: }
0873: }
0874:
0875: return locations;
0876: }
0877:
0878: // ------------------------------- Persistent Interfaces ---------------------------------------
0879:
0880: /**
0881: * Main accessor for the MetaData for a "persistent-interface".
0882: * All MetaData returned from this method will be initialised and ready for full use.
0883: * @param c The interface to find MetaData for
0884: * @param clr the ClassLoaderResolver
0885: * @return The InterfaceMetaData for this interface (or null if not found)
0886: */
0887: public InterfaceMetaData getMetaDataForInterface(Class c,
0888: ClassLoaderResolver clr) {
0889: if (c == null || !c.isInterface()) {
0890: return null;
0891: }
0892:
0893: InterfaceMetaData imd = (InterfaceMetaData) getMetaDataForClassInternal(
0894: c, clr);
0895: if (imd != null) {
0896: // Make sure that anything returned is initialised
0897: if (!imd.isPopulated() && !imd.isInitialised()) {
0898: // Initialise the interface in question
0899: imd.populate(clr, c.getClassLoader());
0900: }
0901: // Make sure that anything returned is initialised
0902: if (!imd.isInitialised()) {
0903: // Initialise the interface in question
0904: imd.initialise();
0905: }
0906:
0907: if (utilisedFileMetaData.size() > 0) {
0908: // Initialise all FileMetaData that were processed in this call
0909: Iterator iter = utilisedFileMetaData.iterator();
0910: while (iter.hasNext()) {
0911: FileMetaData filemd = (FileMetaData) iter.next();
0912: initialiseFileMetaData(filemd, clr, c
0913: .getClassLoader());
0914: }
0915: }
0916: }
0917:
0918: utilisedFileMetaData.clear();
0919: return imd;
0920: }
0921:
0922: /**
0923: * Convenience method to return if the passed class name is a "persistent-interface".
0924: * @param name Name if the interface
0925: * @return Whether it is a "persistent-interface"
0926: */
0927: public boolean isPersistentInterface(String name) {
0928: // Find if this class has <interface> metadata
0929: AbstractClassMetaData acmd = (AbstractClassMetaData) classMetaDataByClass
0930: .get(name);
0931: return (acmd != null && acmd instanceof InterfaceMetaData);
0932: }
0933:
0934: /**
0935: * Convenience method to return if the passed class name is an implementation of the passed "persistent-interface".
0936: * @param interfaceName Name of the persistent interface
0937: * @param implName The implementation name
0938: * @return Whether it is a (JPOX-generated) impl of the persistent interface
0939: */
0940: public boolean isPersistentInterfaceImplementation(
0941: String interfaceName, String implName) {
0942: ClassMetaData cmd = (ClassMetaData) classMetaDataByInterface
0943: .get(interfaceName);
0944: return (cmd != null && cmd.getFullClassName().equals(implName));
0945: }
0946:
0947: /**
0948: * Convenience method to return if the passed class name is an implementation of a "persistent definition".
0949: * @param implName The implementation name
0950: * @return Whether it is a (JPOX-generated) impl of the persistent interface or abstract class
0951: */
0952: public boolean isPersistentDefinitionImplementation(String implName) {
0953: ClassMetaData cmd = (ClassMetaData) classMetaDataByClass
0954: .get(implName);
0955: return (cmd != null && cmd
0956: .isImplementationOfPersistentDefinition());
0957: }
0958:
0959: /**
0960: * Accessor for the implementation name for the specified "persistent-interface".
0961: * @param interfaceName The name of the persistent interface
0962: * @return The name of the implementation class
0963: */
0964: public String getImplementationNameForPersistentInterface(
0965: String interfaceName) {
0966: ClassMetaData cmd = (ClassMetaData) classMetaDataByInterface
0967: .get(interfaceName);
0968: return (cmd != null ? cmd.getFullClassName() : null);
0969: }
0970:
0971: /**
0972: * Accessor for the metadata for the implementation of the specified "persistent-interface".
0973: * @param interfaceName The name of the persistent interface
0974: * @return The ClassMetaData of the implementation class
0975: */
0976: public ClassMetaData getClassMetaDataForImplementationOfPersistentInterface(
0977: String interfaceName) {
0978: return (ClassMetaData) classMetaDataByInterface
0979: .get(interfaceName);
0980: }
0981:
0982: /**
0983: * Method to register a persistent interface and its implementation with the MetaData system.
0984: * @param imd MetaData for the interface
0985: * @param implClass The implementation class
0986: * @param clr ClassLoader Resolver to use
0987: */
0988: public void registerPersistentInterface(InterfaceMetaData imd,
0989: Class implClass, ClassLoaderResolver clr) {
0990: // Create ClassMetaData for the implementation
0991: ClassMetaData cmd = new ClassMetaData(imd, implClass.getName(),
0992: true);
0993:
0994: cmd.addImplements(new ImplementsMetaData(cmd, imd
0995: .getFullClassName()));
0996:
0997: // Register the ClassMetaData for the implementation
0998: classMetaDataByClass.put(cmd.getFullClassName(), cmd);
0999:
1000: // Register the metadata for the implementation against this persistent interface
1001: classMetaDataByInterface.put(imd.getFullClassName(), cmd);
1002:
1003: initialiseClassMetaData(cmd, implClass, clr);
1004:
1005: // Deregister the metadata for the implementation from those "not found"
1006: if (JPOXLogger.METADATA.isDebugEnabled()) {
1007: JPOXLogger.METADATA.debug(LOCALISER.msg("044044", implClass
1008: .getName()));
1009: }
1010: classesWithoutPersistenceInfo.remove(implClass.getName());
1011: }
1012:
1013: /**
1014: * Method to register the metadata for an implementation of a persistent abstract class.
1015: * @param cmd MetaData for the abstract class
1016: * @param implClass The implementation class
1017: * @param clr ClassLoader resolver
1018: */
1019: public void registerImplementationOfAbstractClass(
1020: ClassMetaData cmd, Class implClass, ClassLoaderResolver clr) {
1021: ClassMetaData implCmd = new ClassMetaData(cmd, implClass
1022: .getName());
1023:
1024: // Register the ClassMetaData for the implementation
1025: classMetaDataByClass.put(implCmd.getFullClassName(), implCmd);
1026: initialiseClassMetaData(implCmd, implClass, clr);
1027:
1028: // Deregister the metadata for the implementation from those "not found"
1029: if (JPOXLogger.METADATA.isDebugEnabled()) {
1030: JPOXLogger.METADATA.debug(LOCALISER.msg("044044", implClass
1031: .getName()));
1032: }
1033: classesWithoutPersistenceInfo.remove(implClass.getName());
1034: }
1035: }
|