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: 2007 Xuan Baldauf - Contrib of notifyMainMemoryCopyIsInvalid(), findObject() (needed by DB4O plugin).
0018: ...
0019: **********************************************************************/package org.jpox.store;
0020:
0021: import java.io.PrintStream;
0022: import java.io.Writer;
0023: import java.sql.Timestamp;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.Date;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.List;
0030:
0031: import org.jpox.ClassLoaderResolver;
0032: import org.jpox.OMFContext;
0033: import org.jpox.ObjectManager;
0034: import org.jpox.ObjectManagerFactoryImpl;
0035: import org.jpox.StateManager;
0036: import org.jpox.api.ApiAdapter;
0037: import org.jpox.exceptions.ClassNotResolvedException;
0038: import org.jpox.exceptions.JPOXDataStoreException;
0039: import org.jpox.exceptions.JPOXException;
0040: import org.jpox.exceptions.JPOXObjectNotFoundException;
0041: import org.jpox.exceptions.JPOXOptimisticException;
0042: import org.jpox.exceptions.JPOXUserException;
0043: import org.jpox.identity.OID;
0044: import org.jpox.identity.OIDFactory;
0045: import org.jpox.management.ManagementServer;
0046: import org.jpox.management.runtime.StoreManagerRuntime;
0047: import org.jpox.metadata.AbstractClassMetaData;
0048: import org.jpox.metadata.AbstractMemberMetaData;
0049: import org.jpox.metadata.IdentityStrategy;
0050: import org.jpox.metadata.IdentityType;
0051: import org.jpox.metadata.InheritanceStrategy;
0052: import org.jpox.metadata.MetaDataManager;
0053: import org.jpox.metadata.SequenceMetaData;
0054: import org.jpox.metadata.VersionMetaData;
0055: import org.jpox.metadata.VersionStrategy;
0056: import org.jpox.store.exceptions.DatastoreInitialisationException;
0057: import org.jpox.store.exceptions.NoTableManagedException;
0058: import org.jpox.store.poid.PoidManager;
0059: import org.jpox.store.scostore.Store;
0060: import org.jpox.util.ClassUtils;
0061: import org.jpox.util.JPOXLogger;
0062: import org.jpox.util.Localiser;
0063: import org.jpox.util.MacroString;
0064: import org.jpox.util.StringUtils;
0065:
0066: /**
0067: * An abstract representation of a Store Manager.
0068: * Manages the persistence of objects to the store.
0069: * Will be implemented for the type of datastore (RDBMS, ODBMS, OLAP) in question.
0070: * The store manager's responsibilities include:
0071: * <ul>
0072: * <li>Creating and/or validating datastore tables according to the persistent classes being
0073: * accessed by the application.</li>
0074: * <li>Serving as the primary intermediary between StateManagers and the database.</li>
0075: * <li>Serving as the base Extent and Query factory.</li>
0076: * </ul>
0077: * <p>
0078: * A store manager's knowledge of its contents is typically not complete. It knows about
0079: * the classes that it has encountered in its lifetime. The PersistenceManager can make the
0080: * StoreManager aware of a class, and can check if the StoreManager knows about a particular class.
0081: * The Auto-Start mechanism provides a way of inheriting knowledge from the last time the store
0082: * was used.
0083: *
0084: * @version $Revision: 1.180 $
0085: */
0086: public abstract class StoreManager {
0087: /** Localiser for messages. */
0088: protected static final Localiser LOCALISER = Localiser
0089: .getInstance("org.jpox.store.Localisation");
0090:
0091: /** Key for this StoreManager e.g "rdbms", "db4o" */
0092: protected final String storeManagerKey;
0093:
0094: /** Adapter for the datastore being used. */
0095: protected DatastoreAdapter dba;
0096:
0097: /** Whether this datastore is read only. */
0098: protected final boolean readOnlyDatastore;
0099:
0100: /** What should happen if read-only and an update is invoked. */
0101: protected final String readOnlyDatastoreAction;
0102:
0103: /** Whether this datastore is fixed (no mods to datastore classes allowed). */
0104: protected final boolean fixedDatastore;
0105:
0106: /** Auto-Start mechanism to use. */
0107: protected AutoStartMechanism starter = null;
0108:
0109: /** Whether the AutoStart mechanism is initialised */
0110: protected boolean starterInitialised = false;
0111:
0112: /** ObjectManagerFactory context. */
0113: protected final OMFContext omfContext;
0114:
0115: /** Manager for identity generation. */
0116: protected final PoidManager poidManager;
0117:
0118: /** Factory for identifiers for this datastore. Really should be on MappedStoreManager. */
0119: protected IdentifierFactory identifierFactory;
0120:
0121: /** StoreManager Runtime. Used when providing management of services. */
0122: protected StoreManagerRuntime storeManagerRuntime;
0123:
0124: /** Manager for the data definition in the datastore. */
0125: protected StoreDataManager storeDataMgr = new StoreDataManager();
0126:
0127: /** Name of the AutoStart mechanism. */
0128: protected String autoStartMechanism = null;
0129:
0130: /**
0131: * Constructor for a new StoreManager. Stores the basic information required for the datastore management.
0132: * @param key Key for this StoreManager
0133: * @param clr the ClassLoaderResolver
0134: * @param omf The corresponding ObjectManagerFactory.
0135: * @see StoreManagerFactory
0136: */
0137: protected StoreManager(String key, ClassLoaderResolver clr,
0138: ObjectManagerFactoryImpl omf) {
0139: this .storeManagerKey = key;
0140: this .omfContext = omf.getOMFContext();
0141: this .readOnlyDatastore = omf.getReadOnlyDatastore();
0142: this .readOnlyDatastoreAction = omf.getReadOnlyDatastoreAction();
0143: this .fixedDatastore = omf.getFixedDatastore();
0144:
0145: // Register this StoreManager with the OMFContext
0146: omfContext.setStoreManager(this );
0147:
0148: this .poidManager = new PoidManager();
0149:
0150: if (omfContext.getManagement() != null) {
0151: // register MBean in MbeanServer
0152: ManagementServer mgntServer = omfContext.getManagement()
0153: .getManagementServer();
0154: this .storeManagerRuntime = new StoreManagerRuntime();
0155: String mbeanName = omfContext.getDomainName()
0156: + ":InstanceName="
0157: + omfContext.getInstanceName()
0158: + ",Type="
0159: + ClassUtils
0160: .getClassNameForClass(storeManagerRuntime
0161: .getClass())
0162: + ",Name=StoreManagerRuntime";
0163: mgntServer.registerMBean(this .storeManagerRuntime,
0164: mbeanName);
0165: }
0166: }
0167:
0168: /**
0169: * Release of resources
0170: */
0171: public void close() {
0172: poidManager.clear();
0173: storeDataMgr.clear();
0174: starterInitialised = false;
0175: starter = null;
0176: dba = null;
0177: }
0178:
0179: /**
0180: * Method to return a datastore sequence for this datastore matching the passed sequence MetaData.
0181: * @param om The Object Manager
0182: * @param seqmd SequenceMetaData
0183: * @return The Sequence
0184: */
0185: public abstract JPOXSequence getJPOXSequence(ObjectManager om,
0186: SequenceMetaData seqmd);
0187:
0188: /**
0189: * Method to return a JPOX connection for the ObjectManager.
0190: * @param om ObjectManager
0191: * @return The JPOX Connection
0192: */
0193: public abstract JPOXConnection getJPOXConnection(ObjectManager om);
0194:
0195: /**
0196: * Accessor for the POIDManager for obtaining sequences.
0197: * @return The POID manager for this datastore
0198: */
0199: public PoidManager getPoidManager() {
0200: return poidManager;
0201: }
0202:
0203: /**
0204: * Accessor for the API adapter.
0205: * @return API adapter
0206: */
0207: public ApiAdapter getApiAdapter() {
0208: return omfContext.getApiAdapter();
0209: }
0210:
0211: /**
0212: * Accessor for the key for this store manager.
0213: * @return StoreManager key
0214: */
0215: public String getStoreManagerKey() {
0216: return storeManagerKey;
0217: }
0218:
0219: // -------------------------------- Management of Classes --------------------------------
0220:
0221: /**
0222: * Method to register some data with the store.
0223: * This will also register the data with the starter process.
0224: * @param data The StoreData to add
0225: */
0226: protected void registerStoreData(StoreData data) {
0227: storeDataMgr.registerStoreData(data);
0228:
0229: // Keep the AutoStarter in step with our managed classes/fields
0230: if (starter != null && starterInitialised) {
0231: starter.addClass(data);
0232: }
0233: }
0234:
0235: /**
0236: * Method to deregister all existing store data so that we are managing nothing.
0237: */
0238: protected void deregisterAllStoreData() {
0239: storeDataMgr.clear();
0240: starterInitialised = false;
0241:
0242: // Keep the AutoStarter in step with our managed classes/fields
0243: clearAutoStarter();
0244: }
0245:
0246: /**
0247: * Method to output the datastore information to the specified PrintStream.
0248: * @param ps PrintStream
0249: * @throws Exception If an error occurs obtaining the datastore info
0250: */
0251: public abstract void outputDatastoreInformation(PrintStream ps)
0252: throws Exception;
0253:
0254: /**
0255: * Method to output the schema information to the specified PrintStream.
0256: * @param ps PrintStream
0257: * @throws Exception If an error occurs obtaining the schema info
0258: */
0259: public abstract void outputSchemaInformation(PrintStream ps)
0260: throws Exception;
0261:
0262: // -------------------------------- Auto Starter ------------------------------------
0263:
0264: /**
0265: * Method to initialise the auto-start mechanism, loading up the classes
0266: * from its store into memory so that we start from where we got to last time.
0267: * @param mechanism Auto-Start mechanism
0268: * @param mode Mode of operation
0269: * @param clr The ClassLoaderResolver
0270: * @throws DatastoreInitialisationException
0271: */
0272: protected void initialiseAutoStart(String mechanism, String mode,
0273: ClassLoaderResolver clr)
0274: throws DatastoreInitialisationException {
0275: if (starterInitialised) {
0276: return;
0277: }
0278:
0279: autoStartMechanism = mechanism;
0280:
0281: String autoStarterClassName = getOMFContext()
0282: .getPluginManager().getAttributeValueForExtension(
0283: "org.jpox.autostart", "name", mechanism,
0284: "class-name");
0285: if (autoStarterClassName != null) {
0286: Class[] argsClass = new Class[] {
0287: org.jpox.store.StoreManager.class,
0288: org.jpox.ClassLoaderResolver.class };
0289: Object[] args = new Object[] { this , clr };
0290: try {
0291: starter = (AutoStartMechanism) getOMFContext()
0292: .getPluginManager().createExecutableExtension(
0293: "org.jpox.autostart", "name",
0294: mechanism, "class-name", argsClass,
0295: args);
0296: } catch (Exception e) {
0297: JPOXLogger.PERSISTENCE.error(StringUtils
0298: .getStringFromStackTrace(e));
0299: }
0300: starter.setMode(mode);
0301: }
0302: if (starter == null) {
0303: starterInitialised = true;
0304: return;
0305: }
0306:
0307: boolean illegalState = false;
0308: try {
0309: if (!starter.isOpen()) {
0310: starter.open();
0311: }
0312: Collection existingData = starter.getAllClassData();
0313: if (existingData != null && existingData.size() > 0) {
0314: List classesNeedingAdding = new ArrayList();
0315: Iterator existingDataIter = existingData.iterator();
0316: while (existingDataIter.hasNext()) {
0317: StoreData data = (StoreData) existingDataIter
0318: .next();
0319: if (data.isFCO()) {
0320: // Catch classes that don't exist (e.g in use by a different app)
0321: Class classFound = null;
0322: try {
0323: classFound = clr.classForName(data
0324: .getName());
0325: } catch (ClassNotResolvedException cnre) {
0326: if (data.getInterfaceName() != null) {
0327: try {
0328: getOMFContext()
0329: .getImplementationCreator()
0330: .newInstance(
0331: clr
0332: .classForName(data
0333: .getInterfaceName()),
0334: getMetaDataManager(),
0335: clr);
0336: classFound = clr.classForName(data
0337: .getName());
0338: } catch (ClassNotResolvedException cnre2) {
0339: // Do nothing
0340: }
0341: }
0342: // Thrown if class not found
0343: }
0344:
0345: if (classFound != null) {
0346: JPOXLogger.PERSISTENCE.info(LOCALISER.msg(
0347: "032003", data.getName()));
0348: classesNeedingAdding.add(data.getName());
0349: if (data.getMetaData() == null) {
0350: // StoreData doesnt have its metadata set yet so load it
0351: // This ensures that the MetaDataManager always knows about these classes
0352: AbstractClassMetaData acmd = getMetaDataManager()
0353: .getMetaDataForClass(
0354: classFound, clr);
0355: if (acmd != null) {
0356: data.setMetaData(acmd);
0357: } else {
0358: String msg = LOCALISER.msg(
0359: "034004", data.getName());
0360: if (starter
0361: .getMode()
0362: .equals(
0363: AutoStartMechanism.MODE_CHECKED)) {
0364: JPOXLogger.PERSISTENCE
0365: .error(msg);
0366: throw new DatastoreInitialisationException(
0367: msg);
0368: } else if (starter
0369: .getMode()
0370: .equals(
0371: AutoStartMechanism.MODE_IGNORED)) {
0372: JPOXLogger.PERSISTENCE
0373: .warn(msg);
0374: } else if (starter
0375: .getMode()
0376: .equals(
0377: AutoStartMechanism.MODE_QUIET)) {
0378: JPOXLogger.PERSISTENCE
0379: .warn(msg);
0380: JPOXLogger.PERSISTENCE
0381: .warn(LOCALISER.msg(
0382: "034001",
0383: data.getName()));
0384: starter.deleteClass(data
0385: .getName());
0386: }
0387: }
0388: }
0389: } else {
0390: String msg = LOCALISER.msg("034000", data
0391: .getName());
0392: if (starter.getMode().equals(
0393: AutoStartMechanism.MODE_CHECKED)) {
0394: JPOXLogger.PERSISTENCE.error(msg);
0395: throw new DatastoreInitialisationException(
0396: msg);
0397: } else if (starter.getMode().equals(
0398: AutoStartMechanism.MODE_IGNORED)) {
0399: JPOXLogger.PERSISTENCE.warn(msg);
0400: } else if (starter.getMode().equals(
0401: AutoStartMechanism.MODE_QUIET)) {
0402: JPOXLogger.PERSISTENCE.warn(msg);
0403: JPOXLogger.PERSISTENCE.warn(LOCALISER
0404: .msg("034001", data.getName()));
0405: starter.deleteClass(data.getName());
0406: }
0407: }
0408: }
0409: }
0410: String[] classesToLoad = new String[classesNeedingAdding
0411: .size()];
0412: Iterator classesNeedingAddingIter = classesNeedingAdding
0413: .iterator();
0414: int n = 0;
0415: while (classesNeedingAddingIter.hasNext()) {
0416: classesToLoad[n++] = (String) classesNeedingAddingIter
0417: .next();
0418: }
0419:
0420: // Load the classes
0421: try {
0422: addClasses(classesToLoad, clr);
0423: } catch (Exception e) {
0424: // Exception while adding so some of the (referenced) classes dont exist
0425: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034002",
0426: e));
0427: //if an exception happens while loading AutoStart data, them we disable it, since
0428: //it was unable to load the data from AutoStart. The memory state of AutoStart does
0429: //not represent the database, and if we don't disable it, we could
0430: //think that the autostart store is empty, and we would try to insert new entries in
0431: //the autostart store that are already in there
0432: illegalState = true;
0433:
0434: // TODO Go back and add classes one-by-one to eliminate the class(es) with the problem
0435: }
0436: }
0437: } finally {
0438: if (starter.isOpen()) {
0439: starter.close();
0440: }
0441: if (illegalState) {
0442: JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034003"));
0443: starter = null;
0444: }
0445: }
0446:
0447: starterInitialised = true;
0448: }
0449:
0450: /**
0451: * Method to clear the Auto-Starter status.
0452: */
0453: protected void clearAutoStarter() {
0454: // AutoStarter - Delete all supported classes
0455: if (starter != null) {
0456: try {
0457: if (!starter.isOpen()) {
0458: starter.open();
0459: }
0460: starter.deleteAllClasses();
0461: } finally {
0462: if (starter.isOpen()) {
0463: starter.close();
0464: }
0465: }
0466: }
0467: }
0468:
0469: /**
0470: * Accessor for the {@link AutoStartMechanism}
0471: * @return may return null if auto start mechanism is not initialized
0472: */
0473: public AutoStartMechanism getAutoStartMechanism() {
0474: return starter;
0475: }
0476:
0477: // ------------------------------- Class Management -----------------------------------
0478:
0479: /**
0480: * Accessor for whether the specified class is managed currently
0481: * @param className The name of the class
0482: * @return Whether it is managed
0483: */
0484: public boolean managesClass(String className) {
0485: return storeDataMgr.managesClass(className);
0486: }
0487:
0488: /**
0489: * Method to add a class to the managed list for this datastore manager.
0490: * @param className Name of the class
0491: * @param clr The ClassLoaderResolver
0492: */
0493: public void addClass(String className, ClassLoaderResolver clr) {
0494: addClasses(new String[] { className }, clr, null, false);
0495: }
0496:
0497: /**
0498: * Add classes to the persistence model for the datastore.
0499: * In the case of an RDBMS, this will map these classes to RDBMS tables.
0500: * In the case of XML, this will map these classes to XML datafiles.
0501: * <p>
0502: * This method is primarily useful for applications that wish to perform all
0503: * of their datastore initialization up front, rather than wait for the JPOX
0504: * runtime to do it on-demand.
0505: * @param classes The class(es) to be added.
0506: * @param clr The ClassLoaderResolver
0507: * @param writer Optional writer when you just want the DDL for persisting the specified classes
0508: * @param completeDdl whether complete DDL will be created when writing DDL to a file, or only for missing elements
0509: * @exception org.jpox.store.exceptions.DatastoreValidationException
0510: * If there is some mismatch between the current datastore contents and
0511: * those necessary to enable persistence of the given classes.
0512: */
0513: public abstract void addClasses(String[] classes,
0514: ClassLoaderResolver clr, Writer writer, boolean completeDdl);
0515:
0516: /**
0517: * Add classes to the persistence model for the datastore.
0518: * In the case of an RDBMS, this will map these classes to RDBMS tables.
0519: * In the case of XML, this will map these classes to XML datafiles.
0520: * <p>
0521: * This method is primarily useful for applications that wish to perform all
0522: * of their datastore initialization up front, rather than wait for the JPOX
0523: * runtime to do it on-demand.
0524: * @param classNames The class(es) to be added.
0525: * @param clr The ClassLoaderResolver
0526: * @exception org.jpox.store.exceptions.DatastoreValidationException
0527: * If there is some mismatch between the current datastore contents and
0528: * those necessary to enable persistence of the given classes.
0529: */
0530: public void addClasses(String[] classNames, ClassLoaderResolver clr) {
0531: addClasses(classNames, clr, null, false);
0532: }
0533:
0534: /**
0535: * Drops all tables in the datastore.
0536: * This empties the datastore of all datastore objects managed by JPOX.
0537: * All objects not managed by JPOX are left untouched.
0538: * @param clr The ClassLoaderResolver
0539: */
0540: public abstract void removeAllClasses(ClassLoaderResolver clr);
0541:
0542: /**
0543: * Method to return the class(es) that has a table managing the persistence of
0544: * the fields of the supplied class. For the 3 inheritance strategies, the following
0545: * occurs :-
0546: * <UL>
0547: * <LI>new-table : will return the same ClassMetaData</LI>
0548: * <LI>subclass-table : will return all subclasses that have a table managing its fields</LI>
0549: * <LI>superclass-table : will return the next superclass that has a table</LI>
0550: * </UL>
0551: * @param cmd The supplied class.
0552: * @param clr ClassLoader resolver
0553: * @return The ClassMetaData's managing the fields of the supplied class
0554: */
0555: public AbstractClassMetaData[] getClassesManagingTableForClass(
0556: AbstractClassMetaData cmd, ClassLoaderResolver clr) {
0557: // Null input, so just return null;
0558: if (cmd == null) {
0559: return null;
0560: }
0561:
0562: if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.COMPLETE_TABLE
0563: || cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.NEW_TABLE) {
0564: // Class manages a table so return the classes metadata.
0565: return new AbstractClassMetaData[] { cmd };
0566: } else if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE) {
0567: // Check the subclasses that we have metadata for and make sure they are managed before proceeding
0568: String[] subclasses = getMetaDataManager()
0569: .getSubclassesForClass(cmd.getFullClassName(), true);
0570: if (subclasses != null) {
0571: for (int i = 0; i < subclasses.length; i++) {
0572: if (!storeDataMgr.managesClass(subclasses[i])) {
0573: addClass(subclasses[i], clr);
0574: }
0575: }
0576: }
0577:
0578: // Find subclasses who manage the tables winto which our class is persisted
0579: HashSet managingClasses = new HashSet();
0580: Iterator managedClassesIter = storeDataMgr
0581: .getManagedStoreData().iterator();
0582: while (managedClassesIter.hasNext()) {
0583: StoreData data = (StoreData) managedClassesIter.next();
0584: if (data.isFCO()
0585: && ((AbstractClassMetaData) data.getMetaData())
0586: .getSuperAbstractClassMetaData() != null
0587: && ((AbstractClassMetaData) data.getMetaData())
0588: .getSuperAbstractClassMetaData()
0589: .getFullClassName().equals(
0590: cmd.getFullClassName())) {
0591: AbstractClassMetaData[] super Cmds = getClassesManagingTableForClass(
0592: (AbstractClassMetaData) data.getMetaData(),
0593: clr);
0594: if (super Cmds != null) {
0595: for (int i = 0; i < super Cmds.length; i++) {
0596: managingClasses.add(super Cmds[i]);
0597: }
0598: }
0599: }
0600: }
0601:
0602: Iterator managingClassesIter = managingClasses.iterator();
0603: AbstractClassMetaData managingCmds[] = new AbstractClassMetaData[managingClasses
0604: .size()];
0605: int i = 0;
0606: while (managingClassesIter.hasNext()) {
0607: managingCmds[i++] = (AbstractClassMetaData) (managingClassesIter
0608: .next());
0609: }
0610: return managingCmds;
0611: } else if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUPERCLASS_TABLE) {
0612: // Fields managed by superclass, so recurse to that
0613: return getClassesManagingTableForClass(cmd
0614: .getSuperAbstractClassMetaData(), clr);
0615: }
0616: return null;
0617: }
0618:
0619: /**
0620: * Convenience method to ensure that the class defined by the passed OID/SingleFIeldIdentity is
0621: * managed by the store.
0622: * @param id OID
0623: * @param clr ClassLoader resolver
0624: * @return The class name of the class associated to this identity
0625: * @throws JPOXUserException if the identity is assigned to the wrong class
0626: */
0627: public String manageClassForIdentity(Object id,
0628: ClassLoaderResolver clr) {
0629: String className = null;
0630:
0631: if (id instanceof OID) {
0632: // Check that the implied class is managed
0633: className = ((OID) id).getPcClass();
0634: AbstractClassMetaData cmd = getMetaDataManager()
0635: .getMetaDataForClass(className, clr);
0636: if (cmd.getIdentityType() != IdentityType.DATASTORE) {
0637: throw new JPOXUserException(LOCALISER.msg("038001", id,
0638: cmd.getFullClassName()));
0639: }
0640:
0641: } else if (getApiAdapter().isSingleFieldIdentity(id)) {
0642: className = getApiAdapter()
0643: .getTargetClassNameForSingleFieldIdentity(id);
0644: AbstractClassMetaData cmd = getMetaDataManager()
0645: .getMetaDataForClass(className, clr);
0646: if (cmd.getIdentityType() != IdentityType.APPLICATION
0647: || !cmd.getObjectidClass().equals(
0648: id.getClass().getName())) {
0649: throw new JPOXUserException(LOCALISER.msg("038001", id,
0650: cmd.getFullClassName()));
0651: }
0652: } else {
0653: throw new JPOXException(
0654: "StoreManager.manageClassForIdentity called for id="
0655: + id
0656: + " yet should only be called for datastore-identity/SingleFieldIdentity");
0657: }
0658:
0659: // If the class is not yet managed, manage it
0660: if (!managesClass(className)) {
0661: addClass(className, clr);
0662: }
0663:
0664: return className;
0665: }
0666:
0667: /**
0668: * Method returning whether the datastore has a "datastore class" equivalent that the StoreManager
0669: * uses to represent it. For datastores like RDBMS this will return "true" since we have a "table".
0670: * For object datastores this is typically "false" since the objects are stored in some internal form
0671: * not visible via its API.
0672: * @return Whether datastore classes are used by the StoreManager
0673: */
0674: public boolean usesDatastoreClass() {
0675: return false;
0676: }
0677:
0678: // ------------------------------ PersistenceManager interface -----------------------------------
0679:
0680: /**
0681: * Interface to getting an Extent for a class.
0682: * @param om The Object Manager
0683: * @param c The class requiring the Extent
0684: * @param subclasses Whether to include subclasses of 'c'
0685: * @return The Extent.
0686: */
0687: public abstract Extent getExtent(ObjectManager om, Class c,
0688: boolean subclasses);
0689:
0690: /**
0691: * Accessor for whether this query language is supported.
0692: * @param language The language
0693: * @return Whether it is supported.
0694: * @see org.jpox.store.StoreManager#supportsQueryLanguage(java.lang.String)
0695: */
0696: public abstract boolean supportsQueryLanguage(String language);
0697:
0698: /**
0699: * Returns the class corresponding to the given object identity.
0700: * If the object is an OID (datastore-identity), return the PC class specified in the identity.
0701: * If the object is SingleFieldIdentity, return the PC class specified in the identity
0702: * If the object is an AppID PK, return the PC class that uses it.
0703: * If the object is a SCOID, return the SCO class.
0704: * If the object is a PersistenceCapable class, return the class.
0705: * @param id The identity of some object.
0706: * @param clr ClassLoader resolver
0707: * @param om Object Manager
0708: * @return For datastore identity, return the class of the corresponding
0709: * object. For application identity, return the class of the corresponding
0710: * object or null if object does not exist.
0711: * @exception ClassCastException If the type of ID is not recognized ({@link OID}
0712: * or {@link SCOID}).
0713: */
0714: public String getClassNameForObjectID(Object id,
0715: ClassLoaderResolver clr, ObjectManager om) {
0716: if (id instanceof SCOID) {
0717: // Object is a SCOID
0718: return ((SCOID) id).getSCOClass();
0719: } else if (id instanceof OID) {
0720: // Object is an OID
0721: return ((OID) id).getPcClass();
0722: } else if (getApiAdapter().isSingleFieldIdentity(id)) {
0723: // Using SingleFieldIdentity so can assume that object is of the target class
0724: return getApiAdapter()
0725: .getTargetClassNameForSingleFieldIdentity(id);
0726: } else {
0727: // Application identity with user PK class
0728: // Find all of the application identity PK classes of this type
0729: Collection c = storeDataMgr.getByPrimaryKeyClass(id
0730: .getClass().getName());
0731: if (c != null) {
0732: Iterator iter = c.iterator();
0733: while (iter.hasNext()) {
0734: // Just return the class name of the first one using this id - could be any in this tree
0735: // TODO Provide a way of finding the precise type - e.g cont.get(object with this id)
0736: StoreData store_data = (StoreData) iter.next();
0737: return store_data.getName();
0738: }
0739: }
0740: return null;
0741: }
0742: }
0743:
0744: /**
0745: * Check if the strategy is attributed by the database when the
0746: * PersistenceCapable object is inserted into the database
0747: * @param identityStrategy the identityStrategy
0748: * @param datastoreIdentityField Whether this is for the surrogate datastore identity field
0749: * @return if the object for the strategy is attributed by the database
0750: */
0751: public boolean isStrategyDatastoreAttributed(
0752: IdentityStrategy identityStrategy,
0753: boolean datastoreIdentityField) {
0754: if (identityStrategy == null) {
0755: return false;
0756: }
0757:
0758: /*if (identityStrategy == IdentityStrategy.NATIVE && dba.supportsAutoIncrementFields())
0759: {
0760: // If the user leaves it to us and the DBA supports autoincrement then we use it
0761: // which means attributing the id in the datastore.
0762: return true;
0763: }*/
0764:
0765: // "identity" is processed in the datastore
0766: if (identityStrategy == IdentityStrategy.IDENTITY) {
0767: // TODO "identity" could imply using a SEQUENCE, so we need to allow for this here
0768: return true;
0769: }
0770:
0771: return false;
0772: }
0773:
0774: /**
0775: * Method to retrieve the value for a strategy for a particular field.
0776: * @param om The Object Manager
0777: * @param table The datastore table
0778: * @param cmd AbstractClassMetaData for the class
0779: * @param absoluteFieldNumber The field number
0780: * @return The value
0781: */
0782: public abstract Object getStrategyValue(ObjectManager om,
0783: DatastoreClass table, AbstractClassMetaData cmd,
0784: int absoluteFieldNumber);
0785:
0786: /**
0787: * Returns a new, unique ID for an object of the given class.
0788: * @param om The Object Manager
0789: * @param className Name of the class of the object.
0790: * @param pc The persistable object. Used for application-identity
0791: * @return A new object ID.
0792: * @throws JPOXUserException if the class has no available table in the datastore
0793: */
0794: public Object newObjectID(ObjectManager om, String className,
0795: Object pc) {
0796: AbstractClassMetaData cmd = getMetaDataManager()
0797: .getMetaDataForClass(className,
0798: om.getClassLoaderResolver());
0799: if (cmd.getIdentityType() == IdentityType.DATASTORE) {
0800: DatastoreClass t = null;
0801: if (usesDatastoreClass()) {
0802: t = getDatastoreClass(className, om
0803: .getClassLoaderResolver());
0804: if (t == null
0805: && cmd.getInheritanceMetaData()
0806: .getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE) {
0807: throw new JPOXUserException(LOCALISER.msg("032013",
0808: className));
0809: }
0810:
0811: // Go up to overall superclass to find id for that class.
0812: boolean has_super class = true;
0813: while (has_super class) {
0814: DatastoreClass super t = t.getSuperDatastoreClass();
0815: if (super t != null) {
0816: t = super t;
0817: } else {
0818: has_super class = false;
0819: }
0820: }
0821: }
0822:
0823: // Populate any strategy value for the "datastore-identity" element
0824: Object nextIdentifier = getStrategyValue(om, t, cmd, -1);
0825: return OIDFactory.getInstance(om, cmd.getFullClassName(),
0826: nextIdentifier);
0827: } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
0828: return getApiAdapter().getNewApplicationIdentityObjectId(
0829: pc, cmd); // All values will have been populated before arriving here
0830: } else {
0831: // All "nondurable" cases (views, JPOXSQL) will come through here
0832: return new SCOID(className);
0833: }
0834: }
0835:
0836: /**
0837: * Inserts a persistent object into the database.
0838: * @param sm The state manager of the object to be inserted.
0839: * @throws JPOXDataStoreException when an error occurs in the datastore communication
0840: */
0841: public abstract void insertObject(StateManager sm);
0842:
0843: /**
0844: * Fetches a persistent object from the database.
0845: * @param sm The state manager of the object to be fetched.
0846: * @param fieldNumbers The numbers of the fields to be fetched.
0847: * @throws JPOXObjectNotFoundException if the object doesnt exist
0848: * @throws JPOXDataStoreException when an error occurs in the datastore communication
0849: */
0850: public abstract void fetchObject(StateManager sm,
0851: int fieldNumbers[]);
0852:
0853: /**
0854: * Updates a persistent object in the datastore.
0855: * @param sm The state manager of the object to be updated.
0856: * @param fieldNumbers The numbers of the fields to be updated.
0857: * @throws JPOXDataStoreException when an error occurs in the datastore communication
0858: */
0859: public abstract void updateObject(StateManager sm,
0860: int fieldNumbers[]);
0861:
0862: /**
0863: * Deletes a persistent object from the datastore.
0864: * @param sm The state manager of the object to be deleted.
0865: * @throws JPOXDataStoreException when an error occurs in the datastore communication
0866: */
0867: public abstract void deleteObject(StateManager sm);
0868:
0869: /**
0870: * Locates this object in the datastore.
0871: * @param sm The StateManager for the object to be found
0872: * @throws JPOXObjectNotFoundException if the object doesnt exist
0873: * @throws JPOXDataStoreException when an error occurs in the datastore communication
0874: */
0875: public abstract void locateObject(StateManager sm);
0876:
0877: /**
0878: * Method to find a persistable object with the specified id from the datastore, if the StoreManager supports
0879: * this operation (optional). This allows for datastores that perform the instantiation of objects directly
0880: * (such as ODBMS). With other types of datastores (e.g RDBMS) this method returns null. If the StoreManager
0881: * supports this operation yet the object is not found an exception should be thrown.
0882: * @param om the ObjectManager which will manage the object
0883: * @param id the id of the object in question.
0884: * @return a persistable object with a valid object state (for example: hollow) or null,
0885: * indicating that the implementation leaves the instantiation work to JPOX.
0886: */
0887: public abstract Object findObject(ObjectManager om, Object id);
0888:
0889: /**
0890: * Perform an optimistic version check on the passed object, against the passed version in the datastore.
0891: * @param sm StateManager of the object to check
0892: * @param versionDatastore Version of the object in the datastore
0893: * @param versionMetaData VersionMetaData to use for checking
0894: * @throws JPOXUserException thrown when an invalid strategy is specified
0895: * @throws JPOXOptimisticException thrown when the version check fails
0896: */
0897: public void performVersionCheck(StateManager sm,
0898: Object versionDatastore, VersionMetaData versionMetaData) {
0899: // Extract the version of the object (that we are updating)
0900: Object versionObject = sm.getTransactionalVersion(sm
0901: .getObject());
0902: if (versionObject == null) {
0903: return;
0904: }
0905:
0906: if (versionMetaData == null) {
0907: // No version specification so no check needed
0908: JPOXLogger.JDO
0909: .info(sm.getClassMetaData().getFullClassName()
0910: + " has no version metadata so no check of version is required, since this will not have the version flag in its table");
0911: return;
0912: }
0913:
0914: boolean valid;
0915: if (versionMetaData.getVersionStrategy() == VersionStrategy.DATE_TIME) {
0916: valid = ((Timestamp) versionObject).getTime() == ((Timestamp) versionDatastore)
0917: .getTime();
0918: } else if (versionMetaData.getVersionStrategy() == VersionStrategy.VERSION_NUMBER) {
0919: valid = ((Number) versionObject).longValue() == ((Number) versionDatastore)
0920: .longValue();
0921: } else if (versionMetaData.getVersionStrategy() == VersionStrategy.STATE_IMAGE) {
0922: // TODO Support state-image strategy
0923: throw new JPOXUserException(LOCALISER.msg("032017", sm
0924: .getClassMetaData().getFullClassName(),
0925: versionMetaData.getVersionStrategy()));
0926: } else {
0927: throw new JPOXUserException(LOCALISER.msg("032017", sm
0928: .getClassMetaData().getFullClassName(),
0929: versionMetaData.getVersionStrategy()));
0930: }
0931:
0932: if (!valid) {
0933: String msg = LOCALISER.msg("032016", StringUtils
0934: .toJVMIDString(sm.getObject()), sm
0935: .getInternalObjectId(), "" + versionDatastore, ""
0936: + versionObject);
0937: JPOXLogger.PERSISTENCE.error(msg);
0938: throw new JPOXOptimisticException(msg, sm.getObject());
0939: }
0940: }
0941:
0942: /**
0943: * Utility to return the names of the classes that are known subclasses of the provided
0944: * class. Actually uses the MetaDataManager for determining what is a subclass
0945: * since the MetaData is often registered before being needed by the Store.
0946: * @param className Class for which we search for subclasses.
0947: * @param includeDescendents Whether to include subclasses of subclasses etc
0948: * @param clr The ClassLoaderResolver
0949: * @return Set of classes that are subclasses of the input class.
0950: */
0951: public HashSet getSubClassesForClass(String className,
0952: boolean includeDescendents, ClassLoaderResolver clr) {
0953: HashSet subclasses = new HashSet();
0954:
0955: String[] subclassNames = getMetaDataManager()
0956: .getSubclassesForClass(className, includeDescendents);
0957: if (subclassNames != null) {
0958: // Load up the table for any classes that are not yet loaded
0959: for (int i = 0; i < subclassNames.length; i++) {
0960: if (!storeDataMgr.managesClass(subclassNames[i])) {
0961: // We have no knowledge of this class so load it now
0962: addClass(subclassNames[i], clr);
0963: }
0964: subclasses.add(subclassNames[i]);
0965: }
0966: }
0967:
0968: return subclasses;
0969: }
0970:
0971: // ------------------------------- Accessors ------------------------------------
0972:
0973: /**
0974: * Accessor for the identifier factory.
0975: * @return Returns the identifier factory.
0976: */
0977: public IdentifierFactory getIdentifierFactory() {
0978: return identifierFactory;
0979: }
0980:
0981: /**
0982: * Accessor for the context in which this RDBMSManager is running
0983: * @return Returns the context.
0984: */
0985: public OMFContext getOMFContext() {
0986: return omfContext;
0987: }
0988:
0989: /**
0990: * Gets the MetaDataManager to use for this store.
0991: * @return Returns the MetaDataManager.
0992: */
0993: public MetaDataManager getMetaDataManager() {
0994: return omfContext.getMetaDataManager();
0995: }
0996:
0997: /**
0998: * Gets the DatastoreAdapter to use for this store.
0999: * @return Returns the DatastoreAdapter
1000: */
1001: public DatastoreAdapter getDatastoreAdapter() {
1002: return dba;
1003: }
1004:
1005: /**
1006: * Get the date/time of the datastore.
1007: * @return Date/time of the datastore
1008: */
1009: public abstract Date getDatastoreDate();
1010:
1011: /**
1012: * Returns the primary datastore container serving as backing for the given class.
1013: * If the class is not yet known to the store manager, {@link #addClass}is called
1014: * to add it. Classes which have inheritance strategy of "new-table" and
1015: * "superclass-table" will return a table here, whereas "subclass-table" will
1016: * return null since it doesn't have a table as such.
1017: * <p>
1018: * @param className Name of the class whose table is be returned.
1019: * @param clr The ClassLoaderResolver
1020: * @return The corresponding class table.
1021: * @exception NoTableManagedException If the given class has no table managed in the database.
1022: */
1023: public DatastoreClass getDatastoreClass(String className,
1024: ClassLoaderResolver clr) {
1025: DatastoreClass ct = null;
1026: if (className == null) {
1027: JPOXLogger.PERSISTENCE.error(LOCALISER.msg("032015"));
1028: return null;
1029: }
1030:
1031: StoreData sd = storeDataMgr.get(className);
1032: if (sd != null && sd instanceof TableStoreData) {
1033: ct = (DatastoreClass) ((TableStoreData) sd)
1034: .getDatastoreContainerObject();
1035: if (ct != null) {
1036: // Class known about
1037: return ct;
1038: }
1039: }
1040:
1041: // Class not known so consider adding it to our list of supported classes.
1042: // Currently we only consider PC classes
1043: boolean toBeAdded = false;
1044: if (clr != null) {
1045: Class cls = clr.classForName(className);
1046: ApiAdapter api = getApiAdapter();
1047: if (cls != null && !cls.isInterface()
1048: && api.isPersistable(cls)) {
1049: toBeAdded = true;
1050: }
1051: } else {
1052: toBeAdded = true;
1053: }
1054:
1055: boolean classKnown = false;
1056: if (toBeAdded) {
1057: // Add the class to our supported list
1058: addClass(className, clr);
1059:
1060: // Retry
1061: synchronized (storeDataMgr) {
1062: sd = storeDataMgr.get(className);
1063: if (sd != null && sd instanceof TableStoreData) {
1064: classKnown = true;
1065: ct = (DatastoreClass) ((TableStoreData) sd)
1066: .getDatastoreContainerObject();
1067: }
1068: }
1069: }
1070:
1071: // Throw an exception if class still not known and no table
1072: // Note : "subclass-table" inheritance strategies will return null from this method
1073: if (!classKnown && ct == null) {
1074: throw new NoTableManagedException(className);
1075: }
1076:
1077: return ct;
1078: }
1079:
1080: /**
1081: * Returns the JDO table having the given SQL identifier.
1082: * Returns 'null' if no such table is (yet) known to the store manager.
1083: * @param name The identifier name of the table.
1084: * @return The corresponding JDO table, or 'null'
1085: */
1086: public synchronized DatastoreClass getDatastoreClass(
1087: DatastoreIdentifier name) {
1088: Iterator iterator = storeDataMgr.getManagedStoreData()
1089: .iterator();
1090: while (iterator.hasNext()) {
1091: StoreData sd = (StoreData) iterator.next();
1092: if (sd instanceof TableStoreData) {
1093: TableStoreData tsd = (TableStoreData) sd;
1094: if (tsd.hasTable()
1095: && tsd.getDatastoreIdentifier().equals(name)) {
1096: return (DatastoreClass) tsd
1097: .getDatastoreContainerObject();
1098: }
1099: }
1100: }
1101: return null;
1102: }
1103:
1104: /**
1105: * Utility to navigate the inheritance hierarchy to find the base class that defines the primary keys
1106: * for this tree. This will either go up to the next class in the hierarchy that has a table
1107: * OR go up to the base class, whichever is first.
1108: * @param cmd AbstractClassMetaData for this class
1109: * @param clr The ClassLoaderResolver
1110: * @return The AbstractClassMetaData for the class defining the primary keys
1111: */
1112: public AbstractClassMetaData getClassWithPrimaryKeyForClass(
1113: AbstractClassMetaData cmd, ClassLoaderResolver clr) {
1114: if (cmd == null) {
1115: return null;
1116: }
1117:
1118: // Base class will have primary key fields
1119: if (cmd.getSuperAbstractClassMetaData() == null) {
1120: return cmd;
1121: }
1122: // Class has its own table so has the PK fields already
1123: else if (getDatastoreClass(cmd.getFullClassName(), clr) != null) {
1124: return cmd;
1125: }
1126:
1127: return getClassWithPrimaryKeyForClass(cmd
1128: .getSuperAbstractClassMetaData(), clr);
1129: }
1130:
1131: /**
1132: * Notifies this store manager that the main memory (RAM, heap) copy of the PC object of the supplied
1133: * StateManager may not be regarded as valid anymore.
1134: * (The most common case is that the state of the PC becomes HOLLOW).
1135: * This is especially important for object databases employing implicit storing
1136: * from the main memory to the database (like DB4O).
1137: * These databases may stop tracking the main memory copy and linking it with its on-disk copy,
1138: * thus releasing memory.
1139: * More importantly, these databases then know that the object should be reloaded when it
1140: * is (maybe even implicitly) accessed again.
1141: *
1142: * To be clear: There may be multiple copies of the data of one PC object (from the user perspective),
1143: * namely a copy in main memory (on the Java heap) and a copy in the database (usually on disk).
1144: * As there may be multiple copies, some of these copies may be outdated or invalid. In case such
1145: * a copy is to be accessed, its contents should not be used. Rather than that, the outdated copy should
1146: * be overwritten by an authorative copy.
1147: *
1148: * This method marks the main memory copy of the object (on the Java heap) to be outdated in that sense.
1149: */
1150: public void notifyObjectIsOutdated(StateManager sm) {
1151: }
1152:
1153: /**
1154: * Resolves an identifier macro. The public fields <var>clazz </var>,
1155: * <var>fieldName </var>, and <var>subfieldName </var> of the given macro
1156: * are taken as inputs, and the public <var>value </var> field is set to the
1157: * SQL identifier of the corresponding database table or column.
1158: * @param im The macro to resolve.
1159: * @param clr The ClassLoaderResolver
1160: */
1161: public abstract void resolveIdentifierMacro(
1162: MacroString.IdentifierMacro im, ClassLoaderResolver clr);
1163:
1164: public abstract Store getStore(ClassLoaderResolver clr,
1165: AbstractMemberMetaData fmd, Class type);
1166: }
|