| /* |
| * 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.feature.Feature; |
| import org.apache.cxf.helpers.IOUtils; |
| import org.apache.cxf.interceptor.Interceptor; |
| import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; |
| import org.apache.cxf.jaxrs.JAXRSServiceImpl; |
| 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.ApplicationInfo; |
| 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.provider.ProviderFactory; |
| import org.apache.cxf.jaxrs.provider.ServerProviderFactory; |
| import org.apache.cxf.jaxrs.sse.SseContextProvider; |
| import org.apache.cxf.jaxrs.utils.JAXRSUtils; |
| import org.apache.cxf.jaxrs.utils.HttpUtils; |
| import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor; |
| import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor; |
| import org.apache.cxf.jaxrs.validation.ValidationExceptionMapper; |
| import org.apache.cxf.message.Message; |
| import org.apache.cxf.service.invoker.Invoker; |
| import org.apache.cxf.transport.DestinationFactory; |
| import org.apache.cxf.transport.servlet.BaseUrlHelper; |
| import org.apache.cxf.validation.BeanValidationFeature; |
| import org.apache.cxf.validation.BeanValidationInInterceptor; |
| import org.apache.cxf.validation.BeanValidationOutInterceptor; |
| import org.apache.cxf.validation.BeanValidationProvider; |
| import org.apache.cxf.validation.ResponseConstraintViolationException; |
| import org.apache.johnzon.jaxrs.WadlDocumentMessageBodyWriter; |
| 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.rs.sse.TomEESseEventSinkContextProvider; |
| import org.apache.openejb.server.cxf.transport.HttpDestination; |
| 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.openejb.util.reflection.Reflections; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.webbeans.container.BeanManagerImpl; |
| import org.apache.webbeans.context.creational.CreationalContextImpl; |
| |
| import javax.enterprise.context.Dependent; |
| import javax.enterprise.context.spi.CreationalContext; |
| import javax.enterprise.inject.InjectionException; |
| import javax.enterprise.inject.spi.Bean; |
| import javax.inject.Singleton; |
| import javax.management.ObjectName; |
| import javax.management.openmbean.TabularData; |
| import javax.naming.Context; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletOutputStream; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletRequestWrapper; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.validation.Validation; |
| import javax.validation.Validator; |
| import javax.validation.ValidatorFactory; |
| import javax.validation.metadata.MethodDescriptor; |
| import javax.ws.rs.ConstrainedTo; |
| import javax.ws.rs.RuntimeType; |
| import javax.ws.rs.WebApplicationException; |
| import javax.ws.rs.client.ClientRequestFilter; |
| import javax.ws.rs.core.Application; |
| import javax.ws.rs.core.Configuration; |
| import javax.ws.rs.core.MediaType; |
| import javax.ws.rs.core.MultivaluedMap; |
| import javax.ws.rs.core.MultivaluedHashMap; |
| import javax.ws.rs.core.Response; |
| import javax.ws.rs.ext.ExceptionMapper; |
| import javax.ws.rs.ext.MessageBodyReader; |
| import javax.ws.rs.ext.MessageBodyWriter; |
| import javax.ws.rs.ext.Provider; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| 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.Iterator; |
| 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 java.util.Arrays.asList; |
| 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"; |
| |
| private static final String GLOBAL_PROVIDERS = SystemInstance.get().getProperty(PROVIDERS_KEY); |
| public static final boolean TRY_STATIC_RESOURCES = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.static-first", "true")); |
| private static final boolean FAIL_ON_CONSTRAINED_TO = "true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.jaxrs.fail-on-constrainedto", "true")); |
| |
| private static final Map<String, String> STATIC_CONTENT_TYPES; |
| private static final String[] DEFAULT_WELCOME_FILES = new String[]{"/index.html", "/index.htm"}; |
| |
| // we have proxies etc so we can't really give it to cxf properly, |
| // bval impl supports it (message just uses Object instead of the real instance) |
| private static final Object FAKE_SERVICE_OBJECT = new Object(); |
| |
| private final DestinationFactory transportFactory; |
| private final String wildcard; |
| private final CxfRSService service; |
| private HttpDestination destination; |
| private Server server; |
| private String context = ""; |
| private String servlet = ""; |
| private final Collection<Pattern> staticResourcesList = new CopyOnWriteArrayList<>(); |
| private final List<ObjectName> jmxNames = new ArrayList<>(); |
| private final Collection<CreationalContext<?>> toRelease = new LinkedHashSet<>(); |
| private final Collection<CdiSingletonResourceProvider> 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("htm", "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"); |
| } |
| |
| private String pattern; |
| |
| public CxfRsHttpListener(final DestinationFactory destinationFactory, final String star, final CxfRSService cxfRSService) { |
| transportFactory = destinationFactory; |
| wildcard = star; |
| service = cxfRSService; |
| } |
| |
| 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) |
| { |
| ServletRequest unwrapped = httpRequest; |
| while (ServletRequestAdapter.class.isInstance(unwrapped)) { |
| unwrapped = ServletRequestAdapter.class.cast(unwrapped).getRequest(); |
| } |
| while (HttpServletRequestWrapper.class.isInstance(unwrapped)) { |
| unwrapped = HttpServletRequestWrapper.class.cast(unwrapped).getRequest(); |
| } |
| if (HttpRequestImpl.class.isInstance(unwrapped)) { |
| final HttpRequestImpl requestImpl = HttpRequestImpl.class.cast(unwrapped); |
| requestImpl.initPathFromContext((!context.startsWith("/") ? "/" : "") + 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); |
| } finally { |
| CxfUtil.clearBusLoader(oldLoader); |
| } |
| } |
| |
| public boolean matchPath(final HttpServletRequest request) { |
| if (staticResourcesList.isEmpty()) { |
| return false; |
| } |
| |
| String path = request.getRequestURI().substring(request.getContextPath().length()); |
| if (path.isEmpty()) { |
| 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); |
| } |
| } |
| if (pathInfo.endsWith("/") || pathInfo.isEmpty()) { // root of path is redirected to welcomefiles |
| if (pathInfo.endsWith("/")) { |
| pathInfo = pathInfo.substring(0, pathInfo.length() - 1); |
| } |
| for (final String n : welcomeFiles) { |
| final InputStream is = request.getServletContext().getResourceAsStream(pathInfo + n); |
| if (is != null) { |
| return is; |
| } |
| } |
| return null; // "/" resolves to an empty string otherwise, we need to avoid it |
| } |
| return request.getServletContext().getResourceAsStream(pathInfo); |
| } |
| |
| 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"); |
| } finally { |
| try { |
| is.close(); |
| } catch (final IOException e) { |
| // no-op |
| } |
| } |
| return true; |
| } |
| |
| private Application findApplication() { |
| try { |
| ApplicationInfo appInfo = (ApplicationInfo)server.getEndpoint().get(Application.class.getName()); |
| return (Application)appInfo.getProvider(); |
| } catch (final Exception e) { |
| } |
| return null; |
| } |
| |
| private boolean applicationProvidesResources(final Application application) { |
| try { |
| if (application == null) { |
| return false; |
| } |
| if (InternalApplication.class.isInstance(application) && (InternalApplication.class.cast(application).getOriginal() == null)) { |
| return false; |
| } |
| return !application.getClasses().isEmpty() || !application.getSingletons().isEmpty(); |
| } catch (final Exception e) { |
| return false; |
| } |
| } |
| |
| public boolean isCXFResource(final HttpServletRequest request) { |
| try { |
| Application application = findApplication(); |
| if (!applicationProvidesResources(application)) { |
| JAXRSServiceImpl service = (JAXRSServiceImpl)server.getEndpoint().getService(); |
| |
| if( service == null ) { |
| return false; |
| } |
| |
| String pathToMatch = HttpUtils.getPathToMatch(request.getServletPath(), pattern, true); |
| |
| final List<ClassResourceInfo> resources = service.getClassResourceInfos(); |
| for (final ClassResourceInfo info : resources) { |
| if (info.getResourceClass() == null || info.getURITemplate() == null) { // possible? |
| continue; |
| } |
| |
| final MultivaluedMap<String, String> parameters = new MultivaluedHashMap<>(); |
| if (info.getURITemplate().match(pathToMatch, parameters)) { |
| return true; |
| } |
| } |
| } else { |
| return true; |
| } |
| } catch (final Exception e) { |
| LOGGER.info("No JAX-RS service"); |
| } |
| return false; |
| } |
| |
| @Override |
| @Deprecated // we could drop it now I think |
| public void deploySingleton(final String contextRoot, final String fullContext, final Object o, final Application appInstance, |
| final Collection<Object> additionalProviders, final ServiceConfiguration configuration) { |
| deploy(contextRoot, o.getClass(), fullContext, new SingletonResourceProvider(o), o, appInstance, null, additionalProviders, configuration, null); |
| } |
| |
| @Override |
| @Deprecated // we could drop it now I think |
| public void deployPojo(final ClassLoader loader, |
| final String contextRoot, |
| final String fullContext, |
| final Class<?> loadedClazz, |
| final Application app, |
| final Collection<Injection> injections, |
| final Context context, |
| final WebBeansContext owbCtx, |
| final Collection<Object> additionalProviders, |
| final ServiceConfiguration configuration) { |
| deploy(contextRoot, loadedClazz, fullContext, new OpenEJBPerRequestPojoResourceProvider(loader, loadedClazz, injections, context, owbCtx), |
| null, app, null, additionalProviders, configuration, owbCtx); |
| } |
| |
| @Override |
| @Deprecated // we could drop it now I think |
| public void deployEJB(final String contextRoot, |
| final String fullContext, |
| final BeanContext beanContext, |
| final Collection<Object> 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<Object> 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); |
| } |
| |
| factory.setTransportId("http://cxf.apache.org/transports/http/sse"); |
| |
| server = factory.create(); |
| |
| destination = (HttpDestination) 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<Object>() { |
| @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<Object> providers(final Collection<ServiceInfo> services, final Collection<Object> additionalProviders, final WebBeansContext ctx) { |
| final List<Object> 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; |
| if (isNotServerProvider(clazz)) { |
| continue; |
| } |
| |
| final String name = clazz.getName(); |
| if (shouldSkipProvider(name)) { |
| continue; |
| } |
| |
| if (bm != null && bm.isInUse()) { |
| try { |
| final Set<Bean<?>> 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<Object> instance = ServiceInfos.resolve(services, new String[]{name}, OpenEJBProviderFactory.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 Class<?> clazz = o.getClass(); |
| if (isNotServerProvider(clazz)) { |
| continue; |
| } |
| final String name = clazz.getName(); |
| if (shouldSkipProvider(name)) { |
| continue; |
| } |
| instances.add(o); |
| } |
| } |
| |
| return instances; |
| } |
| |
| private boolean isNotServerProvider(Class<?> clazz) { |
| final ConstrainedTo ct = clazz.getAnnotation(ConstrainedTo.class); |
| if (ct != null && ct.value() != RuntimeType.SERVER) { |
| if (!FAIL_ON_CONSTRAINED_TO) { |
| LOGGER.warning(clazz + " is not a SERVER provider, ignoring"); |
| return true; |
| } |
| throw new IllegalArgumentException(clazz + " is not a SERVER provider"); |
| } |
| |
| if (ClientRequestFilter.class.isAssignableFrom(clazz)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private boolean shouldSkipProvider(final String name) { |
| return !service.isActive(name) || name.startsWith("org.apache.wink.common.internal."); |
| } |
| |
| private void addMandatoryProviders(final Collection<Object> instances, final ServiceConfiguration serviceConfiguration) { |
| if (SystemInstance.get().getProperty("openejb.jaxrs.jsonProviders") == null) { |
| if (!shouldSkipProvider(WadlDocumentMessageBodyWriter.class.getName())) { |
| instances.add(new WadlDocumentMessageBodyWriter()); |
| } |
| } |
| if (!shouldSkipProvider(EJBExceptionMapper.class.getName())) { |
| instances.add(new EJBExceptionMapper()); |
| } |
| if (!shouldSkipProvider(ValidationExceptionMapper.class.getName())) { |
| instances.add(new ValidationExceptionMapper()); |
| final String level = SystemInstance.get() |
| .getProperty( |
| "openejb.cxf.rs.bval.log.level", |
| serviceConfiguration.getProperties().getProperty(CXF_JAXRS_PREFIX + "bval.log.level")); |
| if (level != null) { |
| try { |
| LogUtils.getL7dLogger(ValidationExceptionMapper.class).setLevel(Level.parse(level)); |
| } catch (final UnsupportedOperationException uoe) { |
| LOGGER.warning("Can't set level " + level + " on " + |
| "org.apache.cxf.jaxrs.validation.ValidationExceptionMapper logger, " + |
| "please configure it in your logging framework."); |
| } |
| } |
| } |
| } |
| |
| private Object newProvider(final Class<?> clazz) throws IllegalAccessException, InstantiationException { |
| return clazz.newInstance(); |
| } |
| |
| @Override |
| public void undeploy() { |
| 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<Object> additionalProviders, |
| final Map<String, EJBRestServiceInfo> restEjbs, final ClassLoader classLoader, |
| final Collection<Injection> 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<Class<?>> 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 { |
| // check if its a singleton. |
| |
| if (owbCtx != null) { |
| final BeanManagerImpl bm = owbCtx.getBeanManagerImpl(); |
| Bean<?> bean = null; |
| |
| if (bm != null && bm.isInUse()) { |
| try { |
| final Set<Bean<?>> beans = bm.getBeans(clazz); |
| bean = bm.resolve(beans); |
| } catch (final InjectionException ie) { |
| final String msg = "Resource class " + clazz.getName() + " can not be instantiated"; |
| LOGGER.warning(msg, ie); |
| throw new WebApplicationException(Response.serverError().entity(msg).build()); |
| } |
| |
| if (bean != null && isConsideredSingleton(bean.getScope())) { |
| final Object reference = bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean)); |
| factory.setResourceProvider(clazz, new CdiSingletonResourceProvider( |
| classLoader, clazz, reference, injections, context, owbCtx)); |
| |
| continue; |
| } |
| } |
| } |
| |
| |
| 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 != null && 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 { |
| factory.setProvider(new SseContextProvider()); |
| factory.setProvider(new TomEESseEventSinkContextProvider()); |
| |
| server = factory.create(); |
| fixProviderIfKnown(); |
| fireServerCreated(oldLoader); |
| |
| final ServerProviderFactory spf = ServerProviderFactory.class.cast(server.getEndpoint().get(ServerProviderFactory.class.getName())); |
| LOGGER.info("Using readers:"); |
| for (final Object provider : List.class.cast(Reflections.get(spf, "messageReaders"))) { |
| LOGGER.info(" " + ProviderInfo.class.cast(provider).getProvider()); |
| } |
| LOGGER.info("Using writers:"); |
| for (final Object provider : List.class.cast(Reflections.get(spf, "messageWriters"))) { |
| LOGGER.info(" " + ProviderInfo.class.cast(provider).getProvider()); |
| } |
| LOGGER.info("Using exception mappers:"); |
| for (final Object provider : List.class.cast(Reflections.get(spf, "exceptionMappers"))) { |
| LOGGER.info(" " + ProviderInfo.class.cast(provider).getProvider()); |
| } |
| } 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 = (HttpDestination) 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 boolean isConsideredSingleton(final Class<?> scope) { |
| return Singleton.class == scope || Dependent.class == scope; |
| } |
| |
| private void fixProviderIfKnown() { |
| final ServerProviderFactory spf = ServerProviderFactory.class.cast(server.getEndpoint().get(ServerProviderFactory.class.getName())); |
| for (final String field : asList("messageWriters", "messageReaders")) { |
| final List<ProviderInfo<?>> values = List.class.cast(Reflections.get(spf, field)); |
| |
| boolean customJsonProvider = false; |
| for (final ProviderInfo<?> o : values) { // using getName to not suppose any classloader setup |
| final String name = o.getResourceClass().getName(); |
| if ("org.apache.johnzon.jaxrs.ConfigurableJohnzonProvider".equals(name) |
| || "org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider".equals(name) |
| // contains in case of proxying |
| || name.contains("com.fasterxml.jackson.jaxrs.json")) { |
| customJsonProvider = true; |
| break; // cause we only handle json for now |
| } |
| } |
| |
| if (customJsonProvider) { // remove JohnzonProvider default versions |
| final Iterator<ProviderInfo<?>> it = values.iterator(); |
| while (it.hasNext()) { |
| final String name = it.next().getResourceClass().getName(); |
| if ("org.apache.johnzon.jaxrs.JohnzonProvider".equals(name) || |
| "org.apache.openejb.server.cxf.rs.CxfRSService$TomEEJohnzonProvider".equals(name)) { |
| it.remove(); |
| break; |
| } |
| } |
| } |
| } |
| |
| } |
| |
| 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 EJBRestServiceInfo getEjbRestServiceInfo(Map<String, EJBRestServiceInfo> restEjbs, 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 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<String, EJBRestServiceInfo> restEjbs, |
| final JAXRSServerFactoryBean factory, final String base) { |
| final List<Logs.LogResourceEndpointInfo> resourcesToLog = new ArrayList<>(); |
| int classSize = 0; |
| int addressSize = 0; |
| |
| final JAXRSServiceImpl service = (JAXRSServiceImpl) factory.getServiceFactory().getService(); |
| final List<ClassResourceInfo> 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<Logs.LogOperationEndpointInfo> 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) + " -> " + |
| (InternalApplication.class.isInstance(application) && InternalApplication.class.cast(application).getOriginal() != null ? |
| InternalApplication.class.cast(application).getOriginal() : application)); |
| |
| 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<Object> givenAdditionalProviders, |
| final ServiceConfiguration serviceConfiguration, |
| final JAXRSServerFactoryBean factory, |
| final WebBeansContext ctx) { |
| if (!"true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.cxf.rs.skip-provider-sorting", "false"))) { |
| final Comparator<?> providerComparator = findProviderComparator(serviceConfiguration, ctx); |
| if (providerComparator != null) { |
| factory.setProviderComparator(providerComparator); |
| } |
| } |
| CxfUtil.configureEndpoint(factory, serviceConfiguration, CXF_JAXRS_PREFIX); |
| |
| boolean enforceCxfBvalMapper = false; |
| if (ctx == null || !ctx.getBeanManagerImpl().isInUse()) { // activate bval |
| boolean bvalActive = Boolean.parseBoolean( |
| SystemInstance.get().getProperty("openejb.cxf.rs.bval.active", |
| serviceConfiguration.getProperties().getProperty(CXF_JAXRS_PREFIX + "bval.active", "true"))); |
| if (factory.getFeatures() == null && bvalActive) { |
| factory.setFeatures(new ArrayList<>()); |
| } else if (bvalActive) { // check we should activate it and user didn't configure it |
| for (final Feature f : factory.getFeatures()) { |
| if (BeanValidationFeature.class.isInstance(f)) { |
| bvalActive = false; |
| break; |
| } |
| } |
| for (final Interceptor<?> i : factory.getInInterceptors()) { |
| if (BeanValidationInInterceptor.class.isInstance(i)) { |
| bvalActive = false; |
| break; |
| } |
| } |
| for (final Interceptor<?> i : factory.getOutInterceptors()) { |
| if (BeanValidationOutInterceptor.class.isInstance(i)) { |
| bvalActive = false; |
| break; |
| } |
| } |
| } |
| if (bvalActive) { // bval doesn't need the actual instance so faking it to avoid to lookup the bean |
| final BeanValidationProvider provider = new BeanValidationProvider(); // todo: close the factory |
| ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); |
| final Validator validator = validatorFactory.getValidator(); |
| |
| final BeanValidationInInterceptor in = new JAXRSBeanValidationInInterceptor() { |
| @Override |
| protected Object getServiceObject(final Message message) { |
| return CxfRsHttpListener.this.getServiceObject(message); |
| } |
| }; |
| in.setProvider(provider); |
| in.setServiceObject(FAKE_SERVICE_OBJECT); |
| factory.getInInterceptors().add(in); |
| |
| final BeanValidationOutInterceptor out = new JAXRSBeanValidationOutInterceptor() { |
| @Override |
| protected Object getServiceObject(final Message message) { |
| return CxfRsHttpListener.this.getServiceObject(message); |
| } |
| |
| @Override |
| protected void handleValidation(final Message message, final Object resourceInstance, final Method method, final List<Object> arguments) { |
| final MethodDescriptor constraintsForMethod = validator.getConstraintsForClass(resourceInstance.getClass()) |
| .getConstraintsForMethod(method.getName(), method.getParameterTypes()); |
| |
| if (constraintsForMethod != null && constraintsForMethod.hasConstrainedReturnValue()) { |
| super.handleValidation(message, resourceInstance, method, arguments); |
| } |
| } |
| }; |
| out.setEnforceOnlyBeanConstraints(true); |
| out.setProvider(provider); |
| out.setServiceObject(FAKE_SERVICE_OBJECT); |
| factory.getOutInterceptors().add(out); |
| |
| // and add a mapper to get back a 400 like for bval |
| enforceCxfBvalMapper = true; |
| } |
| } |
| |
| final Collection<ServiceInfo> 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<String> providersConfig = null; |
| |
| { |
| final String provider = serviceConfiguration.getProperties().getProperty(PROVIDERS_KEY); |
| if (provider != null) { |
| providersConfig = new HashSet<>(); |
| for (final String p : Arrays.asList(provider.split(","))) { |
| providersConfig.add(p.trim()); |
| } |
| } |
| |
| { |
| if (GLOBAL_PROVIDERS != null) { |
| 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<Object> additionalProviders = ignoreAutoProviders ? Collections.emptyList() : givenAdditionalProviders; |
| List<Object> providers = null; |
| if (providersConfig != null) { |
| providers = ServiceInfos.resolve(services, providersConfig.toArray(new String[providersConfig.size()]), OpenEJBProviderFactory.INSTANCE); |
| if (providers != null && additionalProviders != null && !additionalProviders.isEmpty()) { |
| providers.addAll(providers(serviceConfiguration.getAvailableServices(), additionalProviders, ctx)); |
| } |
| } |
| if (providers == null) { |
| providers = new ArrayList<>(4); |
| if (additionalProviders != null && !additionalProviders.isEmpty()) { |
| providers.addAll(providers(serviceConfiguration.getAvailableServices(), additionalProviders, ctx)); |
| } |
| } |
| |
| if (!ignoreAutoProviders) { |
| addMandatoryProviders(providers, serviceConfiguration); |
| if (enforceCxfBvalMapper) { |
| if (!shouldSkipProvider(CxfResponseValidationExceptionMapper.class.getName())) { |
| providers.add(new CxfResponseValidationExceptionMapper()); |
| } |
| } |
| } |
| |
| SystemInstance.get().fireEvent(new ExtensionProviderRegistration( |
| AppFinder.findAppContextOrWeb(Thread.currentThread().getContextClassLoader(), AppFinder.AppContextTransformer.INSTANCE), providers)); |
| |
| if (!providers.isEmpty()) { |
| factory.setProviders(providers); |
| } |
| } |
| |
| private Object getServiceObject(final Message message) { |
| final OperationResourceInfo ori = message.getExchange().get(OperationResourceInfo.class); |
| if (ori == null) { |
| return null; |
| } |
| if (!ori.getClassResourceInfo().isRoot()) { |
| return message.getExchange().get("org.apache.cxf.service.object.last"); |
| } |
| final ResourceProvider resourceProvider = ori.getClassResourceInfo().getResourceProvider(); |
| if (resourceProvider.isSingleton()) { |
| return resourceProvider.getInstance(message); |
| } |
| final Object o = message.getExchange().get(CdiResourceProvider.INSTANCE_KEY); |
| return o != null || !OpenEJBPerRequestPojoResourceProvider.class.isInstance(resourceProvider) ? o : resourceProvider.getInstance(message); |
| } |
| |
| private Comparator<?> findProviderComparator(final ServiceConfiguration serviceConfiguration, final WebBeansContext ctx) { |
| final String comparatorKey = CXF_JAXRS_PREFIX + "provider-comparator"; |
| final String comparatorClass = serviceConfiguration.getProperties() |
| .getProperty(comparatorKey, SystemInstance.get().getProperty(comparatorKey)); |
| |
| Comparator<Object> comparator = null; |
| if (comparatorClass == null) { |
| return null; // try to rely on CXF behavior otherwise just reactivate DefaultProviderComparator.INSTANCE if it is an issue |
| } else { |
| final BeanManagerImpl bm = ctx == null ? null : ctx.getBeanManagerImpl(); |
| if (bm != null && bm.isInUse()) { |
| try { |
| final Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(comparatorClass); |
| final Set<Bean<?>> 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(serviceConfiguration.getAvailableServices(), comparatorClass)); |
| } |
| if (comparator == null) { |
| try { |
| comparator = Comparator.class.cast(Thread.currentThread().getContextClassLoader().loadClass(comparatorClass).newInstance()); |
| } catch (final Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| for (final Type itf : comparator.getClass().getGenericInterfaces()) { |
| if (!ParameterizedType.class.isInstance(itf)) { |
| continue; |
| } |
| |
| final ParameterizedType pt = ParameterizedType.class.cast(itf); |
| if (Comparator.class == pt.getRawType() && pt.getActualTypeArguments().length > 0) { |
| final Type t = pt.getActualTypeArguments()[0]; |
| if (Class.class.isInstance(t) && ProviderInfo.class == t) { |
| return comparator; |
| } |
| if (ParameterizedType.class.isInstance(t) && ProviderInfo.class == ParameterizedType.class.cast(t).getRawType()) { |
| return comparator; |
| } |
| } |
| } |
| |
| return new ProviderComparatorWrapper(comparator); |
| } |
| } |
| |
| // public to ensure it can be configured since not setup by default anymore |
| public static final class DefaultProviderComparator extends ProviderFactory implements Comparator<ProviderInfo<?>> { |
| private static final ClassLoader SYSTEM_LOADER = ClassLoader.getSystemClassLoader(); |
| |
| public DefaultProviderComparator() { |
| super(CxfUtil.getBus()); |
| } |
| |
| @Override |
| public int compare(final ProviderInfo<?> o1, final ProviderInfo<?> 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.getProvider().getClass(); |
| final Class<?> c2 = o2.getProvider().getClass(); |
| if (c1.getName().startsWith("org.apache.cxf.")) { |
| if (!c2.getName().startsWith("org.apache.cxf.")) { |
| return 1; |
| } |
| return -1; |
| } |
| if (c2.getName().startsWith("org.apache.cxf.")) { |
| return -1; |
| } |
| |
| 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 { |
| int result = compareClasses(o1.getProvider(), o2.getProvider()); |
| if (result != 0) { |
| return result; |
| } |
| |
| if (MessageBodyWriter.class.isInstance(o1.getProvider())) { |
| final List<MediaType> types1 = |
| JAXRSUtils.sortMediaTypes(JAXRSUtils.getProviderProduceTypes(MessageBodyWriter.class.cast(o1.getProvider())), JAXRSUtils.MEDIA_TYPE_QS_PARAM); |
| final List<MediaType> types2 = |
| JAXRSUtils.sortMediaTypes(JAXRSUtils.getProviderProduceTypes(MessageBodyWriter.class.cast(o2.getProvider())), JAXRSUtils.MEDIA_TYPE_QS_PARAM); |
| |
| if (types1.contains(MediaType.WILDCARD_TYPE) && !types2.contains(MediaType.WILDCARD_TYPE)) { |
| return 1; |
| } |
| if (types2.contains(MediaType.WILDCARD_TYPE) && !types1.contains(MediaType.WILDCARD_TYPE)) { |
| return -1; |
| } |
| |
| result = JAXRSUtils.compareSortedMediaTypes(types1, types2, JAXRSUtils.MEDIA_TYPE_QS_PARAM); |
| if (result != 0) { |
| return result; |
| } |
| } else if (MessageBodyReader.class.isInstance(o1.getProvider())) { // else is not super good but using both is not sa well so let it be for now |
| final List<MediaType> types1 = |
| JAXRSUtils.sortMediaTypes(JAXRSUtils.getProviderConsumeTypes(MessageBodyReader.class.cast(o1.getProvider())), null); |
| final List<MediaType> types2 = |
| JAXRSUtils.sortMediaTypes(JAXRSUtils.getProviderConsumeTypes(MessageBodyReader.class.cast(o2.getProvider())), null); |
| |
| if (types1.contains(MediaType.WILDCARD_TYPE) && !types2.contains(MediaType.WILDCARD_TYPE)) { |
| return 1; |
| } |
| if (types2.contains(MediaType.WILDCARD_TYPE) && !types1.contains(MediaType.WILDCARD_TYPE)) { |
| return -1; |
| } |
| |
| result = JAXRSUtils.compareSortedMediaTypes(types1, types2, JAXRSUtils.MEDIA_TYPE_QS_PARAM); |
| if (result != 0) { |
| return result; |
| } |
| } |
| |
| final Boolean custom1 = o1.isCustom(); |
| final Boolean custom2 = o2.isCustom(); |
| final int customComp = custom1.compareTo(custom2) * -1; |
| if (customComp != 0) { |
| return customComp; |
| } |
| |
| try { // WEB-INF/classes will be before WEB-INF/lib |
| final File file1 = jarLocation(c1); |
| final File file2 = jarLocation(c2); |
| if ("classes".equals(file1.getName())) { |
| if ("classes".equals(file2.getName())) { |
| return c1.getName().compareTo(c2.getName()); |
| } |
| return -1; |
| } |
| if ("classes".equals(file2.getName())) { |
| return 1; |
| } |
| } catch (final Exception e) { |
| // no-op: sort by class name |
| } |
| } |
| return c1.getName().compareTo(c2.getName()); |
| } |
| |
| private static boolean isParent(final ClassLoader l1, 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; |
| } |
| |
| @Override |
| public Configuration getConfiguration(final Message message) { |
| throw new UnsupportedOperationException("not a real inheritance"); |
| } |
| |
| @Override |
| protected void setProviders(final boolean b, final boolean b1, final Object... objects) { |
| throw new UnsupportedOperationException("not a real inheritance"); |
| } |
| } |
| |
| // we use Object cause an app with a custom comparator can desire to compare instances |
| private static final class ProviderComparatorWrapper implements Comparator<ProviderInfo<?>> { |
| private final Comparator<Object> delegate; |
| |
| private ProviderComparatorWrapper(final Comparator<Object> delegate) { |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public int compare(final ProviderInfo<?> o1, final ProviderInfo<?> o2) { |
| return delegate.compare(o1.getProvider(), o2.getProvider()); |
| } |
| } |
| |
| private static class OpenEJBProviderFactory implements ServiceInfos.Factory { |
| private static final ServiceInfos.Factory INSTANCE = new OpenEJBProviderFactory(); |
| |
| @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; |
| } |
| } |
| |
| @Provider |
| public static class CxfResponseValidationExceptionMapper implements ExceptionMapper<ResponseConstraintViolationException> { |
| @Override |
| public Response toResponse(final ResponseConstraintViolationException exception) { |
| return JAXRSUtils.toResponse(Response.Status.BAD_REQUEST); |
| } |
| } |
| } |