/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.cxf.jaxrs.servlet.sci; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import javax.servlet.ServletRegistration.Dynamic; 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 org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet; import org.apache.cxf.jaxrs.utils.ResourceUtils; @HandlesTypes({ Application.class, Provider.class, Path.class }) public class JaxrsServletContainerInitializer implements ServletContainerInitializer { private static final Logger LOG = LogUtils.getL7dLogger(JaxrsServletContainerInitializer.class); private static final String IGNORE_PACKAGE = "org.apache.cxf"; private static final String JAXRS_APPLICATION_SERVLET_NAME = "javax.ws.rs.core.Application"; private static final String JAXRS_APPLICATION_PARAM = "javax.ws.rs.Application"; private static final String CXF_JAXRS_APPLICATION_PARAM = "jaxrs.application"; private static final String CXF_JAXRS_CLASSES_PARAM = "jaxrs.classes"; @Override public void onStartup(final Set< Class< ? > > classes, final ServletContext ctx) throws ServletException { Application app = null; String servletName = null; String servletMapping = null; final Class< ? > appClass = findCandidate(classes); if (appClass != null) { // The best effort at detecting a CXFNonSpringJaxrsServlet handling this application. // Custom servlets using non-standard mechanisms to create Application will not be detected if (isApplicationServletAvailable(ctx, appClass)) { return; } try { app = (Application)appClass.newInstance(); } catch (Throwable t) { throw new ServletException(t); } // Servlet name is the application class name servletName = appClass.getName(); ApplicationPath appPath = ResourceUtils.locateApplicationPath(appClass); // If ApplicationPath is available - use its value as a mapping otherwise get it from // a servlet registration with an application implementation class name if (appPath != null) { servletMapping = appPath.value() + "/*"; } else { servletMapping = getServletMapping(ctx, servletName); } } // If application is null or empty then try to create a new application from available // resource and provider classes if (app == null || (app.getClasses().isEmpty() && app.getSingletons().isEmpty())) { // The best effort at detecting a CXFNonSpringJaxrsServlet // Custom servlets using non-standard mechanisms to create Application will not be detected if (isCxfServletAvailable(ctx)) { return; } final Map< Class< ? extends Annotation >, Collection< Class< ? > > > providersAndResources = groupByAnnotations(classes); if (!providersAndResources.get(Path.class).isEmpty() || !providersAndResources.get(Provider.class).isEmpty()) { if (app == null) { // Servlet name is a JAX-RS Application class name servletName = JAXRS_APPLICATION_SERVLET_NAME; // Servlet mapping is obtained from a servlet registration // with a JAX-RS Application class name servletMapping = getServletMapping(ctx, servletName); } final Map<String, Object> appProperties = app != null ? app.getProperties() : Collections.emptyMap(); app = new Application() { @Override public Set<Class<?>> getClasses() { Set<Class<?>> set = new HashSet<Class<?>>(); set.addAll(providersAndResources.get(Path.class)); set.addAll(providersAndResources.get(Provider.class)); return set; } @Override public Map<String, Object> getProperties() { return appProperties; } }; } } if (app == null) { return; } CXFNonSpringJaxrsServlet cxfServlet = new CXFNonSpringJaxrsServlet(app); final Dynamic servlet = ctx.addServlet(servletName, cxfServlet); servlet.addMapping(servletMapping); } private boolean isCxfServletAvailable(ServletContext ctx) { for (Map.Entry<String, ? extends ServletRegistration> entry : ctx.getServletRegistrations().entrySet()) { if (entry.getValue().getInitParameter(CXF_JAXRS_CLASSES_PARAM) != null) { return true; } } return false; } private String getServletMapping(final ServletContext ctx, final String name) throws ServletException { ServletRegistration sr = ctx.getServletRegistration(name); if (sr != null) { return sr.getMappings().iterator().next(); } else { final String error = "Servlet with a name " + name + " is not available"; throw new ServletException(error); } } private boolean isApplicationServletAvailable(final ServletContext ctx, final Class<?> appClass) { for (Map.Entry<String, ? extends ServletRegistration> entry : ctx.getServletRegistrations().entrySet()) { String appParam = entry.getValue().getInitParameter(JAXRS_APPLICATION_PARAM); if (appParam == null) { appParam = entry.getValue().getInitParameter(CXF_JAXRS_APPLICATION_PARAM); } if (appParam != null && appParam.equals(appClass.getName())) { return true; } } return false; } private Map< Class< ? extends Annotation >, Collection< Class< ? > > > groupByAnnotations( final Set< Class< ? > > classes) { final Map< Class< ? extends Annotation >, Collection< Class< ? > > > grouped = new HashMap< Class< ? extends Annotation >, Collection< Class< ? > > >(); grouped.put(Provider.class, new ArrayList< Class< ? > >()); grouped.put(Path.class, new ArrayList< Class< ? > >()); if (classes != null) { for (final Class< ? > clazz: classes) { if (!classShouldBeIgnored(clazz)) { for (final Class< ? extends Annotation > annotation: grouped.keySet()) { if (clazz.isAnnotationPresent(annotation)) { grouped.get(annotation).add(clazz); } } } } } return grouped; } private static boolean classShouldBeIgnored(final Class<?> clazz) { return clazz.getPackage().getName().startsWith(IGNORE_PACKAGE); } private static Class< ? > findCandidate(final Set< Class< ? > > classes) { if (classes != null) { for (final Class< ? > clazz: classes) { if (Application.class.isAssignableFrom(clazz) && !classShouldBeIgnored(clazz)) { LOG.fine("Found JAX-RS application to initialize: " + clazz.getName()); return clazz; } } } return null; } }