package de.javakaffee.web.msm.integration; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import java.util.Hashtable; import javax.annotation.Nonnull; import javax.naming.NamingException; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; import org.apache.catalina.Valve; import org.apache.catalina.authenticator.AuthenticatorBase; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardEngine; import org.apache.catalina.core.StandardServer; import org.apache.catalina.deploy.LoginConfig; import org.apache.catalina.deploy.SecurityCollection; import org.apache.catalina.deploy.SecurityConstraint; import org.apache.catalina.realm.UserDatabaseRealm; import org.apache.catalina.startup.Embedded; import org.apache.naming.NamingContext; import de.javakaffee.web.msm.MemcachedBackupSessionManager; import de.javakaffee.web.msm.MemcachedSessionService; import de.javakaffee.web.msm.MemcachedSessionService.SessionManager; /** * @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a> */ public class Tomcat6Builder extends TomcatBuilder<Embedded> { private Embedded tomcat; @Override public Tomcat6Builder buildAndStart() throws Exception { tomcat = build(); tomcat.start(); return this; } @Override public void stop() throws Exception { tomcat.stop(); } @Override public Context getContext() { return (Context) tomcat.getContainer().findChild( DEFAULT_HOST ).findChild( CONTEXT_PATH ); } @Override public MemcachedSessionService.SessionManager getManager() { return (MemcachedSessionService.SessionManager) getContext().getManager(); } @Override public MemcachedSessionService getService() { return ((MemcachedSessionService.SessionManager) getContext().getManager()).getMemcachedSessionService(); } @Override public Engine getEngine() { return (Engine) tomcat.getContainer(); } @Override public void setChangeSessionIdOnAuth(final boolean changeSessionIdOnAuth) { final Engine engine = (StandardEngine)tomcat.getContainer(); final Host host = (Host)engine.findChild( DEFAULT_HOST ); final Container context = host.findChild( CONTEXT_PATH ); final Valve first = context.getPipeline().getFirst(); if ( first instanceof AuthenticatorBase) { ((AuthenticatorBase)first).setChangeSessionIdOnAuthentication( changeSessionIdOnAuth ); } } @edu.umd.cs.findbugs.annotations.SuppressWarnings( "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" ) public Embedded build() throws MalformedURLException, UnknownHostException, LifecycleException { final Embedded catalina = new Embedded(); final StandardServer server = new StandardServer(); server.addService( catalina ); try { final NamingContext globalNamingContext = new NamingContext( new Hashtable<String, Object>(), "ctxt" ); server.setGlobalNamingContext( globalNamingContext ); globalNamingContext.bind( USER_DATABASE, createUserDatabase() ); } catch ( final NamingException e ) { throw new RuntimeException( e ); } final URL root = new URL( TestUtils.class.getResource( "/" ), "../test-classes" ); // use file to get correct separator char, replace %20 introduced by URL for spaces final String cleanedRoot = new File( root.getFile().replaceAll("%20", " ") ).toString(); final String fileSeparator = File.separator.equals( "\\" ) ? "\\\\" : File.separator; final String docBase = cleanedRoot + File.separator + TestUtils.class.getPackage().getName().replaceAll( "\\.", fileSeparator ); final Engine engine = catalina.createEngine(); /* we must have a unique name for mbeans */ engine.setName( "engine-" + port ); engine.setDefaultHost( DEFAULT_HOST ); engine.setJvmRoute( jvmRoute ); catalina.addEngine( engine ); engine.setService( catalina ); final UserDatabaseRealm realm = new UserDatabaseRealm(); realm.setResourceName( USER_DATABASE ); engine.setRealm( realm ); final Host host = catalina.createHost( DEFAULT_HOST, docBase ); engine.addChild( host ); new File( docBase ).mkdirs(); final Context context = createContext( catalina, CONTEXT_PATH, "webapp" ); host.addChild( context ); final MemcachedSessionService.SessionManager sessionManager = createSessionManager(); context.setManager( sessionManager ); context.setBackgroundProcessorDelay( 1 ); context.setCookies(cookies); new File( "webapp" + File.separator + "webapp" ).mkdirs(); if ( loginType != null ) { context.addConstraint( createSecurityConstraint( "/*", ROLE_NAME ) ); // context.addConstraint( createSecurityConstraint( "/j_security_check", null ) ); context.addSecurityRole( ROLE_NAME ); final LoginConfig loginConfig = loginType == TestUtils.LoginType.FORM ? new LoginConfig( "FORM", null, "/login", "/error" ) : new LoginConfig( "BASIC", null, null, null ); context.setLoginConfig( loginConfig ); } /* we must set the maxInactiveInterval after the context, * as setContainer(context) uses the session timeout set on the context */ sessionManager.getMemcachedSessionService().setMemcachedNodes( memcachedNodes ); sessionManager.getMemcachedSessionService().setFailoverNodes( failoverNodes ); sessionManager.getMemcachedSessionService().setEnabled(enabled); sessionManager.getMemcachedSessionService().setSticky(sticky); if(lockingMode != null) { sessionManager.getMemcachedSessionService().setLockingMode(lockingMode.name()); } sessionManager.getMemcachedSessionService().setMemcachedProtocol(memcachedProtocol); sessionManager.getMemcachedSessionService().setUsername(username); sessionManager.setMaxInactiveInterval( sessionTimeout ); // 1 second sessionManager.getMemcachedSessionService().setSessionBackupAsync( false ); sessionManager.getMemcachedSessionService().setSessionBackupTimeout( 100 ); sessionManager.setProcessExpiresFrequency( 1 ); // 1 second (factor for context.setBackgroundProcessorDelay) sessionManager.getMemcachedSessionService().setTranscoderFactoryClass( transcoderFactoryClassName != null ? transcoderFactoryClassName : DEFAULT_TRANSCODER_FACTORY ); sessionManager.getMemcachedSessionService().setRequestUriIgnorePattern(".*\\.(png|gif|jpg|css|js|ico)$"); sessionManager.getMemcachedSessionService().setStorageKeyPrefix(storageKeyPrefix); final Connector connector = catalina.createConnector( "localhost", port, false ); connector.setProperty("bindOnInit", "false"); catalina.addConnector( connector ); return catalina; } @Nonnull protected Context createContext( @Nonnull final Embedded catalina, @Nonnull final String contextPath, @Nonnull final String docBase ) { return catalina.createContext( contextPath, docBase ); } /** * Must create a {@link SessionManager} for the current tomcat version. */ @Override @Nonnull protected SessionManager createSessionManager() { return new MemcachedBackupSessionManager(); } private static SecurityConstraint createSecurityConstraint( final String pattern, final String role ) { final SecurityConstraint constraint = new SecurityConstraint(); final SecurityCollection securityCollection = new SecurityCollection(); securityCollection.addPattern( pattern ); constraint.addCollection( securityCollection ); if ( role != null ) { constraint.addAuthRole( role ); } return constraint; } }