com.cyc.session.SessionManagerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cyc-session-client Show documentation
Show all versions of cyc-session-client Show documentation
Session API implementation for managing configurations and connections to Cyc servers.
package com.cyc.session;
/*
* #%L
* File: SessionManagerImpl.java
* Project: Session Client
* %%
* Copyright (C) 2013 - 2017 Cycorp, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.cyc.session.CycSession.ConnectionStatus;
import com.cyc.session.CycSession.SessionListener;
import com.cyc.session.configuration.ConfigurationValidator;
import com.cyc.session.exception.SessionCommunicationException;
import com.cyc.session.exception.SessionConfigurationException;
import com.cyc.session.exception.SessionException;
import com.cyc.session.exception.SessionInitializationException;
import com.cyc.session.exception.SessionRuntimeException;
import com.cyc.session.exception.SessionServiceException;
import com.cyc.session.internal.ConfigurationCache;
import com.cyc.session.internal.ConfigurationLoaderManager;
import com.cyc.session.internal.CurrentObjectCache;
import com.cyc.session.internal.CycSessionCache;
import com.cyc.session.selection.CycServerSelector;
import com.cyc.session.selection.SessionSelector;
import com.cyc.session.services.EnvironmentConfigurationLoader;
import com.cyc.session.spi.SessionFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reference implementation of SessionManager.
* @author nwinant
* @param
*/
public class SessionManagerImpl implements SessionManager {
// Static
/**
* Returns a list of all implementations of SessionFactory which can be located
* via the {@link java.util.ServiceLoader}.
* @return the available session factories
*/
static protected List loadAllSessionFactories() {
// Note: The relevant service provider file in META-INF/services
// is generated by the serviceloader-maven-plugin, specified
// in the pom.xml file.
final List factories = new ArrayList<>();
final ServiceLoader loader =
ServiceLoader.load(SessionFactory.class);
for (SessionFactory factory : loader) {
factories.add(factory);
}
return factories;
}
static final private Logger LOGGER = LoggerFactory.getLogger(SessionManagerImpl.class);
// Fields
private final SessionManagerConfiguration configuration;
private final ConfigurationLoaderManager loaderMgr;
private final SessionFactory sessionFactory;
private final ConfigurationCache configCache;
private final CycSessionCache sessionCache;
private final CurrentObjectCache currentObjectCache;
private final EnvironmentConfigurationLoader environmentLoader;
private boolean closed = false;
// Constructors
public SessionManagerImpl(SessionManagerConfiguration configuration)
throws SessionServiceException, SessionConfigurationException {
this.configuration = configuration;
this.loaderMgr = new ConfigurationLoaderManager();
this.sessionFactory = loadSessionFactory();
this.configCache = new ConfigurationCache();
this.sessionCache = new CycSessionCache<>();
this.currentObjectCache = new CurrentObjectCache<>();
this.environmentLoader = new EnvironmentConfigurationLoader();
LOGGER.debug("SessionManager instance created: {}", this);
}
// Public
@Override
public SessionManagerConfiguration getManagerConfiguration() {
return this.configuration;
}
//@Override
public EnvironmentConfiguration getEnvironmentConfiguration()
throws SessionConfigurationException {
errorIfClosed("Cannot retrieve EnvironmentConfiguration.");
return environmentLoader.getConfiguration();
}
@Override
public CycSessionConfiguration getSessionConfiguration() throws SessionConfigurationException {
errorIfClosed("Cannot retrieve configuration.");
final EnvironmentConfiguration environment = getEnvironmentConfiguration();
final CycSessionConfiguration cachedConfig = configCache.get(environment);
if (cachedConfig != null) {
LOGGER.debug("Retrieving config from cache: {}", cachedConfig);
return cachedConfig;
}
LOGGER.debug("#getConfiguration() Loading new configuration");
CycSessionConfiguration config = loaderMgr.getConfiguration(environment);
if (!(config instanceof EnvironmentConfiguration)) {
configCache.put(environment, config);
}
return config;
}
@Override
public T getCurrentSession()
throws SessionConfigurationException, SessionInitializationException, SessionCommunicationException {
errorIfClosed("Cannot retrieve current session.");
reapDeadSessions();
if (!this.hasCurrentSession()) {
if (!configuration.isSessionAutoCreationAllowed()) {
throw new SessionInitializationException("No currrent session is set, and session auto-creation is not allowed: ");
}
LOGGER.debug("#getCurrentSession: There is no current session. Retrieving...");
initCurrentSession();
}
return currentObjectCache.getCurrentSession();
}
public Set getSessions(SessionSelector criteria) throws SessionException {
return sessionCache.getAll(criteria);
}
@Override
public boolean isClosed() {
return this.closed;
}
/**
* Closes the SessionManagerImpl instance, releasing any underlying resources used by it, by all
* of its caches and factories, and by all CycSessions which it has created.
*
* @throws IOException
*/
@Override
public void close() throws IOException {
try {
LOGGER.debug("Closing SessionManager instance {}", this);
closed = true;
// TODO: is there more cleanup which would be necessary? - nwinant, 2015-10-20
clearCurrentSession();
final Collection sessions = this.sessionCache.getAll();
for (T session : sessions) {
try {
this.removeSession(session);
} catch (SessionException ex) {
LOGGER.error("Error releasing session " + session, ex);
}
}
} finally {
this.getSessionFactory().close();
LOGGER.warn("{} closed: {}", SessionManager.class.getSimpleName(), this);
}
}
// Protected
protected T createSession(CycSessionConfiguration config)
throws SessionConfigurationException, SessionCommunicationException, SessionInitializationException {
errorIfClosed("New sessions cannot be created.");
final long startMillis = System.currentTimeMillis();
final SessionFactory factory = getSessionFactory();
final T session = factory.createSession(config);
if (!isValidSession(session)) {
throw new SessionConfigurationException(
"Session " + session + " is not valid; could not retrieve a valid session from " + factory.getClass());
}
getSessionFactory().initializeSession(session);
if (ConnectionStatus.UNINITIALIZED.equals(session.getConnectionStatus())) {
throw new SessionInitializationException("Session " + session + " could not be initialized by " + factory);
}
session.addListener(new SessionListener() {
@Override
public void onClose(Thread thread) {
try {
onSessionRelease(session, thread);
} catch (SessionException ex) {
throw SessionRuntimeException.fromThrowable(ex);
}
}
});
sessionCache.add(session, getEnvironmentConfiguration());
LOGGER.debug("Created new session in {}ms; {} sessions total. Session: {}", (System.currentTimeMillis() - startMillis), sessionCache.size(), session);
return session;
}
protected T createSession() throws SessionConfigurationException, SessionCommunicationException, SessionInitializationException {
// This method formerly implemented SessionManager#getSession before that method was removed (and this method was renamed) - nwinant, 2015-10-16
final CycSessionConfiguration config = getSessionConfiguration();
final ConfigurationValidator configUtils = new ConfigurationValidator(config);
if (!configUtils.isSufficient()) {
throw new SessionConfigurationException("Configuration " + config + " is not sufficient to create a valid session");
}
return createSession(config);
}
/**
* Sets the current CycSession. It will not allow null values. If you need to
* clear the current CycSession, call {@link #clearCurrentSession}.
*
* @param session
* @return the session
* @throws SessionConfigurationException
*/
public T setCurrentSession(T session) throws SessionConfigurationException {
if (!isValidSession(session)) {
throw new SessionConfigurationException("Session " + session + " is not valid. Cannot set it to the current session.");
}
currentObjectCache.setCurrentSession(session);
LOGGER.debug("Current session set: {}", session);
return session;
}
protected boolean hasCurrentSession() {
return currentObjectCache.hasCurrentSession();
}
protected SessionFactory getSessionFactory() {
return this.sessionFactory;
}
// Private
private boolean isValidSession(CycSession session) {
// TODO: this could be just a biiiiiiiit more fleshed out...
return (session != null) && !session.isClosed();
}
private void initCurrentSession() throws SessionConfigurationException, SessionInitializationException, SessionCommunicationException {
LOGGER.debug("Initializing current session");
setCurrentSession(createSession());
}
private void clearCurrentSession() {
LOGGER.debug("Clearing current session");
currentObjectCache.clearCurrentSession();
}
private void releaseCycServerIfAppropriate(T session) throws SessionException {
// We skip this altogether when called during/after SessionManagerImpl#close(). It can no longer
// retrieve a configuration, and it's unnecessary anyway: the servers get released when
// SessionManagerImpl#close() closes the SessionFactory. - nwinant, 2016-08-03
if (!isClosed() && configuration.isServerReleasedWhenAllSessionsAreClosed()) {
final CycAddress server = session.getServerInfo().getCycAddress();
if (getSessions(new CycServerSelector(server)).isEmpty()) {
LOGGER.info("Releasing all server resources for {}", server);
this.getSessionFactory().releaseResourcesForServer(server);
}
}
}
synchronized private void removeSession(T session) throws SessionException {
if (session == null) {
return;
}
if (hasCurrentSession() && (currentObjectCache.getCurrentSession().equals(session))) {
LOGGER.debug("Reaping current session with status {}: {}", session.getConnectionStatus(), session);
// TODO: This behavior should be correct, but let's evaluate it more carefully. - nwinant, 2015-10-16
this.clearCurrentSession();
} else {
LOGGER.debug("Reaping session with status {}: {}", session.getConnectionStatus(), session);
}
if (!session.isClosed()) {
session.close();
}
sessionCache.remove(session);
LOGGER.debug("{} sessions remaining", sessionCache.size());
releaseCycServerIfAppropriate(session);
}
private void onSessionRelease(T session, Thread thread) throws SessionException {
LOGGER.debug("Releasing session {} from thread {}", session, thread);
removeSession(session);
}
private boolean isSessionDead(CycSession session) {
return (session == null)
|| session.isClosed()
|| !ConnectionStatus.CONNECTED.equals(session.getConnectionStatus());
}
synchronized private void reapDeadSessions() {
try {
// TODO: Should we consider allowing closed sessions to remain in the cache for diagnostics / debugging / least surprise?
final long startMillis = System.currentTimeMillis();
int numSessionsReaped = 0;
if (hasCurrentSession()) {
T currSession = currentObjectCache.getCurrentSession();
if (isSessionDead(currSession)) {
removeSession(currSession);
numSessionsReaped++;
}
}
final Collection cachedSessions = sessionCache.getAll();
for (T cachedSession : cachedSessions) {
if ((cachedSession != null) && isSessionDead(cachedSession)) {
removeSession(cachedSession);
numSessionsReaped++;
}
}
final long duration = System.currentTimeMillis() - startMillis;
if ((numSessionsReaped > 0) || (duration > 1)) {
LOGGER.debug("Reaped {} sessions in {}ms; {} sessions remaining", numSessionsReaped, (System.currentTimeMillis() - startMillis), sessionCache.size());
}
} catch (SessionException ex) {
throw SessionRuntimeException.fromThrowable(ex);
}
}
/**
* Returns an implementation of {@link SessionFactory}. It will return the first
* implementation it finds which has been registered per {@link java.util.ServiceLoader}.
*
* @return an object which implements SessionFactory.
* @throws com.cyc.session.exception.SessionServiceException
*/
private SessionFactory loadSessionFactory() throws SessionServiceException {
final List factories = loadAllSessionFactories();
if (factories.isEmpty()) {
throw new SessionServiceException(SessionFactory.class, "Could not find a service implementation!");
}
for (SessionFactory factory : factories) {
LOGGER.debug("Found {}: {}", SessionFactory.class.getSimpleName(), factory.getClass().getName());
}
return factories.get(0);
}
private void errorIfClosed(String msg) {
if (isClosed()) {
throw new SessionRuntimeException(getClass().getSimpleName() + " has been closed. " + msg + " " + this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy