/* * 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.lang.instrument.ClassFileTransformer; import java.util.*; import java.net.URL; import java.lang.reflect.Constructor; import javax.persistence.spi.PersistenceUnitInfo; import oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider; import oracle.toplink.essentials.internal.databaseaccess.DatasourcePlatform; import oracle.toplink.essentials.internal.security.PrivilegedAccessController; import oracle.toplink.essentials.internal.weaving.TransformerFactory; import oracle.toplink.essentials.jndi.JNDIConnector; import oracle.toplink.essentials.logging.AbstractSessionLog; import oracle.toplink.essentials.logging.SessionLog; import oracle.toplink.essentials.platform.database.oracle.OraclePlatform; import oracle.toplink.essentials.platform.server.sunas.SunAS9ServerPlatform; import oracle.toplink.essentials.internal.sessions.AbstractSession; import oracle.toplink.essentials.sequencing.Sequence; import oracle.toplink.essentials.sessions.*; import oracle.toplink.essentials.threetier.ServerSession; import oracle.toplink.essentials.internal.annotations.*; import oracle.toplink.essentials.internal.ejb.cmp3.xml.EntityMappingsXMLProcessor; import oracle.toplink.essentials.tools.sessionmanagement.SessionManager; import oracle.toplink.essentials.descriptors.ClassDescriptor; import oracle.toplink.essentials.internal.ejb.cmp3.base.CMP3Policy; import oracle.toplink.essentials.platform.server.CustomServerPlatform; import oracle.toplink.essentials.platform.server.NoServerPlatform; import oracle.toplink.essentials.platform.server.ServerPlatform; import oracle.toplink.essentials.exceptions.*; import oracle.toplink.essentials.platform.database.DBPlatformHelper; /** * INTERNAL: * A TopLink specific implementer of the EntityManagerInitializer interface. */ public class EntityManagerSetupImpl { protected EJBAnnotationsProcessor processor = null; protected EntityMappingsXMLProcessor entityMappingsProcessor = null; protected PersistenceUnitInfo persistenceUnitInfo = null; protected int logLevel; protected String deployedSessionName = null; protected Project sessionProject = null; //Bug#4452468 Used as an internal flag to indicate if there is no weaving (agent). By default, weaving is on. public static final String NO_WEAVING = "no.weaving"; public static final String ERROR_LOADING_XML_FILE = "error_loading_xml_file"; public static final String EXCEPTION_LOADING_ENTITY_CLASS = "exception_loading_entity_class"; /** * Build a default ServerSession. This will create an ServerSession with an empty login * that uses the OC4jServerPlatfrom */ protected ServerSession buildDefaultSession(Project project) { ServerSession session = null; if (project == null) { session = new ServerSession(); session.setProject(new Project(new DatabaseLogin())); } else { session = (ServerSession)project.createServerSession(); } if (logLevel >= 0) { session.getSessionLog().setLevel(logLevel); } else { session.getSessionLog().setLevel(SessionLog.INFO); } session.setName(persistenceUnitInfo.getPersistenceUnitName()); session.getLogin().usePlatform(new OraclePlatform()); if (persistenceUnitInfo != null && persistenceUnitInfo.getJtaDataSource() != null){ session.setServerPlatform(new SunAS9ServerPlatform(session)); } return session; } /** * Deploy a persistence session and return an EntityManagerFactory. * * @param entities The collection of persistent entity classes. These will have been * loaded by the loader that is also passed in. Any enhancement * and class manipulation must have already been done to the classes. * @param loader The class loader that was used to load the entity classes. This loader * will be maintained for the lifespan of the loaded classes. * @param datasourceName The name of the datasource that should be used for this * persistence session. * @return An EntityManagerFactory to be used by the Container to obtain EntityManagers */ public ServerSession deploy(Collection entities, ClassLoader realClassLoader) { ServerSession session; session = buildDefaultSession(sessionProject); session.getProject().convertClassNamesToClasses(realClassLoader); addSessionToGlobalSessionManager(session); deployedSessionName = session.getName(); entityMappingsProcessor.setClassLoader(realClassLoader); entityMappingsProcessor.addEntityListeners(session, entities); entityMappingsProcessor.addNamedQueriesToSession(session); // Set the real class loader on the processor before asking for // listeners and queries. processor.setClassLoader(realClassLoader); processor.addEntityListeners(session, entities); processor.addNamedQueriesToSession(session); //let the specific implementation decide when to login. ServerSession returnSession = updateServerSession(session, persistenceUnitInfo.getProperties()); cleanUp(); if (returnSession.getIntegrityChecker().hasErrors()){ returnSession.handleException(new IntegrityException(returnSession.getIntegrityChecker())); } returnSession.getDatasourcePlatform().getConversionManager().setLoader(realClassLoader); return returnSession; } /** * INTERNAL: * Adds descriptors plus sequencing info found on the project to the session. */ protected void addProjectToSession(ServerSession session, Project project) { DatasourcePlatform sessionPlatform = (DatasourcePlatform)session.getDatasourceLogin().getDatasourcePlatform(); DatasourcePlatform projectPlatform = (DatasourcePlatform)project.getDatasourceLogin().getDatasourcePlatform(); if (!sessionPlatform.hasDefaultSequence() && projectPlatform.hasDefaultSequence()) { sessionPlatform.setDefaultSequence(projectPlatform.getDefaultSequence()); } if ((sessionPlatform.getSequences() == null) || sessionPlatform.getSequences().isEmpty()) { if ((projectPlatform.getSequences() != null) && !projectPlatform.getSequences().isEmpty()) { sessionPlatform.setSequences(projectPlatform.getSequences()); } } else { if ((projectPlatform.getSequences() != null) && !projectPlatform.getSequences().isEmpty()) { Iterator itProjectSequences = projectPlatform.getSequences().values().iterator(); while (itProjectSequences.hasNext()) { Sequence sequence = (Sequence)itProjectSequences.next(); if (!sessionPlatform.getSequences().containsKey(sequence.getName())) { sessionPlatform.addSequence(sequence); } } } } session.addDescriptors(project); } /** * INTERNAL: * Put the given session into the session manager so it can be looked up later */ protected static void addSessionToGlobalSessionManager(ServerSession ss) { AbstractSession session = (AbstractSession)SessionManager.getManager().getSessions().get(ss.getName()); if(session != null) { if (session.isDatabaseSession() && session.isConnected()) { ((DatabaseSession)session).logout(); } SessionManager.getManager().getSessions().remove(ss.getName()); } SessionManager.getManager().addSession(ss); } /** * INTERNAL: * Assign a CMP3Policy to each descriptor */ protected static void assignCMP3Policy(Project project) { // all descriptors assigned CMP3Policy for (Iterator iterator = project.getDescriptors().values().iterator(); iterator.hasNext();){ //bug:4406101 changed class cast to base class, which is used in projects generated from 904 xml ClassDescriptor descriptor = (ClassDescriptor)iterator.next(); if(descriptor.getCMPPolicy() == null) { descriptor.setCMPPolicy(new CMP3Policy()); } } } /** * INTERNAL: * Build a default TopLink ServerPlatform class for use with this platform. */ protected static void assignServerPlatform(ServerSession ss, Map m) { String serverPlatformClassName = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_SERVER_PLATFORM_PROPERTY, m); if(serverPlatformClassName == null || serverPlatformClassName.equals("")) { return; } ServerPlatform serverPlatform = null; try { Class cls = oracle.toplink.essentials.internal.security.PrivilegedAccessController.getClassForName(serverPlatformClassName); Constructor constructor = cls.getConstructor(new Class[]{oracle.toplink.essentials.internal.sessions.DatabaseSessionImpl.class}); serverPlatform = (ServerPlatform)constructor.newInstance(new Object[]{ss}); } catch (ClassNotFoundException ex1) { throw EntityManagerSetupException.classNotFoundForProperty(serverPlatformClassName, EntityManagerFactoryProvider.TOPLINK_SERVER_PLATFORM_PROPERTY, ex1); } catch (Exception ex2) { throw EntityManagerSetupException.failedToInstantiateServerPlatform(serverPlatformClassName, EntityManagerFactoryProvider.TOPLINK_SERVER_PLATFORM_PROPERTY, ex2); } if(serverPlatform instanceof CustomServerPlatform) { CustomServerPlatform customServerPlatform = (CustomServerPlatform)serverPlatform; String externalTransactionControllerClassName = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_EXTERNAL_TRANSACTION_CONTROLLER_PROPERTY, m); if(externalTransactionControllerClassName != null && !externalTransactionControllerClassName.equals("")) { try{ customServerPlatform.setExternalTransactionControllerClass(oracle.toplink.essentials.internal.security.PrivilegedAccessController.getClassForName(externalTransactionControllerClassName)); } catch (ClassNotFoundException ex1) { throw EntityManagerSetupException.classNotFoundForProperty(externalTransactionControllerClassName, EntityManagerFactoryProvider.TOPLINK_EXTERNAL_TRANSACTION_CONTROLLER_PROPERTY, ex1); } } } ss.setServerPlatform(serverPlatform); } /** * Perform any steps necessary prior to actual deployment. * * @param sessionName The name of the persistence session. Null indicates that * the persistence layer should use its own default name. * @param entities The collection of persistent entity classes. These will have been * loaded by the loader that is also passed in. * @param privateClassLoader The class loader that was used to load the entity * classes. This loader (and all of the classes that it has * loaded) will dissappear immediately after this call. * @return A transformer (which may be null) that should be plugged into the proper * classloader to allow classes to be transformed as they get loaded. * @see predeploy(Collection entities, ClassLoader privateClassLoader, PersistenceUnitInfo info) */ public ClassFileTransformer predeploy(Collection entities, ClassLoader privateClassLoader, PersistenceUnitInfo info) { persistenceUnitInfo = info; if (SessionManager.getManager().getSessions().get(persistenceUnitInfo.getPersistenceUnitName()) != null){ undeploy(); } logLevel = AbstractSessionLog.translateStringToLoggingLevel(EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_LOGGING_LEVEL, persistenceUnitInfo.getProperties())); AbstractSession session = buildDefaultSession(null); boolean enableLazyForOneToOne = false; String noWeaving = EntityManagerFactoryProvider.getConfigProperty(NO_WEAVING, persistenceUnitInfo.getProperties()); ClassFileTransformer transformer = null; if ((noWeaving == null) || noWeaving.equalsIgnoreCase("false")) { enableLazyForOneToOne = true; } entityMappingsProcessor = new EntityMappingsXMLProcessor(enableLazyForOneToOne); entityMappingsProcessor.setClassLoader(privateClassLoader); if (persistenceUnitInfo != null) { if (!persistenceUnitInfo.getMappingFileNames().isEmpty()) { Iterator mappingFiles = persistenceUnitInfo.getMappingFileNames().iterator(); String mappingFileResourceName = null; while (mappingFiles.hasNext()) { try{ mappingFileResourceName = (String)mappingFiles.next(); URL mappingFileResource = privateClassLoader.getResource(mappingFileResourceName); session = (AbstractSession)entityMappingsProcessor.processEntityMappings(session, mappingFileResource); } catch (RuntimeException e){ String throwXMLExceptions = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_ORM_THROW_EXCEPTIONS, persistenceUnitInfo.getProperties()); // if fail quietly if (throwXMLExceptions == null || throwXMLExceptions.equalsIgnoreCase("false")) { session.log(SessionLog.CONFIG, SessionLog.EJB_ORM, ERROR_LOADING_XML_FILE, new Object[] {mappingFileResourceName, e}); } else { // fail loudly session.handleException(e); } } } } } // add any classes processed through ORM XML to the list of entities that require weaving for (Iterator descriptorIt = session.getProject().getOrderedDescriptors().iterator(); descriptorIt.hasNext();) { ClassDescriptor descriptor = (ClassDescriptor) descriptorIt.next(); String jClassName = descriptor.getJavaClassName(); try { Class jClass = PrivilegedAccessController.getClassForName(jClassName, false, privateClassLoader); if (!entities.contains(jClass)) { entities.add(jClass); } } catch (ClassNotFoundException cnfe) { session.log(SessionLog.WARNING, SessionLog.EJB_ORM, EXCEPTION_LOADING_ENTITY_CLASS, new Object[] {jClassName, cnfe}); } } processor = new EJBAnnotationsProcessor(session, privateClassLoader, entities, enableLazyForOneToOne); session = processor.processORAnnotations(); if (enableLazyForOneToOne){ transformer = TransformerFactory.createTransformer(session, entities, privateClassLoader); } session.getProject().getLogin().setConnector(new DefaultConnector()); sessionProject = session.getProject(); if (session.getIntegrityChecker().hasErrors()){ session.handleException(new IntegrityException(session.getIntegrityChecker())); } return transformer; } /** * Ensure any unnecessary data is nulled out to allow garbage collection */ protected void cleanUp() { processor = null; } /** * INTERNAL: * Load a session from a given xml file. This method will first look for the session named by * sessionName, then for a session named: default and then for the only session in the xml. */ public ServerSession getServerSession(Map m, ClassLoader classLoader) { // don't login the session ServerSession ss = (ServerSession)SessionManager.getManager().getSession(deployedSessionName, false); return updateServerSession(ss, m); } /** * Override the default login creation method. * If persistenceInfo is available, use the information from it to setup the login. * @param ss * @param m * @return */ protected DatasourceLogin updateLoginInformation(ServerSession ss, Map m){ if (persistenceUnitInfo == null){ return updateLoginInformationForNonJTA(ss, m); } javax.sql.DataSource jtaDatasource = persistenceUnitInfo.getJtaDataSource(); DatasourceLogin login = ss.getLogin(); if(jtaDatasource != null) { if(!(login.getConnector() instanceof JNDIConnector)) { JNDIConnector jndiConnector = new JNDIConnector(jtaDatasource); login.setConnector(jndiConnector); login.setUsesExternalConnectionPooling(true); login.setUsesExternalTransactionController(true); } } else { return updateLoginInformationForNonJTA(ss, m); } DatasourceLogin readLogin = (DatasourceLogin)login.clone(); javax.sql.DataSource nonJtaDatasource = persistenceUnitInfo.getNonJtaDataSource(); if(nonJtaDatasource != null) { JNDIConnector jndiConnector = new JNDIConnector(nonJtaDatasource); readLogin.setConnector(jndiConnector); ss.setReadConnectionPool(readLogin); } return login; } /** * In cases where there is no JTA, we will use properties to configure the login for * our session. This method gets those properties and sets them on the session. * @param ss * @param m * @return */ protected DatasourceLogin updateLoginInformationForNonJTA(ServerSession ss, Map m){ DatasourceLogin login = ss.getLogin(); if((login.getConnector() instanceof DefaultConnector)) { DatabaseLogin dbLogin = (DatabaseLogin)login; String jdbcDriver = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.JDBC_DRIVER_PROPERTY, m); String user = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.JDBC_USER_PROPERTY, m); String password = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.JDBC_PASSWORD_PROPERTY, m); String connectionString = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.JDBC_CONNECTION_STRING_PROPERTY, m); if(user != null && !user.equals("")) { dbLogin.setUserName(user); } if(password != null && !password.equals("")) { dbLogin.setPassword(password); } if(connectionString != null && !connectionString.equals("")) { dbLogin.setConnectionString(connectionString); } if(jdbcDriver != null && !jdbcDriver.equals("")) { dbLogin.setDriverClassName(jdbcDriver); } } return login; } /** * Make any changes to our ServerSession that can be made after it is created. * This includes setting the login information, platforms and CMP3Policy * @param ss * @param m * @return */ protected ServerSession updateServerSession(ServerSession ss, Map m) { if (ss == null || ss.isConnected()) { return ss; } assignCMP3Policy(ss.getProject()); DatasourceLogin login = updateLoginInformation(ss, m); String toplinkPlatform = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_PLATFORM_PROPERTY, m); if (toplinkPlatform == null || toplinkPlatform.equals("")) { toplinkPlatform = getDBPlatform(login); } login.setPlatformClassName(toplinkPlatform); if(login.shouldUseExternalTransactionController()) { assignServerPlatform(ss, m); } String logLevel = EntityManagerFactoryProvider.getConfigProperty(EntityManagerFactoryProvider.TOPLINK_LOGGING_LEVEL, m); if (logLevel != null) { ss.getSessionLog().setLevel(AbstractSessionLog.translateStringToLoggingLevel(logLevel)); } return ss; } /** * Undeploy a persistence session and remove the persistence entities. */ public void undeploy() { if(deployedSessionName != null) { ServerSession ss = (ServerSession)SessionManager.getManager().getSessions().remove(deployedSessionName); if(ss != null && ss.isConnected()) { ss.logout(); } deployedSessionName = null; } } private String getDBPlatform(DatasourceLogin login) { java.sql.Connection conn = null; try { conn = (java.sql.Connection) login.connectToDatasource(null); String dbName = conn.getMetaData().getDatabaseProductName(); String platformName = DBPlatformHelper.getDBPlatform(dbName); return platformName; } catch (java.sql.SQLException ex) { //TODO: Need to i18n throw new RuntimeException("Can not connect to database", ex); } finally { try { if (null != conn) conn.close(); } catch (java.sql.SQLException ex) { } } } }