Skip to content

Commit

Permalink
WFLY-4792 Add support for session manager statistics for distributabl…
Browse files Browse the repository at this point in the history
…e web applications
  • Loading branch information
pferraro committed Jul 27, 2015
1 parent 727e0e0 commit 8f90579
Show file tree
Hide file tree
Showing 22 changed files with 541 additions and 72 deletions.
@@ -0,0 +1,40 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2015, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.wildfly.clustering.ee;

/**
* Records some other object.
* @author Paul Ferraro
*/
public interface Recordable<T> {
/**
* Records the specified object
* @param object an object to record
*/
void record(T object);

/**
* Resets any previously recorded objects
*/
void reset();
}
Expand Up @@ -60,9 +60,6 @@ public InfinispanSession(String id, SessionMetaData metaData, SessionAttributes


@Override @Override
public SessionAttributes getAttributes() { public SessionAttributes getAttributes() {
if (!this.valid.get()) {
throw InfinispanWebLogger.ROOT_LOGGER.invalidSession(this.getId());
}
return this.attributes; return this.attributes;
} }


Expand All @@ -81,9 +78,6 @@ public boolean isValid() {


@Override @Override
public SessionMetaData getMetaData() { public SessionMetaData getMetaData() {
if (!this.valid.get()) {
throw InfinispanWebLogger.ROOT_LOGGER.invalidSession(this.getId());
}
return this.metaData; return this.metaData;
} }


Expand Down
Expand Up @@ -58,6 +58,7 @@
import org.wildfly.clustering.ee.Batch; import org.wildfly.clustering.ee.Batch;
import org.wildfly.clustering.ee.Batcher; import org.wildfly.clustering.ee.Batcher;
import org.wildfly.clustering.ee.Invoker; import org.wildfly.clustering.ee.Invoker;
import org.wildfly.clustering.ee.Recordable;
import org.wildfly.clustering.ee.infinispan.RetryingInvoker; import org.wildfly.clustering.ee.infinispan.RetryingInvoker;
import org.wildfly.clustering.ee.infinispan.TransactionBatch; import org.wildfly.clustering.ee.infinispan.TransactionBatch;
import org.wildfly.clustering.group.Node; import org.wildfly.clustering.group.Node;
Expand Down Expand Up @@ -94,6 +95,7 @@ public class InfinispanSessionManager<V, L> implements SessionManager<L, Transac
private final boolean persistent; private final boolean persistent;
private final Invoker invoker = new RetryingInvoker(0, 10, 100); private final Invoker invoker = new RetryingInvoker(0, 10, 100);
private final SessionIdentifierFilter filter = new SessionIdentifierFilter(); private final SessionIdentifierFilter filter = new SessionIdentifierFilter();
private final Recordable<ImmutableSession> recorder;


volatile CommandDispatcher<Scheduler> dispatcher; volatile CommandDispatcher<Scheduler> dispatcher;
private volatile Scheduler scheduler; private volatile Scheduler scheduler;
Expand All @@ -107,6 +109,7 @@ public InfinispanSessionManager(SessionFactory<V, L> factory, InfinispanSessionM
this.dispatcherFactory = configuration.getCommandDispatcherFactory(); this.dispatcherFactory = configuration.getCommandDispatcherFactory();
this.nodeFactory = configuration.getNodeFactory(); this.nodeFactory = configuration.getNodeFactory();
this.maxActiveSessions = configuration.getMaxActiveSessions(); this.maxActiveSessions = configuration.getMaxActiveSessions();
this.recorder = configuration.getInactiveSessionRecorder();
Configuration config = this.cache.getCacheConfiguration(); Configuration config = this.cache.getCacheConfiguration();
// If cache is clustered or configured with a write-through cache store // If cache is clustered or configured with a write-through cache store
// then we need to trigger any HttpSessionActivationListeners per request // then we need to trigger any HttpSessionActivationListeners per request
Expand All @@ -116,6 +119,9 @@ public InfinispanSessionManager(SessionFactory<V, L> factory, InfinispanSessionM


@Override @Override
public void start() { public void start() {
if (this.recorder != null) {
this.recorder.reset();
}
this.identifierFactory.start(); this.identifierFactory.start();
final List<Scheduler> schedulers = new ArrayList<>(2); final List<Scheduler> schedulers = new ArrayList<>(2);
schedulers.add(new SessionExpirationScheduler(this.batcher, new ExpiredSessionRemover<>(this.factory))); schedulers.add(new SessionExpirationScheduler(this.batcher, new ExpiredSessionRemover<>(this.factory)));
Expand Down Expand Up @@ -283,6 +289,16 @@ private Set<String> getSessions(Flag... flags) {
return result; return result;
} }


@Override
public int getMaxActiveSessions() {
return this.maxActiveSessions;
}

@Override
public long getActiveSessionCount() {
return this.getActiveSessions().size();
}

@CacheEntryActivated @CacheEntryActivated
public void activated(CacheEntryActivatedEvent<String, ?> event) { public void activated(CacheEntryActivatedEvent<String, ?> event) {
if (!event.isPre() && !this.persistent) { if (!event.isPre() && !this.persistent) {
Expand Down Expand Up @@ -332,6 +348,10 @@ public void removed(CacheEntryRemovedEvent<String, ?> event) {
listener.valueUnbound(new HttpSessionBindingEvent(httpSession, name, attribute)); listener.valueUnbound(new HttpSessionBindingEvent(httpSession, name, attribute));
} }
} }

if (this.recorder != null) {
this.recorder.record(session);
}
} }
} }
} }
Expand Down Expand Up @@ -421,6 +441,9 @@ public String getId() {


@Override @Override
public SessionMetaData getMetaData() { public SessionMetaData getMetaData() {
if (!this.isValid()) {
throw InfinispanWebLogger.ROOT_LOGGER.invalidSession(this.getId());
}
return this.session.getMetaData(); return this.session.getMetaData();
} }


Expand All @@ -436,6 +459,9 @@ public void invalidate() {


@Override @Override
public SessionAttributes getAttributes() { public SessionAttributes getAttributes() {
if (!this.isValid()) {
throw InfinispanWebLogger.ROOT_LOGGER.invalidSession(this.getId());
}
return this.session.getAttributes(); return this.session.getAttributes();
} }


Expand Down
Expand Up @@ -25,9 +25,11 @@
import org.infinispan.remoting.transport.Address; import org.infinispan.remoting.transport.Address;
import org.wildfly.clustering.dispatcher.CommandDispatcherFactory; import org.wildfly.clustering.dispatcher.CommandDispatcherFactory;
import org.wildfly.clustering.ee.Batcher; import org.wildfly.clustering.ee.Batcher;
import org.wildfly.clustering.ee.Recordable;
import org.wildfly.clustering.ee.infinispan.TransactionBatch; import org.wildfly.clustering.ee.infinispan.TransactionBatch;
import org.wildfly.clustering.group.NodeFactory; import org.wildfly.clustering.group.NodeFactory;
import org.wildfly.clustering.web.IdentifierFactory; import org.wildfly.clustering.web.IdentifierFactory;
import org.wildfly.clustering.web.session.ImmutableSession;
import org.wildfly.clustering.web.session.SessionContext; import org.wildfly.clustering.web.session.SessionContext;


/** /**
Expand All @@ -42,4 +44,5 @@ public interface InfinispanSessionManagerConfiguration {
CommandDispatcherFactory getCommandDispatcherFactory(); CommandDispatcherFactory getCommandDispatcherFactory();
NodeFactory<Address> getNodeFactory(); NodeFactory<Address> getNodeFactory();
int getMaxActiveSessions(); int getMaxActiveSessions();
Recordable<ImmutableSession> getInactiveSessionRecorder();
} }
Expand Up @@ -39,6 +39,7 @@
import org.jboss.modules.Module; import org.jboss.modules.Module;
import org.wildfly.clustering.dispatcher.CommandDispatcherFactory; import org.wildfly.clustering.dispatcher.CommandDispatcherFactory;
import org.wildfly.clustering.ee.Batcher; import org.wildfly.clustering.ee.Batcher;
import org.wildfly.clustering.ee.Recordable;
import org.wildfly.clustering.ee.infinispan.InfinispanBatcher; import org.wildfly.clustering.ee.infinispan.InfinispanBatcher;
import org.wildfly.clustering.ee.infinispan.TransactionBatch; import org.wildfly.clustering.ee.infinispan.TransactionBatch;
import org.wildfly.clustering.group.NodeFactory; import org.wildfly.clustering.group.NodeFactory;
Expand All @@ -51,6 +52,7 @@
import org.wildfly.clustering.web.infinispan.session.fine.FineSessionCacheEntry; import org.wildfly.clustering.web.infinispan.session.fine.FineSessionCacheEntry;
import org.wildfly.clustering.web.infinispan.session.fine.FineSessionFactory; import org.wildfly.clustering.web.infinispan.session.fine.FineSessionFactory;
import org.wildfly.clustering.web.infinispan.session.fine.SessionAttributeCacheKey; import org.wildfly.clustering.web.infinispan.session.fine.SessionAttributeCacheKey;
import org.wildfly.clustering.web.session.ImmutableSession;
import org.wildfly.clustering.web.session.SessionContext; import org.wildfly.clustering.web.session.SessionContext;
import org.wildfly.clustering.web.session.SessionManager; import org.wildfly.clustering.web.session.SessionManager;
import org.wildfly.clustering.web.session.SessionManagerConfiguration; import org.wildfly.clustering.web.session.SessionManagerConfiguration;
Expand All @@ -69,7 +71,7 @@ public InfinispanSessionManagerFactory(InfinispanSessionManagerFactoryConfigurat
} }


@Override @Override
public <L> SessionManager<L, TransactionBatch> createSessionManager(final SessionContext context, IdentifierFactory<String> identifierFactory, LocalContextFactory<L> localContextFactory) { public <L> SessionManager<L, TransactionBatch> createSessionManager(final SessionContext context, IdentifierFactory<String> identifierFactory, LocalContextFactory<L> localContextFactory, final Recordable<ImmutableSession> inactiveSessionRecorder) {
final Batcher<TransactionBatch> batcher = new InfinispanBatcher(this.config.getCache()); final Batcher<TransactionBatch> batcher = new InfinispanBatcher(this.config.getCache());
final Cache<String, ?> cache = this.config.getCache(); final Cache<String, ?> cache = this.config.getCache();
final IdentifierFactory<String> factory = new AffinityIdentifierFactory<>(identifierFactory, cache, this.config.getKeyAffinityServiceFactory()); final IdentifierFactory<String> factory = new AffinityIdentifierFactory<>(identifierFactory, cache, this.config.getKeyAffinityServiceFactory());
Expand Down Expand Up @@ -111,6 +113,11 @@ public NodeFactory<Address> getNodeFactory() {
public int getMaxActiveSessions() { public int getMaxActiveSessions() {
return maxActiveSessions; return maxActiveSessions;
} }

@Override
public Recordable<ImmutableSession> getInactiveSessionRecorder() {
return inactiveSessionRecorder;
}
}; };
return new InfinispanSessionManager<>(this.getSessionFactory(context, localContextFactory), config); return new InfinispanSessionManager<>(this.getSessionFactory(context, localContextFactory), config);
} }
Expand Down
Expand Up @@ -21,8 +21,7 @@
*/ */
package org.wildfly.clustering.web.infinispan.session; package org.wildfly.clustering.web.infinispan.session;


import static java.security.AccessController.doPrivileged; import java.security.AccessController;

import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -59,7 +58,8 @@ public SessionExpirationScheduler(Batcher<TransactionBatch> batcher, Remover<Str
} }


private static ThreadFactory createThreadFactory() { private static ThreadFactory createThreadFactory() {
return doPrivileged(new PrivilegedAction<ThreadFactory>() { return AccessController.doPrivileged(new PrivilegedAction<ThreadFactory>() {
@Override
public ThreadFactory run() { public ThreadFactory run() {
return new JBossThreadFactory(new ThreadGroup(SessionExpirationScheduler.class.getSimpleName()), Boolean.FALSE, null, "%G - %t", null, null); return new JBossThreadFactory(new ThreadGroup(SessionExpirationScheduler.class.getSimpleName()), Boolean.FALSE, null, "%G - %t", null, null);
} }
Expand Down
Expand Up @@ -35,6 +35,7 @@
*/ */
public class SimpleSessionMetaDataExternalizer extends AbstractSimpleExternalizer<SimpleSessionMetaData> { public class SimpleSessionMetaDataExternalizer extends AbstractSimpleExternalizer<SimpleSessionMetaData> {
private static final long serialVersionUID = 1371677643229192026L; private static final long serialVersionUID = 1371677643229192026L;
private static final TimeUnit SERIALIZED_UNIT = TimeUnit.SECONDS;


public SimpleSessionMetaDataExternalizer() { public SimpleSessionMetaDataExternalizer() {
super(SimpleSessionMetaData.class); super(SimpleSessionMetaData.class);
Expand All @@ -44,14 +45,14 @@ public SimpleSessionMetaDataExternalizer() {
public void writeObject(ObjectOutput output, SimpleSessionMetaData metaData) throws IOException { public void writeObject(ObjectOutput output, SimpleSessionMetaData metaData) throws IOException {
output.writeLong(metaData.getCreationTime().getTime()); output.writeLong(metaData.getCreationTime().getTime());
output.writeLong(metaData.getLastAccessedTime().getTime()); output.writeLong(metaData.getLastAccessedTime().getTime());
output.writeInt((int) metaData.getMaxInactiveInterval(TimeUnit.SECONDS)); output.writeInt((int) metaData.getMaxInactiveInterval(SERIALIZED_UNIT));
} }


@Override @Override
public SimpleSessionMetaData readObject(ObjectInput input) throws IOException { public SimpleSessionMetaData readObject(ObjectInput input) throws IOException {
Date creationTime = new Date(input.readLong()); Date creationTime = new Date(input.readLong());
Date lastAccessedTime = new Date(input.readLong()); Date lastAccessedTime = new Date(input.readLong());
Time maxInactiveInterval = new Time(input.readInt(), TimeUnit.SECONDS); Time maxInactiveInterval = new Time(input.readInt(), SERIALIZED_UNIT);
return new SimpleSessionMetaData(creationTime, lastAccessedTime, maxInactiveInterval); return new SimpleSessionMetaData(creationTime, lastAccessedTime, maxInactiveInterval);
} }
} }
Expand Up @@ -27,7 +27,7 @@
* Struct for specifying a time duration. * Struct for specifying a time duration.
* @author Paul Ferraro * @author Paul Ferraro
*/ */
public class Time { public class Time implements Comparable<Time> {
private final long value; private final long value;
private final TimeUnit unit; private final TimeUnit unit;


Expand All @@ -48,6 +48,12 @@ public long convert(TimeUnit unit) {
return unit.convert(this.value, this.unit); return unit.convert(this.value, this.unit);
} }


@Override
public int compareTo(Time time) {
TimeUnit compareUnit = TimeUnit.values()[Math.min(this.unit.ordinal(), time.unit.ordinal())];
return Long.compare(this.convert(compareUnit), time.convert(compareUnit));
}

@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
if ((object == null) || !(object instanceof Time)) return false; if ((object == null) || !(object instanceof Time)) return false;
Expand Down
Expand Up @@ -65,31 +65,11 @@ public void getId() {
@Test @Test
public void getAttributes() { public void getAttributes() {
assertSame(this.attributes, this.session.getAttributes()); assertSame(this.attributes, this.session.getAttributes());

this.session.invalidate();

IllegalStateException exception = null;
try {
this.session.getAttributes();
} catch (IllegalStateException e) {
exception = e;
}
assertNotNull(exception);
} }


@Test @Test
public void getMetaData() { public void getMetaData() {
assertSame(this.metaData, this.session.getMetaData()); assertSame(this.metaData, this.session.getMetaData());

this.session.invalidate();

IllegalStateException exception = null;
try {
this.session.getMetaData();
} catch (IllegalStateException e) {
exception = e;
}
assertNotNull(exception);
} }


@Test @Test
Expand Down
@@ -0,0 +1,39 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2015, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.wildfly.clustering.web.session;

/**
* Statistics for active sessions.
* @author Paul Ferraro
*/
public interface ActiveSessionStatistics {
/**
* @return the maximum number of active sessions allowed by this session manager
*/
int getMaxActiveSessions();

/**
* @return The number of active sessions
*/
long getActiveSessionCount();
}
@@ -0,0 +1,47 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2015, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.wildfly.clustering.web.session;

import java.util.concurrent.TimeUnit;

/**
* Statistics for inactive sessions.
* @author Paul Ferraro
*/
public interface InactiveSessionStatistics {

/**
* @return The number of expired sessions
*/
long getExpiredSessionCount();

/**
* @return The longest a session has been alive using the specified unit
*/
long getMaxSessionLifetime(TimeUnit unit);

/**
* @return The average session lifetime using the specified unit
*/
long getMeanSessionLifetime(TimeUnit unit);
}
Expand Up @@ -28,7 +28,7 @@
import org.wildfly.clustering.ee.Batcher; import org.wildfly.clustering.ee.Batcher;
import org.wildfly.clustering.web.IdentifierFactory; import org.wildfly.clustering.web.IdentifierFactory;


public interface SessionManager<L, B extends Batch> extends IdentifierFactory<String> { public interface SessionManager<L, B extends Batch> extends IdentifierFactory<String>, ActiveSessionStatistics {


/** /**
* Indicates whether or not the session with the specified identifier is known to this session manager. * Indicates whether or not the session with the specified identifier is known to this session manager.
Expand Down

0 comments on commit 8f90579

Please sign in to comment.