de.javakaffee.web.msm.MemcachedBackupSessionManager Maven / Gradle / Ivy
/*
* Copyright 2009 Martin Grotzke
*
* 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.javakaffee.web.msm;
import static de.javakaffee.web.msm.Statistics.StatsType.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.spy.memcached.MemcachedClient;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.ha.session.SerializablePrincipal;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import de.javakaffee.web.msm.LockingStrategy.LockingMode;
/**
* This {@link Manager} stores session in configured memcached nodes after the
* response is finished (committed).
*
* Use this session manager in a Context element, like this
* <Context path="/foo">
* <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
* memcachedNodes="n1.localhost:11211 n2.localhost:11212" failoverNodes="n2"
* connectionType="SASL" non-required
* username="username" non-required
* password="password" non-required
* requestUriIgnorePattern=".*\.(png|gif|jpg|css|js)$" />
* </Context>
*
*
*
* @author Martin Grotzke
* @version $Id$
*/
public class MemcachedBackupSessionManager extends ManagerBase implements Lifecycle, PropertyChangeListener, MemcachedSessionService.SessionManager {
protected static final String NAME = MemcachedBackupSessionManager.class.getSimpleName();
private static final String INFO = NAME + "/1.0";
protected final Log _log = LogFactory.getLog( getClass() );
private final LifecycleSupport _lifecycle = new LifecycleSupport( this );
private int _maxActiveSessions = -1;
private int _rejectedSessions;
/**
* Has this component been _started yet?
*/
protected boolean _started = false;
protected MemcachedSessionService _msm;
public MemcachedBackupSessionManager() {
_msm = new MemcachedSessionService( this );
}
/**
* Return descriptive information about this Manager implementation and the
* corresponding version number, in the format
* <description>/<version>
.
*
* @return the info string
*/
@Override
public String getInfo() {
return INFO;
}
/**
* Return the descriptive short name of this Manager implementation.
*
* @return the short name
*/
@Override
public String getName() {
return NAME;
}
/**
* Initialize this manager. The memcachedClient parameter is there for testing
* purposes. If the memcachedClient is provided it's used, otherwise a "real"/new
* memcached client is created based on the configuration (like {@link #setMemcachedNodes(String)} etc.).
*
* @param memcachedClient the memcached client to use, for normal operations this should be null
.
*/
protected void startInternal( final MemcachedClient memcachedClient ) throws LifecycleException {
_msm.setMemcachedClient(memcachedClient);
_msm.startInternal();
}
/**
* {@inheritDoc}
*/
@Override
public void setContainer( final Container container ) {
// De-register from the old Container (if any)
if ( this.container != null && this.container instanceof Context ) {
( (Context) this.container ).removePropertyChangeListener( this );
}
// Default processing provided by our superclass
super.setContainer( container );
// Register with the new Container (if any)
if ( this.container != null && this.container instanceof Context ) {
setMaxInactiveInterval( ( (Context) this.container ).getSessionTimeout() * 60 );
( (Context) this.container ).addPropertyChangeListener( this );
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized String generateSessionId() {
return _msm.newSessionId( super.generateSessionId() );
}
/**
* {@inheritDoc}
*/
@Override
public void expireSession( final String sessionId ) {
if ( _log.isDebugEnabled() ) {
_log.debug( "expireSession invoked: " + sessionId );
}
super.expireSession( sessionId );
_msm.deleteFromMemcached( sessionId );
}
/**
* {@inheritDoc}
*/
@Override
public void remove( final Session session ) {
remove( session, session.getNote( MemcachedSessionService.NODE_FAILURE ) != Boolean.TRUE );
}
@Override
public void removeInternal( final Session session, final boolean update ) {
// update is there for tomcat7, not available in tomcat6
super.remove( session );
}
private void remove( final Session session, final boolean removeFromMemcached ) {
if ( _log.isDebugEnabled() ) {
_log.debug( "remove invoked, removeFromMemcached: " + removeFromMemcached +
", id: " + session.getId() );
}
if ( removeFromMemcached ) {
_msm.deleteFromMemcached( session.getId() );
}
super.remove( session );
_msm.sessionRemoved((MemcachedBackupSession) session);
}
/**
* Return the active Session, associated with this Manager, with the
* specified session id (if any); otherwise return null
.
*
* @param id
* The session id for the session to be returned
* @return the session or null
if no session was found locally
* or in memcached.
*
* @exception IllegalStateException
* if a new session cannot be instantiated for any reason
* @exception IOException
* if an input/output error occurs while processing this
* request
*/
@Override
public Session findSession( final String id ) throws IOException {
return _msm.findSession( id );
}
/**
* {@inheritDoc}
*/
@Override
public MemcachedBackupSession createSession( final String sessionId ) {
return _msm.createSession( sessionId );
}
/**
* {@inheritDoc}
*/
@Override
public MemcachedBackupSession createEmptySession() {
return _msm.createEmptySession();
}
@Override
public MemcachedBackupSession newMemcachedBackupSession() {
return new MemcachedBackupSession( this );
}
@Override
public void changeSessionId( final Session session ) {
// e.g. invoked by the AuthenticatorBase (for BASIC auth) on login to prevent session fixation
// so that session backup won't be omitted we must store this event
super.changeSessionId( session );
((MemcachedBackupSession)session).setSessionIdChanged( true );
}
/**
* Set the maximum number of active Sessions allowed, or -1 for no limit.
*
* @param max
* The new maximum number of sessions
*/
public void setMaxActiveSessions( final int max ) {
final int oldMaxActiveSessions = _maxActiveSessions;
_maxActiveSessions = max;
support.firePropertyChange( "maxActiveSessions",
Integer.valueOf( oldMaxActiveSessions ),
Integer.valueOf( _maxActiveSessions ) );
}
@Override
public int getMaxActiveSessions() {
return _maxActiveSessions;
}
@Override
public void setRejectedSessions( final int rejectedSessions ) {
_rejectedSessions = rejectedSessions;
}
@Override
public int getRejectedSessions() {
return _rejectedSessions;
}
/**
* {@inheritDoc}
*/
@Override
public void load() throws ClassNotFoundException, IOException {
}
/**
* {@inheritDoc}
*/
@Override
public void unload() throws IOException {
}
/**
* Set the memcached nodes space or comma separated.
*
* E.g. n1.localhost:11211 n2.localhost:11212
*
*
* When the memcached nodes are set when this manager is already initialized,
* the new configuration will be loaded.
*
*
* @param memcachedNodes
* the memcached node definitions, whitespace or comma separated
*/
@Override
public void setMemcachedNodes( final String memcachedNodes ) {
_msm.setMemcachedNodes( memcachedNodes );
}
/**
* The memcached nodes configuration as provided in the server.xml/context.xml.
*
* This getter is there to make this configuration accessible via jmx.
*
* @return the configuration string for the memcached nodes.
*/
public String getMemcachedNodes() {
return _msm.getMemcachedNodes();
}
/**
* The node ids of memcached nodes, that shall only be used for session
* backup by this tomcat/manager, if there are no other memcached nodes
* left. Node ids are separated by whitespace or comma.
*
* E.g. n1 n2
*
*
* When the failover nodes are set when this manager is already initialized,
* the new configuration will be loaded.
*
*
* @param failoverNodes
* the failoverNodes to set, whitespace or comma separated
*/
@Override
public void setFailoverNodes( final String failoverNodes ) {
_msm.setFailoverNodes( failoverNodes );
}
/**
* The memcached failover nodes configuration as provided in the server.xml/context.xml.
*
* This getter is there to make this configuration accessible via jmx.
*
* @return the configuration string for the failover nodes.
*/
public String getFailoverNodes() {
return _msm.getFailoverNodes();
}
/**
* Set the regular expression for request uris to ignore for session backup.
* This should include static resources like images, in the case they are
* served by tomcat.
*
* E.g. .*\.(png|gif|jpg|css|js)$
*
*
* @param requestUriIgnorePattern
* the requestUriIgnorePattern to set
* @author Martin Grotzke
*/
public void setRequestUriIgnorePattern( final String requestUriIgnorePattern ) {
_msm.setRequestUriIgnorePattern( requestUriIgnorePattern );
}
/**
* Return the compiled pattern used for including session attributes to a session-backup.
*
* @return the sessionAttributePattern
*/
@CheckForNull
Pattern getSessionAttributePattern() {
return _msm.getSessionAttributePattern();
}
/**
* Return the string pattern used for including session attributes to a session-backup.
*
* @return the sessionAttributeFilter
*/
@CheckForNull
public String getSessionAttributeFilter() {
return _msm.getSessionAttributeFilter();
}
/**
* Set the pattern used for including session attributes to a session-backup.
* If not set, all session attributes will be part of the session-backup.
*
* E.g. ^(userName|sessionHistory)$
*
*
* @param sessionAttributeFilter
* the sessionAttributeNames to set
*/
public void setSessionAttributeFilter( @Nullable final String sessionAttributeFilter ) {
_msm.setSessionAttributeFilter( sessionAttributeFilter );
}
/**
* The class of the factory that creates the
* {@link net.spy.memcached.transcoders.Transcoder} to use for serializing/deserializing
* sessions to/from memcached (requires a default/no-args constructor).
* The default value is the {@link JavaSerializationTranscoderFactory} class
* (used if this configuration attribute is not specified).
*
* After the {@link TranscoderFactory} instance was created from the specified class,
* {@link TranscoderFactory#setCopyCollectionsForSerialization(boolean)}
* will be invoked with the currently set copyCollectionsForSerialization
propery, which
* has either still the default value (false
) or the value provided via
* {@link #setCopyCollectionsForSerialization(boolean)}.
*
*
* @param transcoderFactoryClassName the {@link TranscoderFactory} class name.
*/
public void setTranscoderFactoryClass( final String transcoderFactoryClassName ) {
_msm.setTranscoderFactoryClass( transcoderFactoryClassName );
}
/**
* Specifies, if iterating over collection elements shall be done on a copy
* of the collection or on the collection itself. The default value is false
* (used if this configuration attribute is not specified).
*
* This option can be useful if you have multiple requests running in
* parallel for the same session (e.g. AJAX) and you are using
* non-thread-safe collections (e.g. {@link java.util.ArrayList} or
* {@link java.util.HashMap}). In this case, your application might modify a
* collection while it's being serialized for backup in memcached.
*
*
* Note: This must be supported by the {@link TranscoderFactory}
* specified via {@link #setTranscoderFactoryClass(String)}: after the {@link TranscoderFactory} instance
* was created from the specified class, {@link TranscoderFactory#setCopyCollectionsForSerialization(boolean)}
* will be invoked with the provided copyCollectionsForSerialization
value.
*
*
* @param copyCollectionsForSerialization
* true
, if iterating over collection elements shall be done
* on a copy of the collection, false
if the collections own iterator
* shall be used.
*/
public void setCopyCollectionsForSerialization( final boolean copyCollectionsForSerialization ) {
_msm.setCopyCollectionsForSerialization( copyCollectionsForSerialization );
}
/**
* Custom converter allow you to provide custom serialization of application specific
* types. Multiple converter classes are separated by comma (with optional space following the comma).
*
* This option is useful if reflection based serialization is very verbose and you want
* to provide a more efficient serialization for a specific type.
*
*
* Note: This must be supported by the {@link TranscoderFactory}
* specified via {@link #setTranscoderFactoryClass(String)}: after the {@link TranscoderFactory} instance
* was created from the specified class, {@link TranscoderFactory#setCustomConverterClassNames(String[])}
* is invoked with the provided custom converter class names.
*
* Requirements regarding the specific custom converter classes depend on the
* actual serialization strategy, but a common requirement would be that they must
* provide a default/no-args constructor.
* For more details have a look at
* SerializationStrategies.
*
*
* @param customConverterClassNames a list of class names separated by comma
*/
public void setCustomConverter( final String customConverterClassNames ) {
_msm.setCustomConverter( customConverterClassNames );
}
/**
* Specifies if statistics (like number of requests with/without session) shall be
* gathered. Default value of this property is true
.
*
* Statistics will be available via jmx and the Manager mbean (
* e.g. in the jconsole mbean tab open the attributes node of the
* Catalina/Manager/<context-path>/<host name>
* mbean and check for msmStat* values.
*
*
* @param enableStatistics true
if statistics shall be gathered.
*/
public void setEnableStatistics( final boolean enableStatistics ) {
_msm.setEnableStatistics( enableStatistics );
}
/**
* Specifies the number of threads that are used if {@link #setSessionBackupAsync(boolean)}
* is set to true
.
*
* @param backupThreadCount the number of threads to use for session backup.
*/
public void setBackupThreadCount( final int backupThreadCount ) {
_msm.setBackupThreadCount( backupThreadCount );
}
/**
* The number of threads to use for session backup if session backup shall be
* done asynchronously.
* @return the number of threads for session backup.
*/
public int getBackupThreadCount() {
return _msm.getBackupThreadCount();
}
/**
* Specifies the memcached protocol to use, either "text" (default) or "binary".
*
* @param memcachedProtocol one of "text" or "binary".
*/
public void setMemcachedProtocol( final String memcachedProtocol ) {
_msm.setMemcachedProtocol( memcachedProtocol );
}
/**
* Enable/disable memcached-session-manager (default true
/ enabled).
* If disabled, sessions are neither looked up in memcached nor stored in memcached.
*
* @param enabled specifies if msm shall be disabled or not.
* @throws IllegalStateException it's not allowed to disable this session manager when running in non-sticky mode.
*/
@Override
public void setEnabled( final boolean enabled ) throws IllegalStateException {
_msm.setEnabled( enabled );
}
/**
* Specifies, if msm is enabled or not.
*
* @return true
if enabled, otherwise false
.
*/
public boolean isEnabled() {
return _msm.isEnabled();
}
@Override
public void setSticky( final boolean sticky ) {
_msm.setSticky( sticky );
}
public boolean isSticky() {
return _msm.isSticky();
}
@Override
public void setOperationTimeout(final long operationTimeout ) {
_msm.setOperationTimeout(operationTimeout);
}
public long getOperationTimeout() {
return _msm.getOperationTimeout();
}
/**
* Sets the session locking mode. Possible values:
*
* none
- does not lock the session at all (default for non-sticky sessions).
* all
- the session is locked for each request accessing the session.
* auto
- locks the session for each request except for those the were detected to access the session only readonly.
* uriPattern:<regexp>
- locks the session for each request with a request uri (with appended querystring) matching
* the provided regular expression.
*
*/
@Override
public void setLockingMode( @Nullable final String lockingMode ) {
_msm.setLockingMode( lockingMode );
}
@Override
public void setLockingMode( @Nullable final LockingMode lockingMode, @Nullable final Pattern uriPattern, final boolean storeSecondaryBackup ) {
_msm.setLockingMode( lockingMode, uriPattern, storeSecondaryBackup );
}
@Override
public void setUsername(final String username) {
_msm.setUsername(username);
}
@Override
public void setPassword(final String password) {
_msm.setPassword(password);
}
/**
* {@inheritDoc}
*/
@Override
public void addLifecycleListener( final LifecycleListener arg0 ) {
_lifecycle.addLifecycleListener( arg0 );
}
/**
* {@inheritDoc}
*/
@Override
public LifecycleListener[] findLifecycleListeners() {
return _lifecycle.findLifecycleListeners();
}
/**
* {@inheritDoc}
*/
@Override
public void removeLifecycleListener( final LifecycleListener arg0 ) {
_lifecycle.removeLifecycleListener( arg0 );
}
/**
* {@inheritDoc}
*/
@Override
public void start() throws LifecycleException {
if( ! initialized ) {
init();
}
// Validate and update our current component state
if (_started) {
return;
}
_lifecycle.fireLifecycleEvent(START_EVENT, null);
_started = true;
// Force initialization of the random number generator
if (log.isDebugEnabled()) {
log.debug("Force random number initialization starting");
}
super.generateSessionId();
if (log.isDebugEnabled()) {
log.debug("Force random number initialization completed");
}
startInternal( null );
}
/**
* {@inheritDoc}
*/
@Override
public void stop() throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug("Stopping");
}
// Validate and update our current component state
if (!_started) {
throw new LifecycleException
(sm.getString("standardManager.notStarted"));
}
_lifecycle.fireLifecycleEvent(STOP_EVENT, null);
_started = false;
// Require a new random number generator if we are restarted
random = null;
if ( initialized ) {
if ( _msm.isSticky() ) {
_log.info( "Removing sessions from local session map." );
for( final Session session : sessions.values() ) {
swapOut( (StandardSession) session );
}
}
_msm.shutdown();
destroy();
}
}
private void swapOut( @Nonnull final StandardSession session ) {
// implementation like the one in PersistentManagerBase.swapOut
if (!session.isValid()) {
return;
}
session.passivate();
remove( session, false );
session.recycle();
}
/**
* {@inheritDoc}
*/
@Override
public void backgroundProcess() {
_msm.updateExpirationInMemcached();
super.backgroundProcess();
}
/**
* {@inheritDoc}
*/
@Override
public void propertyChange( final PropertyChangeEvent event ) {
// Validate the source of this event
if ( !( event.getSource() instanceof Context ) ) {
return;
}
// Process a relevant property change
if ( event.getPropertyName().equals( "sessionTimeout" ) ) {
try {
setMaxInactiveInterval( ( (Integer) event.getNewValue() ).intValue() * 60 );
} catch ( final NumberFormatException e ) {
_log.warn( "standardManager.sessionTimeout: " + event.getNewValue().toString() );
}
}
}
/**
* Specifies if the session shall be stored asynchronously in memcached as
* {@link MemcachedClient#set(String, int, Object)} supports it. If this is
* false, the timeout set via {@link #setSessionBackupTimeout(int)} is
* evaluated. If this is true
, the {@link #setBackupThreadCount(int)}
* is evaluated.
*
* By default this property is set to true
- the session
* backup is performed asynchronously.
*
*
* @param sessionBackupAsync
* the sessionBackupAsync to set
*/
public void setSessionBackupAsync( final boolean sessionBackupAsync ) {
_msm.setSessionBackupAsync( sessionBackupAsync );
}
/**
* Specifies if the session shall be stored asynchronously in memcached as
* {@link MemcachedClient#set(String, int, Object)} supports it. If this is
* false, the timeout from {@link #getSessionBackupTimeout()} is
* evaluated.
*/
public boolean isSessionBackupAsync() {
return _msm.isSessionBackupAsync();
}
/**
* The timeout in milliseconds after that a session backup is considered as
* beeing failed.
*
* This property is only evaluated if sessions are stored synchronously (set
* via {@link #setSessionBackupAsync(boolean)}).
*
*
* The default value is 100
millis.
*
* @param sessionBackupTimeout
* the sessionBackupTimeout to set (milliseconds)
*/
public void setSessionBackupTimeout( final int sessionBackupTimeout ) {
_msm.setSessionBackupTimeout( sessionBackupTimeout );
}
/**
* The timeout in milliseconds after that a session backup is considered as
* beeing failed when {@link #getSessionBackupAsync()}) is false
.
*/
public long getSessionBackupTimeout() {
return _msm.getSessionBackupTimeout();
}
// ------------------------- statistics via jmx ----------------
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithBackupFailure()
*/
public long getMsmStatNumBackupFailures() {
return _msm.getStatistics().getRequestsWithBackupFailure();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithMemcachedFailover()
*/
public long getMsmStatNumTomcatFailover() {
return _msm.getStatistics().getRequestsWithTomcatFailover();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithMemcachedFailover()
*/
public long getMsmStatNumMemcachedFailover() {
return _msm.getStatistics().getRequestsWithMemcachedFailover();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithoutSession()
*/
public long getMsmStatNumRequestsWithoutSession() {
return _msm.getStatistics().getRequestsWithoutSession();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithoutSessionAccess()
*/
public long getMsmStatNumNoSessionAccess() {
return _msm.getStatistics().getRequestsWithoutSessionAccess();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithoutAttributesAccess()
*/
public long getMsmStatNumNoAttributesAccess() {
return _msm.getStatistics().getRequestsWithoutAttributesAccess();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithoutSessionModification()
*/
public long getMsmStatNumNoSessionModification() {
return _msm.getStatistics().getRequestsWithoutSessionModification();
}
/**
* @return
* @see de.javakaffee.web.msm.Statistics#getRequestsWithSession()
*/
public long getMsmStatNumRequestsWithSession() {
return _msm.getStatistics().getRequestsWithSession();
}
public long getMsmStatNumNonStickySessionsPingFailed() {
return _msm.getStatistics().getNonStickySessionsPingFailed();
}
public long getMsmStatNumNonStickySessionsReadOnlyRequest() {
return _msm.getStatistics().getNonStickySessionsReadOnlyRequest();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that took the attributes serialization.
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatAttributesSerializationInfo() {
return _msm.getStatistics().getProbe( ATTRIBUTES_SERIALIZATION ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that session backups took in the request thread (including omitted
* session backups e.g. because the session attributes were not accessed).
* This time was spent in the request thread.
*
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatEffectiveBackupInfo() {
return _msm.getStatistics().getProbe( EFFECTIVE_BACKUP ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that session backups took (excluding backups where a session
* was relocated). This time was spent in the request thread if session backup
* is done synchronously, otherwise another thread used this time.
*
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatBackupInfo() {
return _msm.getStatistics().getProbe( BACKUP ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that loading sessions from memcached took (including deserialization).
* @return a String array for statistics inspection via jmx.
* @see #getMsmStatSessionDeserializationInfo()
* @see #getMsmStatNonStickyAfterLoadFromMemcachedInfo()
*/
public String[] getMsmStatSessionsLoadedFromMemcachedInfo() {
return _msm.getStatistics().getProbe( LOAD_FROM_MEMCACHED ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that deleting sessions from memcached took.
* @return a String array for statistics inspection via jmx.
* @see #getMsmStatNonStickyAfterDeleteFromMemcachedInfo()
*/
public String[] getMsmStatSessionsDeletedFromMemcachedInfo() {
return _msm.getStatistics().getProbe( DELETE_FROM_MEMCACHED ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that deserialization of session data took.
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatSessionDeserializationInfo() {
return _msm.getStatistics().getProbe( SESSION_DESERIALIZATION ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the size of the data that was sent to memcached.
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatCachedDataSizeInfo() {
return _msm.getStatistics().getProbe( CACHED_DATA_SIZE ).getInfo();
}
/**
* Returns a string array with labels and values of count, min, avg and max
* of the time that storing data in memcached took (excluding serialization,
* including compression).
* @return a String array for statistics inspection via jmx.
*/
public String[] getMsmStatMemcachedUpdateInfo() {
return _msm.getStatistics().getProbe( MEMCACHED_UPDATE ).getInfo();
}
/**
* Info about locks acquired in non-sticky mode.
*/
public String[] getMsmStatNonStickyAcquireLockInfo() {
return _msm.getStatistics().getProbe( ACQUIRE_LOCK ).getInfo();
}
/**
* Lock acquiration in non-sticky session mode.
*/
public String[] getMsmStatNonStickyAcquireLockFailureInfo() {
return _msm.getStatistics().getProbe( ACQUIRE_LOCK_FAILURE ).getInfo();
}
/**
* Lock release in non-sticky session mode.
*/
public String[] getMsmStatNonStickyReleaseLockInfo() {
return _msm.getStatistics().getProbe( RELEASE_LOCK ).getInfo();
}
/**
* Tasks executed (in the request thread) for non-sticky sessions at the end of requests that did not access
* the session (validity load/update, ping session, ping 2nd session backup, update validity backup).
*/
public String[] getMsmStatNonStickyOnBackupWithoutLoadedSessionInfo() {
return _msm.getStatistics().getProbe( NON_STICKY_ON_BACKUP_WITHOUT_LOADED_SESSION ).getInfo();
}
/**
* Tasks executed for non-sticky sessions after session backup (ping session, store validity info / meta data,
* store additional backup in secondary memcached).
*/
public String[] getMsmStatNonStickyAfterBackupInfo() {
return _msm.getStatistics().getProbe( NON_STICKY_AFTER_BACKUP ).getInfo();
}
/**
* Tasks executed for non-sticky sessions after a session was loaded from memcached (load validity info / meta data).
*/
public String[] getMsmStatNonStickyAfterLoadFromMemcachedInfo() {
return _msm.getStatistics().getProbe( NON_STICKY_AFTER_LOAD_FROM_MEMCACHED ).getInfo();
}
/**
* Tasks executed for non-sticky sessions after a session was deleted from memcached (delete validity info and backup data).
*/
public String[] getMsmStatNonStickyAfterDeleteFromMemcachedInfo() {
return _msm.getStatistics().getProbe( NON_STICKY_AFTER_DELETE_FROM_MEMCACHED ).getInfo();
}
// ---------------------------------------------------------------------------
@Override
public String getSessionCookieName() {
String result = getSessionCookieNameFromContext((Context) getContainer());
if ( result == null ) {
result = Globals.SESSION_COOKIE_NAME;
_log.debug( "Using session cookie name from context: " + result );
}
return result;
}
@CheckForNull
private String getSessionCookieNameFromContext( final Context context ) {
// since 6.0.27 the session cookie name, domain and path is configurable per context,
// see issue http://issues.apache.org/bugzilla/show_bug.cgi?id=48379
try {
final Method getSessionCookieName = Context.class.getDeclaredMethod( "getSessionCookieName" );
final String result = (String) getSessionCookieName.invoke( context );
if ( result != null ) {
_log.debug( "Using session cookie name from context: " + result );
}
return result;
} catch( final NoSuchMethodException e ) {
// the context does not provide the method getSessionCookieName
} catch ( final Exception e ) {
throw new RuntimeException( "Could not read session cookie name from context.", e );
}
return null;
}
@Override
public MemcachedBackupSession getSessionInternal( final String sessionId ) {
return (MemcachedBackupSession) sessions.get( sessionId );
}
@Override
public Map getSessionsInternal() {
return sessions;
}
@Override
public String getString( final String key ) {
return sm.getString( key );
}
@Override
public void incrementSessionCounter() {
sessionCounter++;
}
@Override
public void incrementRejectedSessions() {
_rejectedSessions++;
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public String getString( final String key, final Object ... args ) {
return sm.getString( key, args );
}
@Override
public Principal readPrincipal( final ObjectInputStream ois ) throws ClassNotFoundException, IOException {
return SerializablePrincipal.readPrincipal( ois, getContainer().getRealm() );
}
@Override
public MemcachedSessionService getMemcachedSessionService() {
return _msm;
}
}