/* * Copyright 2013 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.integration; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import java.security.Security; import javax.annotation.Nonnull; import javax.security.auth.message.config.AuthConfigFactory; import javax.servlet.ServletException; import de.javakaffee.web.msm.MemcachedSessionService; 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.authenticator.jaspic.AuthConfigFactoryImpl; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import de.javakaffee.web.msm.MemcachedBackupSessionManager; import de.javakaffee.web.msm.MemcachedSessionService.SessionManager; import de.javakaffee.web.msm.integration.TestUtils.LoginType; import edu.umd.cs.findbugs.annotations.SuppressWarnings; /** * Builder for {@link Tomcat} tomcat. * * @author @author <a href="mailto:martin.grotzke@javakaffee.de">Martin Grotzke</a> */ public class Tomcat8Builder extends TomcatBuilder<Tomcat> { static { // set jaspic AuthConfigFactory to prevent NPEs like this: // java.lang.NullPointerException // at org.apache.catalina.authenticator.AuthenticatorBase.getJaspicProvider(AuthenticatorBase.java:1140) // at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:431) // at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) Security.setProperty(AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY, AuthConfigFactoryImpl.class.getName()); } @SuppressWarnings( "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" ) public Tomcat build() throws MalformedURLException, UnknownHostException, LifecycleException { final Tomcat tomcat = new Tomcat(); 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 Connector connector = tomcat.getConnector(); connector.setPort(port); connector.setProperty("bindOnInit", "false"); /* we must have a unique name for mbeans */ tomcat.getEngine().setName("engine-" + port); tomcat.getEngine().setJvmRoute(jvmRoute); tomcat.addUser(USER_NAME,PASSWORD); tomcat.addRole(USER_NAME,ROLE_NAME); // brings logging.properties tomcat.setBaseDir(docBase); final SessionManager sessionManager = createSessionManager(); Context context; try { context = tomcat.addWebapp(CONTEXT_PATH, docBase + fileSeparator + "webapp"); } catch (ServletException e) { throw new IllegalStateException(e); } context.setManager( sessionManager ); context.setBackgroundProcessorDelay( 1 ); context.setCookies(cookies); if ( loginType != null ) { context.addConstraint( createSecurityConstraint( "/*", ROLE_NAME ) ); context.addSecurityRole( ROLE_NAME ); final LoginConfig loginConfig = loginType == 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().setLockExpiration(lockExpire); } 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); return tomcat; } /** * Must create a {@link SessionManager} for the current tomcat version. */ @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; } private Tomcat tomcat; @Override public Tomcat8Builder buildAndStart() throws Exception { tomcat = build(); tomcat.start(); return this; } @Override public void stop() throws Exception { tomcat.stop(); } @Override public Context getContext() { return (Context) tomcat.getHost().findChild( CONTEXT_PATH ); } @Override public SessionManager getManager() { return (SessionManager) getContext().getManager(); } @Override public MemcachedSessionService getService() { return ((SessionManager) getContext().getManager()).getMemcachedSessionService(); } @Override public Engine getEngine() { return tomcat.getEngine(); } @Override public void setChangeSessionIdOnAuth(final boolean changeSessionIdOnAuth) { final Engine engine = tomcat.getEngine(); 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 ); } } }