/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, 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.jboss.test.cluster.testutil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.servlet.ServletException;
import org.apache.catalina.Context;
import org.apache.catalina.Manager;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.config.BuddyReplicationConfig;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.loader.FileCacheLoaderConfig;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.metadata.javaee.spec.EmptyMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.jboss.PassivationConfig;
import org.jboss.metadata.web.jboss.ReplicationConfig;
import org.jboss.metadata.web.jboss.ReplicationGranularity;
import org.jboss.metadata.web.jboss.ReplicationTrigger;
import org.jboss.metadata.web.jboss.SnapshotMode;
import org.jboss.test.cluster.web.CacheHelper;
import org.jboss.test.cluster.web.mocks.MockEngine;
import org.jboss.test.cluster.web.mocks.MockHost;
import org.jboss.test.cluster.web.mocks.MockRequest;
import org.jboss.test.cluster.web.mocks.MockValve;
import org.jboss.test.cluster.web.mocks.RequestHandler;
import org.jboss.test.cluster.web.mocks.RequestHandlerValve;
import org.jboss.web.tomcat.service.session.JBossCacheManager;
import org.jboss.web.tomcat.service.session.distributedcache.impl.DistributedCacheManagerFactoryImpl;
import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactoryFactory;
import org.jgroups.Address;
/**
* Utilities for session testing.
*
* @author <a href="mailto://brian.stansberry@jboss.com">Brian Stansberry</a>
* @version $Revision: 81705 $
*/
public class SessionTestUtil
{
private static final String[] STRING_ONLY_TYPES = { String.class.getName() };
private static final String[] STRING_BOOLEAN_TYPES = { String.class.getName(), boolean.class.getName() };
private static final String CONFIG_LOCATION = "cluster/http/jboss-web-test-service.xml";
private static DistributedCacheManagerFactoryImpl distributedManagerFactory;
static
{
try
{
distributedManagerFactory = (DistributedCacheManagerFactoryImpl) DistributedCacheManagerFactoryFactory.getInstance().getDistributedCacheManagerFactory();
}
catch (ClusteringNotSupportedException e)
{
e.printStackTrace();
}
}
public static JBossCacheManager createManager(String warName, int maxInactiveInterval,
boolean local, String passivationDir,
boolean totalReplication, boolean marshalling,
String jvmRoute, Set<PojoCache> allCaches)
{
PojoCache cache = createCache(local, passivationDir, totalReplication, marshalling, allCaches);
return createManager(warName, maxInactiveInterval, cache, jvmRoute);
}
public static JBossCacheManager createManager(String warName,
int maxInactiveInterval,
PojoCache cache,
String jvmRoute)
{
if (distributedManagerFactory == null)
throw new IllegalStateException("Failed to initialize distributedManagerFactory");
distributedManagerFactory.setPojoCache(cache);
JBossCacheManager jbcm = new JBossCacheManager(distributedManagerFactory);
jbcm.setSnapshotMode(SnapshotMode.INSTANT);
MockEngine engine = new MockEngine();
engine.setJvmRoute(jvmRoute);
MockHost host = new MockHost();
engine.addChild(host);
host.setName("localhost");
StandardContext container = new StandardContext();
container.setName(warName);
host.addChild(container);
container.setManager(jbcm);
// Do this after assigning the manager to the container, or else
// the container's setting will override ours
// Can't just set the container as their config is per minute not per second
jbcm.setMaxInactiveInterval(maxInactiveInterval);
return jbcm;
}
public static PojoCache createCache(boolean local, String passivationDir,
boolean totalReplication, boolean marshalling, Set<PojoCache> allCaches)
{
Configuration cfg = getConfiguration(local, passivationDir, totalReplication, marshalling);
PojoCache cache = PojoCacheFactory.createCache(cfg, true);
if (allCaches != null)
allCaches.add(cache);
return cache;
}
public static Configuration getConfiguration(boolean local, String passivationDir,
boolean totalReplication, boolean marshalling)
{
PojoCache temp = PojoCacheFactory.createCache(CONFIG_LOCATION, false);
Configuration config = temp.getCache().getConfiguration();
if (local)
config.setCacheMode(CacheMode.LOCAL);
if (passivationDir == null)
{
config.setCacheLoaderConfig(null);
}
else
{
CacheLoaderConfig clc = config.getCacheLoaderConfig();
FileCacheLoaderConfig fclc = new FileCacheLoaderConfig();
fclc.setProperties(clc.getFirstCacheLoaderConfig().getProperties());
fclc.setLocation(passivationDir);
fclc.setFetchPersistentState(true);
ArrayList<IndividualCacheLoaderConfig> iclcs = new ArrayList<IndividualCacheLoaderConfig>();
iclcs.add(fclc);
clc.setIndividualCacheLoaderConfigs(iclcs);
}
BuddyReplicationConfig brc = config.getBuddyReplicationConfig();
brc.setEnabled(!local && !totalReplication);
config.setUseRegionBasedMarshalling(marshalling);
config.setInactiveOnStartup(marshalling);
// No async marshalling or notifications
config.setSerializationExecutorPoolSize(0);
config.setListenerAsyncPoolSize(0);
// Block for commits -- no races between test driver and replication
config.setSyncCommitPhase(true);
config.setSyncRollbackPhase(true);
return config;
}
public static PojoCache getDistributedCacheManagerFactoryPojoCache()
{
return distributedManagerFactory.getPojoCache();
}
@SuppressWarnings("unchecked")
public static Cache<Object, Object> getDistributedCacheManagerFactoryPlainCache()
{
return distributedManagerFactory.getPlainCache();
}
public static void clearDistributedCacheManagerFactory()
{
distributedManagerFactory.clearCaches();
}
public static JBossWebMetaData createWebMetaData(int maxSessions)
{
return createWebMetaData(ReplicationGranularity.SESSION,
ReplicationTrigger.SET_AND_NON_PRIMITIVE_GET,
maxSessions, false, -1, -1, false, 0);
}
public static JBossWebMetaData createWebMetaData(int maxSessions, boolean passivation,
int maxIdle, int minIdle)
{
return createWebMetaData(ReplicationGranularity.SESSION,
ReplicationTrigger.SET_AND_NON_PRIMITIVE_GET,
maxSessions, passivation, maxIdle, minIdle, false, 60);
}
public static JBossWebMetaData createWebMetaData(ReplicationGranularity granularity,
ReplicationTrigger trigger,boolean batchMode,
int maxUnreplicated)
{
return createWebMetaData(granularity, trigger, -1, false,
-1, -1, batchMode, maxUnreplicated);
}
public static JBossWebMetaData createWebMetaData(ReplicationGranularity granularity,
ReplicationTrigger trigger,
int maxSessions, boolean passivation,
int maxIdle, int minIdle,
boolean batchMode,
int maxUnreplicated)
{
JBossWebMetaData webMetaData = new JBossWebMetaData();
webMetaData.setDistributable(new EmptyMetaData());
webMetaData.setMaxActiveSessions(new Integer(maxSessions));
PassivationConfig pcfg = new PassivationConfig();
pcfg.setUseSessionPassivation(Boolean.valueOf(passivation));
pcfg.setPassivationMaxIdleTime(new Integer(maxIdle));
pcfg.setPassivationMinIdleTime(new Integer(minIdle));
webMetaData.setPassivationConfig(pcfg);
ReplicationConfig repCfg = new ReplicationConfig();
repCfg.setReplicationGranularity(granularity);
repCfg.setReplicationTrigger(trigger);
repCfg.setReplicationFieldBatchMode(Boolean.valueOf(batchMode));
repCfg.setMaxUnreplicatedInterval(Integer.valueOf(maxUnreplicated));
webMetaData.setReplicationConfig(repCfg);
return webMetaData;
}
public static void invokeRequest(Manager manager, RequestHandler handler, String sessionId)
throws ServletException, IOException
{
Valve valve = setupPipeline(manager, handler);
MockRequest request = new MockRequest();
request.setRequestedSessionId(sessionId);
request.setContext((Context) manager.getContainer());
Response response = new Response();
request.setResponse(response);
valve.invoke(request, response);
// StandardHostValve calls request.getSession(false) on way out, so we will too
request.getSession(false);
request.recycle();
}
public static Valve setupPipeline(Manager manager, RequestHandler requestHandler)
{
Pipeline pipeline = manager.getContainer().getPipeline();
// Clean out any existing request handler
Valve[] valves = pipeline.getValves();
RequestHandlerValve mockValve = null;
for (Valve valve: valves)
{
if (valve instanceof RequestHandlerValve)
{
mockValve = (RequestHandlerValve) valve;
break;
}
}
if (mockValve == null)
{
mockValve = new RequestHandlerValve(requestHandler);
pipeline.addValve(mockValve);
}
else
{
mockValve.setRequestHandler(requestHandler);
}
return pipeline.getFirst();
}
public static void cleanupPipeline(Manager manager)
{
Pipeline pipeline = manager.getContainer().getPipeline();
Valve[] valves = pipeline.getValves();
for (Valve valve: valves)
{
if (valve instanceof MockValve)
{
((MockValve) valve).clear();
}
}
}
public static Object getSessionVersion(MBeanServerConnection adaptor, String sessionFqn) throws Exception
{
return adaptor.invoke(CacheHelper.OBJECT_NAME,
"getSessionVersion",
new Object[] { sessionFqn },
STRING_ONLY_TYPES);
}
public static Object getBuddySessionVersion(MBeanServerConnection adaptor, String sessionFqn) throws Exception
{
return adaptor.invoke(CacheHelper.OBJECT_NAME,
"getBuddySessionVersion",
new Object[] { sessionFqn },
STRING_ONLY_TYPES);
}
public static void setCacheConfigName(MBeanServerConnection adaptor, String cacheConfigName, boolean usePojoCache) throws Exception
{
adaptor.invoke(CacheHelper.OBJECT_NAME,
"setCacheConfigName",
new Object[] { cacheConfigName, Boolean.valueOf(usePojoCache) },
new String[]{ String.class.getName(), boolean.class.getName() });
}
@SuppressWarnings("unchecked")
public static Set<String> getSessionIds(MBeanServerConnection adaptor, String warFqn) throws Exception
{
return (Set<String>) adaptor.invoke(CacheHelper.OBJECT_NAME,
"getSessionIds",
new Object[] { warFqn },
STRING_ONLY_TYPES);
}
@SuppressWarnings("unchecked")
public static Set<String> getSessionIds(MBeanServerConnection adaptor, String warFqn, boolean includeBuddies) throws Exception
{
return (Set<String>) adaptor.invoke(CacheHelper.OBJECT_NAME,
"getSessionIds",
new Object[] { warFqn, Boolean.valueOf(includeBuddies) },
STRING_BOOLEAN_TYPES);
}
public static boolean isBuddyReplication() throws Exception
{
return Boolean.parseBoolean(System.getProperty("jbosstest.cluster.web.cache.br", "false"));
}
public static String getSessionFqn(String contextPath, String sessionId)
{
return "/JSESSION/" + SessionTestUtil.getContextHostPath("localhost", contextPath) + "/" + sessionId;
}
public static String getContextHostPath(String hostname, String contextPath)
{
return contextPath + "_" + hostname;
}
/**
* Loops, continually calling {@link #areCacheViewsComplete(org.jboss.cache.Cache[])}
* until it either returns true or <code>timeout</code> ms have elapsed.
*
* @param caches caches which must all have consistent views
* @param timeout max number of ms to loop
* @throws RuntimeException if <code>timeout</code> ms have elapse without
* all caches having the same number of members.
*/
public static void blockUntilViewsReceived(Cache<?, ?>[] caches, long timeout)
{
long failTime = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < failTime)
{
sleepThread(100);
if (areCacheViewsComplete(caches))
{
return;
}
}
throw new RuntimeException("timed out before caches had complete views");
}
/**
* Checks each cache to see if the number of elements in the array
* returned by {@link CacheSPI#getMembers()} matches the size of
* the <code>caches</code> parameter.
*
* @param caches caches that should form a View
* @return <code>true</code> if all caches have
* <code>caches.length</code> members; false otherwise
* @throws IllegalStateException if any of the caches have MORE view
* members than caches.length
*/
public static boolean areCacheViewsComplete(Cache<?, ?>[] caches)
{
return areCacheViewsComplete(caches, true);
}
public static boolean areCacheViewsComplete(Cache<?, ?>[] caches, boolean barfIfTooManyMembers)
{
int memberCount = caches.length;
for (int i = 0; i < memberCount; i++)
{
if (!isCacheViewComplete(caches[i], memberCount, barfIfTooManyMembers))
{
return false;
}
}
return true;
}
public static boolean isCacheViewComplete(Cache<?, ?> c, int memberCount, boolean barfIfTooManyMembers)
{
CacheSPI<?, ?> cache = (CacheSPI<?, ?>) c;
List<Address> members = cache.getMembers();
if (members == null || memberCount > members.size())
{
return false;
}
else if (memberCount < members.size())
{
if (barfIfTooManyMembers)
{
// This is an exceptional condition
StringBuffer sb = new StringBuffer("Cache at address ");
sb.append(cache.getLocalAddress());
sb.append(" had ");
sb.append(members.size());
sb.append(" members; expecting ");
sb.append(memberCount);
sb.append(". Members were (");
for (int j = 0; j < members.size(); j++)
{
if (j > 0)
{
sb.append(", ");
}
sb.append(members.get(j));
}
sb.append(')');
throw new IllegalStateException(sb.toString());
}
else return false;
}
return true;
}
public static Cookie getSessionCookie(HttpClient client)
{
// Get the state for the JSESSIONID
HttpState state = client.getState();
// Get the JSESSIONID so we can reset the host
Cookie[] cookies = state.getCookies();
Cookie sessionID = null;
for(int c = 0; c < cookies.length; c ++)
{
Cookie k = cookies[c];
if( k.getName().equalsIgnoreCase("JSESSIONID") )
sessionID = k;
}
return sessionID;
}
public static void setCookieDomainToThisServer(HttpClient client, String server)
{
// Get the session cookie
Cookie sessionID = getSessionCookie(client);
if (sessionID == null)
throw new IllegalStateException("No session cookie found on " + client);
// Reset the domain so that the cookie will be sent to server1
sessionID.setDomain(server);
client.getState().addCookie(sessionID);
}
/**
* Puts the current thread to sleep for the desired number of ms, suppressing
* any exceptions.
*
* @param sleeptime number of ms to sleep
*/
public static void sleepThread(long sleeptime)
{
try
{
Thread.sleep(sleeptime);
}
catch (InterruptedException ie)
{
}
}
public static Object getAttributeValue(int value)
{
return Integer.valueOf(value);
}
private SessionTestUtil() {}
}