package org.ops4j.pax.web.service.jetty.internal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mortbay.jetty.Connector; import org.mortbay.jetty.Handler; import org.mortbay.jetty.servlet.*; import org.mortbay.util.LazyList; import org.mortbay.xml.XmlConfiguration; import org.ops4j.pax.swissbox.core.ContextClassLoaderUtils; import org.ops4j.pax.web.service.jetty.spi.SessionHandlerBuilder; import org.ops4j.pax.web.service.spi.model.*; import org.osgi.service.http.HttpContext; import java.net.URL; import java.util.*; import java.util.concurrent.Callable; /** * @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a> * @version $Id$ */ public class JettyServerImpl implements JettyServer { private static final Log LOG = LogFactory.getLog( JettyServerImpl.class ); private final JettyServerWrapper m_server; JettyServerImpl( final ServerModel serverModel, final SessionHandlerBuilder sessionHandlerBuilder) { m_server = new JettyServerWrapper( serverModel, sessionHandlerBuilder ); } public void start() { LOG.debug( "Starting " + this ); try { URL resource = getClass().getResource( "/jetty.xml" ); if( resource != null ) { LOG.debug( "Configure using resource " + resource ); XmlConfiguration configuration = new XmlConfiguration( resource ); configuration.configure( m_server ); } m_server.start(); } catch( Exception e ) { LOG.error( e ); } } public void stop() { LOG.debug( "Stopping " + this ); try { m_server.stop(); } catch( Exception e ) { LOG.error( e ); } } /** * @see JettyServer#addConnector(org.mortbay.jetty.Connector) */ public void addConnector( final Connector connector ) { LOG.info( String.format( "Pax Web available at [%s]:[%s]", connector.getHost() == null ? "0.0.0.0" : connector.getHost(), connector.getPort() ) ); m_server.addConnector( connector ); } /** * {@inheritDoc} */ public void configureContext( final Map<String, Object> attributes, final Integer sessionTimeout, final String sessionCookie, final String sessionUrl, final String workerName ) { m_server.configureContext( attributes, sessionTimeout, sessionCookie, sessionUrl, workerName ); } public void addServlet( final ServletModel model ) { LOG.debug( "Adding servlet [" + model + "]" ); final ServletMapping mapping = new ServletMapping(); mapping.setServletName( model.getName() ); mapping.setPathSpecs( model.getUrlPatterns() ); final Context context = m_server.getOrCreateContext( model ); final ServletHandler servletHandler = context.getServletHandler(); if( servletHandler == null ) { throw new IllegalStateException( "Internal error: Cannot find the servlet holder" ); } final ServletHolder holder = new ServletHolder( model.getServlet() ); holder.setName( model.getName() ); if( model.getInitParams() != null ) { holder.setInitParameters( model.getInitParams() ); } // Jetty does not set the context class loader on adding the filters so we do that instead try { ContextClassLoaderUtils.doWithClassLoader( context.getClassLoader(), new Callable<Void>() { public Void call() { servletHandler.addServlet( holder ); servletHandler.addServletMapping( mapping ); return null; } } ); } catch( Exception e ) { if( e instanceof RuntimeException ) { throw (RuntimeException) e; } LOG.error( "Ignored exception during servlet registration", e ); } } public void removeServlet( final ServletModel model ) { LOG.debug( "Removing servlet [" + model + "]" ); // jetty does not provide a method fro removing a servlet so we have to do it by our own // the facts bellow are found by analyzing ServletHolder implementation boolean removed = false; final Context context = m_server.getContext( model.getContextModel().getHttpContext() ); final ServletHandler servletHandler = context.getServletHandler(); final ServletHolder[] holders = servletHandler.getServlets(); if( holders != null ) { final ServletHolder holder = servletHandler.getServlet( model.getName() ); if( holder != null ) { servletHandler.setServlets( (ServletHolder[]) LazyList.removeFromArray( holders, holder ) ); // we have to find the servlet mapping by hand :( as there is no method provided by jetty // and the remove is done based on equals, that is not implemented by servletmapping // so it is == based. ServletMapping[] mappings = servletHandler.getServletMappings(); if( mappings != null ) { ServletMapping mapping = null; for( ServletMapping item : mappings ) { if( holder.getName().equals( item.getServletName() ) ) { mapping = item; break; } } if( mapping != null ) { servletHandler.setServletMappings( (ServletMapping[]) LazyList.removeFromArray( mappings, mapping ) ); removed = true; } } // if servlet is still started stop the servlet holder (=servlet.destroy()) as Jetty will not do that if( holder.isStarted() ) { try { ContextClassLoaderUtils.doWithClassLoader( context.getClassLoader(), new Callable<Void>() { public Void call() throws Exception { holder.stop(); return null; } } ); } catch( Exception e ) { if( e instanceof RuntimeException ) { throw (RuntimeException) e; } LOG.warn( "Exception during unregistering of servlet [" + model + "]" ); } } } } if( !removed ) { throw new IllegalStateException( model + " was not found" ); } } public void addEventListener( final EventListenerModel model ) { m_server.getOrCreateContext( model ).addEventListener( model.getEventListener() ); } public void removeEventListener( final EventListenerModel model ) { final Context context = m_server.getContext( model.getContextModel().getHttpContext() ); final List<EventListener> listeners = new ArrayList<EventListener>( Arrays.asList( context.getEventListeners() ) ); listeners.remove( model.getEventListener() ); context.setEventListeners( listeners.toArray( new EventListener[listeners.size()] ) ); } public void removeContext( final HttpContext httpContext ) { m_server.removeContext( httpContext ); } public void addFilter( final FilterModel model ) { LOG.debug( "Adding filter model [" + model + "]" ); final FilterMapping mapping = new FilterMapping(); mapping.setFilterName( model.getName() ); if( model.getUrlPatterns() != null && model.getUrlPatterns().length > 0 ) { mapping.setPathSpecs( model.getUrlPatterns() ); } if( model.getServletNames() != null && model.getServletNames().length > 0 ) { mapping.setServletNames( model.getServletNames() ); } // set-up dispatcher int dispatcher = Handler.DEFAULT; for( String d : model.getDispatcher() ) { dispatcher |= Dispatcher.type( d ); } mapping.setDispatches( dispatcher ); final Context context = m_server.getOrCreateContext( model ); final ServletHandler servletHandler = context.getServletHandler(); if( servletHandler == null ) { throw new IllegalStateException( "Internal error: Cannot find the servlet holder" ); } final FilterHolder holder = new FilterHolder( model.getFilter() ); holder.setName( model.getName() ); if( model.getInitParams() != null ) { holder.setInitParameters( model.getInitParams() ); } // Jetty does not set the context class loader on adding the filters so we do that instead try { ContextClassLoaderUtils.doWithClassLoader( context.getClassLoader(), new Callable<Void>() { public Void call() { servletHandler.addFilter( holder, mapping ); return null; } } ); } catch( Exception e ) { if( e instanceof RuntimeException ) { throw (RuntimeException) e; } LOG.error( "Ignored exception during filter registration", e ); } } public void removeFilter( FilterModel model ) { LOG.debug( "Removing filter model [" + model + "]" ); final Context context = m_server.getContext( model.getContextModel().getHttpContext() ); final ServletHandler servletHandler = context.getServletHandler(); // first remove filter mappings for the removed filter final FilterMapping[] filterMappings = servletHandler.getFilterMappings(); FilterMapping[] newFilterMappings = null; for( FilterMapping filterMapping : filterMappings ) { if( filterMapping.getFilterName().equals( model.getName() ) ) { if( newFilterMappings == null ) { newFilterMappings = filterMappings; } newFilterMappings = (FilterMapping[]) LazyList.removeFromArray( newFilterMappings, filterMapping ); } } servletHandler.setFilterMappings( newFilterMappings ); // then remove the filter final FilterHolder filterHolder = servletHandler.getFilter( model.getName() ); final FilterHolder[] filterHolders = servletHandler.getFilters(); final FilterHolder[] newFilterHolders = (FilterHolder[]) LazyList.removeFromArray( filterHolders, filterHolder ); servletHandler.setFilters( newFilterHolders ); // if filter is still started stop the filter (=filter.destroy()) as Jetty will not do that if( filterHolder.isStarted() ) { try { ContextClassLoaderUtils.doWithClassLoader( context.getClassLoader(), new Callable<Void>() { public Void call() throws Exception { filterHolder.stop(); return null; } } ); } catch( Exception e ) { if( e instanceof RuntimeException ) { throw (RuntimeException) e; } LOG.warn( "Exception during unregistering of filter [" + filterHolder.getFilter() + "]" ); } } } @SuppressWarnings( "unchecked" ) public void addErrorPage( final ErrorPageModel model ) { final Context context = m_server.getOrCreateContext( model ); final ErrorPageErrorHandler errorPageHandler = (ErrorPageErrorHandler) context.getErrorHandler(); if( errorPageHandler == null ) { throw new IllegalStateException( "Internal error: Cannot find the error handler. Please report." ); } Map<String, String> errorPages = errorPageHandler.getErrorPages(); if( errorPages == null ) { errorPages = new HashMap<String, String>(); } errorPages.put( model.getError(), model.getLocation() ); errorPageHandler.setErrorPages( errorPages ); } @SuppressWarnings( "unchecked" ) public void removeErrorPage( final ErrorPageModel model ) { final Context context = m_server.getOrCreateContext( model ); final ErrorPageErrorHandler errorPageHandler = (ErrorPageErrorHandler) context.getErrorHandler(); if( errorPageHandler == null ) { throw new IllegalStateException( "Internal error: Cannot find the error handler. Please report." ); } final Map<String, String> errorPages = errorPageHandler.getErrorPages(); if( errorPages != null ) { errorPages.remove( model.getError() ); if( errorPages.size() == 0 ) { errorPageHandler.setErrorPages( null ); } } } @Override public String toString() { return new StringBuilder().append( JettyServerImpl.class.getSimpleName() ).append( "{" ).append( "}" ) .toString(); } }