/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * glassfish/bootstrap/legal/CDDLv1.0.txt or * https://glassfish.dev.java.net/public/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, * add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your * own identifying information: Portions Copyright [yyyy] * [name of copyright owner] */ // Copyright (c) 1998, 2005, Oracle. All rights reserved. package oracle.toplink.essentials.internal.ejb.cmp3; import java.util.*; import java.net.URL; import java.net.URLClassLoader; import java.lang.instrument.*; import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitInfo; import oracle.toplink.essentials.logging.AbstractSessionLog; import oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl; import oracle.toplink.essentials.threetier.ServerSession; import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor; import oracle.toplink.essentials.exceptions.*; import oracle.toplink.essentials.logging.SessionLog; import oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider; /** * INTERNAL: * * JavaSECMPInitializer is used to bootstrap the deployment of EntityBeans in EJB 3.0 * when deployed in a non-managed setting * * It is called internally by our Provider * * @see oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider */ public class JavaSECMPInitializer { // Config data passed in at initialization time // This is the list of Entities that shoudl be deployed protected Set classSet; // Used when byte code enhancing public static Instrumentation globalInstrumentation; // The internal loader is used by applications that do weaving to pre load classes // When this flag is set to false, we will not be able to weave. protected boolean shouldCreateInternalLoader = true; // The JavaSECMPInitializer - a singleton protected static JavaSECMPInitializer javaSECMPInitializer; // We create an EntityManagerSetupImpl for each persistence unit the following // two variables maintain a dictionary of both the EntityManagerSetupImpls and // their associated PersistenceUnitInfo objects protected HashMap emSetupImpls = null; protected HashMap emSetupPersistenceUnitInfos = null; protected ClassLoader sessionClassLoader = null; /** * INTERNAL: * Get the singleton entityContainer. * @return EnityContainer */ public static JavaSECMPInitializer getJavaSECMPInitializer(Map properties) { if (javaSECMPInitializer == null) { initializeFromMain(properties); } return javaSECMPInitializer; } /** * Return whether initialization has occured without actually triggering * initialization */ public static boolean isSingletonInitialized(){ return javaSECMPInitializer != null; } /** * Look in the System properties for a logging level property and return a integer * that can be used to set the logging level in TopLink * @return */ public static int getTopLinkLoggingLevel(){ String logLevel = System.getProperty(EntityManagerFactoryProvider.TOPLINK_LOGGING_LEVEL); return AbstractSessionLog.translateStringToLoggingLevel(logLevel); } /** * INTERNAL: * User should not instantiate JavaSECMPInitializer. */ protected JavaSECMPInitializer() { super(); emSetupImpls = new HashMap(); emSetupPersistenceUnitInfos = new HashMap(); } /** * INTERNAL * predeploy (with deploy) is one of the two steps required in deployment of entities * This method will prepare to call predeploy, call it and finally register the * transformer returned to be used for weaving. */ protected boolean callPredeploy(PersistenceUnitInfo persistenceUnitInfo, Map m) { // we will only attempt to deploy when TopLink is specified as the provider or the provider is unspecified String providerClassName = persistenceUnitInfo.getPersistenceProviderClassName(); if (providerClassName != null || providerClassName.equals("") || providerClassName.equals(EntityManagerFactoryProvider.class.getName())){ Set tempLoaderSet = PersistenceUnitProcessor.buildClassSet(persistenceUnitInfo); // Create the temp loader ClassLoader tempLoader = createTempLoader(tempLoaderSet); classSet = PersistenceUnitProcessor.buildPersistentClassSet(persistenceUnitInfo, tempLoader); // Make the callback AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_invoke_predeploy", persistenceUnitInfo.getPersistenceUnitName()); Set entityClasses = loadEntityClasses(classSet, tempLoader); //Bug#4452468 When globalInstrumentation is null, there is no weaving if (globalInstrumentation == null) { m.put(EntityManagerSetupImpl.NO_WEAVING, "true"); } EntityManagerSetupImpl emSetupImpl = new EntityManagerSetupImpl(); emSetupImpls.put(persistenceUnitInfo.getPersistenceUnitName(), emSetupImpl); emSetupPersistenceUnitInfos.put(persistenceUnitInfo.getPersistenceUnitName(), persistenceUnitInfo); ClassFileTransformer transformer = emSetupImpl.predeploy(entityClasses, tempLoader, persistenceUnitInfo); // If we got a transformer then register it if ((transformer != null) && (globalInstrumentation != null)) { AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_register_transformer", persistenceUnitInfo.getPersistenceUnitName()); globalInstrumentation.addTransformer(transformer); } else if (transformer == null) { AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_transformer_is_null"); } else if (globalInstrumentation == null) { AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_globalInstrumentation_is_null"); } return true; } return false; } /** * INTERNAL * deploy (with predeploy) is one of the two steps required in deployment of entities * This method will call deploy and return the ServerSession returned by deploy */ protected ServerSession callDeploy(String emName) { // Call with the classes loaded by the "real" loader AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_invoke_deploy", emName); ClassLoader loader = getMainLoader(); Collection entityClasses = loadEntityClasses(classSet, loader); ServerSession ss = ((EntityManagerSetupImpl)getEntityManagerSetupImpl(emName)).deploy(entityClasses, loader); AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_completed_deploy", emName); return ss; } /** * Create a temporary class loader that can be used to inspect classes and then * thrown away. This allows classes to be introspected prior to loading them * with application's main class loader enabling weaving. */ protected ClassLoader createTempLoader() { return createTempLoader(classSet); } /** * Create a temporary class loader that can be used to inspect classes and then * thrown away. This allows classes to be introspected prior to loading them * with application's main class loader enabling weaving. */ protected ClassLoader createTempLoader(Collection col) { return createTempLoader(col, true); } protected ClassLoader createTempLoader(Collection col, boolean shouldOverrideLoadClassForCollectionMembers) { if (!shouldCreateInternalLoader) { return Thread.currentThread().getContextClassLoader(); } // ClassLoader currentLoader = ClassLoader.getSystemClassLoader(); ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); if (!(currentLoader instanceof URLClassLoader)) { throw ValidationException.currentLoaderNotValid(currentLoader); } URL[] urlPath = ((URLClassLoader)currentLoader).getURLs(); ClassLoader tempLoader = new TempEntityLoader(urlPath, currentLoader, col, shouldOverrideLoadClassForCollectionMembers); AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_tempLoader_created", tempLoader); AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_shouldOverrideLoadClassForCollectionMembers", new Boolean(shouldOverrideLoadClassForCollectionMembers)); return tempLoader; } /** * INTERNAL: * Obtain the list of classes to configure * @return EnityContainerConfig */ public Set getClassSet() { return this.classSet; } /** * Return the setup class for a given entity manager name * @param emName */ public EntityManagerSetupImpl getEntityManagerSetupImpl(String emName){ if (emName == null){ return (EntityManagerSetupImpl)emSetupImpls.get(""); } return (EntityManagerSetupImpl)emSetupImpls.get(emName); } protected ClassLoader getMainLoader() { return Thread.currentThread().getContextClassLoader(); } /** * Get a ServerSession that was bootstraped by this JavaSECMPInitializer */ public ServerSession getServerSession(String emName, Map m) { PersistenceUnitInfo persistenceInfo = emSetupPersistenceUnitInfos.get(emName); if (persistenceInfo == null){ return null; } // build a map that contains the properties from the parameter 'm' and from the // properties stored in the persistenceInfo. Properties from the 'm' will win. // when there are conflicts Map configMap = new HashMap(); Object key = null; if (m != null) { Iterator mapKeys = m.keySet().iterator(); while (mapKeys.hasNext()) { key = mapKeys.next(); configMap.put(key, m.get(key)); } } if (persistenceInfo.getProperties() != null) { Iterator propertyKeys = persistenceInfo.getProperties().keySet().iterator(); while (propertyKeys.hasNext()) { key = propertyKeys.next(); if (configMap.get(key) == null) { configMap.put(key, persistenceInfo.getProperties().get(key)); } } } return getEntityManagerSetupImpl(emName).getServerSession(configMap, sessionClassLoader); } /** * Initialize one persistence unit. * Initialization is a two phase process. First the predeploy process builds the metadata * and creates any required transformers. * Second the deploy process creates a TopLink session based on that metadata. */ protected void initPersistenceUnits(URL url, Map m) { Iterator persistenceUnits = PersistenceUnitProcessor.getPersistenceUnits(url).iterator(); while (persistenceUnits.hasNext()){ PersistenceUnitInfo persistenceUnitInfo = persistenceUnits.next(); // Make the callback on the persistence layer boolean predeployed = callPredeploy(persistenceUnitInfo, m); if (predeployed){ // Make the next callback on the persistence layer callDeploy(persistenceUnitInfo.getPersistenceUnitName()); } } } /** * INTERNAL * This method initializes the container. Essentially, it will try to load the * class that contains the list of entities and reflectively call the method that * contains that list. It will then initialize the container with that list. * If succeeded return true, false otherwise. */ public void initialize(Map m) { sessionClassLoader = getMainLoader(); for (URL url: PersistenceUnitProcessor.findPersistenceArchives()){ AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_initialize", url); initPersistenceUnits(url, m); } } /** * INTERNAL: * Should be called only by the agent. (when weaving classes) * If succeeded return true, false otherwise. */ protected static void initializeFromAgent(Instrumentation instrumentation) throws Exception { AbstractSessionLog.getLog().setLevel(JavaSECMPInitializer.getTopLinkLoggingLevel()); // Squirrel away the instrumentation for later globalInstrumentation = instrumentation; // Create JavaSECMPInitializer singleton javaSECMPInitializer = new JavaSECMPInitializer(); // Initialize it javaSECMPInitializer.initialize(new HashMap()); } /** * Initialize the static entityContainer from a main method. The entityContainer * can also be initialized from a premain method. with slightly different behavior * @param cls the class of the JavaSECMPInitializer. We will instantiate it reflectively * @param m a map containing the set of properties to intantiate with. */ public static void initializeFromMain(Map m) { if (javaSECMPInitializer != null) { return; } javaSECMPInitializer = new JavaSECMPInitializer(); AbstractSessionLog.getLog().setLevel(JavaSECMPInitializer.getTopLinkLoggingLevel()); AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_init_initialize_from_main"); // Initialize it javaSECMPInitializer.initialize(m); } /** * INTERNAL: * Create a list of java.lang.Class that contains the classes of all the entities * that we will be deploying */ protected Set loadEntityClasses(Collection entityNames, ClassLoader classLoader) { Set entityClasses = new HashSet(); // Load the classes using the loader passed in AbstractSessionLog.getLog().log(SessionLog.FINER, "cmp_loading_entities_using_loader", classLoader); for (Iterator iter = entityNames.iterator(); iter.hasNext();) { String entityClassName = (String)iter.next(); try { entityClasses.add(classLoader.loadClass(entityClassName)); } catch (ClassNotFoundException cnfEx) { throw ValidationException.entityClassNotFound(entityClassName, classLoader, cnfEx); } } return entityClasses; } public Properties getPersistenceUnitProperties(String emName) { return emSetupPersistenceUnitInfos.get(emName).getProperties(); } /*********************************/ /***** Temporary Classloader *****/ /*********************************/ /** This class loader is provided at initialization time to allow us to temporarily load * domain classes so we can examine them for annotations. After they are loaded we will throw this * class loader away. Transformers can then be registered on the real class loader to allow * weaving to occur. * * It selectively loads classes based on the list of classnames it is instantiated with. Classes * not on that list are allowed to be loaded by the parent. */ public class TempEntityLoader extends URLClassLoader { Collection classNames; boolean shouldOverrideLoadClassForCollectionMembers; public TempEntityLoader(URL[] urls, ClassLoader parent, Collection classNames, boolean shouldOverrideLoadClassForCollectionMembers) { super(urls, parent); this.classNames = classNames; this.shouldOverrideLoadClassForCollectionMembers = shouldOverrideLoadClassForCollectionMembers; } public TempEntityLoader(URL[] urls, ClassLoader parent, Collection classNames) { this(urls, parent, classNames, true); } // Indicates if the classLoad should be overridden for the passed className. // Returns true in case the class should NOT be loaded by parent classLoader. protected boolean shouldOverrideLoadClass(String name) { if (shouldOverrideLoadClassForCollectionMembers) { // Override classLoad if the name is in collection return (classNames != null) && classNames.contains(name); } else { // Directly opposite: Override classLoad if the name is NOT in collection. // Forced to check for java. and javax. packages here, because even if the class // has been loaded by parent loader we would load it again // (see comment in loadClass) return !name.startsWith("java.") && !name.startsWith("javax.") && ((classNames == null) || !classNames.contains(name)); } } protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (shouldOverrideLoadClass(name)) { // First, check if the class has already been loaded. // Note that the check only for classes loaded by this loader, // it doesn't return true if the class has been loaded by parent loader // (forced to live with that because findLoadedClass method defined as final protected: // neither can override it nor call it on the parent loader) Class c = findLoadedClass(name); if (c == null) { c = findClass(name); } if (resolve) { resolveClass(c); } return c; } else { return super.loadClass(name, resolve); } } } }