package org.ops4j.pax.web.service.jetty.internal; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mortbay.jetty.*; import org.mortbay.jetty.handler.HandlerCollection; import org.mortbay.jetty.servlet.AbstractSessionIdManager; import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.HashSessionIdManager; import org.mortbay.jetty.servlet.SessionHandler; import org.ops4j.pax.web.service.jetty.ehcache.EHCacheSessionManager; import org.ops4j.pax.web.service.jetty.spi.SessionHandlerBuilder; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.http.HttpContext; import org.ops4j.pax.swissbox.core.BundleUtils; import org.ops4j.pax.web.service.WebContainerConstants; import org.ops4j.pax.web.service.spi.model.Model; import org.ops4j.pax.web.service.spi.model.ServerModel; /** * @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a> * @version $Id$ */ public class JettyServerWrapper extends Server { private static final Log LOG = LogFactory.getLog( JettyServerWrapper.class ); private final ServerModel m_serverModel; private final Map<HttpContext, Context> m_contexts; private SessionHandlerBuilder m_sessionHandlerBuilder; private Map<String, Object> m_contextAttributes; private Integer m_sessionTimeout; private String m_sessionCookie; private String m_sessionUrl; private String m_sessionWorkerName; JettyServerWrapper( ServerModel serverModel, SessionHandlerBuilder sessionHandlerBuilder ) { m_serverModel = serverModel; m_contexts = new IdentityHashMap<HttpContext, Context>(); m_sessionHandlerBuilder = sessionHandlerBuilder; } public void setSessionHandlerBuilder(SessionHandlerBuilder b) { this.m_sessionHandlerBuilder = b; } public SessionHandlerBuilder getSessionHandlerBuilder() { return m_sessionHandlerBuilder; } @Override public void addConnector(Connector connector) { // Let's make some configuration before adding this connector super.addConnector(connector); } @Override public void addHandler( final Handler handler ) { if( getHandler() == null ) { setHandler( new JettyServerHandlerCollection( m_serverModel ) ); } ( (HandlerCollection) getHandler() ).addHandler( handler ); } /** * {@inheritDoc} */ public void configureContext( final Map<String, Object> attributes, final Integer sessionTimeout, final String sessionCookie, final String sessionUrl, final String sessionWorkerName ) { m_contextAttributes = attributes; m_sessionTimeout = sessionTimeout; m_sessionCookie = sessionCookie; m_sessionUrl = sessionUrl; m_sessionWorkerName = sessionWorkerName; } Context getContext( final HttpContext httpContext ) { return m_contexts.get( httpContext ); } Context getOrCreateContext( final Model model ) { Context context = m_contexts.get( model.getContextModel().getHttpContext() ); if( context == null ) { context = addContext( model ); m_contexts.put( model.getContextModel().getHttpContext(), context ); } return context; } void removeContext( final HttpContext httpContext ) { removeHandler( getContext( httpContext ) ); m_contexts.remove( httpContext ); } private Context addContext( final Model model ) { // Create session handler for context SessionHandler sessionHandler = createSessionHandler(model); Context context = new ConfigurableHttpServiceContext( this, sessionHandler, model.getContextModel().getContextParams(), getContextAttributes( BundleUtils.getBundleContext( model.getContextModel().getBundle() ) ), model.getContextModel().getContextName(), model.getContextModel().getHttpContext(), model.getContextModel().getAccessControllerContext() ); context.setClassLoader( model.getContextModel().getClassLoader() ); Integer sessionTimeout = model.getContextModel().getSessionTimeout(); if( sessionTimeout == null ) { sessionTimeout = m_sessionTimeout; } String sessionCookie = model.getContextModel().getSessionCookie(); if( sessionCookie == null ) { sessionCookie = m_sessionCookie; } String sessionUrl = model.getContextModel().getSessionUrl(); if( sessionUrl == null ) { sessionUrl = m_sessionUrl; } String workerName = model.getContextModel().getSessionWorkerName(); if( workerName == null ) { workerName = m_sessionWorkerName; } configureSessionManager( context, sessionTimeout, sessionCookie, sessionUrl, workerName ); LOG.debug( "Added servlet context: " + context ); if( isStarted() ) { try { LOG.debug( "(Re)starting servlet contexts..." ); // start the server handler if not already started Handler serverHandler = getHandler(); if( !serverHandler.isStarted() && !serverHandler.isStarting() ) { serverHandler.start(); } // if the server handler is a handler collection, seems like jetty will not automatically // start inner handlers. So, force the start of the created context if( !context.isStarted() && !context.isStarting() ) { context.start(); } } catch( Exception ignore ) { LOG.error( "Could not start the servlet context for http context [" + model.getContextModel().getHttpContext() + "]", ignore ); } } return context; } /** * Returns a list of servlet context attributes out of configured properties and attribues containing the bundle * context associated with the bundle that created the model (web element). * * @param bundleContext bundle context to be set as attribute * * @return context attributes map */ private Map<String, Object> getContextAttributes( final BundleContext bundleContext ) { final Map<String, Object> attributes = new HashMap<String, Object>(); if( m_contextAttributes != null ) { attributes.putAll( m_contextAttributes ); } attributes.put( WebContainerConstants.BUNDLE_CONTEXT_ATTRIBUTE, bundleContext ); attributes.put( "org.springframework.osgi.web.org.osgi.framework.BundleContext", bundleContext ); return attributes; } /** * Configures the session time out by extracting the session handlers->sessionManager for the context. * * @param context the context for which the session timeout should be configured * @param minutes timeout in minutes * @param cookie Session cookie name. Defaults to JSESSIONID. * @param url session URL parameter name. Defaults to jsessionid. If set to null or "none" no URL * rewriting will be done. * @param workerName name appended to session id, used to assist session affinity in a load balancer */ private void configureSessionManager( final Context context, final Integer minutes, final String cookie, final String url, final String workerName ) { LOG.debug( "configureSessionManager for context [" + context + "] using - timeout:" + minutes + ", cookie:" + cookie + ", url:" + url + ", workerName:" + workerName ); final SessionHandler sessionHandler = context.getSessionHandler(); if( sessionHandler != null ) { final SessionManager sessionManager = sessionHandler.getSessionManager(); if( sessionManager != null ) { if( minutes != null ) { sessionManager.setMaxInactiveInterval( minutes * 60 ); LOG.debug( "Session timeout set to " + minutes + " minutes for context [" + context + "]" ); } if( cookie != null ) { sessionManager.setSessionCookie( cookie ); LOG.debug( "Session cookie set to " + cookie + " for context [" + context + "]" ); } if( url != null ) { sessionManager.setSessionURL( url ); LOG.debug( "Session URL set to " + url + " for context [" + context + "]" ); } if( workerName != null ) { SessionIdManager sessionIdManager = sessionManager.getIdManager(); if( sessionIdManager == null ) { sessionIdManager = new HashSessionIdManager(); sessionManager.setIdManager( sessionIdManager ); LOG.debug( "Hash Session ID Manager created for contxt [" + context + "]" ); } if( sessionIdManager instanceof HashSessionIdManager ) { HashSessionIdManager s = (HashSessionIdManager) sessionIdManager; s.setWorkerName( workerName ); LOG.debug( "Worker name set to " + workerName + " for context [" + context + "]" ); } if (sessionIdManager instanceof AbstractSessionIdManager) { ((AbstractSessionIdManager)sessionIdManager).setWorkerName(workerName); LOG.debug( "Worker name set to " + workerName + " for context [" + context + "]" ); } } if (sessionManager instanceof EHCacheSessionManager) { // TODO : Make this configurable ! long saveInterval = 100; ((EHCacheSessionManager)sessionManager).setSaveInterval(saveInterval); LOG.debug( "Session Save Interval set to " + saveInterval + " for context [" + context + "]" ); } } } } protected SessionHandler createSessionHandler(Model model) { if (this.m_sessionHandlerBuilder == null ) { BundleContext ctx = BundleUtils.getBundleContext( model.getContextModel().getBundle()); ServiceReference ref = ctx.getServiceReference(SessionHandlerBuilder.class.getName()); if (ref == null) { LOG.error("Session Handler Builder Service is unavailable. (no service reference)"); return null; } try { SessionHandlerBuilder svc = (SessionHandlerBuilder) ctx.getService(ref); if (svc == null) { LOG.error("Session Handler Builder Service service is unavailable. (no service)"); return null; } if (LOG.isTraceEnabled()) LOG.trace("Using Session Handler Builder " + svc); return svc.build(this, model); } finally { ctx.ungetService(ref); } } else { if (LOG.isTraceEnabled()) LOG.trace("Using Session Handler Builder " + m_sessionHandlerBuilder); return m_sessionHandlerBuilder.build(this, model); } } }