001: /**********************************************************************
002: Copyright (c) 2007 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.jdo;
018:
019: import java.io.ByteArrayOutputStream;
020: import java.io.IOException;
021: import java.io.ObjectOutputStream;
022: import java.lang.reflect.Method;
023: import java.util.Enumeration;
024: import java.util.HashMap;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.Properties;
029:
030: import javax.jdo.JDOUserException;
031: import javax.jdo.PersistenceManager;
032: import javax.jdo.PersistenceManagerFactory;
033: import javax.jdo.listener.InstanceLifecycleListener;
034: import javax.naming.Context;
035: import javax.naming.Name;
036: import javax.naming.Reference;
037: import javax.naming.Referenceable;
038: import javax.naming.StringRefAddr;
039: import javax.naming.spi.ObjectFactory;
040:
041: import org.jpox.ClassLoaderResolver;
042: import org.jpox.ConnectionManagerImpl;
043: import org.jpox.PersistenceConfiguration;
044: import org.jpox.api.ApiAdapterFactory;
045: import org.jpox.exceptions.ClassNotResolvedException;
046: import org.jpox.exceptions.JPOXException;
047: import org.jpox.jdo.JDOClassNameConstants;
048: import org.jpox.jdo.JDOPersistenceManager;
049: import org.jpox.metadata.PersistenceUnitMetaData;
050: import org.jpox.metadata.TransactionType;
051: import org.jpox.util.ClassUtils;
052: import org.jpox.util.JPOXLogger;
053: import org.jpox.util.Localiser;
054: import org.jpox.util.StringUtils;
055:
056: /**
057: * Implementation of a JDO PersistenceManagerFactory, used to obtain
058: * {@link javax.jdo.PersistenceManager} instances.
059: *
060: * @version $Revision: 1.1 $
061: */
062: public class JDOPersistenceManagerFactory extends
063: AbstractPersistenceManagerFactory implements
064: PersistenceManagerFactory, ObjectFactory, Referenceable {
065: /** Localisation utility for output messages */
066: protected static final Localiser LOCALISER = Localiser.getInstance(
067: "org.jpox.jdo.Localisation",
068: JDOPersistenceManagerFactory.class.getClassLoader());
069:
070: /**
071: * Return a new PersistenceManagerFactoryImpl with options set according to the given Properties.
072: * This method exists for JDO1 compliance whereas in JDO2 the method takes a Map.
073: * @param overridingProps The Properties to initialize the PersistenceManagerFactory with.
074: * @return A PersistenceManagerFactoryImpl with options set according to the given Properties.
075: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
076: */
077: public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(
078: Properties overridingProps) {
079: // Extract the properties into a Map allowing for a Properties object being used
080: Map overridingMap = new HashMap();
081: for (Enumeration e = overridingProps.propertyNames(); e
082: .hasMoreElements();) {
083: // Make sure we handle default properties too (SUN Properties class oddness)
084: String param = (String) e.nextElement();
085: overridingMap
086: .put(param, overridingProps.getProperty(param));
087: }
088:
089: // Create the PMF
090: final JDOPersistenceManagerFactory pmf = createInstance();
091: setPMFOptions(pmf, overridingMap);
092:
093: // Freeze the PMF config now that we are handing out PM's : see JDO 1.0.1 [11.7]
094: pmf.freezeConfiguration();
095:
096: return pmf;
097: }
098:
099: /**
100: * Return a new PersistenceManagerFactoryImpl with options set according to the given Properties.
101: * @param overridingProps The Map of properties to initialize the PersistenceManagerFactory with.
102: * @return A PersistenceManagerFactoryImpl with options set according to the given Properties.
103: * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
104: */
105: public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(
106: Map overridingProps) {
107: // Extract the properties into a Map allowing for a Properties object being used
108: Map overridingMap = null;
109: if (overridingProps instanceof Properties) {
110: // Make sure we handle default properties too (SUN Properties class oddness)
111: overridingMap = new HashMap();
112: for (Enumeration e = ((Properties) overridingProps)
113: .propertyNames(); e.hasMoreElements();) {
114: String param = (String) e.nextElement();
115: overridingMap.put(param, ((Properties) overridingProps)
116: .getProperty(param));
117: }
118: } else {
119: overridingMap = overridingProps;
120: }
121:
122: // Create the PMF
123: final JDOPersistenceManagerFactory pmf = createInstance();
124: setPMFOptions(pmf, overridingMap);
125:
126: // Freeze the PMF config now that we are handing out PM's : see JDO 1.0.1 [11.7]
127: pmf.freezeConfiguration();
128:
129: return pmf;
130: }
131:
132: /**
133: * Return a new PersistenceManagerFactoryImpl with options set according to the given properties and
134: * given overrides.
135: * @param overrides Map of properties to override the supplied props (if any)
136: * @param props Map of properties to initialise the PMF with
137: * @return A PersistenceManagerFactoryImpl with options set according to the given Properties
138: */
139: public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(
140: Map overrides, Map props) {
141: // Extract the props into a Map allowing for a Properties object being used
142: Map propsMap = null;
143: if (props instanceof Properties) {
144: // Make sure we handle default properties too (SUN Properties class oddness)
145: propsMap = new HashMap();
146: for (Enumeration e = ((Properties) props).propertyNames(); e
147: .hasMoreElements();) {
148: String param = (String) e.nextElement();
149: propsMap.put(param, ((Properties) props)
150: .getProperty(param));
151: }
152: } else {
153: propsMap = props;
154: }
155:
156: // Extract the overrides into a Map allowing for a Properties object being used
157: Map overridesMap = null;
158: if (overrides instanceof Properties) {
159: // Make sure we handle default properties too (SUN Properties class oddness)
160: overridesMap = new HashMap();
161: for (Enumeration e = ((Properties) overrides)
162: .propertyNames(); e.hasMoreElements();) {
163: String param = (String) e.nextElement();
164: overridesMap.put(param, ((Properties) overrides)
165: .getProperty(param));
166: }
167: } else {
168: overridesMap = overrides;
169: }
170:
171: // Create the PMF
172: final JDOPersistenceManagerFactory pmf = createInstance();
173:
174: // Set the properties of the PMF, taking propsMap+overridesMap
175: Map overallMap = null;
176: if (propsMap != null) {
177: overallMap = new HashMap(propsMap);
178: } else {
179: overallMap = new HashMap();
180: }
181: if (overridesMap != null) {
182: overallMap.putAll(overridesMap);
183: }
184: setPMFOptions(pmf, overallMap);
185:
186: // Process any listener properties
187: processLifecycleListenersInProperties(pmf, overallMap);
188:
189: // Freeze the PMF config now that we are handing out PM's : see JDO 1.0.1 [11.7]
190: pmf.freezeConfiguration();
191:
192: return pmf;
193: }
194:
195: /**
196: * Method to process any lifecycle listeners specified in the properties.
197: * @param pmf PMF being used
198: * @param props The properties to process for lifecycle listener specifications
199: */
200: protected static void processLifecycleListenersInProperties(
201: JDOPersistenceManagerFactory pmf, Map props) {
202: Iterator propsIter = props.keySet().iterator();
203: while (propsIter.hasNext()) {
204: String key = (String) propsIter.next();
205: if (key
206: .startsWith("javax.jdo.listener.InstanceLifecycleListener")) {
207: String listenerClsName = key.substring(45);
208: String listenerClasses = (String) props.get(key);
209: ClassLoaderResolver clr = pmf.getOMFContext()
210: .getClassLoaderResolver(null);
211: Class listenerCls = null;
212: try {
213: listenerCls = clr.classForName(listenerClsName);
214: } catch (ClassNotResolvedException cnre) {
215: throw new JDOUserException(LOCALISER.msg("012022",
216: listenerClsName));
217: }
218:
219: InstanceLifecycleListener listener = null;
220:
221: // Find method getInstance()
222: Method method = ClassUtils.getMethodForClass(
223: listenerCls, "getInstance", null);
224: if (method != null) {
225: // Create instance via getInstance()
226: try {
227: listener = (InstanceLifecycleListener) method
228: .invoke(null, null);
229: } catch (Exception e) {
230: throw new JDOUserException(LOCALISER.msg(
231: "012021", listenerClsName), e);
232: }
233: } else {
234: // Try default constructor
235: try {
236: listener = (InstanceLifecycleListener) listenerCls
237: .newInstance();
238: } catch (Exception e) {
239: throw new JDOUserException(LOCALISER.msg(
240: "012020", listenerClsName), e);
241: }
242: }
243:
244: Class[] classes = null;
245: if (!StringUtils.isWhitespace(listenerClasses)) {
246: String[] classNames = StringUtils.split(
247: listenerClasses, ",");
248: classes = new Class[classNames.length];
249: for (int i = 0; i < classNames.length; i++) {
250: classes[i] = clr.classForName(classNames[i]);
251: }
252: }
253:
254: pmf.addInstanceLifecycleListener(listener, classes);
255: }
256: }
257: }
258:
259: /**
260: * Convenience method to set the API and properties that a PMF will use.
261: * @param pmf The PMF to set the API/options for
262: * @param overridingProps The overriding properties supplied by the user
263: */
264: protected static void setPMFOptions(
265: JDOPersistenceManagerFactory pmf, Map overridingProps) {
266: // Apply any PMF properties that affect startup (before creating OMFContext)
267: if (overridingProps != null) {
268: Map startupProps = null;
269: if (overridingProps
270: .containsKey(PersistenceConfiguration.PLUGIN_REGISTRY_CLASS_NAME)) {
271: if (startupProps == null) {
272: startupProps = new HashMap();
273: }
274: startupProps
275: .put(
276: PersistenceConfiguration.PLUGIN_REGISTRY_CLASS_NAME,
277: overridingProps
278: .get(PersistenceConfiguration.PLUGIN_REGISTRY_CLASS_NAME));
279: }
280: if (overridingProps
281: .containsKey(PersistenceConfiguration.PLUGIN_REGISTRY_BUNDLE_CHECK)) {
282: if (startupProps == null) {
283: startupProps = new HashMap();
284: }
285: startupProps
286: .put(
287: PersistenceConfiguration.PLUGIN_REGISTRY_BUNDLE_CHECK,
288: overridingProps
289: .get(PersistenceConfiguration.PLUGIN_REGISTRY_BUNDLE_CHECK));
290: }
291: if (overridingProps
292: .containsKey(PersistenceConfiguration.CLASS_LOADER_RESOLVER_NAME_PROPERTY)) {
293: if (startupProps == null) {
294: startupProps = new HashMap();
295: }
296: startupProps
297: .put(
298: PersistenceConfiguration.CLASS_LOADER_RESOLVER_NAME_PROPERTY,
299: overridingProps
300: .get(PersistenceConfiguration.CLASS_LOADER_RESOLVER_NAME_PROPERTY));
301: }
302: if (startupProps != null) {
303: pmf.setOptions(startupProps);
304: }
305: }
306:
307: // Initialise the OMFContext and the ConnectionManager for J2SE
308: pmf.initialiseOMFContext();
309: pmf.getOMFContext().setConnectionManager(
310: new ConnectionManagerImpl(pmf.getOMFContext()));
311:
312: // Generate the properties to apply to the PMF
313: Map pmfProps = new HashMap();
314:
315: // Determine the API to be used by the PMF
316: String api = "JDO"; // Default to JDO unless specified
317: if (overridingProps != null
318: && overridingProps
319: .get(PersistenceConfiguration.PERSISTENCE_API_NAME) != null) {
320: api = (String) overridingProps
321: .get(PersistenceConfiguration.PERSISTENCE_API_NAME);
322: }
323: pmfProps.putAll(ApiAdapterFactory.getInstance().getApiAdapter(
324: api).getDefaultFactoryProperties());
325: pmf.getOMFContext().setApi(api); // Set the API used by the context
326:
327: // J2SE : Use "ResourceLocal" transactions by default
328: pmfProps.put(
329: PersistenceConfiguration.JDO_TRANSACTION_TYPE_PROPERTY,
330: TransactionType.RESOURCE_LOCAL.toString());
331:
332: PersistenceUnitMetaData pumd = null;
333: if (overridingProps != null) {
334: String persistenceUnitName = (String) overridingProps
335: .get(PersistenceConfiguration.JDO_PERSISTENCE_UNIT_NAME_PROPERTY);
336: if (persistenceUnitName != null) {
337: // Specified to use a "persistence-unit", so take any props defined for the unit
338: try {
339: pumd = pmf.getOMFContext().getMetaDataManager()
340: .getMetaDataForPersistenceUnit(
341: persistenceUnitName);
342: if (pumd != null) {
343: // Add the properties for the unit
344: if (pumd.getProperties() != null) {
345: pmfProps.putAll(pumd.getProperties());
346: }
347: } else {
348: throw new JDOUserException(LOCALISER.msg(
349: "012004", persistenceUnitName));
350: }
351:
352: pumd.clearJarFiles(); // Dont use JARs when in J2SE
353: } catch (JPOXException jpe) {
354: throw new JDOUserException(LOCALISER.msg("012005",
355: persistenceUnitName));
356: }
357: }
358: }
359:
360: // Append on any user properties
361: if (overridingProps != null) {
362: pmfProps.putAll(overridingProps);
363: }
364:
365: // Apply the properties to the PMF
366: pmf.setOptions(pmfProps);
367:
368: if (pumd != null) {
369: // Initialise the MetaDataManager with all files/classes for this persistence-unit
370: // This is done now that all PMF properties are set (including the persistence-unit props)
371: pmf.getOMFContext().getMetaDataManager().initialise(pumd,
372: pmf.getOMFContext().getClassLoaderResolver(null));
373: }
374: }
375:
376: /**
377: * Constructs a new PersistenceManagerFactoryImpl.
378: */
379: public JDOPersistenceManagerFactory() {
380: }
381:
382: /**
383: * Convenience method to create a new PMF of this type.
384: * TODO When we remove org.jpox.PersistenceManagerFactoryImpl remove this too.
385: * @return The PMF
386: */
387: public static JDOPersistenceManagerFactory createInstance() {
388: return new JDOPersistenceManagerFactory();
389: }
390:
391: /**
392: * Freezes the current configuration.
393: * @throws JPOXException if the configuration was invalid or inconsistent in some way
394: */
395: protected void freezeConfiguration() {
396: if (configurable) {
397: if (omfContext == null) {
398: // User has instantiated a PMF via the default constructor and JavaBean setters,
399: // so make sure we have the OMFContext etc ready
400: initialiseOMFContext();
401: omfContext
402: .setConnectionManager(new ConnectionManagerImpl(
403: omfContext));
404: }
405: super .freezeConfiguration();
406: }
407: }
408:
409: /**
410: * Get an instance of <tt>PersistenceManager</tt> from this factory. The instance has default values for options.
411: * <p>After the first use of getPersistenceManager, no "set" methods will succeed.</p>
412: * @return a <tt>PersistenceManager</tt> instance with default options.
413: */
414: public synchronized PersistenceManager getPersistenceManager() {
415: // Just relay to other getPersistenceManager() method
416: return getPersistenceManager(this .getPersistenceConfiguration()
417: .getConnectionUserName(), this
418: .getPersistenceConfiguration().getConnectionPassword());
419: }
420:
421: /**
422: * Get an instance of <tt>PersistenceManager</tt> from this factory.
423: * The instance has default values for options. The parameters userid/password are used when obtaining
424: * datastore connections from the connection pool.
425: * <p>After the first use of getPersistenceManager, no "set" methods will succeed.</p>
426: * @param userName the user name for the connection
427: * @param password the password for the connection
428: * @return <tt>PersistenceManager</tt> instance with default options.
429: */
430: public synchronized PersistenceManager getPersistenceManager(
431: String userName, String password) {
432: assertIsOpen();
433:
434: // Freeze the PMF config now that we are handing out PM's
435: freezeConfiguration();
436:
437: PersistenceManager pm = new JDOPersistenceManager(this ,
438: userName, password);
439:
440: if (lifecycleListeners != null) {
441: // Add PMF lifecycle listeners to the PM
442: Iterator listenerIter = lifecycleListeners.iterator();
443: while (listenerIter.hasNext()) {
444: LifecycleListenerForClass listener = (LifecycleListenerForClass) listenerIter
445: .next();
446: pm.addInstanceLifecycleListener(listener.getListener(),
447: listener.getClasses());
448: }
449: }
450:
451: getPmCache().add(pm);
452:
453: return pm;
454: }
455:
456: /**
457: * Equality operator.
458: * @param obj Object to compare against
459: * @return Whether the objects are the same.
460: */
461: public synchronized boolean equals(Object obj) {
462: if (obj == this ) {
463: return true;
464: }
465:
466: if (!(obj instanceof JDOPersistenceManagerFactory)) {
467: return false;
468: }
469:
470: return super .equals(obj);
471: }
472:
473: /**
474: * Create a PMF using the (JNDI) location or reference information specified.
475: * @param obj The object
476: * @param name Name of the object relative to the context
477: * @param ctx The context
478: * @param env properties used for creating the object
479: * @return The PMF instance
480: * @throws Exception If an error occurs generating the referenced object
481: */
482: public Object getObjectInstance(Object obj, Name name, Context ctx,
483: Hashtable env) throws Exception {
484: JDOPersistenceManagerFactory pmf = null;
485: if (JPOXLogger.NAMING.isDebugEnabled()) {
486: JPOXLogger.NAMING
487: .debug("Creating PersistenceManagerFactory instance via JNDI with values "
488: + "[object] "
489: + (obj == null ? "" : obj.toString())
490: + " "
491: + "[name] "
492: + (name == null ? "" : name.toString())
493: + " "
494: + "[context] "
495: + (ctx == null ? "" : ctx.toString())
496: + " "
497: + "[env] "
498: + (env == null ? "" : env.toString()) + " ");
499: }
500:
501: if (obj instanceof Reference) {
502: Reference ref = (Reference) obj;
503: if (ref.getClassName().equals(
504: JDOClassNameConstants.JDOPersistenceManagerFactory)
505: || ref
506: .getClassName()
507: .equals(
508: JDOClassNameConstants.JAVAX_JDO_PersistenceManagerFactory)) {
509: // Extract the properties to use for PMF creation
510: Properties p = new Properties();
511: for (Enumeration e = ref.getAll(); e.hasMoreElements();) {
512: StringRefAddr sra = (StringRefAddr) e.nextElement();
513: p.setProperty(sra.getType(), (String) sra
514: .getContent());
515: }
516:
517: // Create the PMF
518: pmf = createInstance();
519: setPMFOptions(pmf, p);
520:
521: // Freeze the PMF config now that we are handing out PM's : see JDO 1.0.1 [11.7]
522: pmf.freezeConfiguration();
523:
524: if (JPOXLogger.NAMING.isDebugEnabled()) {
525: JPOXLogger.NAMING.debug(LOCALISER.msg("012006",
526: name.toString()));
527: }
528: } else {
529: JPOXLogger.NAMING
530: .warn(LOCALISER
531: .msg(
532: "012007",
533: ref.getClassName(),
534: JDOClassNameConstants.JDOPersistenceManagerFactory));
535: }
536: } else {
537: JPOXLogger.NAMING.warn(LOCALISER.msg("012008", (obj
538: .getClass().getName())));
539: }
540: return pmf;
541: }
542:
543: /**
544: * Retrieves the (JNDI) reference of this PMF object.
545: * @return The reference
546: */
547: public Reference getReference() {
548: Reference rc = null;
549: ByteArrayOutputStream baos = new ByteArrayOutputStream();
550:
551: try {
552: ObjectOutputStream oos = new ObjectOutputStream(baos);
553: oos.writeObject(this );
554: rc = new Reference(
555: JDOClassNameConstants.JAVAX_JDO_PersistenceManagerFactory,
556: JDOClassNameConstants.JDOPersistenceManagerFactory,
557: null);
558:
559: Map p = getOptions();
560: for (Iterator i = p.keySet().iterator(); i.hasNext();) {
561: String key = (String) i.next();
562: if (p.get(key) instanceof String) {
563: String value = (String) p.get(key);
564: rc.add(new StringRefAddr(key, value));
565: if (JPOXLogger.NAMING.isDebugEnabled()) {
566: JPOXLogger.NAMING.debug(LOCALISER.msg("012009",
567: key, value));
568: }
569: } else {
570: JPOXLogger.NAMING
571: .warn(LOCALISER.msg("012010", key));
572: }
573: }
574: if (JPOXLogger.NAMING.isDebugEnabled()) {
575: if (p.isEmpty()) {
576: JPOXLogger.NAMING.debug(LOCALISER.msg("012011"));
577: }
578: }
579: } catch (IOException ex) {
580: JPOXLogger.NAMING.error(ex.getMessage());
581: throw new JPOXException(ex.getMessage(), ex);
582: }
583: return rc;
584: }
585:
586: /**
587: * Accessor for the PersistenceManager proxy object
588: * @return The PMF proxy
589: */
590: public PersistenceManager getPersistenceManagerProxy() {
591: throw new UnsupportedOperationException(
592: "PMF.getPersistenceManagerProxy() not yet implemented");
593: }
594: }
|