/**
* Copyright 2012 the original author or authors.
*
* 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.
*/
package de.jbellmann.tomcat.cassandra;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Loader;
import org.apache.catalina.Session;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.StandardSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
/**
*
* @author Joerg Bellmann
*
*/
public abstract class CassandraManager extends ManagerBase {
private final Log log = LogFactory.getLog(CassandraManager.class);
protected String name = "cassandra-manager";
protected AtomicInteger rejected = new AtomicInteger(0);
protected CassandraTemplate cassandraTemplate = createCassandraTemplate();
public abstract CassandraTemplate createCassandraTemplate();
void setName(String name) {
this.name = name;
}
protected CassandraOperations getCassandraOperations() {
return this.cassandraTemplate;
}
public void setCassandraTemplate(CassandraTemplate cassandraTemplate) {
this.cassandraTemplate = cassandraTemplate;
}
public ClassLoader getClassLoader() {
ClassLoader clazzLoader = getClass().getClassLoader();
Container container = getContainer();
if (container != null) {
Loader loader = container.getLoader();
if (loader != null) {
ClassLoader classLoader = loader.getClassLoader();
if (classLoader != null) {
clazzLoader = classLoader;
}
}
}
return clazzLoader;
}
@Override
public int getRejectedSessions() {
return rejected.get();
}
public void setRejectedSessions(int i) {
rejected.set(i);
}
/**
* Should we really load all sessions. 10000, 1000000, 10000000000. Should we?
*
*/
@Override
public void load() {
List<String> sessionIds = getCassandraOperations().findSessionKeys();
for (String sessionId : sessionIds) {
try {
if (log.isDebugEnabled()) {
log.debug("Loading session : " + sessionId);
}
findSession(sessionId);
} catch (IOException e) {
log.error("IOException on CassandraManager.load() with sessionId: " + sessionId, e);
}
}
}
@Override
protected void startInternal() throws LifecycleException {
log.info("Starting Cassandra Session Manager");
try {
this.cassandraTemplate.initialize(getClassLoader());
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerLoad"), t);
}
super.startInternal();
// Load unloaded sessions, if any
try {
load();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerLoad"), t);
}
setState(LifecycleState.STARTING);
log.info("Cassandra Session Manager started");
}
@Override
protected void stopInternal() throws LifecycleException {
log.info("Stopping Cassandra Session Manager");
setState(LifecycleState.STOPPING);
// Write out sessions
try {
unload();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerUnload"), t);
}
// Expire all active sessions
Session[] sessions = findSessions();
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
try {
if (session.isValid()) {
session.expire();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
// Measure against memory leaking if references to the session
// object are kept in a shared field somewhere
session.recycle();
}
}
try {
this.cassandraTemplate.shutdown();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("standardManager.managerUnload"), t);
}
log.info("Cassandra Session Manager stopped");
// Require a new random number generator if we are restarted
super.stopInternal();
}
@Override
public void unload() {
// NO-OP
}
@Override
public Session findSession(String id) throws IOException {
Session sess = super.findSession(id);
if (null != sess) {
if (log.isDebugEnabled()) {
log.debug("Returning cached session: " + sess);
}
return sess;
}
long lastAccessedTime = getCassandraOperations().getLastAccessedTime(id);
if (lastAccessedTime < 0) {
// no session found in cassandra
return null;
} else {
sess = createSession(id);
sessions.put(sess.getId(), sess);
}
return sess;
}
protected Session findSessionInternal(String id) {
try {
return super.findSession(id);
} catch (IOException e) {
log.error("IOException on Cassandra.findSessionInternal() with sessionId " + id, e);
}
return null;
}
@Override
public Session[] findSessions() {
List<Session> sessions = new ArrayList<Session>();
List<String> sessionIds = getCassandraOperations().findSessionKeys();
for (String sessionId : sessionIds) {
Session s = null;
try {
s = findSession(sessionId);
} catch (IOException e) {
log.error("IOException when finding session with id " + sessionId, e);
}
if (s != null) {
sessions.add(s);
}
}
return sessions.toArray(new Session[sessions.size()]);
}
@Override
public void remove(Session session) {
getCassandraOperations().removeSession(session.getId());
}
@Override
protected StandardSession getNewSession() {
return new CassandraSession(this);
}
@Override
public HashMap<String, String> getSession(String sessionId) {
return super.getSession(sessionId);
}
@Override
public void expireSession(String sessionId) {
Session session = null;
try {
session = findSession(sessionId);
} catch (IOException e) {
log.error("IOException on CassandraManager.expireSession()-method with sessionId:" + sessionId, e);
}
if (session != null) {
session.expire();
}
}
@Override
public String getCreationTime(String sessionId) {
Session session = findSessionInternal(sessionId);
return session != null ? new Date(session.getCreationTime()).toString() : "";
}
@Override
public long getCreationTimestamp(String sessionId) {
Session session = findSessionInternal(sessionId);
return session != null ? session.getCreationTime() : -1;
}
@Override
public long getLastAccessedTimestamp(String sessionId) {
Session session = findSessionInternal(sessionId);
return session != null ? session.getLastAccessedTime() : -1;
}
@Override
public String getLastAccessedTime(String sessionId) {
Session session = findSessionInternal(sessionId);
return session != null ? new Date(session.getLastAccessedTime()).toString() : "";
}
@Override
public String getSessionAttribute(String sessionId, String key) {
return getCassandraOperations().getAttribute(sessionId, key).toString();
}
@Override
public Session createEmptySession() {
return (getNewSession());
}
@Override
public Session createSession(String sessionId) {
CassandraSession session = (CassandraSession) createEmptySession();
session.setNew(true);
session.setValid(true);
session.setMaxInactiveInterval(maxInactiveInterval);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id, true);
session.setCreationTime(System.currentTimeMillis());
session.setLastAccessedTime(System.currentTimeMillis());
sessionCounter++;
//
return session;
}
// GETTER-SETTER
public String getColumnFamilyName() {
return this.cassandraTemplate.getColumnFamilyName();
}
public void setColumnFamilyName(String columnFamilyName) {
this.cassandraTemplate.setColumnFamilyName(columnFamilyName);
}
public String getClusterName() {
return this.cassandraTemplate.getClusterName();
}
public void setClusterName(String clusterName) {
this.cassandraTemplate.setClusterName(clusterName);
}
public String getKeyspaceName() {
return this.cassandraTemplate.getKeyspaceName();
}
public void setKeyspaceName(String keyspaceName) {
this.cassandraTemplate.setKeyspaceName(keyspaceName);
}
public String getHosts() {
return this.cassandraTemplate.getHosts();
}
public void setHosts(String hosts) {
this.cassandraTemplate.setHosts(hosts);
}
public int getMaxActiveConnections() {
return this.cassandraTemplate.getMaxActive();
}
public void setMaxActiveConnections(int maxActive) {
this.cassandraTemplate.setMaxActive(maxActive);
}
public int getMaxIdle() {
return this.cassandraTemplate.getMaxIdle();
}
public void setMaxIdle(int maxIdle) {
this.cassandraTemplate.setMaxIdle(maxIdle);
}
public int getThriftSocketTimeout() {
return this.cassandraTemplate.getThriftSocketTimeout();
}
public void setThriftSocketTimeout(int thriftSocketTimeout) {
this.cassandraTemplate.setThriftSocketTimeout(thriftSocketTimeout);
}
public long getMaxWaitTimeWhenExhausted() {
return this.cassandraTemplate.getMaxWaitTimeWhenExhausted();
}
public void setMaxWaitTimeWhenExhausted(long maxWaitTimeWhenExhausted) {
this.cassandraTemplate.setMaxWaitTimeWhenExhausted(maxWaitTimeWhenExhausted);
}
public String getStrategyClassName() {
return this.cassandraTemplate.getStrategyClassName();
}
public void setStrategyClassName(String strategyClassName) {
this.cassandraTemplate.setStrategyClassName(strategyClassName);
}
public int getReplicationFactor() {
return this.cassandraTemplate.getReplicationFactor();
}
public void setReplicationFactor(int replicationFactor) {
this.cassandraTemplate.setReplicationFactor(replicationFactor);
}
public boolean isLogSessionsOnStartup() {
return this.cassandraTemplate.isLogSessionsOnStartup();
}
public void setLogSessionsOnStartup(boolean logSessionsOnStartup) {
this.cassandraTemplate.setLogSessionsOnStartup(logSessionsOnStartup);
}
}