Return-Path: X-Original-To: apmail-tomee-commits-archive@www.apache.org Delivered-To: apmail-tomee-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id F2EE0180E6 for ; Mon, 23 Nov 2015 14:08:39 +0000 (UTC) Received: (qmail 95158 invoked by uid 500); 23 Nov 2015 14:08:39 -0000 Delivered-To: apmail-tomee-commits-archive@tomee.apache.org Received: (qmail 95075 invoked by uid 500); 23 Nov 2015 14:08:39 -0000 Mailing-List: contact commits-help@tomee.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tomee.apache.org Delivered-To: mailing list commits@tomee.apache.org Received: (qmail 94710 invoked by uid 99); 23 Nov 2015 14:08:39 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 23 Nov 2015 14:08:39 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 095DBE07EC; Mon, 23 Nov 2015 14:08:39 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: andygumbrecht@apache.org To: commits@tomee.apache.org Date: Mon, 23 Nov 2015 14:08:42 -0000 Message-Id: <3de01f4258534aca8bce9d003f7758f3@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [5/9] tomee git commit: EOL http://git-wip-us.apache.org/repos/asf/tomee/blob/5dd70c83/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java ---------------------------------------------------------------------- diff --git a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java index bbdbbd5..0bdd6d9 100644 --- a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java +++ b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRsHttpListener.java @@ -1,1097 +1,1097 @@ -/* - * 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.openejb.server.cxf.rs; - -import org.apache.cxf.BusException; -import org.apache.cxf.common.logging.LogUtils; -import org.apache.cxf.endpoint.Endpoint; -import org.apache.cxf.endpoint.EndpointException; -import org.apache.cxf.endpoint.ManagedEndpoint; -import org.apache.cxf.endpoint.Server; -import org.apache.cxf.endpoint.ServerImpl; -import org.apache.cxf.helpers.IOUtils; -import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; -import org.apache.cxf.jaxrs.JAXRSServiceImpl; -import org.apache.cxf.jaxrs.ext.RequestHandler; -import org.apache.cxf.jaxrs.ext.ResourceComparator; -import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; -import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; -import org.apache.cxf.jaxrs.model.ClassResourceInfo; -import org.apache.cxf.jaxrs.model.MethodDispatcher; -import org.apache.cxf.jaxrs.model.OperationResourceInfo; -import org.apache.cxf.jaxrs.model.ProviderInfo; -import org.apache.cxf.jaxrs.model.wadl.WadlGenerator; -import org.apache.cxf.jaxrs.provider.JAXBElementProvider; -import org.apache.cxf.jaxrs.provider.json.JSONProvider; -import org.apache.cxf.service.invoker.Invoker; -import org.apache.cxf.transport.http.AbstractHTTPDestination; -import org.apache.cxf.transport.http.HTTPTransportFactory; -import org.apache.cxf.transport.servlet.BaseUrlHelper; -import org.apache.openejb.AppContext; -import org.apache.openejb.BeanContext; -import org.apache.openejb.Injection; -import org.apache.openejb.api.internal.Internal; -import org.apache.openejb.api.jmx.Description; -import org.apache.openejb.api.jmx.MBean; -import org.apache.openejb.api.jmx.ManagedAttribute; -import org.apache.openejb.api.jmx.ManagedOperation; -import org.apache.openejb.assembler.classic.ServiceInfo; -import org.apache.openejb.assembler.classic.util.ServiceConfiguration; -import org.apache.openejb.assembler.classic.util.ServiceInfos; -import org.apache.openejb.core.WebContext; -import org.apache.openejb.dyni.DynamicSubclass; -import org.apache.openejb.loader.IO; -import org.apache.openejb.loader.SystemInstance; -import org.apache.openejb.monitoring.LocalMBeanServer; -import org.apache.openejb.monitoring.ObjectNameBuilder; -import org.apache.openejb.rest.ThreadLocalContextManager; -import org.apache.openejb.server.cxf.rs.event.ExtensionProviderRegistration; -import org.apache.openejb.server.cxf.rs.event.ServerCreated; -import org.apache.openejb.server.cxf.rs.event.ServerDestroyed; -import org.apache.openejb.server.cxf.transport.util.CxfUtil; -import org.apache.openejb.server.httpd.HttpRequest; -import org.apache.openejb.server.httpd.HttpRequestImpl; -import org.apache.openejb.server.httpd.HttpResponse; -import org.apache.openejb.server.httpd.ServletRequestAdapter; -import org.apache.openejb.server.rest.EJBRestServiceInfo; -import org.apache.openejb.server.rest.InternalApplication; -import org.apache.openejb.server.rest.RsHttpListener; -import org.apache.openejb.util.AppFinder; -import org.apache.openejb.util.LogCategory; -import org.apache.openejb.util.Logger; -import org.apache.openejb.util.proxy.ProxyEJB; -import org.apache.webbeans.config.WebBeansContext; -import org.apache.webbeans.container.BeanManagerImpl; -import org.apache.webbeans.context.creational.CreationalContextImpl; - -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.Bean; -import javax.management.ObjectName; -import javax.management.openmbean.TabularData; -import javax.naming.Context; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.Application; -import javax.xml.bind.Marshaller; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Level; -import java.util.regex.Pattern; - -import static org.apache.openejb.loader.JarLocation.jarLocation; - -public class CxfRsHttpListener implements RsHttpListener { - - private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_RS, CxfRsHttpListener.class); - - private static final java.util.logging.Logger SERVER_IMPL_LOGGER = LogUtils.getL7dLogger(ServerImpl.class); - - public static final String CXF_JAXRS_PREFIX = "cxf.jaxrs."; - public static final String PROVIDERS_KEY = CXF_JAXRS_PREFIX + "providers"; - public static final String STATIC_RESOURCE_KEY = CXF_JAXRS_PREFIX + "static-resources-list"; - public static final String STATIC_SUB_RESOURCE_RESOLUTION_KEY = "staticSubresourceResolution"; - public static final String RESOURCE_COMPARATOR_KEY = CXF_JAXRS_PREFIX + "resourceComparator"; - - public static final boolean TRY_STATIC_RESOURCES = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.static-first", "true")); - private static final String GLOBAL_PROVIDERS = SystemInstance.get().getProperty(PROVIDERS_KEY); - - private static final Map STATIC_CONTENT_TYPES; - private static final String[] DEFAULT_WELCOME_FILES = new String[]{ "/index.html", "/index.htm" }; - - private final HTTPTransportFactory transportFactory; - private final String wildcard; - private AbstractHTTPDestination destination; - private Server server; - private String context = ""; - private String servlet = ""; - private final Collection staticResourcesList = new CopyOnWriteArrayList(); - private final List jmxNames = new ArrayList(); - private final Collection> toRelease = new LinkedHashSet>(); - private final Collection singletons = new LinkedHashSet(); - - private static final char[] URL_SEP = new char[] { '?', '#', ';' }; - - static { - STATIC_CONTENT_TYPES = new HashMap(); - STATIC_CONTENT_TYPES.put("html", "text/html"); - STATIC_CONTENT_TYPES.put("xhtml", "text/html"); - STATIC_CONTENT_TYPES.put("txt", "text/plain"); - STATIC_CONTENT_TYPES.put("css", "text/css"); - STATIC_CONTENT_TYPES.put("jpg", "image/jpg"); - STATIC_CONTENT_TYPES.put("png", "image/png"); - STATIC_CONTENT_TYPES.put("ico", "image/ico"); - STATIC_CONTENT_TYPES.put("pdf", "application/pdf"); - STATIC_CONTENT_TYPES.put("xsd", "application/xml"); - - for (final ProviderInfo rh : org.apache.cxf.jaxrs.provider.ProviderFactory.getSharedInstance().getRequestHandlers()) { - final RequestHandler provider = rh.getProvider(); - if (WadlGenerator.class.isInstance(provider)) { - final WadlGenerator wadlGenerator = WadlGenerator.class.cast(provider); - final String ignoreRequests = SystemInstance.get().getProperty("openejb.cxf-rs.wadl-generator.ignoreRequests"); - final String ignoreMessageWriters = SystemInstance.get().getProperty("openejb.cxf-rs.wadl-generator.ignoreMessageWriters", "false"); - - if (ignoreRequests != null) { - wadlGenerator.setIgnoreRequests(Boolean.parseBoolean(ignoreRequests)); - } - // CXF-5319: bug in CXF? it prevents to get the wadl as json otherwise - if (ignoreMessageWriters != null) { - wadlGenerator.setIgnoreMessageWriters(Boolean.parseBoolean(ignoreMessageWriters)); - } - } - } - } - - private String pattern; - - public CxfRsHttpListener(final HTTPTransportFactory httpTransportFactory, final String star) { - transportFactory = httpTransportFactory; - wildcard = star; - } - - public void setUrlPattern(final String pattern) { - this.pattern = pattern; - } - - @Override - public void onMessage(final HttpRequest httpRequest, final HttpResponse httpResponse) throws Exception { - // fix the address (to manage multiple connectors) - if (HttpRequestImpl.class.isInstance(httpRequest)) { - final HttpRequestImpl requestImpl = HttpRequestImpl.class.cast(httpRequest); - requestImpl.initPathFromContext(context); - requestImpl.initServletPath(servlet); - } - - boolean matchedStatic = false; - if (TRY_STATIC_RESOURCES || (matchedStatic = matchPath(httpRequest))) { - final String pathInfo = httpRequest.getPathInfo(); - if (serveStaticContent(httpRequest, httpResponse, pathInfo)) { - if (matchedStatic) { // we should have gotten the resource - throw new ServletException("Static resource " + pathInfo + " is not available"); - } - return; // ok that's a surely rest service - } - } - - doInvoke(httpRequest, httpResponse); - } - - // normal endpoint without static resource handling - public void doInvoke(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { - String baseURL = BaseUrlHelper.getBaseURL(pattern != null ? new ServletRequestAdapter(httpRequest) { - @Override // we have a filter so we need the computed servlet path to not break CXF - public String getServletPath() { - return pattern; - } - } : httpRequest); - if (!baseURL.endsWith("/")) { - baseURL += "/"; - } - httpRequest.setAttribute("org.apache.cxf.transport.endpoint.address", baseURL); - - final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); - try { - destination.invoke(null, httpRequest.getServletContext(), httpRequest, httpResponse); - } catch (final Throwable t) { - throw new IOException("Failed to invoke AbstractHTTPDestination", t); - } finally { - CxfUtil.clearBusLoader(oldLoader); - } - } - - public boolean matchPath(final HttpServletRequest request) { - if (staticResourcesList.isEmpty()) { - return false; - } - - String path = request.getPathInfo(); - if (path == null) { - path = "/"; - } - for (final Pattern pattern : staticResourcesList) { - if (pattern.matcher(path).matches()) { - return true; - } - } - return false; - } - - public InputStream findStaticContent(final HttpServletRequest request, final String[] welcomeFiles) throws ServletException { - String pathInfo = request.getRequestURI().substring(request.getContextPath().length()); - for (final char c : URL_SEP) { - final int indexOf = pathInfo.indexOf(c); - if (indexOf > 0) { - pathInfo = pathInfo.substring(0, indexOf); - } - } - InputStream is = request.getServletContext().getResourceAsStream(pathInfo); - if (is == null && ("/".equals(pathInfo) || pathInfo.isEmpty())) { - for (final String n : welcomeFiles) { - is = request.getServletContext().getResourceAsStream(n); - if (is != null) { - break; - } - } - } - return is; - } - - public boolean serveStaticContent(final HttpServletRequest request, - final HttpServletResponse response, - final String pathInfo) throws ServletException { - final InputStream is = findStaticContent(request, DEFAULT_WELCOME_FILES); - if (is == null) { - return false; - } - try { - final int ind = pathInfo.lastIndexOf("."); - if (ind != -1 && ind < pathInfo.length()) { - final String type = STATIC_CONTENT_TYPES.get(pathInfo.substring(ind + 1)); - if (type != null) { - response.setContentType(type); - } - } - - final ServletOutputStream os = response.getOutputStream(); - IOUtils.copy(is, os); - os.flush(); - response.setStatus(HttpURLConnection.HTTP_OK); - } catch (final IOException ex) { - throw new ServletException("Static resource " + pathInfo + " can not be written to the output stream"); - } - return true; - } - - @Deprecated - @Override - public void deploySingleton(final String contextRoot, final String fullContext, final Object o, final Application appInstance, - final Collection additionalProviders, final ServiceConfiguration configuration) { - deploy(contextRoot, o.getClass(), fullContext, new SingletonResourceProvider(o), - o, appInstance, null, additionalProviders, configuration, null); - } - - @Deprecated - @Override - public void deployPojo(final ClassLoader loader, - final String contextRoot, - final String fullContext, - final Class loadedClazz, - final Application app, - final Collection injections, - final Context context, - final WebBeansContext owbCtx, - final Collection additionalProviders, - final ServiceConfiguration configuration) { - deploy(contextRoot, loadedClazz, fullContext, new OpenEJBPerRequestPojoResourceProvider(loader, loadedClazz, injections, context, owbCtx), - null, app, null, additionalProviders, configuration, null); - } - - @Deprecated - @Override - public void deployEJB(final String contextRoot, - final String fullContext, - final BeanContext beanContext, - final Collection additionalProviders, - final ServiceConfiguration configuration) { - final Object proxy = ProxyEJB.subclassProxy(beanContext); - - deploy(contextRoot, beanContext.getBeanClass(), fullContext, new NoopResourceProvider(beanContext.getBeanClass(), proxy), - proxy, null, new OpenEJBEJBInvoker(Collections.singleton(beanContext)), additionalProviders, configuration, beanContext.getWebBeansContext()); - } - - private void deploy(final String contextRoot, final Class clazz, final String address, final ResourceProvider rp, final Object serviceBean, - final Application app, final Invoker invoker, final Collection additionalProviders, final ServiceConfiguration configuration, - final WebBeansContext webBeansContext) { - final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); - try { - final JAXRSServerFactoryBean factory = newFactory(address, createServiceJmxName(clazz.getClassLoader()), createEndpointName(app)); - configureFactory(additionalProviders, configuration, factory, webBeansContext); - factory.setResourceClasses(clazz); - context = contextRoot; - if (context == null) { - context = ""; - } - if (!context.startsWith("/")) { - context = "/" + context; - } - - if (rp != null) { - factory.setResourceProvider(rp); - } - if (app != null) { - factory.setApplication(app); - } - if (invoker != null) { - factory.setInvoker(invoker); - } - if (serviceBean != null) { - factory.setServiceBean(serviceBean); - } else { - factory.setServiceClass(clazz); - } - - server = factory.create(); - destination = (AbstractHTTPDestination) server.getDestination(); - fireServerCreated(oldLoader); - } finally { - if (oldLoader != null) { - CxfUtil.clearBusLoader(oldLoader); - } - } - } - - private void fireServerCreated(final ClassLoader oldLoader) { - final Object ctx = AppFinder.findAppContextOrWeb(oldLoader, new AppFinder.Transformer() { - @Override - public Object from(final AppContext appCtx) { - return appCtx; - } - - @Override - public Object from(final WebContext webCtx) { - return webCtx; - } - }); - final AppContext appCtx = AppContext.class.isInstance(ctx) ? AppContext.class.cast(ctx) : WebContext.class.cast(ctx).getAppContext(); - WebContext webContext = appCtx == ctx ? null : WebContext.class.cast(ctx); - if (webContext == null && appCtx.getWebContexts().size() == 1 && appCtx.getWebContexts().get(0).getClassLoader() == oldLoader) { - webContext = appCtx.getWebContexts().get(0); - } - SystemInstance.get().fireEvent(new ServerCreated(server, appCtx, webContext, this.context)); - } - - private List providers(final Collection services, final Collection additionalProviders, final WebBeansContext ctx) { - final List instances = new ArrayList(); - final BeanManagerImpl bm = ctx == null ? null : ctx.getBeanManagerImpl(); - for (final Object o : additionalProviders) { - if (o instanceof Class) { - final Class clazz = (Class) o; - final String name = clazz.getName(); - if (shouldSkipProvider(name)) { - continue; - } - - if (bm != null && bm.isInUse()) { - try { - final Set> beans = bm.getBeans(clazz); - if (beans != null && !beans.isEmpty()) { - final Bean bean = bm.resolve(beans); - final CreationalContextImpl creationalContext = bm.createCreationalContext(bean); - instances.add(bm.getReference(bean, clazz, creationalContext)); - toRelease.add(creationalContext); - continue; - } - } catch (final Throwable th) { - LOGGER.info("Can't use CDI to create provider " + name); - } - } - - final Collection instance = ServiceInfos.resolve(services, new String[]{name}, ProviderFactory.INSTANCE); - if (instance != null && !instance.isEmpty()) { - instances.add(instance.iterator().next()); - } else { - try { - instances.add(newProvider(clazz)); - } catch (final Exception e) { - LOGGER.error("can't instantiate " + name, e); - } - } - } else { - final String name = o.getClass().getName(); - if (shouldSkipProvider(name)) { - continue; - } - instances.add(o); - } - } - - return instances; - } - - private static boolean shouldSkipProvider(final String name) { - return "false".equalsIgnoreCase(SystemInstance.get().getProperty(name + ".activated", "true")) - || name.startsWith("org.apache.wink.common.internal."); - } - - private static void addMandatoryProviders(final Collection instances) { - instances.add(EJBAccessExceptionMapper.INSTANCE); - } - - private Object newProvider(final Class clazz) throws IllegalAccessException, InstantiationException { - return clazz.newInstance(); - } - - @Override - public void undeploy() { - // unregister all MBeans - for (final ObjectName objectName : jmxNames) { - LocalMBeanServer.unregisterSilently(objectName); - } - - for (final CreationalContext cc : toRelease) { - try { - cc.release(); - } catch (final Exception e) { - LOGGER.warning(e.getMessage(), e); - } - } - for (final CdiSingletonResourceProvider provider : singletons) { - try { - provider.release(); - } catch (final Exception e) { - LOGGER.warning(e.getMessage(), e); - } - } - - final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); - try { - server.destroy(); - SystemInstance.get().fireEvent(new ServerDestroyed(server)); - } catch (final RuntimeException ise) { - LOGGER.warning("Can't stop correctly the endpoint " + server); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(ise.getMessage(), ise); - } - } finally { - if (oldLoader != null) { - CxfUtil.clearBusLoader(oldLoader); - } - } - } - - @Override - public void deployApplication(final Application application, final String prefix, final String webContext, - final Collection additionalProviders, - final Map restEjbs, final ClassLoader classLoader, - final Collection injections, final Context context, final WebBeansContext owbCtx, - final ServiceConfiguration serviceConfiguration) { - final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); - try { - final JAXRSServerFactoryBean factory = newFactory(prefix, createServiceJmxName(classLoader), createEndpointName(application)); - configureFactory(additionalProviders, serviceConfiguration, factory, owbCtx); - factory.setApplication(application); - - final List> classes = new ArrayList>(); - - for (final Class clazz : application.getClasses()) { - if (!additionalProviders.contains(clazz) && !clazz.isInterface()) { - classes.add(clazz); - - final EJBRestServiceInfo restServiceInfo = getEjbRestServiceInfo(restEjbs, clazz); - - if (restServiceInfo != null) { - final Object proxy = ProxyEJB.subclassProxy(restServiceInfo.context); - factory.setResourceProvider(clazz, new NoopResourceProvider(restServiceInfo.context.getBeanClass(), proxy)); - } else { - factory.setResourceProvider(clazz, new OpenEJBPerRequestPojoResourceProvider( - classLoader, clazz, injections, context, owbCtx)); - } - } - } - - for (final Object o : application.getSingletons()) { - if (!additionalProviders.contains(o)) { - final Class clazz = realClass(o.getClass()); - classes.add(clazz); - - final EJBRestServiceInfo restServiceInfo = getEjbRestServiceInfo(restEjbs, clazz); - - if (restServiceInfo != null) { - final Object proxy = ProxyEJB.subclassProxy(restServiceInfo.context); - factory.setResourceProvider(clazz, new NoopResourceProvider(restServiceInfo.context.getBeanClass(), proxy)); - } else { - if (owbCtx.getBeanManagerImpl().isInUse()) { - final CdiSingletonResourceProvider provider = new CdiSingletonResourceProvider(classLoader, clazz, o, injections, context, owbCtx); - singletons.add(provider); - factory.setResourceProvider(clazz, provider); - } else { - factory.setResourceProvider(clazz, new SingletonResourceProvider(o)); - } - } - } - } - - factory.setResourceClasses(classes); - factory.setInvoker(new AutoJAXRSInvoker(restEjbs)); - - this.context = webContext; - if (!webContext.startsWith("/")) { - this.context = "/" + webContext; - } // /webcontext/servlet for event firing - - final Level level = SERVER_IMPL_LOGGER.getLevel(); - try { - SERVER_IMPL_LOGGER.setLevel(Level.OFF); - } catch (final UnsupportedOperationException e) { - //ignore - } - try { - server = factory.create(); - fireServerCreated(oldLoader); - } finally { - try { - SERVER_IMPL_LOGGER.setLevel(level); - } catch (final UnsupportedOperationException e) { - //ignore - } - } - - final int servletIdx = 1 + this.context.substring(1).indexOf('/'); - if (servletIdx > 0) { - this.servlet = this.context.substring(servletIdx); - this.context = this.context.substring(0, servletIdx); - } - destination = (AbstractHTTPDestination) server.getDestination(); - - final String base; - if (prefix.endsWith("/")) { - base = prefix.substring(0, prefix.length() - 1); - } else if (prefix.endsWith(wildcard)) { - base = prefix.substring(0, prefix.length() - wildcard.length()); - } else { - base = prefix; - } - - // stack info to log to get nice logs - logEndpoints(application, prefix, restEjbs, factory, base); - } finally { - if (oldLoader != null) { - CxfUtil.clearBusLoader(oldLoader); - } - } - } - - private EJBRestServiceInfo getEjbRestServiceInfo(final Map restEjbs, final Class clazz) { - String name = clazz.getName(); - EJBRestServiceInfo restServiceInfo = restEjbs.get(name); - - if (name.endsWith(DynamicSubclass.IMPL_SUFFIX)) { - name = name.substring(0, name.length() - DynamicSubclass.IMPL_SUFFIX.length()); - restServiceInfo = restEjbs.get(name); - if (restServiceInfo != null) { // AutoJAXRSInvoker relies on it - restEjbs.put(clazz.getName(), restServiceInfo); - } - } - return restServiceInfo; - } - - private static Class realClass(final Class aClass) { - Class result = aClass; - while (result.getName().contains("$$")) { - result = result.getSuperclass(); - if (result == null) { - return aClass; - } - } - return result; - } - - private static String createEndpointName(final Application application) { - if (application == null) { - return "jaxrs-application"; - } - if (InternalApplication.class.isInstance(application)) { - final Application original = InternalApplication.class.cast(application).getOriginal(); - if (original != null) { - return original.getClass().getSimpleName(); - } - return "jaxrs-application"; - } - return application.getClass().getSimpleName(); - } - - private static String createServiceJmxName(final ClassLoader classLoader) { - final AppContext app = AppFinder.findAppContextOrWeb(classLoader, AppFinder.AppContextTransformer.INSTANCE); - return app == null ? "application" : app.getId(); - } - - private void logEndpoints(final Application application, final String prefix, - final Map restEjbs, - final JAXRSServerFactoryBean factory, final String base) { - final List resourcesToLog = new ArrayList(); - int classSize = 0; - int addressSize = 0; - - final JAXRSServiceImpl service = (JAXRSServiceImpl) factory.getServiceFactory().getService(); - final List resources = service.getClassResourceInfos(); - for (final ClassResourceInfo info : resources) { - if (info.getResourceClass() == null) { // possible? - continue; - } - - final String address = Logs.singleSlash(base, info.getURITemplate().getValue()); - - final String clazz = info.getResourceClass().getName(); - final String type; - if (restEjbs.containsKey(clazz)) { - type = "EJB"; - } else { - type = "Pojo"; - } - - classSize = Math.max(classSize, clazz.length()); - addressSize = Math.max(addressSize, address.length()); - - int methodSize = 7; - int methodStrSize = 0; - - final List toLog = new ArrayList(); - - final MethodDispatcher md = info.getMethodDispatcher(); - for (final OperationResourceInfo ori : md.getOperationResourceInfos()) { - final String httpMethod = ori.getHttpMethod(); - final String currentAddress = Logs.singleSlash(address, ori.getURITemplate().getValue()); - final String methodToStr = Logs.toSimpleString(ori.getMethodToInvoke()); - toLog.add(new Logs.LogOperationEndpointInfo(httpMethod, currentAddress, methodToStr)); - - if (httpMethod != null) { - methodSize = Math.max(methodSize, httpMethod.length()); - } - addressSize = Math.max(addressSize, currentAddress.length()); - methodStrSize = Math.max(methodStrSize, methodToStr.length()); - } - - Collections.sort(toLog); - - resourcesToLog.add(new Logs.LogResourceEndpointInfo(type, address, clazz, toLog, methodSize, methodStrSize)); - } - - // effective logging - - LOGGER.info("REST Application: " + Logs.forceLength(prefix, addressSize, true) + " -> " + application.getClass().getName()); - - Collections.sort(resourcesToLog); - - for (final Logs.LogResourceEndpointInfo resource : resourcesToLog) { - - // Init and register MBeans - final ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb.management") - .set("j2eeType", "JAX-RS") - .set("J2EEServer", "openejb") - .set("J2EEApplication", base) - .set("EndpointType", resource.type) - .set("name", resource.classname); - - final ObjectName jmxObjectName = jmxName.build(); - LocalMBeanServer.registerDynamicWrapperSilently( - new RestServiceMBean(resource), - jmxObjectName); - - jmxNames.add(jmxObjectName); - - LOGGER.info(" Service URI: " - + Logs.forceLength(resource.address, addressSize, true) + " -> " - + Logs.forceLength(resource.type, 4, false) + " " - + Logs.forceLength(resource.classname, classSize, true)); - - for (final Logs.LogOperationEndpointInfo log : resource.operations) { - LOGGER.info(" " - + Logs.forceLength(log.http, resource.methodSize, false) + " " - + Logs.forceLength(log.address, addressSize, true) + " -> " - + Logs.forceLength(log.method, resource.methodStrSize, true)); - } - - resource.operations.clear(); - } - resourcesToLog.clear(); - } - - private JAXRSServerFactoryBean newFactory(final String prefix, final String service, final String endpoint) { - final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean() { - @Override - protected Endpoint createEndpoint() throws BusException, EndpointException { - final Endpoint created = super.createEndpoint(); - created.put(ManagedEndpoint.SERVICE_NAME, service); - created.put(ManagedEndpoint.ENDPOINT_NAME, endpoint); - return created; - } - }; - factory.setDestinationFactory(transportFactory); - factory.setBus(CxfUtil.getBus()); - factory.setAddress(prefix); - return factory; - } - - private void configureFactory(final Collection givenAdditionalProviders, - final ServiceConfiguration serviceConfiguration, - final JAXRSServerFactoryBean factory, - final WebBeansContext ctx) { - CxfUtil.configureEndpoint(factory, serviceConfiguration, CXF_JAXRS_PREFIX); - - final Collection services = serviceConfiguration.getAvailableServices(); - - final String staticSubresourceResolution = serviceConfiguration.getProperties().getProperty(CXF_JAXRS_PREFIX + STATIC_SUB_RESOURCE_RESOLUTION_KEY); - if (staticSubresourceResolution != null) { - factory.setStaticSubresourceResolution("true".equalsIgnoreCase(staticSubresourceResolution)); - } - - // resource comparator - final String resourceComparator = serviceConfiguration.getProperties().getProperty(RESOURCE_COMPARATOR_KEY); - if (resourceComparator != null) { - try { - ResourceComparator instance = (ResourceComparator) ServiceInfos.resolve(services, resourceComparator); - if (instance == null) { - instance = (ResourceComparator) Thread.currentThread().getContextClassLoader() - .loadClass(resourceComparator).newInstance(); - } - factory.setResourceComparator(instance); - } catch (final Exception e) { - LOGGER.error("Can't create the resource comparator " + resourceComparator, e); - } - } - - // static resources - final String staticResources = serviceConfiguration.getProperties().getProperty(STATIC_RESOURCE_KEY); - if (staticResources != null) { - final String[] resources = staticResources.split(","); - for (final String r : resources) { - final String trimmed = r.trim(); - if (!trimmed.isEmpty()) { - staticResourcesList.add(Pattern.compile(trimmed)); - } - } - } - - // providers - Set providersConfig = null; - - { - final String provider = serviceConfiguration.getProperties().getProperty(PROVIDERS_KEY); - if (provider != null) { // already ordered and high priority since they were configured manually - providersConfig = new HashSet(); - for (final String p : Arrays.asList(provider.split(","))) { - providersConfig.add(p.trim()); - } - } - - { - if (GLOBAL_PROVIDERS != null) { // same idea, Note: this doesn't affect much cases which are not embedded so don't spend time on it - if (providersConfig == null) { - providersConfig = new HashSet(); - } - providersConfig.addAll(Arrays.asList(GLOBAL_PROVIDERS.split(","))); - } - } - } - - // another property to configure the scanning of providers but this one is consistent with current cxf config - // the other one is more generic but need another file - final String key = CXF_JAXRS_PREFIX + "skip-provider-scanning"; - final boolean ignoreAutoProviders = "true".equalsIgnoreCase(SystemInstance.get().getProperty(key, serviceConfiguration.getProperties().getProperty(key, "false"))); - final Collection additionalProviders = ignoreAutoProviders ? Collections.emptyList() : givenAdditionalProviders; - List providers = null; - if (providersConfig != null) { - providers = ServiceInfos.resolve(services, providersConfig.toArray(new String[providersConfig.size()]), ProviderFactory.INSTANCE); - if (providers != null && additionalProviders != null && !additionalProviders.isEmpty()) { - providers.addAll(sortProviders(serviceConfiguration, ctx, additionalProviders)); - } - } - if (providers == null) { - providers = new ArrayList(4); - if (additionalProviders != null && !additionalProviders.isEmpty()) { - providers.addAll(sortProviders(serviceConfiguration, ctx, additionalProviders)); - } else { - providers.addAll(defaultProviders()); - } - } - - if (!ignoreAutoProviders) { - addMandatoryProviders(providers); - } - - SystemInstance.get().fireEvent(new ExtensionProviderRegistration( - AppFinder.findAppContextOrWeb(Thread.currentThread().getContextClassLoader(), AppFinder.AppContextTransformer.INSTANCE), providers)); - - LOGGER.info("Using providers:"); - for (final Object provider : providers) { - LOGGER.info(" " + provider); - } - factory.setProviders(providers); - } - - private List sortProviders(final ServiceConfiguration serviceConfiguration, final WebBeansContext ctx, - final Collection additionalProviders) { - final Collection services = serviceConfiguration.getAvailableServices(); - final List loadedProviders = providers(services, additionalProviders, ctx); - if ("true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.cxf.rs.skip-provider-sorting", "false"))) { - return loadedProviders; - } - - final String comparatorClass = serviceConfiguration.getProperties().getProperty(CXF_JAXRS_PREFIX + "provider-comparator"); - - Comparator comparator = null; - if (comparatorClass == null) { - comparator = DefaultProviderComparator.INSTANCE; - } else { - final BeanManagerImpl bm = ctx.getBeanManagerImpl(); - if (bm != null && bm.isInUse()) { - try { - final Class clazz = Thread.currentThread().getContextClassLoader().loadClass(comparatorClass); - final Set> beans = bm.getBeans(clazz); - if (beans != null && !beans.isEmpty()) { - final Bean bean = bm.resolve(beans); - final CreationalContextImpl creationalContext = bm.createCreationalContext(bean); - comparator = Comparator.class.cast(bm.getReference(bean, clazz, creationalContext)); - toRelease.add(creationalContext); - } - } catch (final Throwable th) { - LOGGER.debug("Can't use CDI to load comparator " + comparatorClass); - } - } - - if (comparator == null) { - comparator = Comparator.class.cast(ServiceInfos.resolve(services, comparatorClass)); - } - if (comparator == null) { - try { - comparator = Comparator.class.cast(Thread.currentThread().getContextClassLoader().loadClass(comparatorClass).newInstance()); - } catch (final Exception e) { - throw new IllegalArgumentException(e); - } - } - } - Collections.sort(loadedProviders, comparator); - return loadedProviders; - } - - private static List defaultProviders() { - final JAXBElementProvider jaxb = new JAXBElementProvider(); - final Map jaxbProperties = new HashMap(); - jaxbProperties.put(Marshaller.JAXB_FRAGMENT, true); - jaxb.setMarshallerProperties(jaxbProperties); - - final List providers = new ArrayList(4); - providers.add(jaxb); - providers.add(new JSONProvider()); - return providers; - } - - // we use Object cause an app with a custom comparator can desire to compare instances - private static final class DefaultProviderComparator implements Comparator { - private static final DefaultProviderComparator INSTANCE = new DefaultProviderComparator(); - private static final ClassLoader SYSTEM_LOADER = ClassLoader.getSystemClassLoader(); - - @Override - public int compare(final Object o1, final Object o2) { - if (o1 == o2 || (o1 != null && o1.equals(o2))) { - return 0; - } - if (o1 == null) { - return -1; - } - if (o2 == null) { - return 1; - } - - final Class c1 = o1.getClass(); - final Class c2 = o2.getClass(); - - final ClassLoader classLoader1 = c1.getClassLoader(); - final ClassLoader classLoader2 = c2.getClassLoader(); - - final boolean loadersNotNull = classLoader1 != null && classLoader2 != null; - - if (classLoader1 != classLoader2 - && loadersNotNull - && !classLoader1.equals(classLoader2) && !classLoader2.equals(classLoader1)) { - if (isParent(classLoader1, classLoader2)) { - return 1; - } - if (isParent(classLoader2, classLoader1)) { - return -1; - } - } else { - final File l1 = jarLocation(c1); - final File l2 = jarLocation(c2); - if (l1 == null) { - return 1; - } - if (l2 == null) { - return -1; - } - - try { // WEB-INF/classes will be before WEB-INF/lib automatically - return l1.getCanonicalPath().compareTo(l2.getCanonicalPath()); - } catch (final IOException e) { - // no-op: sort by class name - } - } - - return c1.getName().compareTo(c2.getName()); - } - - private static boolean isParent(final ClassLoader l1, final ClassLoader l2) { - ClassLoader current = l2; - while (current != null && current != SYSTEM_LOADER) { - if (current.equals(l1) || l1.equals(current)) { - return true; - } - current = current.getParent(); - } - return false; - } - } - - private static class ProviderFactory implements ServiceInfos.Factory { - - private static final ServiceInfos.Factory INSTANCE = new ProviderFactory(); - - @Override - public Object newInstance(final Class clazz) throws Exception { - boolean found = false; - Object instance = null; - for (final Constructor c : clazz.getConstructors()) { - int contextAnnotations = 0; - for (final Annotation[] annotations : c.getParameterAnnotations()) { - for (final Annotation a : annotations) { - if (javax.ws.rs.core.Context.class.equals(a.annotationType())) { - contextAnnotations++; - break; - } - } - } - if (contextAnnotations == c.getParameterTypes().length) { - if (found) { - LOGGER.warning("Found multiple matching constructor for " + clazz.getName()); - return instance; - } - - final Object[] params = new Object[c.getParameterTypes().length]; - for (int i = 0; i < params.length; i++) { - params[i] = ThreadLocalContextManager.findThreadLocal(c.getParameterTypes()[i]); - // params[i] can be null if not a known type - } - instance = c.newInstance(params); - found = true; - } - } - if (instance != null) { - return instance; - } - return clazz.newInstance(); - } - } - - @SuppressWarnings("UnusedDeclaration") - @MBean - @Internal - @Description("JAX-RS service information") - public class RestServiceMBean { - - private final String type; - private final String address; - private final String classname; - private final TabularData operations; - - public RestServiceMBean(final Logs.LogResourceEndpointInfo jmxName) { - this.type = jmxName.type; - this.address = jmxName.address; - this.classname = jmxName.classname; - - final String[] names = new String[jmxName.operations.size()]; - final String[] values = new String[jmxName.operations.size()]; - int idx = 0; - for (final Logs.LogOperationEndpointInfo operation : jmxName.operations) { - names[idx] = operation.http + " " + operation.address; - values[idx] = operation.method; - idx++; - } - operations = LocalMBeanServer.tabularData("Operations", "Operations for this endpoint", names, values); - } - - @ManagedAttribute - @Description("The type of the REST service") - public String getWadlUrl() { - if (address.endsWith("?_wadl")) { - return address; - } - return address + "?_wadl"; - } - - @ManagedOperation - @Description("The type of the REST service") - public String getWadl(final String format) { - if (format != null && format.toLowerCase().contains("json")) { - InputStream inputStream = null; - try { - final URL url = new URL(getWadlUrl() + "&_type=json"); - final HttpURLConnection connection = HttpURLConnection.class.cast(url.openConnection()); - connection.setRequestProperty("Accept", "application/json"); - connection.setRequestProperty("Content-type", "application/json"); - inputStream = connection.getInputStream(); - return IO.slurp(inputStream); - } catch (final Exception e) { - return e.getMessage(); - } finally { - IO.close(inputStream); - } - } else { // xml - try { - return IO.slurp(new URL(getWadlUrl())); - } catch (final IOException e) { - return e.getMessage(); - } - } - } - - @ManagedAttribute - @Description("The type of the REST service") - public String getType() { - return type; - } - - @ManagedAttribute - @Description("The REST service address") - public String getAddress() { - return address; - } - - @ManagedAttribute - @Description("The REST service class name") - public String getClassname() { - return classname; - } - - @ManagedAttribute - @Description("All available methods") - public TabularData getOperations() { - return operations; - } - } -} +/* + * 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.openejb.server.cxf.rs; + +import org.apache.cxf.BusException; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.endpoint.Endpoint; +import org.apache.cxf.endpoint.EndpointException; +import org.apache.cxf.endpoint.ManagedEndpoint; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.endpoint.ServerImpl; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.JAXRSServiceImpl; +import org.apache.cxf.jaxrs.ext.RequestHandler; +import org.apache.cxf.jaxrs.ext.ResourceComparator; +import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.MethodDispatcher; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.jaxrs.model.ProviderInfo; +import org.apache.cxf.jaxrs.model.wadl.WadlGenerator; +import org.apache.cxf.jaxrs.provider.JAXBElementProvider; +import org.apache.cxf.jaxrs.provider.json.JSONProvider; +import org.apache.cxf.service.invoker.Invoker; +import org.apache.cxf.transport.http.AbstractHTTPDestination; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.cxf.transport.servlet.BaseUrlHelper; +import org.apache.openejb.AppContext; +import org.apache.openejb.BeanContext; +import org.apache.openejb.Injection; +import org.apache.openejb.api.internal.Internal; +import org.apache.openejb.api.jmx.Description; +import org.apache.openejb.api.jmx.MBean; +import org.apache.openejb.api.jmx.ManagedAttribute; +import org.apache.openejb.api.jmx.ManagedOperation; +import org.apache.openejb.assembler.classic.ServiceInfo; +import org.apache.openejb.assembler.classic.util.ServiceConfiguration; +import org.apache.openejb.assembler.classic.util.ServiceInfos; +import org.apache.openejb.core.WebContext; +import org.apache.openejb.dyni.DynamicSubclass; +import org.apache.openejb.loader.IO; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.monitoring.LocalMBeanServer; +import org.apache.openejb.monitoring.ObjectNameBuilder; +import org.apache.openejb.rest.ThreadLocalContextManager; +import org.apache.openejb.server.cxf.rs.event.ExtensionProviderRegistration; +import org.apache.openejb.server.cxf.rs.event.ServerCreated; +import org.apache.openejb.server.cxf.rs.event.ServerDestroyed; +import org.apache.openejb.server.cxf.transport.util.CxfUtil; +import org.apache.openejb.server.httpd.HttpRequest; +import org.apache.openejb.server.httpd.HttpRequestImpl; +import org.apache.openejb.server.httpd.HttpResponse; +import org.apache.openejb.server.httpd.ServletRequestAdapter; +import org.apache.openejb.server.rest.EJBRestServiceInfo; +import org.apache.openejb.server.rest.InternalApplication; +import org.apache.openejb.server.rest.RsHttpListener; +import org.apache.openejb.util.AppFinder; +import org.apache.openejb.util.LogCategory; +import org.apache.openejb.util.Logger; +import org.apache.openejb.util.proxy.ProxyEJB; +import org.apache.webbeans.config.WebBeansContext; +import org.apache.webbeans.container.BeanManagerImpl; +import org.apache.webbeans.context.creational.CreationalContextImpl; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.management.ObjectName; +import javax.management.openmbean.TabularData; +import javax.naming.Context; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Application; +import javax.xml.bind.Marshaller; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.regex.Pattern; + +import static org.apache.openejb.loader.JarLocation.jarLocation; + +public class CxfRsHttpListener implements RsHttpListener { + + private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_RS, CxfRsHttpListener.class); + + private static final java.util.logging.Logger SERVER_IMPL_LOGGER = LogUtils.getL7dLogger(ServerImpl.class); + + public static final String CXF_JAXRS_PREFIX = "cxf.jaxrs."; + public static final String PROVIDERS_KEY = CXF_JAXRS_PREFIX + "providers"; + public static final String STATIC_RESOURCE_KEY = CXF_JAXRS_PREFIX + "static-resources-list"; + public static final String STATIC_SUB_RESOURCE_RESOLUTION_KEY = "staticSubresourceResolution"; + public static final String RESOURCE_COMPARATOR_KEY = CXF_JAXRS_PREFIX + "resourceComparator"; + + public static final boolean TRY_STATIC_RESOURCES = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.static-first", "true")); + private static final String GLOBAL_PROVIDERS = SystemInstance.get().getProperty(PROVIDERS_KEY); + + private static final Map STATIC_CONTENT_TYPES; + private static final String[] DEFAULT_WELCOME_FILES = new String[]{ "/index.html", "/index.htm" }; + + private final HTTPTransportFactory transportFactory; + private final String wildcard; + private AbstractHTTPDestination destination; + private Server server; + private String context = ""; + private String servlet = ""; + private final Collection staticResourcesList = new CopyOnWriteArrayList(); + private final List jmxNames = new ArrayList(); + private final Collection> toRelease = new LinkedHashSet>(); + private final Collection singletons = new LinkedHashSet(); + + private static final char[] URL_SEP = new char[] { '?', '#', ';' }; + + static { + STATIC_CONTENT_TYPES = new HashMap(); + STATIC_CONTENT_TYPES.put("html", "text/html"); + STATIC_CONTENT_TYPES.put("xhtml", "text/html"); + STATIC_CONTENT_TYPES.put("txt", "text/plain"); + STATIC_CONTENT_TYPES.put("css", "text/css"); + STATIC_CONTENT_TYPES.put("jpg", "image/jpg"); + STATIC_CONTENT_TYPES.put("png", "image/png"); + STATIC_CONTENT_TYPES.put("ico", "image/ico"); + STATIC_CONTENT_TYPES.put("pdf", "application/pdf"); + STATIC_CONTENT_TYPES.put("xsd", "application/xml"); + + for (final ProviderInfo rh : org.apache.cxf.jaxrs.provider.ProviderFactory.getSharedInstance().getRequestHandlers()) { + final RequestHandler provider = rh.getProvider(); + if (WadlGenerator.class.isInstance(provider)) { + final WadlGenerator wadlGenerator = WadlGenerator.class.cast(provider); + final String ignoreRequests = SystemInstance.get().getProperty("openejb.cxf-rs.wadl-generator.ignoreRequests"); + final String ignoreMessageWriters = SystemInstance.get().getProperty("openejb.cxf-rs.wadl-generator.ignoreMessageWriters", "false"); + + if (ignoreRequests != null) { + wadlGenerator.setIgnoreRequests(Boolean.parseBoolean(ignoreRequests)); + } + // CXF-5319: bug in CXF? it prevents to get the wadl as json otherwise + if (ignoreMessageWriters != null) { + wadlGenerator.setIgnoreMessageWriters(Boolean.parseBoolean(ignoreMessageWriters)); + } + } + } + } + + private String pattern; + + public CxfRsHttpListener(final HTTPTransportFactory httpTransportFactory, final String star) { + transportFactory = httpTransportFactory; + wildcard = star; + } + + public void setUrlPattern(final String pattern) { + this.pattern = pattern; + } + + @Override + public void onMessage(final HttpRequest httpRequest, final HttpResponse httpResponse) throws Exception { + // fix the address (to manage multiple connectors) + if (HttpRequestImpl.class.isInstance(httpRequest)) { + final HttpRequestImpl requestImpl = HttpRequestImpl.class.cast(httpRequest); + requestImpl.initPathFromContext(context); + requestImpl.initServletPath(servlet); + } + + boolean matchedStatic = false; + if (TRY_STATIC_RESOURCES || (matchedStatic = matchPath(httpRequest))) { + final String pathInfo = httpRequest.getPathInfo(); + if (serveStaticContent(httpRequest, httpResponse, pathInfo)) { + if (matchedStatic) { // we should have gotten the resource + throw new ServletException("Static resource " + pathInfo + " is not available"); + } + return; // ok that's a surely rest service + } + } + + doInvoke(httpRequest, httpResponse); + } + + // normal endpoint without static resource handling + public void doInvoke(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { + String baseURL = BaseUrlHelper.getBaseURL(pattern != null ? new ServletRequestAdapter(httpRequest) { + @Override // we have a filter so we need the computed servlet path to not break CXF + public String getServletPath() { + return pattern; + } + } : httpRequest); + if (!baseURL.endsWith("/")) { + baseURL += "/"; + } + httpRequest.setAttribute("org.apache.cxf.transport.endpoint.address", baseURL); + + final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); + try { + destination.invoke(null, httpRequest.getServletContext(), httpRequest, httpResponse); + } catch (final Throwable t) { + throw new IOException("Failed to invoke AbstractHTTPDestination", t); + } finally { + CxfUtil.clearBusLoader(oldLoader); + } + } + + public boolean matchPath(final HttpServletRequest request) { + if (staticResourcesList.isEmpty()) { + return false; + } + + String path = request.getPathInfo(); + if (path == null) { + path = "/"; + } + for (final Pattern pattern : staticResourcesList) { + if (pattern.matcher(path).matches()) { + return true; + } + } + return false; + } + + public InputStream findStaticContent(final HttpServletRequest request, final String[] welcomeFiles) throws ServletException { + String pathInfo = request.getRequestURI().substring(request.getContextPath().length()); + for (final char c : URL_SEP) { + final int indexOf = pathInfo.indexOf(c); + if (indexOf > 0) { + pathInfo = pathInfo.substring(0, indexOf); + } + } + InputStream is = request.getServletContext().getResourceAsStream(pathInfo); + if (is == null && ("/".equals(pathInfo) || pathInfo.isEmpty())) { + for (final String n : welcomeFiles) { + is = request.getServletContext().getResourceAsStream(n); + if (is != null) { + break; + } + } + } + return is; + } + + public boolean serveStaticContent(final HttpServletRequest request, + final HttpServletResponse response, + final String pathInfo) throws ServletException { + final InputStream is = findStaticContent(request, DEFAULT_WELCOME_FILES); + if (is == null) { + return false; + } + try { + final int ind = pathInfo.lastIndexOf("."); + if (ind != -1 && ind < pathInfo.length()) { + final String type = STATIC_CONTENT_TYPES.get(pathInfo.substring(ind + 1)); + if (type != null) { + response.setContentType(type); + } + } + + final ServletOutputStream os = response.getOutputStream(); + IOUtils.copy(is, os); + os.flush(); + response.setStatus(HttpURLConnection.HTTP_OK); + } catch (final IOException ex) { + throw new ServletException("Static resource " + pathInfo + " can not be written to the output stream"); + } + return true; + } + + @Deprecated + @Override + public void deploySingleton(final String contextRoot, final String fullContext, final Object o, final Application appInstance, + final Collection additionalProviders, final ServiceConfiguration configuration) { + deploy(contextRoot, o.getClass(), fullContext, new SingletonResourceProvider(o), + o, appInstance, null, additionalProviders, configuration, null); + } + + @Deprecated + @Override + public void deployPojo(final ClassLoader loader, + final String contextRoot, + final String fullContext, + final Class loadedClazz, + final Application app, + final Collection injections, + final Context context, + final WebBeansContext owbCtx, + final Collection additionalProviders, + final ServiceConfiguration configuration) { + deploy(contextRoot, loadedClazz, fullContext, new OpenEJBPerRequestPojoResourceProvider(loader, loadedClazz, injections, context, owbCtx), + null, app, null, additionalProviders, configuration, null); + } + + @Deprecated + @Override + public void deployEJB(final String contextRoot, + final String fullContext, + final BeanContext beanContext, + final Collection additionalProviders, + final ServiceConfiguration configuration) { + final Object proxy = ProxyEJB.subclassProxy(beanContext); + + deploy(contextRoot, beanContext.getBeanClass(), fullContext, new NoopResourceProvider(beanContext.getBeanClass(), proxy), + proxy, null, new OpenEJBEJBInvoker(Collections.singleton(beanContext)), additionalProviders, configuration, beanContext.getWebBeansContext()); + } + + private void deploy(final String contextRoot, final Class clazz, final String address, final ResourceProvider rp, final Object serviceBean, + final Application app, final Invoker invoker, final Collection additionalProviders, final ServiceConfiguration configuration, + final WebBeansContext webBeansContext) { + final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); + try { + final JAXRSServerFactoryBean factory = newFactory(address, createServiceJmxName(clazz.getClassLoader()), createEndpointName(app)); + configureFactory(additionalProviders, configuration, factory, webBeansContext); + factory.setResourceClasses(clazz); + context = contextRoot; + if (context == null) { + context = ""; + } + if (!context.startsWith("/")) { + context = "/" + context; + } + + if (rp != null) { + factory.setResourceProvider(rp); + } + if (app != null) { + factory.setApplication(app); + } + if (invoker != null) { + factory.setInvoker(invoker); + } + if (serviceBean != null) { + factory.setServiceBean(serviceBean); + } else { + factory.setServiceClass(clazz); + } + + server = factory.create(); + destination = (AbstractHTTPDestination) server.getDestination(); + fireServerCreated(oldLoader); + } finally { + if (oldLoader != null) { + CxfUtil.clearBusLoader(oldLoader); + } + } + } + + private void fireServerCreated(final ClassLoader oldLoader) { + final Object ctx = AppFinder.findAppContextOrWeb(oldLoader, new AppFinder.Transformer() { + @Override + public Object from(final AppContext appCtx) { + return appCtx; + } + + @Override + public Object from(final WebContext webCtx) { + return webCtx; + } + }); + final AppContext appCtx = AppContext.class.isInstance(ctx) ? AppContext.class.cast(ctx) : WebContext.class.cast(ctx).getAppContext(); + WebContext webContext = appCtx == ctx ? null : WebContext.class.cast(ctx); + if (webContext == null && appCtx.getWebContexts().size() == 1 && appCtx.getWebContexts().get(0).getClassLoader() == oldLoader) { + webContext = appCtx.getWebContexts().get(0); + } + SystemInstance.get().fireEvent(new ServerCreated(server, appCtx, webContext, this.context)); + } + + private List providers(final Collection services, final Collection additionalProviders, final WebBeansContext ctx) { + final List instances = new ArrayList(); + final BeanManagerImpl bm = ctx == null ? null : ctx.getBeanManagerImpl(); + for (final Object o : additionalProviders) { + if (o instanceof Class) { + final Class clazz = (Class) o; + final String name = clazz.getName(); + if (shouldSkipProvider(name)) { + continue; + } + + if (bm != null && bm.isInUse()) { + try { + final Set> beans = bm.getBeans(clazz); + if (beans != null && !beans.isEmpty()) { + final Bean bean = bm.resolve(beans); + final CreationalContextImpl creationalContext = bm.createCreationalContext(bean); + instances.add(bm.getReference(bean, clazz, creationalContext)); + toRelease.add(creationalContext); + continue; + } + } catch (final Throwable th) { + LOGGER.info("Can't use CDI to create provider " + name); + } + } + + final Collection instance = ServiceInfos.resolve(services, new String[]{name}, ProviderFactory.INSTANCE); + if (instance != null && !instance.isEmpty()) { + instances.add(instance.iterator().next()); + } else { + try { + instances.add(newProvider(clazz)); + } catch (final Exception e) { + LOGGER.error("can't instantiate " + name, e); + } + } + } else { + final String name = o.getClass().getName(); + if (shouldSkipProvider(name)) { + continue; + } + instances.add(o); + } + } + + return instances; + } + + private static boolean shouldSkipProvider(final String name) { + return "false".equalsIgnoreCase(SystemInstance.get().getProperty(name + ".activated", "true")) + || name.startsWith("org.apache.wink.common.internal."); + } + + private static void addMandatoryProviders(final Collection instances) { + instances.add(EJBAccessExceptionMapper.INSTANCE); + } + + private Object newProvider(final Class clazz) throws IllegalAccessException, InstantiationException { + return clazz.newInstance(); + } + + @Override + public void undeploy() { + // unregister all MBeans + for (final ObjectName objectName : jmxNames) { + LocalMBeanServer.unregisterSilently(objectName); + } + + for (final CreationalContext cc : toRelease) { + try { + cc.release(); + } catch (final Exception e) { + LOGGER.warning(e.getMessage(), e); + } + } + for (final CdiSingletonResourceProvider provider : singletons) { + try { + provider.release(); + } catch (final Exception e) { + LOGGER.warning(e.getMessage(), e); + } + } + + final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); + try { + server.destroy(); + SystemInstance.get().fireEvent(new ServerDestroyed(server)); + } catch (final RuntimeException ise) { + LOGGER.warning("Can't stop correctly the endpoint " + server); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(ise.getMessage(), ise); + } + } finally { + if (oldLoader != null) { + CxfUtil.clearBusLoader(oldLoader); + } + } + } + + @Override + public void deployApplication(final Application application, final String prefix, final String webContext, + final Collection additionalProviders, + final Map restEjbs, final ClassLoader classLoader, + final Collection injections, final Context context, final WebBeansContext owbCtx, + final ServiceConfiguration serviceConfiguration) { + final ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(CxfUtil.initBusLoader()); + try { + final JAXRSServerFactoryBean factory = newFactory(prefix, createServiceJmxName(classLoader), createEndpointName(application)); + configureFactory(additionalProviders, serviceConfiguration, factory, owbCtx); + factory.setApplication(application); + + final List> classes = new ArrayList>(); + + for (final Class clazz : application.getClasses()) { + if (!additionalProviders.contains(clazz) && !clazz.isInterface()) { + classes.add(clazz); + + final EJBRestServiceInfo restServiceInfo = getEjbRestServiceInfo(restEjbs, clazz); + + if (restServiceInfo != null) { + final Object proxy = ProxyEJB.subclassProxy(restServiceInfo.context); + factory.setResourceProvider(clazz, new NoopResourceProvider(restServiceInfo.context.getBeanClass(), proxy)); + } else { + factory.setResourceProvider(clazz, new OpenEJBPerRequestPojoResourceProvider( + classLoader, clazz, injections, context, owbCtx)); + } + } + } + + for (final Object o : application.getSingletons()) { + if (!additionalProviders.contains(o)) { + final Class clazz = realClass(o.getClass()); + classes.add(clazz); + + final EJBRestServiceInfo restServiceInfo = getEjbRestServiceInfo(restEjbs, clazz); + + if (restServiceInfo != null) { + final Object proxy = ProxyEJB.subclassProxy(restServiceInfo.context); + factory.setResourceProvider(clazz, new NoopResourceProvider(restServiceInfo.context.getBeanClass(), proxy)); + } else { + if (owbCtx.getBeanManagerImpl().isInUse()) { + final CdiSingletonResourceProvider provider = new CdiSingletonResourceProvider(classLoader, clazz, o, injections, context, owbCtx); + singletons.add(provider); + factory.setResourceProvider(clazz, provider); + } else { + factory.setResourceProvider(clazz, new SingletonResourceProvider(o)); + } + } + } + } + + factory.setResourceClasses(classes); + factory.setInvoker(new AutoJAXRSInvoker(restEjbs)); + + this.context = webContext; + if (!webContext.startsWith("/")) { + this.context = "/" + webContext; + } // /webcontext/servlet for event firing + + final Level level = SERVER_IMPL_LOGGER.getLevel(); + try { + SERVER_IMPL_LOGGER.setLevel(Level.OFF); + } catch (final UnsupportedOperationException e) { + //ignore + } + try { + server = factory.create(); + fireServerCreated(oldLoader); + } finally { + try { + SERVER_IMPL_LOGGER.setLevel(level); + } catch (final UnsupportedOperationException e) { + //ignore + } + } + + final int servletIdx = 1 + this.context.substring(1).indexOf('/'); + if (servletIdx > 0) { + this.servlet = this.context.substring(servletIdx); + this.context = this.context.substring(0, servletIdx); + } + destination = (AbstractHTTPDestination) server.getDestination(); + + final String base; + if (prefix.endsWith("/")) { + base = prefix.substring(0, prefix.length() - 1); + } else if (prefix.endsWith(wildcard)) { + base = prefix.substring(0, prefix.length() - wildcard.length()); + } else { + base = prefix; + } + + // stack info to log to get nice logs + logEndpoints(application, prefix, restEjbs, factory, base); + } finally { + if (oldLoader != null) { + CxfUtil.clearBusLoader(oldLoader); + } + } + } + + private EJBRestServiceInfo getEjbRestServiceInfo(final Map restEjbs, final Class clazz) { + String name = clazz.getName(); + EJBRestServiceInfo restServiceInfo = restEjbs.get(name); + + if (name.endsWith(DynamicSubclass.IMPL_SUFFIX)) { + name = name.substring(0, name.length() - DynamicSubclass.IMPL_SUFFIX.length()); + restServiceInfo = restEjbs.get(name); + if (restServiceInfo != null) { // AutoJAXRSInvoker relies on it + restEjbs.put(clazz.getName(), restServiceInfo); + } + } + return restServiceInfo; + } + + private static Class realClass(final Class aClass) { + Class result = aClass; + while (result.getName().contains("$$")) { + result = result.getSuperclass(); + if (result == null) { + return aClass; + } + } + return result; + } + + private static String createEndpointName(final Application application) { + if (application == null) { + return "jaxrs-application"; + } + if (InternalApplication.class.isInstance(application)) { + final Application original = InternalApplication.class.cast(application).getOriginal(); + if (original != null) { + return original.getClass().getSimpleName(); + } + return "jaxrs-application"; + } + return application.getClass().getSimpleName(); + } + + private static String createServiceJmxName(final ClassLoader classLoader) { + final AppContext app = AppFinder.findAppContextOrWeb(classLoader, AppFinder.AppContextTransformer.INSTANCE); + return app == null ? "application" : app.getId(); + } + + private void logEndpoints(final Application application, final String prefix, + final Map restEjbs, + final JAXRSServerFactoryBean factory, final String base) { + final List resourcesToLog = new ArrayList(); + int classSize = 0; + int addressSize = 0; + + final JAXRSServiceImpl service = (JAXRSServiceImpl) factory.getServiceFactory().getService(); + final List resources = service.getClassResourceInfos(); + for (final ClassResourceInfo info : resources) { + if (info.getResourceClass() == null) { // possible? + continue; + } + + final String address = Logs.singleSlash(base, info.getURITemplate().getValue()); + + final String clazz = info.getResourceClass().getName(); + final String type; + if (restEjbs.containsKey(clazz)) { + type = "EJB"; + } else { + type = "Pojo"; + } + + classSize = Math.max(classSize, clazz.length()); + addressSize = Math.max(addressSize, address.length()); + + int methodSize = 7; + int methodStrSize = 0; + + final List toLog = new ArrayList(); + + final MethodDispatcher md = info.getMethodDispatcher(); + for (final OperationResourceInfo ori : md.getOperationResourceInfos()) { + final String httpMethod = ori.getHttpMethod(); + final String currentAddress = Logs.singleSlash(address, ori.getURITemplate().getValue()); + final String methodToStr = Logs.toSimpleString(ori.getMethodToInvoke()); + toLog.add(new Logs.LogOperationEndpointInfo(httpMethod, currentAddress, methodToStr)); + + if (httpMethod != null) { + methodSize = Math.max(methodSize, httpMethod.length()); + } + addressSize = Math.max(addressSize, currentAddress.length()); + methodStrSize = Math.max(methodStrSize, methodToStr.length()); + } + + Collections.sort(toLog); + + resourcesToLog.add(new Logs.LogResourceEndpointInfo(type, address, clazz, toLog, methodSize, methodStrSize)); + } + + // effective logging + + LOGGER.info("REST Application: " + Logs.forceLength(prefix, addressSize, true) + " -> " + application.getClass().getName()); + + Collections.sort(resourcesToLog); + + for (final Logs.LogResourceEndpointInfo resource : resourcesToLog) { + + // Init and register MBeans + final ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb.management") + .set("j2eeType", "JAX-RS") + .set("J2EEServer", "openejb") + .set("J2EEApplication", base) + .set("EndpointType", resource.type) + .set("name", resource.classname); + + final ObjectName jmxObjectName = jmxName.build(); + LocalMBeanServer.registerDynamicWrapperSilently( + new RestServiceMBean(resource), + jmxObjectName); + + jmxNames.add(jmxObjectName); + + LOGGER.info(" Service URI: " + + Logs.forceLength(resource.address, addressSize, true) + " -> " + + Logs.forceLength(resource.type, 4, false) + " " + + Logs.forceLength(resource.classname, classSize, true)); + + for (final Logs.LogOperationEndpointInfo log : resource.operations) { + LOGGER.info(" " + + Logs.forceLength(log.http, resource.methodSize, false) + " " + + Logs.forceLength(log.address, addressSize, true) + " -> " + + Logs.forceLength(log.method, resource.methodStrSize, true)); + } + + resource.operations.clear(); + } + resourcesToLog.clear(); + } + + private JAXRSServerFactoryBean newFactory(final String prefix, final String service, final String endpoint) { + final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean() { + @Override + protected Endpoint createEndpoint() throws BusException, EndpointException { + final Endpoint created = super.createEndpoint(); + created.put(ManagedEndpoint.SERVICE_NAME, service); + created.put(ManagedEndpoint.ENDPOINT_NAME, endpoint); + return created; + } + }; + factory.setDestinationFactory(transportFactory); + factory.setBus(CxfUtil.getBus()); + factory.setAddress(prefix); + return factory; + } + + private void configureFactory(final Collection givenAdditionalProviders, + final ServiceConfiguration serviceConfiguration, + final JAXRSServerFactoryBean factory, + final WebBeansContext ctx) { + CxfUtil.configureEndpoint(factory, serviceConfiguration, CXF_JAXRS_PREFIX); + + final Collection services = serviceConfiguration.getAvailableServices(); + + final String staticSubresourceResolution = serviceConfiguration.getProperties().getProperty(CXF_JAXRS_PREFIX + STATIC_SUB_RESOURCE_RESOLUTION_KEY); + if (staticSubresourceResolution != null) { + factory.setStaticSubresourceResolution("true".equalsIgnoreCase(staticSubresourceResolution)); + } + + // resource comparator + final String resourceComparator = serviceConfiguration.getProperties().getProperty(RESOURCE_COMPARATOR_KEY); + if (resourceComparator != null) { + try { + ResourceComparator instance = (ResourceComparator) ServiceInfos.resolve(services, resourceComparator); + if (instance == null) { + instance = (ResourceComparator) Thread.currentThread().getContextClassLoader() + .loadClass(resourceComparator).newInstance(); + } + factory.setResourceComparator(instance); + } catch (final Exception e) { + LOGGER.error("Can't create the resource comparator " + resourceComparator, e); + } + } + + // static resources + final String staticResources = serviceConfiguration.getProperties().getProperty(STATIC_RESOURCE_KEY); + if (staticResources != null) { + final String[] resources = staticResources.split(","); + for (final String r : resources) { + final String trimmed = r.trim(); + if (!trimmed.isEmpty()) { + staticResourcesList.add(Pattern.compile(trimmed)); + } + } + } + + // providers + Set providersConfig = null; + + { + final String provider = serviceConfiguration.getProperties().getProperty(PROVIDERS_KEY); + if (provider != null) { // already ordered and high priority since they were configured manually + providersConfig = new HashSet(); + for (final String p : Arrays.asList(provider.split(","))) { + providersConfig.add(p.trim()); + } + } + + { + if (GLOBAL_PROVIDERS != null) { // same idea, Note: this doesn't affect much cases which are not embedded so don't spend time on it + if (providersConfig == null) { + providersConfig = new HashSet(); + } + providersConfig.addAll(Arrays.asList(GLOBAL_PROVIDERS.split(","))); + } + } + } + + // another property to configure the scanning of providers but this one is consistent with current cxf config + // the