001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.ejb.cmp3;
038:
039: import java.io.File;
040: import java.net.URL;
041: import java.util.Enumeration;
042: import java.util.Map;
043: import java.util.HashMap;
044: import javax.persistence.*;
045: import javax.persistence.spi.*;
046:
047: import oracle.toplink.essentials.config.TargetDatabase;
048: import oracle.toplink.essentials.config.TopLinkProperties;
049: import oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerFactoryImpl;
050: import oracle.toplink.essentials.internal.ejb.cmp3.JavaSECMPInitializer;
051: import oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl;
052: import oracle.toplink.essentials.internal.ejb.cmp3.PersistenceInitializationActivator;
053: import oracle.toplink.essentials.internal.sessions.AbstractSession;
054: import oracle.toplink.essentials.logging.SessionLog;
055: import oracle.toplink.essentials.threetier.ServerSession;
056:
057: import oracle.toplink.essentials.tools.schemaframework.SchemaManager;
058: import oracle.toplink.essentials.ejb.cmp3.persistence.SEPersistenceUnitInfo;
059: import oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException;
060: import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor;
061:
062: /**
063: * This is the TopLink EJB 3.0 provider
064: * The default constructor can be used to build the provider by reflection, after which it can
065: * be used to create EntityManagerFactories
066: */
067:
068: public class EntityManagerFactoryProvider implements
069: javax.persistence.spi.PersistenceProvider,
070: PersistenceInitializationActivator {
071:
072: // The following constants are used in persistence xml or in createSessionManagerFactory methods to specify properties.
073: // Many property declarations were moved to oracle.toplink.essentials.config.TopLinkProperties class.
074:
075: public static final String TOPLINK_ORM_THROW_EXCEPTIONS = "toplink.orm.throw.exceptions";
076: public static final String TOPLINK_VALIDATION_ONLY_PROPERTY = "toplink.validation-only";
077:
078: public static final String DDL_GENERATION = "toplink.ddl-generation";
079:
080: public static final String CREATE_ONLY = "create-tables";
081: public static final String DROP_AND_CREATE = "drop-and-create-tables";
082: public static final String NONE = "none";
083:
084: public static final String APP_LOCATION = "toplink.application-location";
085:
086: public static final String CREATE_JDBC_DDL_FILE = "toplink.create-ddl-jdbc-file-name";
087: public static final String DROP_JDBC_DDL_FILE = "toplink.drop-ddl-jdbc-file-name";
088:
089: public static final String DEFAULT_APP_LOCATION = "."
090: + File.separator;
091: public static final String DEFAULT_CREATE_JDBC_FILE_NAME = "createDDL.jdbc";
092: public static final String DEFAULT_DROP_JDBC_FILE_NAME = "dropDDL.jdbc";
093: public static final String JAVASE_DB_INTERACTION = "INTERACT_WITH_DB";
094:
095: public static final String DDL_GENERATION_MODE = "toplink.ddl-generation.output-mode";
096: public static final String DDL_SQL_SCRIPT_GENERATION = "sql-script";
097: public static final String DDL_DATABASE_GENERATION = "database";
098: public static final String DDL_BOTH_GENERATION = "both";
099: // This is the default for now to ensure we still play nicely with Glassfish.
100: public static final String DEFAULT_DDL_GENERATION_MODE = DDL_SQL_SCRIPT_GENERATION;
101:
102: protected static final HashMap<String, EntityManagerSetupImpl> emSetupImpls = new HashMap<String, EntityManagerSetupImpl>();
103: // TEMPORARY - WILL BE REMOVED.
104: // Used to warn users about deprecated property name and suggest the valid name.
105: // TEMPORARY the old property names will be translated to the new ones and processed.
106: protected static final String oldPropertyNames[][] = {
107: { TopLinkProperties.JDBC_WRITE_CONNECTIONS_MAX,
108: "toplink.max-write-connections" },
109: { TopLinkProperties.JDBC_WRITE_CONNECTIONS_MIN,
110: "toplink.min-write-connections" },
111: { TopLinkProperties.JDBC_READ_CONNECTIONS_MAX,
112: "toplink.max-read-connections" },
113: { TopLinkProperties.JDBC_READ_CONNECTIONS_MIN,
114: "toplink.min-read-connections" },
115: { TopLinkProperties.JDBC_BIND_PARAMETERS,
116: "toplink.bind-all-parameters" },
117: { TopLinkProperties.TARGET_DATABASE,
118: "toplink.platform.class.name" },
119: { TopLinkProperties.TARGET_SERVER,
120: "toplink.server.platform.class.name" },
121: { TopLinkProperties.CACHE_SIZE_DEFAULT,
122: "toplink.cache.default-size" } };
123:
124: /**
125: * A default constructor is required by all Providers accoring the the EJB 3.0 specification
126: */
127: public EntityManagerFactoryProvider() {
128: }
129:
130: /**
131: * Called by Persistence class when an EntityManagerFactory
132: * is to be created.
133: *
134: * @param emName The name of the persistence unit
135: * @param map A Map of properties for use by the
136: * persistence provider. These properties may be used to
137: * override the values of the corresponding elements in
138: * the persistence.xml file or specify values for
139: * properties not specified in the persistence.xml.
140: * @return EntityManagerFactory for the persistence unit,
141: * or null if the provider is not the right provider
142: */
143: public EntityManagerFactory createEntityManagerFactory(
144: String emName, Map properties) {
145: Map nonNullProperties = (properties == null) ? new HashMap()
146: : properties;
147: String name = emName;
148: if (name == null) {
149: name = "";
150: }
151:
152: JavaSECMPInitializer initializer = JavaSECMPInitializer
153: .getJavaSECMPInitializer();
154: EntityManagerSetupImpl emSetupImpl = null;
155: ClassLoader currentLoader = Thread.currentThread()
156: .getContextClassLoader();
157:
158: try {
159: Enumeration<URL> resources = currentLoader
160: .getResources("META-INF/persistence.xml");
161: boolean initialized = false;
162: while (resources.hasMoreElements()) {
163: URL url = PersistenceUnitProcessor
164: .computePURootURL(resources.nextElement());
165: String urlAndName = url + name;
166:
167: synchronized (EntityManagerFactoryProvider.emSetupImpls) {
168: emSetupImpl = EntityManagerFactoryProvider
169: .getEntityManagerSetupImpl(urlAndName);
170: if (emSetupImpl == null
171: || emSetupImpl.isUndeployed()) {
172: if (!initialized) {
173: initializer.initialize(nonNullProperties,
174: this );
175: initialized = true;
176: }
177:
178: emSetupImpl = EntityManagerFactoryProvider
179: .getEntityManagerSetupImpl(urlAndName);
180: }
181: }
182:
183: // We found a match, stop looking.
184: if (emSetupImpl != null) {
185: break;
186: }
187: }
188: } catch (Exception e) {
189: throw PersistenceUnitLoadingException
190: .exceptionSearchingForPersistenceResources(
191: currentLoader, e);
192: }
193:
194: //gf bug 854 Returns null if EntityManagerSetupImpl for the name doesn't exist (e.g. a non-existant PU)
195: if (emSetupImpl == null) {
196: return null;
197: }
198:
199: if (!isPersistenceProviderSupported(emSetupImpl
200: .getPersistenceUnitInfo()
201: .getPersistenceProviderClassName())) {
202: return null;
203: }
204:
205: // synchronized to prevent overriding of the class loader
206: // and also calls to predeploy and undeploy by other threads -
207: // the latter may alter result of shouldRedeploy method.
208: synchronized (emSetupImpl) {
209: if (emSetupImpl.shouldRedeploy()) {
210: SEPersistenceUnitInfo persistenceInfo = (SEPersistenceUnitInfo) emSetupImpl
211: .getPersistenceUnitInfo();
212: persistenceInfo.setClassLoader(JavaSECMPInitializer
213: .getMainLoader());
214: // if we are in undeployed state, this is a redeploy of an initial deployment that worked
215: // so if weaving were going to occur, it already occured. Therefore we can use the main classloader
216: if (emSetupImpl.isUndeployed()) {
217: persistenceInfo
218: .setNewTempClassLoader(JavaSECMPInitializer
219: .getMainLoader());
220: }
221: }
222: // call predeploy
223: // this will just increment the factory count since we should already be deployed
224: emSetupImpl.predeploy(emSetupImpl.getPersistenceUnitInfo(),
225: nonNullProperties);
226: }
227:
228: EntityManagerFactoryImpl factory = null;
229: try {
230: factory = new EntityManagerFactoryImpl(emSetupImpl,
231: nonNullProperties);
232:
233: // This code has been added to allow validation to occur without actually calling createEntityManager
234: if (emSetupImpl
235: .shouldGetSessionOnCreateFactory(nonNullProperties)) {
236: factory.getServerSession();
237: }
238: return factory;
239: } catch (RuntimeException ex) {
240: if (factory != null) {
241: factory.close();
242: } else {
243: emSetupImpl.undeploy();
244: }
245: throw ex;
246: }
247: }
248:
249: /**
250: * Called by the container when an EntityManagerFactory
251: * is to be created.
252: *
253: * @param info Metadata for use by the persistence provider
254: * @return EntityManagerFactory for the persistence unit
255: * specified by the metadata
256: * @param map A Map of integration-level properties for use
257: * by the persistence provider.
258: */
259: public EntityManagerFactory createContainerEntityManagerFactory(
260: PersistenceUnitInfo info, Map properties) {
261: Map nonNullProperties = (properties == null) ? new HashMap()
262: : properties;
263:
264: EntityManagerSetupImpl emSetupImpl = null;
265: synchronized (EntityManagerFactoryProvider.emSetupImpls) {
266: String urlAndName = info.getPersistenceUnitRootUrl()
267: + info.getPersistenceUnitName();
268: emSetupImpl = EntityManagerFactoryProvider
269: .getEntityManagerSetupImpl(urlAndName);
270: if (emSetupImpl == null) {
271: emSetupImpl = new EntityManagerSetupImpl();
272: emSetupImpl.setIsInContainerMode(true);
273: EntityManagerFactoryProvider.addEntityManagerSetupImpl(
274: urlAndName, emSetupImpl);
275: }
276: }
277:
278: ClassTransformer transformer = null;
279: if (!emSetupImpl.isDeployed()) {
280: transformer = emSetupImpl
281: .predeploy(info, nonNullProperties);
282: }
283: if (transformer != null) {
284: info.addTransformer(transformer);
285: }
286: // When EntityManagerFactory is created, the session is only partially created
287: // When the factory is actually accessed, the emSetupImpl will be used to complete the session construction
288: EntityManagerFactoryImpl factory = new EntityManagerFactoryImpl(
289: emSetupImpl, nonNullProperties);
290:
291: // This code has been added to allow validation to occur without actually calling createEntityManager
292: if (emSetupImpl
293: .shouldGetSessionOnCreateFactory(nonNullProperties)) {
294: factory.getServerSession();
295: }
296: return factory;
297: }
298:
299: /**
300: * Returns whether the given persistence provider class is supported by this implementation
301: * @param providerClassName
302: * @return
303: */
304: public boolean isPersistenceProviderSupported(
305: String providerClassName) {
306: return (providerClassName == null)
307: || providerClassName.equals("")
308: || providerClassName
309: .equals(EntityManagerFactoryProvider.class
310: .getName());
311: }
312:
313: /**
314: * Logs in to given session. If user has not specified <codeTARGET_DATABASE</code>
315: * the plaform would be auto detected
316: * @param session The session to login to.
317: * @param properties User specified properties for the persistence unit
318: */
319: public static void login(ServerSession session, Map properties) {
320: String toplinkPlatform = (String) properties
321: .get(TopLinkProperties.TARGET_DATABASE);
322: if (!session.isConnected()) {
323: if (toplinkPlatform == null
324: || toplinkPlatform.equals(TargetDatabase.Auto)) {
325: // if user has not specified a database platform, try to detect
326: session.loginAndDetectDatasource();
327: } else {
328: session.login();
329: }
330: }
331: }
332:
333: public static void generateDDLFiles(ServerSession session,
334: Map props, boolean inSEmode) {
335: boolean createTables = false, shouldDropFirst = false;
336: String appLocation;
337: String createDDLJdbc;
338: String dropDDLJdbc;
339: String ddlGeneration = NONE;
340:
341: if (null == props) {
342: return;
343: }
344:
345: ddlGeneration = (String) getConfigPropertyAsString(
346: DDL_GENERATION, props, NONE);
347: ddlGeneration = ddlGeneration.toLowerCase();
348: if (ddlGeneration.equals(NONE)) {
349: return;
350: }
351:
352: if (ddlGeneration.equals(CREATE_ONLY)
353: || ddlGeneration.equals(DROP_AND_CREATE)) {
354: createTables = true;
355: if (ddlGeneration.equals(DROP_AND_CREATE)) {
356: shouldDropFirst = true;
357: }
358: }
359:
360: if (createTables) {
361: String ddlGenerationMode = (String) getConfigPropertyAsString(
362: DDL_GENERATION_MODE, props,
363: DEFAULT_DDL_GENERATION_MODE);
364: // Optimize for cases where the value is explicitly set to NONE
365: if (ddlGenerationMode.equals(NONE)) {
366: return;
367: }
368:
369: appLocation = (String) getConfigPropertyAsString(
370: APP_LOCATION, props, DEFAULT_APP_LOCATION);
371: createDDLJdbc = (String) getConfigPropertyAsString(
372: CREATE_JDBC_DDL_FILE, props,
373: DEFAULT_CREATE_JDBC_FILE_NAME);
374: dropDDLJdbc = (String) getConfigPropertyAsString(
375: DROP_JDBC_DDL_FILE, props,
376: DEFAULT_DROP_JDBC_FILE_NAME);
377:
378: SchemaManager mgr = new SchemaManager(session);
379:
380: // The inSEmode checks here are only temporary to ensure we still
381: // play nicely with Glassfish.
382: if (ddlGenerationMode.equals(DDL_DATABASE_GENERATION)
383: || inSEmode) {
384: runInSEMode(mgr, shouldDropFirst);
385:
386: if (inSEmode) {
387: writeDDLsToFiles(mgr, appLocation, createDDLJdbc,
388: dropDDLJdbc);
389: }
390: } else if (ddlGenerationMode
391: .equals(DDL_SQL_SCRIPT_GENERATION)) {
392: writeDDLsToFiles(mgr, appLocation, createDDLJdbc,
393: dropDDLJdbc);
394: } else if (ddlGenerationMode.equals(DDL_BOTH_GENERATION)) {
395: runInSEMode(mgr, shouldDropFirst);
396: writeDDLsToFiles(mgr, appLocation, createDDLJdbc,
397: dropDDLJdbc);
398: }
399: }
400: }
401:
402: public static void runInSEMode(SchemaManager mgr,
403: boolean shouldDropFirst) {
404: String str = getConfigPropertyAsString(JAVASE_DB_INTERACTION,
405: null, "true");
406: boolean interactWithDB = Boolean.valueOf(str.toLowerCase())
407: .booleanValue();
408: if (!interactWithDB) {
409: return;
410: }
411: createOrReplaceDefaultTables(mgr, shouldDropFirst);
412: }
413:
414: /**
415: * Check the provided map for an object with the given key. If that object is not available, check the
416: * System properties. If it is not available from either location, return the default value.
417: * @param propertyKey
418: * @param map
419: * @param defaultValue
420: * @return
421: */
422: public static String getConfigPropertyAsString(String propertyKey,
423: Map overrides, String defaultValue) {
424: String value = getConfigPropertyAsString(propertyKey, overrides);
425: if (value == null) {
426: value = defaultValue;
427: }
428: return value;
429: }
430:
431: public static String getConfigPropertyAsString(String propertyKey,
432: Map overrides) {
433: String value = null;
434: if (overrides != null) {
435: value = (String) overrides.get(propertyKey);
436: }
437: if (value == null) {
438: value = System.getProperty(propertyKey);
439: }
440:
441: return value;
442: }
443:
444: public static String getConfigPropertyAsStringLogDebug(
445: String propertyKey, Map overrides, String defaultValue,
446: AbstractSession session) {
447: String value = getConfigPropertyAsStringLogDebug(propertyKey,
448: overrides, session);
449: if (value == null) {
450: value = defaultValue;
451: session.log(SessionLog.FINEST, SessionLog.PROPERTIES,
452: "property_value_default", new Object[] {
453: propertyKey, value });
454: }
455: return value;
456: }
457:
458: public static String getConfigPropertyAsStringLogDebug(
459: String propertyKey, Map overrides, AbstractSession session) {
460: String value = null;
461: if (overrides != null) {
462: value = (String) overrides.get(propertyKey);
463: }
464: if (value == null) {
465: value = System.getProperty(propertyKey);
466: }
467: if (value != null && session != null) {
468: String overrideValue = TopLinkProperties
469: .getOverriddenLogStringForProperty(propertyKey);
470: ;
471: String logValue = (overrideValue == null) ? value
472: : overrideValue;
473: session.log(SessionLog.FINEST, SessionLog.PROPERTIES,
474: "property_value_specified", new Object[] {
475: propertyKey, logValue });
476: }
477:
478: return value;
479: }
480:
481: public static Object getConfigPropertyLogDebug(String propertyKey,
482: Map overrides, AbstractSession session) {
483: Object value = null;
484: if (overrides != null) {
485: value = overrides.get(propertyKey);
486: }
487: if (value == null) {
488: value = System.getProperty(propertyKey);
489: }
490: if (value != null && session != null) {
491: String overrideValue = TopLinkProperties
492: .getOverriddenLogStringForProperty(propertyKey);
493: ;
494: Object logValue = (overrideValue == null) ? value
495: : overrideValue;
496: session.log(SessionLog.FINEST, SessionLog.PROPERTIES,
497: "property_value_specified", new Object[] {
498: propertyKey, logValue });
499: }
500:
501: return value;
502: }
503:
504: public static void createOrReplaceDefaultTables(SchemaManager mgr,
505: boolean shouldDropFirst) {
506: if (shouldDropFirst) {
507: mgr.replaceDefaultTables(true);
508: } else {
509: mgr.createDefaultTables();
510: }
511: }
512:
513: public static void writeDDLsToFiles(SchemaManager mgr,
514: String appLocation, String createDDLJdbc, String dropDDLJdbc) {
515: // Ensure that the appLocation string ends with File.seperator
516: appLocation = addFileSeperator(appLocation);
517: if (null != createDDLJdbc) {
518: String createJdbcFileName = appLocation + createDDLJdbc;
519: mgr.outputCreateDDLToFile(createJdbcFileName);
520: }
521:
522: if (null != dropDDLJdbc) {
523: String dropJdbcFileName = appLocation + dropDDLJdbc;
524: mgr.outputDropDDLToFile(dropJdbcFileName);
525: }
526:
527: mgr.setCreateSQLFiles(false);
528: // When running in the application server environment always ensure that
529: // we write out both the drop and create table files.
530: createOrReplaceDefaultTables(mgr, true);
531: mgr.closeDDLWriter();
532: }
533:
534: public static String addFileSeperator(String appLocation) {
535: int strLength = appLocation.length();
536: if (appLocation.substring(strLength - 1, strLength).equals(
537: File.separator)) {
538: return appLocation;
539: } else {
540: return appLocation + File.separator;
541: }
542: }
543:
544: /**
545: * Merge the properties from the source object into the target object. If the property
546: * exists in both objects, use the one from the target
547: * @param target
548: * @param source
549: * @return the target object
550: */
551: public static Map mergeMaps(Map target, Map source) {
552: Map map = new HashMap();
553: if (source != null) {
554: map.putAll(source);
555: }
556:
557: if (target != null) {
558: map.putAll(target);
559: }
560: return map;
561: }
562:
563: /**
564: * This is a TEMPORARY method that will be removed.
565: * DON'T USE THIS METHOD - for internal use only.
566: * @param Map m
567: * @param AbstractSession session
568: */
569: public static void translateOldProperties(Map m,
570: AbstractSession session) {
571: for (int i = 0; i < oldPropertyNames.length; i++) {
572: Object value = getConfigPropertyAsString(
573: oldPropertyNames[i][1], m);
574: if (value != null) {
575: if (session != null) {
576: session.log(SessionLog.INFO,
577: SessionLog.TRANSACTION,
578: "deprecated_property", oldPropertyNames[i]);
579: }
580: m.put(oldPropertyNames[i][0], value);
581: }
582: }
583: }
584:
585: public static void warnOldProperties(Map m, AbstractSession session) {
586: for (int i = 0; i < oldPropertyNames.length; i++) {
587: Object value = m.get(oldPropertyNames[i][1]);
588: if (value != null) {
589: session.log(SessionLog.INFO, SessionLog.TRANSACTION,
590: "deprecated_property", oldPropertyNames[i]);
591: }
592: }
593: }
594:
595: /**
596: * Return the setup class for a given entity manager name
597: * @param emName
598: */
599: public static EntityManagerSetupImpl getEntityManagerSetupImpl(
600: String emName) {
601: if (emName == null) {
602: return (EntityManagerSetupImpl) emSetupImpls.get("");
603: }
604: return (EntityManagerSetupImpl) emSetupImpls.get(emName);
605: }
606:
607: /**
608: * Add an EntityManagerSetupImpl to the cached list
609: * These are used to ensure all persistence units that are the same get the same underlying session
610: * @param name
611: * @param setup
612: */
613: public static void addEntityManagerSetupImpl(String name,
614: EntityManagerSetupImpl setup) {
615: if (name == null) {
616: emSetupImpls.put("", setup);
617: }
618: emSetupImpls.put(name, setup);
619: }
620: }
|