package org.jboss.resteasy.plugins.servlet;
import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher;
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.spi.NotImplementedYetException;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.ext.Provider;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@HandlesTypes({Application.class, Path.class, Provider.class})
public class ResteasyServletInitializer implements ServletContainerInitializer
{
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException
{
if (classes == null || classes.size() == 0) return;
for (ServletRegistration reg : servletContext.getServletRegistrations().values())
{
if (reg.getInitParameter("javax.ws.rs.Application") != null)
{
return; // there's already a servlet mapping, do nothing
}
}
Set<Class<?>> appClasses = new HashSet<Class<?>>();
Set<Class<?>> providers = new HashSet<Class<?>>();
Set<Class<?>> resources = new HashSet<Class<?>>();
for (Class<?> clazz : classes)
{
if (clazz.isAnnotationPresent(Path.class))
{
resources.add(clazz);
}
else if (clazz.isAnnotationPresent(Provider.class))
{
providers.add(clazz);
}
else
{
appClasses.add(clazz);
}
}
if (appClasses.size() == 0 && resources.size() == 0) return;
if (appClasses.size() == 0)
{
// todo make sure we can do this on all servlet containers
//handleNoApplicationClass(providers, resources, servletContext);
return;
}
for (Class<?> app : appClasses)
{
register(app, providers, resources, servletContext);
}
}
protected void handleNoApplicationClass(Set<Class<?>> providers, Set<Class<?>> resources, ServletContext servletContext)
{
ServletRegistration defaultApp = null;
for (ServletRegistration reg : servletContext.getServletRegistrations().values())
{
if (reg.getName().equals(Application.class.getName()))
{
defaultApp = reg;
}
}
if (defaultApp == null) return;
throw new NotImplementedYetException("Default Application class not implemented yet");
}
protected void register(Class<?> applicationClass, Set<Class<?>> providers, Set<Class<?>> resources, ServletContext servletContext)
{
ApplicationPath path = applicationClass.getAnnotation(ApplicationPath.class);
if (path == null)
{
// todo we don't support this yet, i'm not sure if partial deployments are supported in all servlet containers
return;
}
ServletRegistration.Dynamic reg = servletContext.addServlet(applicationClass.getName(), HttpServlet30Dispatcher.class);
reg.setLoadOnStartup(1);
reg.setAsyncSupported(true);
reg.setInitParameter("javax.ws.rs.Application", applicationClass.getName());
if (path != null)
{
String mapping = path.value();
if (!mapping.startsWith("/")) mapping = "/" + mapping;
String prefix = mapping;
if (!prefix.equals("/") && prefix.endsWith("/")) prefix = prefix.substring(0, prefix.length() - 1);
if (mapping.endsWith("/")) mapping += "*";
else mapping += "/*";
// resteasy.servlet.mapping.prefix
reg.setInitParameter("resteasy.servlet.mapping.prefix", prefix);
reg.addMapping(mapping);
}
if (resources.size() > 0)
{
StringBuilder builder = new StringBuilder();
boolean first = true;
for (Class resource : resources)
{
if (first)
{
first = false;
}
else
{
builder.append(",");
}
builder.append(resource.getName());
}
reg.setInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_RESOURCES, builder.toString());
}
if (providers.size() > 0)
{
StringBuilder builder = new StringBuilder();
boolean first = true;
for (Class provider : providers)
{
if (first)
{
first = false;
}
else
{
builder.append(",");
}
builder.append(provider.getName());
}
reg.setInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_PROVIDERS, builder.toString());
}
}
}
ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,过滤掉不希望给 onStartup() 处理的类。
目前 tomcat7 支持 servlet 3.0, tomcat6 不支持。这一点需要注意。