001: /**********************************************************************
002: Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox;
018:
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.InvocationTargetException;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.Properties;
024: import java.util.ResourceBundle;
025: import java.util.TimeZone;
026:
027: import org.jpox.cache.Level2Cache;
028: import org.jpox.cache.NullLevel2Cache;
029: import org.jpox.exceptions.JPOXException;
030: import org.jpox.exceptions.JPOXUserException;
031: import org.jpox.plugin.ConfigurationElement;
032: import org.jpox.plugin.Extension;
033: import org.jpox.store.StoreManager;
034: import org.jpox.transaction.TransactionUtils;
035: import org.jpox.util.JPOXLogger;
036: import org.jpox.util.MultiMap;
037:
038: /**
039: * ObjectManagerFactory responsible for creation of ObjectManagers for persistence of objects to datastores.
040: * Will typically be either extended or utilised by PersistenceManagerFactory (JDO) or EntityManagerFactory (JPA).
041: *
042: * @version $Revision: 1.15 $
043: */
044: public class ObjectManagerFactoryImpl extends PersistenceConfiguration {
045: /** Version of JPOX being used. Read in at startup from properties. */
046: private static String jpoxVersion = null;
047:
048: /** Vendor of this version of JPOX. */
049: private static String jpoxVendor = null;
050:
051: /** The context that this ObjectManagerFactory uses. */
052: protected OMFContext omfContext;
053:
054: /** Level 2 Cache, caching across ObjectManagers. */
055: protected Level2Cache cache;
056:
057: /** Whether the ObjectManagerFactory is closed */
058: private boolean closed;
059:
060: /** Map of dynamic fetch groups, keyed by the name + class name. */
061: private MultiMap fetchGroupByNameClass;
062:
063: /**
064: * Constructor.
065: */
066: public ObjectManagerFactoryImpl() {
067: super ();
068: }
069:
070: /**
071: * Asserts that the factory is open.
072: * @throws JPOXUserException if it is already closed
073: */
074: protected void assertIsOpen() {
075: if (isClosed()) {
076: throw new JPOXUserException(LOCALISER.msg("008002"));
077: }
078: }
079:
080: /**
081: * Method to log the configuration of this factory.
082: */
083: protected void logConfiguration() {
084: // Log the Factory configuration
085: JPOXLogger.PERSISTENCE
086: .info("================= Persistence Configuration ===============");
087: JPOXLogger.PERSISTENCE.info(LOCALISER.msg("008000",
088: getVendorName(), getVersionNumber()));
089: JPOXLogger.PERSISTENCE.info(LOCALISER.msg("008001",
090: getConnectionURL(), getConnectionDriverName(),
091: getConnectionUserName()));
092: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
093: JPOXLogger.PERSISTENCE.debug("JDK : "
094: + System.getProperty("java.version") + " on "
095: + System.getProperty("os.name"));
096: JPOXLogger.PERSISTENCE.debug("Persistence API : "
097: + getOMFContext().getApi());
098: JPOXLogger.PERSISTENCE.debug("Plugin Registry : "
099: + getOMFContext().getPluginManager()
100: .getRegistryClassName());
101: if (getPersistenceUnitName() != null) {
102: JPOXLogger.PERSISTENCE.debug("Persistence-Unit : "
103: + getPersistenceUnitName());
104: }
105:
106: String timeZoneID = getServerTimeZoneID();
107: if (timeZoneID == null) {
108: timeZoneID = TimeZone.getDefault().getID();
109: }
110: JPOXLogger.PERSISTENCE
111: .debug("Standard Options : "
112: + (getMultithreaded() ? "multithreaded"
113: : "singlethreaded")
114: + (getRetainValues() ? ", retain-values"
115: : "")
116: + (getRestoreValues() ? ", restore-values"
117: : "")
118: + (getNontransactionalRead() ? ", nontransactional-read"
119: : "")
120: + (getNontransactionalWrite() ? ", nontransactional-write"
121: : "")
122: + (getIgnoreCache() ? ", ignoreCache" : "")
123: + ", serverTimeZone=" + timeZoneID);
124: JPOXLogger.PERSISTENCE
125: .debug("Persistence Options :"
126: + (getPersistenceByReachabilityAtCommit() ? " reachability-at-commit"
127: : "")
128: + (getDetachAllOnCommit() ? " detach-all-on-commit"
129: : "")
130: + (getDetachOnClose() ? " detach-on-close"
131: : "")
132: + (getManageRelationships() ? (getManageRelationshipsChecks() ? " managed-relations(checked)"
133: : "managed-relations(unchecked)")
134: : "") + " deletion-policy="
135: + getDeletionPolicy());
136: JPOXLogger.PERSISTENCE
137: .debug("Types : string-default-length="
138: + getStringDefaultLength());
139: JPOXLogger.PERSISTENCE
140: .debug("Transactions : type="
141: + getTransactionType()
142: + " mode="
143: + (getOptimistic() ? "optimistic"
144: : "datastore")
145: + " isolation="
146: + TransactionUtils
147: .getNameForTransactionIsolationLevel(getTransactionIsolation()));
148: JPOXLogger.PERSISTENCE
149: .debug("Identity Generation :"
150: + " txn-isolation="
151: + TransactionUtils
152: .getNameForTransactionIsolationLevel(getPoidTransactionIsolationLevel())
153: + " connection="
154: + (getPoidTransactionAttribute()
155: .equalsIgnoreCase("New") ? "New"
156: : "PM"));
157: JPOXLogger.PERSISTENCE
158: .debug("ClassLoading : "
159: + getClassLoaderResolverName()
160: + (getPrimaryClassLoader() != null ? ("primary=" + getPrimaryClassLoader())
161: : ""));
162: JPOXLogger.PERSISTENCE.debug("Cache : Level1 ("
163: + getCacheLevel1Type()
164: + ")"
165: + (getCacheLevel2() ? (", Level2 ("
166: + getCacheLevel2Type() + ")") : "")
167: + (getCacheCollections() ? ", Collections/Maps "
168: : ""));
169: }
170: JPOXLogger.PERSISTENCE
171: .info("===========================================================");
172: }
173:
174: /**
175: * Method to initialise the OMFContext.
176: * This should be performed after setting any persistence properties that affect the content
177: * of the OMFContext (e.g PluginRegistry, ClassLoaderResolver, etc).
178: */
179: protected void initialiseOMFContext() {
180: omfContext = new OMFContext(this );
181: }
182:
183: /**
184: * Method to initialise the StoreManager used by this factory.
185: * @param clr ClassLoaderResolver to use for class loading issues
186: */
187: protected void initialiseStoreManager(ClassLoaderResolver clr) {
188: StoreManager srm = null;
189: Extension[] exts = getOMFContext().getPluginManager()
190: .getExtensionPoint("org.jpox.store_manager")
191: .getExtensions();
192:
193: // Find the StoreManager using the persistence property if specified
194: String storeManagerType = getOMFContext()
195: .getPersistenceConfiguration().getStoreManagerType();
196: if (storeManagerType != null) {
197: for (int e = 0; srm == null && e < exts.length; e++) {
198: ConfigurationElement[] confElm = exts[e]
199: .getConfigurationElements();
200: for (int c = 0; srm == null && c < confElm.length; c++) {
201: String storeMgrClassName = confElm[c]
202: .getAttribute("class-name");
203: String key = confElm[c].getAttribute("key");
204: if (key.equalsIgnoreCase(storeManagerType)) {
205: Class[] ctrArgTypes = new Class[] {
206: ClassLoaderResolver.class,
207: ObjectManagerFactoryImpl.class };
208: Object[] ctrArgs = new Object[] { clr, this };
209: try {
210: srm = (StoreManager) getOMFContext()
211: .getPluginManager()
212: .createExecutableExtension(
213: "org.jpox.store_manager",
214: "key", storeManagerType,
215: "class-name", ctrArgTypes,
216: ctrArgs);
217: } catch (InvocationTargetException ex) {
218: Throwable t = ex.getTargetException();
219: if (t instanceof RuntimeException) {
220: throw (RuntimeException) t;
221: } else if (t instanceof Error) {
222: throw (Error) t;
223: } else {
224: throw new JPOXException(t.getMessage(),
225: t).setFatal();
226: }
227: } catch (Exception ex) {
228: throw new JPOXException(ex.getMessage(), ex)
229: .setFatal();
230: }
231: JPOXLogger.JDO.info(">> Found StoreManager "
232: + storeMgrClassName);
233: }
234: }
235: }
236: if (srm == null) {
237: // No StoreManager of the specified type exists, so check plugins in CLASSPATH
238: throw new JPOXUserException(LOCALISER.msg("008004",
239: storeManagerType)).setFatal();
240: }
241: }
242:
243: if (srm == null) {
244: // Try using the URL of the data source
245: String url = getConnectionURL();
246: if (url != null) {
247: int idx = url.indexOf(':');
248: if (idx > -1) {
249: url = url.substring(0, idx);
250: }
251: }
252:
253: for (int e = 0; srm == null && e < exts.length; e++) {
254: ConfigurationElement[] confElm = exts[e]
255: .getConfigurationElements();
256: for (int c = 0; srm == null && c < confElm.length; c++) {
257: String urlKey = confElm[c].getAttribute("url-key");
258: if (url == null || urlKey.equalsIgnoreCase(url)) {
259: // Either no URL, or url defined so take this StoreManager
260: Class[] ctrArgTypes = new Class[] {
261: ClassLoaderResolver.class,
262: ObjectManagerFactoryImpl.class };
263: Object[] ctrArgs = new Object[] { clr, this };
264: try {
265: srm = (StoreManager) getOMFContext()
266: .getPluginManager()
267: .createExecutableExtension(
268: "org.jpox.store_manager",
269: "url-key", url,
270: "class-name", ctrArgTypes,
271: ctrArgs);
272: } catch (InvocationTargetException ex) {
273: Throwable t = ex.getTargetException();
274: if (t instanceof RuntimeException) {
275: throw (RuntimeException) t;
276: } else if (t instanceof Error) {
277: throw (Error) t;
278: } else {
279: throw new JPOXException(t.getMessage(),
280: t).setFatal();
281: }
282: } catch (Exception ex) {
283: throw new JPOXException(ex.getMessage(), ex)
284: .setFatal();
285: }
286: }
287: }
288: }
289:
290: if (srm == null) {
291: throw new JPOXUserException(LOCALISER
292: .msg("008005", url)).setFatal();
293: }
294: }
295: }
296:
297: /**
298: * Method to initialise the L2 cache.
299: */
300: protected void initialiseLevel2Cache() {
301: if (getCacheLevel2()) {
302: // Find the L2 cache class name from its plugin name
303: String level2Type = getCacheLevel2Type();
304: String level2ClassName = getOMFContext().getPluginManager()
305: .getAttributeValueForExtension(
306: "org.jpox.cache_level2", "name",
307: level2Type, "class-name");
308: if (level2ClassName == null) {
309: // Plugin of this name not found
310: throw new JPOXUserException(LOCALISER.msg("004000",
311: level2Type)).setFatal();
312: }
313:
314: try {
315: // Create an instance of the L2 Cache
316: Properties cacheProps = new Properties();
317: if (getCacheLevel2CacheName() != null) {
318: cacheProps.put("CacheName",
319: getCacheLevel2CacheName());
320: }
321: if (getCacheLevel2ConfigurationFile() != null) {
322: cacheProps.put("ConfigurationFile",
323: getCacheLevel2ConfigurationFile());
324: }
325:
326: Class level2CacheClass = Class.forName(level2ClassName);
327: Class[] ctrArgsClasses = new Class[] { Properties.class };
328: Object[] ctrArgs = new Object[] { cacheProps };
329:
330: Constructor ctr = level2CacheClass
331: .getConstructor(ctrArgsClasses);
332: cache = (Level2Cache) ctr.newInstance(ctrArgs);
333: if (JPOXLogger.CACHE.isDebugEnabled()) {
334: JPOXLogger.CACHE.debug(LOCALISER.msg("004002",
335: level2ClassName));
336: }
337: } catch (Exception e) {
338: // Class name for this L2 cache plugin is not found!
339: throw new JPOXUserException(LOCALISER.msg("004001",
340: level2Type, level2ClassName), e).setFatal();
341: }
342: } else {
343: cache = new NullLevel2Cache();
344: }
345: }
346:
347: /**
348: * Close the ObjectManagerFactory.
349: * Cleans out all objects in the Level2 cache and closes the context, marking the factory as closed.
350: */
351: public synchronized void close() {
352: // Clear the Cache
353: if (getCacheLevel2() && cache != null) {
354: cache.clear();
355: JPOXLogger.CACHE.info(LOCALISER.msg("004009"));
356: }
357:
358: // Clear out all fetch groups
359: clearFetchGroups();
360:
361: if (omfContext != null) {
362: omfContext.close();
363: omfContext = null;
364: }
365: closed = true;
366: }
367:
368: /**
369: * Utility to return whether the factory is closed or not.
370: * @return Whether it is closed.
371: */
372: public boolean isClosed() {
373: return closed;
374: }
375:
376: /**
377: * Gets the context for this ObjectManagerFactory
378: * @return Returns the context.
379: */
380: public OMFContext getOMFContext() {
381: if (omfContext == null) {
382: // Construct the OMFContext when it is needed
383: initialiseOMFContext();
384: }
385: return omfContext;
386: }
387:
388: /**
389: * Accessor for the PersistenceConfiguration.
390: * @return Returns the persistence config.
391: */
392: public PersistenceConfiguration getPersistenceConfiguration() {
393: return this ;
394: }
395:
396: /**
397: * Accessor for the DataStore (level 2) Cache
398: * @return The datastore cache
399: * @since 1.1
400: */
401: public Level2Cache getLevel2Cache() {
402: return cache;
403: }
404:
405: /**
406: * Utility to get the version of JPOX.
407: * @return Version number for JPOX.
408: **/
409: public static String getVersionNumber() {
410: if (jpoxVersion != null) {
411: return jpoxVersion;
412: }
413:
414: String version = "Unknown";
415: try {
416: ResourceBundle bundle = ResourceBundle
417: .getBundle("org.jpox.JPOXVersion");
418: try {
419: version = bundle.getString("jpox.version");
420: } catch (Exception e1) {
421: }
422: } catch (Exception e) {
423: }
424:
425: return jpoxVersion = version;
426: }
427:
428: /**
429: * Utility to get the vendor of JPOX.
430: * @return Vendor name for JPOX.
431: **/
432: public static String getVendorName() {
433: if (jpoxVendor != null) {
434: return jpoxVendor;
435: }
436:
437: String vendor = "JPOX";
438: try {
439: ResourceBundle bundle = ResourceBundle
440: .getBundle("org.jpox.JPOXVersion");
441: try {
442: vendor = bundle.getString("jpox.vendor");
443: } catch (Exception e1) {
444: }
445: } catch (Exception e) {
446: }
447:
448: return jpoxVendor = vendor;
449: }
450:
451: // --------------------------- Fetch Groups ---------------------------------
452:
453: /**
454: * Method to create a (dynamic) fetch group.
455: * @param name Name of the group
456: * @param cls Class being represented.
457: */
458: public FetchGroup createFetchGroup(String name, Class cls) {
459: return new FetchGroupImpl(name, cls);
460: }
461:
462: /**
463: * Method to add a fetch group for use by this PMF.
464: * @param group The FetchGroup
465: */
466: public void addFetchGroup(FetchGroup group) {
467: if (fetchGroupByNameClass == null) {
468: fetchGroupByNameClass = new MultiMap();
469: }
470:
471: Collection coll = (Collection) fetchGroupByNameClass.get(group
472: .getName());
473: if (coll != null) {
474: // Check for existing entry for this name and class
475: Iterator iter = coll.iterator();
476: while (iter.hasNext()) {
477: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
478: if (grp.getName().equals(group.getName())
479: && grp.getClassName().equals(
480: group.getClassName())) {
481: // Already have a group for this name and class so replace it
482: grp.disconnectFromListeners(); // Remove the old group from use
483: iter.remove(); // Remove the old group
484: }
485: }
486: }
487:
488: // TODO Make a copy of the group
489: fetchGroupByNameClass.put(group.getName(), group);
490: }
491:
492: /**
493: * Accessor for a fetch group for the specified class.
494: * @param name Name of the group
495: * @param cls The class
496: * @return The FetchGroup
497: */
498: public FetchGroup getFetchGroup(String name, Class cls) {
499: if (fetchGroupByNameClass != null) {
500: Collection coll = (Collection) fetchGroupByNameClass
501: .get(name);
502: if (coll != null) {
503: Iterator iter = coll.iterator();
504: while (iter.hasNext()) {
505: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
506: if (grp.getFetchGroupClass() == cls) {
507: return grp;
508: }
509: }
510: }
511: }
512: return null;
513: }
514:
515: /**
516: * Accessor for the fetch groups for the specified name.
517: * @param name Name of the group
518: * @return The FetchGroup
519: */
520: public FetchGroup[] getFetchGroups(String name) {
521: if (fetchGroupByNameClass != null) {
522: Collection coll = (Collection) fetchGroupByNameClass
523: .get(name);
524: if (coll != null) {
525: return (FetchGroup[]) coll.toArray(new FetchGroup[coll
526: .size()]);
527: }
528: }
529: return null;
530: }
531:
532: /**
533: * Method to remove a dynamic FetchGroup from use by this PMF.
534: * @param name Name of the group
535: * @param cls The class
536: */
537: public void removeFetchGroup(String name, Class cls) {
538: if (fetchGroupByNameClass != null) {
539: Collection coll = (Collection) fetchGroupByNameClass
540: .get(name);
541: if (coll != null) {
542: Iterator iter = coll.iterator();
543: while (iter.hasNext()) {
544: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
545: if (grp.getFetchGroupClass() == cls) {
546: grp.disconnectFromListeners(); // Remove the group from use
547: fetchGroupByNameClass.remove(name, grp);
548: }
549: }
550: }
551: }
552: }
553:
554: /**
555: * Clear all dynamic FetchGroups from use.
556: */
557: public void clearFetchGroups() {
558: if (fetchGroupByNameClass != null) {
559: Collection fetchGroups = fetchGroupByNameClass.values();
560: Iterator iter = fetchGroups.iterator();
561: while (iter.hasNext()) {
562: FetchGroupImpl grp = (FetchGroupImpl) iter.next();
563: grp.disconnectFromListeners(); // Remove the group from use
564: }
565: fetchGroupByNameClass.clear();
566: }
567: }
568: }
|