org.eclipse.persistence.sessions.factories.SessionManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2023 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.sessions.factories;
import org.eclipse.persistence.exceptions.ServerPlatformException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedGetClassLoaderForClass;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.factories.model.SessionConfigs;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.platform.server.ServerPlatform;
import org.eclipse.persistence.platform.server.ServerPlatformUtils;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Session;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
*
* Purpose: Global session location.
*
* Description: This allows for a global session local which can
* be accessed globally from other classes. This is needed for EJB data stores
* as they must have a globally accessible place to access the session.
* This can be by EJB session beans, BMP beans and CMP beans as well as Servlets and
* other three-tier services.
*
* Responsibilities:
*
* - Store a global session.
*
- Allow the storage of alternative sessions as well.
*
*
* @author James Sutherland
* @since TOPLink/Java 3.0
*/
public class SessionManager {
/** Allow for usage of schema validation to be configurable. */
protected static boolean shouldUseSchemaValidation = true;
protected static volatile SessionManager manager;
protected AbstractSession defaultSession;
protected ConcurrentMap sessions;
protected static boolean shouldPerformDTDValidation;
private static volatile ConcurrentMap managers;
private static volatile ServerPlatform detectedPlatform;
private static volatile boolean supportPartitions;
private String context;
private static final Object[] lock = new Object[0];
// not final for tests
private static SessionLog LOG = AbstractSessionLog.getLog();
static {
init();
}
private static void init() {
String platformClass = ServerPlatformUtils.detectServerPlatform(null);
try {
detectedPlatform = platformClass != null
? ServerPlatformUtils.createServerPlatform(null, platformClass, SessionManager.class.getClassLoader())
: null;
} catch (ServerPlatformException e) {
// some platforms may not be handling 'null' session well or
// detected class as such may not be valid for some reason (ie
// not found), so be defensive here and only log throwable here
detectedPlatform = null;
LOG.logThrowable(SessionLog.WARNING, AbstractSessionLog.CONNECTION, e);
}
supportPartitions = detectedPlatform != null && detectedPlatform.usesPartitions();
if (supportPartitions) {
managers = new ConcurrentHashMap<>(4, 0.9f, 1);
SessionManager sm = initializeManager();
manager = sm;
managers.put(sm.context, sm);
} else {
manager = initializeManager();
}
}
/**
* PUBLIC:
* Return if schema validation will be used when parsing the 10g (10.1.3) sessions XML.
*
* @return {@code true} if schema validation will be used when parsing
* the 10g (10.1.3) sessions XML, {@code false} otherwise
*/
public static boolean shouldUseSchemaValidation() {
return shouldUseSchemaValidation;
}
/**
* PUBLIC:
* Set if schema validation will be used when parsing the 10g (10.1.3) sessions XML.
* By default schema validation is on, but can be turned off if validation problems occur,
* or to improve parsing performance.
*
* @param useSchemaValidation {@code true} if schema validation should be used when parsing
* the 10g (10.1.3) sessions XML, {@code false} otherwise
*/
public static void setShouldUseSchemaValidation(boolean useSchemaValidation) {
shouldUseSchemaValidation = useSchemaValidation;
}
/**
* PUBLIC:
* The default constructor to create a new session manager.
*/
public SessionManager() {
sessions = new ConcurrentHashMap<>(5);
if (supportPartitions) {
context = getPartitionID();
}
}
/**
* INTERNAL:
* Add an named session to the hashtable.
* Session must have a name prior to setting into session manager.
*
* @param session session to be added to the session manager
*/
public void addSession(Session session) {
getSessions().put(session.getName(), session);
}
/**
* ADVANCED:
* add an named session to the hashtable.
*
* @param sessionName session name
* @param session session to be added to the session manager
*/
public void addSession(String sessionName, Session session) {
session.setName(sessionName);
getSessions().put(sessionName, session);
}
/**
* PUBLIC:
* Return the default session.
* The session configuration is stored in a sessions.xml file in a
* directory on your classpath. Other sessions are supported through the
* getSession by name API.
*
* @return default session
*/
public Session getDefaultSession() {
if (defaultSession == null) {
defaultSession = getSession("default");
}
return defaultSession;
}
/**
* PUBLIC:
* Destroy current session manager instance.
*/
public void destroy() {
if (supportPartitions) {
if (!managers.isEmpty() && managers.remove(getPartitionID()) == null) {
//should not happen
LOG.log(SessionLog.WARNING, "session_manager_no_partition", new Object[0]);
}
}
}
/**
* INTERNAL:
* Destroy the session defined by sessionName on this manager.
*
* @param sessionName name of the session to be destroyed
*/
public void destroySession(String sessionName) {
Session session = getSessions().get(sessionName);
if (session != null) {
destroy(session);
} else {
logAndThrowException(SessionLog.WARNING, ValidationException.noSessionRegisteredForName(sessionName));
}
}
private void destroy(Session session) {
try {
if (session.isConnected()) {
((DatabaseSession) session).logout();
}
} catch (Throwable ig) {
// EL Bug 321843 - Must handle errors from logout.
LOG.logThrowable(SessionLog.WARNING, AbstractSessionLog.CONNECTION, ig);
}
sessions.remove(session.getName());
session = null;
}
/**
* INTERNAL:
* Destroy all sessions held onto by this manager.
*/
public void destroyAllSessions() {
Iterator toRemoveSessions = new ArrayList<>(getSessions().values()).iterator();
while (toRemoveSessions.hasNext()) {
destroy(toRemoveSessions.next());
}
}
/**
* INTERNAL:
* This method is to be used to load config objects for the Mapping Workbench
* only. Do not call this method.
*
* @param resourceName resource to load
* @param objectClassLoader ClassLoader used to load the resource
* @return parsed session configuration
*/
public synchronized SessionConfigs getInternalMWConfigObjects(String resourceName, ClassLoader objectClassLoader) {
return getInternalMWConfigObjects(resourceName, objectClassLoader, true);
}
/**
* INTERNAL:
* This method is to be used to load config objects for the Mapping Workbench
* only. Do not call this method.
*
* @param resourceName resource to load
* @param objectClassLoader ClassLoader used to load the resource
* @param validate whether to validate the resource passed in
* @return parsed session configuration
*/
public synchronized SessionConfigs getInternalMWConfigObjects(String resourceName, ClassLoader objectClassLoader, boolean validate) {
return new XMLSessionConfigLoader(resourceName).loadConfigsForMappingWorkbench(objectClassLoader, validate);
}
/**
* PUBLIC:
* Return the session manager for current context.
* This allow global access to a set of named sessions.
*
* @return the session manager for current context
*/
public static SessionManager getManager() {
if (!supportPartitions) {
if (manager == null) {
synchronized (lock) {
if (manager == null) {
manager = initializeManager();
}
}
}
return manager;
}
SessionManager current = manager;
String currentCtx = getPartitionID();
if (current != null && currentCtx.equals(current.context)) {
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "returning cached session manager for " + current.context);
}
return current;
}
current = managers.get(currentCtx);
if (current == null) {
synchronized (lock) {
if (current == null) {
current = initializeManager();
current.context = currentCtx;
managers.put(current.context, current);
manager = current;
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "created session manager for " + current.context);
}
}
}
}
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "returning session manager for " + current.context);
}
return current;
}
/**
* ADVANCED:
* Return all session managers.
* This allows global access to all instances of SessionManager.
*
* @return all SessionManager instances
*/
public static Collection getAllManagers() {
return supportPartitions ? managers.values() : Collections.singleton(getManager());
}
/**
* INTERNAL:
* Initialize the singleton session manager.
*
* @return initialized session manager
*/
protected static SessionManager initializeManager() {
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "initializing session manager");
}
return new SessionManager();
}
/**
* PUBLIC:
* Return the session by name.
*
* @param sessionName session name
* @return session with given session name
*/
public org.eclipse.persistence.internal.sessions.AbstractSession getSession(String sessionName) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name.
* Log the session in only if specified.
*
* @param sessionName session name
* @param shouldLoginSession whether the session should be logged in
* @return session with given session name
*/
public org.eclipse.persistence.internal.sessions.AbstractSession getSession(String sessionName, boolean shouldLoginSession) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
loader.setShouldLogin(shouldLoginSession);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name.
* Log the session in only if specified.
* Refresh the session only if specified.
*
* @param sessionName session name
* @param shouldLoginSession whether the session should be logged in
* @param shouldRefreshSession whether the session should be refreshed
* @return session with given session name
*/
public org.eclipse.persistence.internal.sessions.AbstractSession getSession(String sessionName, boolean shouldLoginSession, boolean shouldRefreshSession) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
loader.setShouldLogin(shouldLoginSession);
loader.setShouldRefresh(shouldRefreshSession);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, using the classloader of the Object specified.
* This method is used in older versions of BMP support
* This method will cause the class loader of the provided object to be
* compared with the classloader used to load the original session of this
* name, with this classloader. If they are not the same then the session
* will be refreshed.
*
* @param sessionName session name
* @param objectBean object to get the ClassLoader from
* @return session with given session name
*/
public org.eclipse.persistence.internal.sessions.AbstractSession getSession(String sessionName, Object objectBean) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
ClassLoader classLoader;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try{
classLoader = AccessController.doPrivileged(new PrivilegedGetClassLoaderForClass(objectBean.getClass()));
}catch (PrivilegedActionException ex){
throw (RuntimeException) ex.getCause();
}
}else{
classLoader = PrivilegedAccessHelper.getClassLoaderForClass(objectBean.getClass());
}
loader.setClassLoader(classLoader);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, in the file specified.
* Login the session.
*
* @param sessionName session name
* @param filename file name containing session definition
* @return session with given session name
*/
public org.eclipse.persistence.internal.sessions.AbstractSession getSession(String sessionName, String filename) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
loader.setResourceName(filename);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, in the file specified, using the class loader to find the resource.
* This method will cause the class loader to be compared with the classloader
* used to load the original session of this name.
* If they are not the same then the session will be refreshed.
*
* @param sessionName session name
* @param filename file name containing session definition
* @param classLoader ClassLoader used to load the original session
* @return session with given session name
*/
public AbstractSession getSession(String sessionName, String filename, ClassLoader classLoader) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
loader.setResourceName(filename);
loader.setClassLoader(classLoader);
loader.setShouldCheckClassLoader(true);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name.
* Provide the class loader for loading the project, the configuration file
* and the deployed classes.
* E.g. SessionManager.getManager().getSession("mySession", MySessionBean.getClassLoader());
* This method will cause the class loader to be compared with the classloader
* used to load the original session of this name.
* If they are not the same then the session will be refreshed.
*
* @param sessionName session name
* @param objectClassLoader ClassLoader used to load the original session
* @return session with given session name
*/
public AbstractSession getSession(String sessionName, ClassLoader objectClassLoader) {
XMLSessionConfigLoader loader = new XMLSessionConfigLoader();
loader.setSessionName(sessionName);
loader.setClassLoader(objectClassLoader);
loader.setShouldCheckClassLoader(true);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, loading the configuration from the file
* specified in the XMLSessionConfigLoader. Provide the class loader for
* loading the project, the configuration file and the deployed classes.
* This method will cause the class loader to be compared with the classloader
* used to load the original session of this name.
* If they are not the same then the session will be refreshed.
*
* @param loader {@link XMLSessionConfigLoader} containing session configuration
* @param sessionName session name
* @param objectClassLoader ClassLoader used to load the original session
* @return session with given session name
*/
public AbstractSession getSession(XMLSessionConfigLoader loader, String sessionName, ClassLoader objectClassLoader) {
if (loader == null){
loader = new XMLSessionConfigLoader();
}
loader.setSessionName(sessionName);
loader.setClassLoader(objectClassLoader);
loader.setShouldCheckClassLoader(true);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, loading the configuration from the file
* specified in the loader. Provide the class loader for loading the
* project, the configuration file and the deployed classes. Pass in true for
* shouldLoginSession if the session returned should be logged in before
* returned otherwise false. Pass in true for shouldRefreshSession if the
* XMLSessionConfigLoader should reparse the configuration file for new
* sessions. False, will cause the XMLSessionConfigLoader not to parse the
* file again.
*
* @param loader {@link XMLSessionConfigLoader} containing session configuration
* @param sessionName session name
* @param objectClassLoader ClassLoader used to load the original session
* @param shouldLoginSession whether the session should be logged in
* @param shouldRefreshSession whether the session should be refreshed
* @return session with given session name
*/
public AbstractSession getSession(XMLSessionConfigLoader loader, String sessionName, ClassLoader objectClassLoader, boolean shouldLoginSession, boolean shouldRefreshSession) {
if (loader == null){
loader = new XMLSessionConfigLoader();
}
loader.setSessionName(sessionName);
loader.setClassLoader(objectClassLoader);
loader.setShouldLogin(shouldLoginSession);
loader.setShouldRefresh(shouldRefreshSession);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, loading the configuration from the file
* specified in the loader. Provide the class loader for loading the
* project, the configuration file and the deployed classes. Pass in true for
* shouldLoginSession if the session returned should be logged in before
* returned otherwise false. Pass in true for shouldRefreshSession if the
* XMLSessionConfigLoader should reparse the configuration file for new
* sessions. False, will cause the XMLSessionConfigLoader not to parse the
* file again.
* Pass true for shouldCheckClassLoader will cause the class loader to be compared with the classloader
* used to load the original session of this name.
* If they are not the same then the session will be refreshed, this can be used for re-deployment.
*
* @param loader {@link XMLSessionConfigLoader} containing session configuration
* @param sessionName session name
* @param objectClassLoader ClassLoader used to load the original session
* @param shouldLoginSession whether the session should be logged in
* @param shouldRefreshSession whether the session should be refreshed
* @param shouldCheckClassLoader whether to compare class loaders used to load given session
* @return session with given session name
*/
public AbstractSession getSession(XMLSessionConfigLoader loader, String sessionName, ClassLoader objectClassLoader, boolean shouldLoginSession, boolean shouldRefreshSession, boolean shouldCheckClassLoader) {
if (loader == null){
loader = new XMLSessionConfigLoader();
}
loader.setSessionName(sessionName);
loader.setClassLoader(objectClassLoader);
loader.setShouldLogin(shouldLoginSession);
loader.setShouldRefresh(shouldRefreshSession);
loader.setShouldCheckClassLoader(shouldCheckClassLoader);
return getSession(loader);
}
/**
* PUBLIC:
* Return the session by name, loading the configuration from the file
* specified in the loader, using the loading options provided on the loader.
*
* @param loader {@link XMLSessionConfigLoader} containing session configuration
* @return session with given session name
*/
public AbstractSession getSession(XMLSessionConfigLoader loader) {
AbstractSession session = (AbstractSession)getSessions().get(loader.getSessionName());
boolean shouldRefreshSession = loader.shouldRefresh();
if (loader.shouldCheckClassLoader() && (session != null) && !session.getDatasourcePlatform().getConversionManager().getLoader().equals(loader.getClassLoader())) {
//bug 3766808 if a different classloader is being used then a reload of the session should
//be completed otherwise failures may occur
shouldRefreshSession = true;
}
if ((session == null) || shouldRefreshSession) {
// PERF: Avoid synchronization for normal get.
synchronized (this) {
// Must re-assert checks inside synchronization.
session = (AbstractSession)getSessions().get(loader.getSessionName());
if (loader.shouldCheckClassLoader() && (session != null) && !session.getDatasourcePlatform().getConversionManager().getLoader().equals(loader.getClassLoader())) {
//bug 3766808 if a different classloader is being used then a reload of the session should
//be completed otherwise failures may occur
shouldRefreshSession = true;
}
if ((session == null) || shouldRefreshSession) {
if (session != null) {
if (session.isDatabaseSession() && session.isConnected()) {
// Must handles errors from logout as session maybe hosed.
try {
((DatabaseSession)session).logout();
} catch (Throwable ig) {
LOG.logThrowable(SessionLog.WARNING, AbstractSessionLog.CONNECTION, ig);
}
}
getSessions().remove(loader.getSessionName());
}
if (loader.load(this, loader.getClassLoader())) {
session = (AbstractSession)getSessions().get(loader.getSessionName());
}
}
}
}
// Connect the session if specified.
if (session == null) {
logAndThrowException(SessionLog.WARNING, ValidationException.noSessionFound(loader.getSessionName(), loader.getResourcePath()));
} else if (loader.shouldLogin() && !session.isConnected()) {
// PERF: Avoid synchronization for normal get.
synchronized (this) {
// Must re-assert checks inside synchronization.
if (loader.shouldLogin() && !session.isConnected()) {
((DatabaseSession)session).login();
}
}
}
return session;
}
/**
* INTERNAL:
* Log exceptions to the default log then throw them.
*/
private void logAndThrowException(int level, RuntimeException exception) throws RuntimeException {
LOG.logThrowable(level, AbstractSessionLog.CONNECTION, exception);
throw exception;
}
/**
* INTERNAL:
* Set a hashtable of all sessions
*
* @param sessions sessions for this session manager
*/
public void setSessions(ConcurrentMap sessions) {
this.sessions = sessions;
}
/**
* INTERNAL:
* Return a hashtable on all sessions.
*
* @return all sessions known by this session manager
*/
public ConcurrentMap getSessions() {
return sessions;
}
/**
* PUBLIC:
* Set the default session.
* If not set the session configuration is stored in a sessions.xml
* file in a TopLink directory on your classpath.
* Other sessions are supported through the getSession by name API.
*
* @param defaultSession default session
*/
public void setDefaultSession(Session defaultSession) {
this.defaultSession = (org.eclipse.persistence.internal.sessions.AbstractSession)defaultSession;
addSession("default", defaultSession);
}
/**
* INTERNAL:
* Set the singleton session manager.
* This allows global access to a set of named sessions.
*
* @param theManager session manager for current context
*/
public static void setManager(SessionManager theManager) {
if (!supportPartitions) {
manager = theManager;
} else {
if (theManager.context == null) {
synchronized (lock) {
if (theManager.context == null) {
theManager.context = getPartitionID();
}
managers.put(theManager.context, theManager);
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "registered session manager w/o partitionId for " + theManager.context);
}
}
} else {
managers.put(theManager.context, theManager);
if (LOG.shouldLog(SessionLog.FINER)) {
LOG.log(SessionLog.FINER, "registered session manager w/ partitionId for " + theManager.context);
}
}
}
}
private static String getPartitionID() {
return detectedPlatform.getPartitionID();
}
}