/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.sessions.factories; import java.util.Collection; import org.eclipse.persistence.sessions.broker.SessionBroker; import org.eclipse.persistence.sessions.*; import org.eclipse.persistence.sessions.server.Server; import org.eclipse.persistence.sessions.factories.SessionManager; /** * Helper class to simplify the development and generation of code that accesses * TopLink through the SessionManager (sessions config XML). * Responsibilities:<ul> * <li> Lookup of a session by name using default or provided sessions config location * <li> Support lookup of active UnitOfWork and Session in JTA environments * <li> Hot/Re-deployment handling of applications * <li> Detachment helpers to simplify usage within a local session bean * </ul> * * Basic usage example: * <code> * SessionFactory = sessionFactory = new SessionFactory("session-name"); * * ... * * public List read(Vector args) { * Session session = sessionFactory.acquireSession(); * * List results = (List) session.executeQuery("query-name", MyClass.class, args); * * session.release(); * return results; * } * * public void write(MyClass detachedInstance) { * UnitOfWork uow = sessionFactory.acquireUnitOfWork(); * * MyClass workingCopy = (MyClass) uow.readObject(detachedInstance); * * if (workingCopy == null) { * throw new MyException("Cannot write changes. Object does not exist"); * } * * uow.deepMergeClone(detachedInstance); * * uow.commit(); * } * </code> * * <b>Detachment</b>: The detach helper methods are provided to assist with the * construction of applications. This helper class was designed for use within * session beans (SB) and in the case of local SBs the objects returned are not * serialized. Since EclipseLink's default behavior is to return the shared instance * from the cache and rely on developers to only modify instances within a * UnitOfWork this may be an issue. The client to the local session bean may * try to modify the instance and thus corrupt the cache. By detaching the object * the client to the session bean gets its own isolated copy that it can freely * modify. This provides the same functionality as with a remote session bean * and allows the developer the choice in how/when objects are detached. * <i>Note</i>: The above code example shows how a detached instance can have * changes made to it persisted through use of the UnitOfWork merge API. * * @author Doug Clarke {@literal &} John Braken * @version 10.1.3 * @since Dec 10, 2006 */ public class SessionFactory { /** * Location for the sessions.xml file. The default here is the most common. * If none is provided then EclipseLink's default locations of 'sessions.xml' * and 'META-INF/sessions.xml' will be tried. */ private String sessionXMLPath; private String sessionName; /** * Constructor for creating a new EclipseLinkSessionHelper instance. * * @param sessionsXMLPath - resource path of the sessions configuration xml. * @param sessionName - name of the session to use. */ public SessionFactory(String sessionsXMLPath, String sessionName) { this.sessionXMLPath = sessionsXMLPath; this.sessionName = sessionName; } public SessionFactory(String sessionName) { this.sessionName = sessionName; } public String getSessionName() { return sessionName; } public String getSessionXMLPath() { return this.sessionXMLPath; } /** * The class-loader returned form this call will be used when loading the * EclipseLink configuration. By default this is the current thread's loader. * If this is not the case users can subclass this session factory and * override this method to provide a different loader. */ protected ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * Helper method that looks up the singleton session and ensure that * if the application has been hot-deployed it gets a fresh version of the * server. */ public DatabaseSession getSharedSession() { return getSharedSession(true, false); } /** * Used in place of getSharedSession() when the calling application needs * access to the session prior to login or it wishes to force the session * configuration to be re-loaded an applied. This also makes use of the * current class-loader return from getClassLoader() and a SessionManager * class-loader check to see if the application was loaded by another * class-loader and is should this be refreshed as the application has been * hot deployed. */ public DatabaseSession getSharedSession(boolean login, boolean refresh) { XMLSessionConfigLoader xmlLoader; if (getSessionXMLPath() != null) { xmlLoader = new XMLSessionConfigLoader(getSessionXMLPath()); } else { xmlLoader = new XMLSessionConfigLoader(); } return (DatabaseSession)SessionManager.getManager().getSession(xmlLoader, getSessionName(), getClassLoader(), login, refresh, true); } /** * Returns the Session active for this specified helper. If the EclipseLink * session does not have an external transaction controller or there is * not an active JTA transaction then a newly acquire client session is * returned on each call. * * This method also properly handles acquire a client session from a broker * as well as returning the shared session in the case it is a database * session. */ public Session acquireSession() { Session sharedSession = getSharedSession(); if (sharedSession.hasExternalTransactionController()) { UnitOfWork uow = sharedSession.getActiveUnitOfWork(); if (uow != null) { return uow.getParent(); } } if (sharedSession.isServerSession()) { return ((Server)sharedSession).acquireClientSession(); } if (sharedSession.isSessionBroker()) { SessionBroker broker = (SessionBroker)sharedSession; if (broker.isServerSessionBroker()) { return broker.acquireClientSessionBroker(); } return broker; } // Assume we have a database session and return it. return sharedSession; } /** * Looks up the active UnitOfWork using either the global JTA TX or acquires * a new one from the active session. */ public UnitOfWork acquireUnitOfWork() { return acquireUnitOfWork(getSharedSession()); } /** * Looks up the active UnitOfWork using either the global JTA TX or acquires * a new one from the active session. THis method should be used if a session * has already been acquired. */ public UnitOfWork acquireUnitOfWork(Session session) { if (session.hasExternalTransactionController()) { return session.getActiveUnitOfWork(); } return session.acquireUnitOfWork(); } /** * Build a detached copy using a one-off UnitOfWork. * This simulates creation of a copy through serialization when using a * remote session bean if this copy process is not done the user of this * helper would return the shared copy from the cache that should not be * modified. These detachment methods *MUST* be used for local session * beans where the returned objects can be modified by the client. * * @param entity an existing persistent entity * @return a copy of the entity for use in a local client that may make changes to it */ public Object detach(Object entity) { UnitOfWork uow = ((org.eclipse.persistence.internal.sessions.AbstractSession)getSharedSession()).acquireNonSynchronizedUnitOfWork(null); Object copy = uow.registerObject(entity); uow.release(); return copy; } public Collection detach(Collection entities) { UnitOfWork uow = ((org.eclipse.persistence.internal.sessions.AbstractSession)getSharedSession()).acquireNonSynchronizedUnitOfWork(null); Collection copies = uow.registerAllObjects(entities); uow.release(); return copies; } }