/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.jaxrs.spring; import java.lang.annotation.Annotation; import java.lang.reflect.Proxy; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import javax.ws.rs.ApplicationPath; import javax.ws.rs.Path; import javax.ws.rs.core.Application; import javax.ws.rs.ext.Provider; import org.apache.cxf.annotations.Provider.Scope; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.ClassHelper; import org.apache.cxf.common.util.ClasspathScanner; import org.apache.cxf.common.util.PackageUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.endpoint.Server; import org.apache.cxf.feature.Feature; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.Interceptor; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; import org.apache.cxf.jaxrs.utils.ResourceUtils; import org.apache.cxf.service.factory.ServiceConstructionException; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; @ComponentScan( includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {ApplicationPath.class, Path.class, Provider.class, org.apache.cxf.annotations.Provider.class}) ) public abstract class AbstractSpringComponentScanServer extends AbstractSpringConfigurationFactory { private static final Logger LOG = LogUtils.getL7dLogger(AbstractSpringComponentScanServer.class); @Value("${cxf.jaxrs.classes-scan-packages:}") private String classesScanPackages; @Value("${cxf.jaxrs.component-scan-packages:}") private String componentScanPackages; @Value("${cxf.jaxrs.component-scan-beans:}") private String componentScanBeans; private List<ResourceProvider> resourceProviders = new LinkedList<ResourceProvider>(); private List<Object> jaxrsProviders = new LinkedList<Object>(); private List<Feature> cxfFeatures = new LinkedList<Feature>(); private Class<? extends Annotation> serviceAnnotation; protected AbstractSpringComponentScanServer() { } protected AbstractSpringComponentScanServer(Class<? extends Annotation> serviceAnnotation) { this.serviceAnnotation = serviceAnnotation; } @Override protected Server createJaxRsServer() { JAXRSServerFactoryBean factoryBean = null; String[] beanNames = applicationContext.getBeanNamesForAnnotation(ApplicationPath.class); if (beanNames.length > 0) { Set<String> componentScanPackagesSet = parseSetProperty(componentScanPackages); Set<String> componentScanBeansSet = parseSetProperty(componentScanBeans); for (String beanName : beanNames) { if (isComponentMatched(beanName, componentScanPackagesSet, componentScanBeansSet)) { Application app = applicationContext.getBean(beanName, Application.class); factoryBean = createFactoryBeanFromApplication(app); for (String cxfBeanName : applicationContext.getBeanNamesForAnnotation( org.apache.cxf.annotations.Provider.class)) { if (isComponentMatched(cxfBeanName, componentScanPackagesSet, componentScanBeansSet)) { addCxfProvider(getProviderBean(cxfBeanName)); } } break; } } } if (!StringUtils.isEmpty(classesScanPackages)) { try { final Map< Class< ? extends Annotation >, Collection< Class< ? > > > appClasses = ClasspathScanner.findClasses(classesScanPackages, ApplicationPath.class); List<Application> apps = CastUtils.cast(JAXRSServerFactoryBeanDefinitionParser .createBeansFromDiscoveredClasses(super.applicationContext, appClasses.get(ApplicationPath.class), null)); if (apps.size() > 0) { factoryBean = createFactoryBeanFromApplication(apps.get(0)); final Map< Class< ? extends Annotation >, Collection< Class< ? > > > cxfClasses = ClasspathScanner.findClasses(classesScanPackages, org.apache.cxf.annotations.Provider.class); addCxfProvidersFromClasses(cxfClasses.get(org.apache.cxf.annotations.Provider.class)); } } catch (Exception ex) { throw new ServiceConstructionException(ex); } } if (factoryBean != null) { setFactoryCxfProviders(factoryBean); return factoryBean.create(); } return super.createJaxRsServer(); } protected void setJaxrsResources(JAXRSServerFactoryBean factory) { Set<String> componentScanPackagesSet = parseSetProperty(componentScanPackages); Set<String> componentScanBeansSet = parseSetProperty(componentScanBeans); for (String beanName : applicationContext.getBeanDefinitionNames()) { if (isValidComponent(beanName, Path.class, componentScanPackagesSet, componentScanBeansSet)) { SpringResourceFactory resourceFactory = new SpringResourceFactory(beanName); resourceFactory.setApplicationContext(applicationContext); resourceProviders.add(resourceFactory); } else if (isValidComponent(beanName, Provider.class, componentScanPackagesSet, componentScanBeansSet)) { jaxrsProviders.add(getProviderBean(beanName)); } else if (isValidComponent(beanName, org.apache.cxf.annotations.Provider.class, componentScanPackagesSet, componentScanBeansSet)) { addCxfProvider(getProviderBean(beanName)); } } if (!StringUtils.isEmpty(classesScanPackages)) { try { final Map< Class< ? extends Annotation >, Collection< Class< ? > > > classes = ClasspathScanner.findClasses(classesScanPackages, Provider.class, org.apache.cxf.annotations.Provider.class); jaxrsProviders.addAll(JAXRSServerFactoryBeanDefinitionParser .createBeansFromDiscoveredClasses(applicationContext, classes.get(Provider.class), null)); warnIfDuplicatesAvailable(jaxrsProviders); addCxfProvidersFromClasses(classes.get(org.apache.cxf.annotations.Provider.class)); } catch (Exception ex) { throw new ServiceConstructionException(ex); } } factory.setResourceProviders(getResourceProviders()); factory.setProviders(getJaxrsProviders()); setFactoryCxfProviders(factory); } protected void setFactoryCxfProviders(JAXRSServerFactoryBean factory) { factory.setFeatures(getFeatures()); factory.setInInterceptors(getInInterceptors()); factory.setOutInterceptors(getOutInterceptors()); factory.setOutFaultInterceptors(getOutFaultInterceptors()); } protected void addCxfProvidersFromClasses(Collection<Class<?>> classes) { List<Object> cxfProviders = JAXRSServerFactoryBeanDefinitionParser .createBeansFromDiscoveredClasses(applicationContext, classes, null); for (Object cxfProvider : cxfProviders) { addCxfProvider(cxfProvider); } warnIfDuplicatesAvailable(cxfFeatures); } protected boolean isValidComponent(String beanName, Class<? extends Annotation> ann, Set<String> componentScanPackagesSet, Set<String> componentScanBeansSet) { return isAnnotationAvailable(beanName, ann) && nonProxyClass(beanName) && isComponentMatched(beanName, componentScanPackagesSet, componentScanBeansSet); } protected boolean isComponentMatched(String beanName, Set<String> componentScanPackagesSet, Set<String> componentScanBeansSet) { return matchesServiceAnnotation(beanName) && matchesComponentPackage(beanName, componentScanPackagesSet) && matchesComponentName(beanName, componentScanBeansSet); } protected boolean nonProxyClass(String beanName) { // JAX-RS runtime needs to be able to access the real component class to introspect it for // JAX-RS annotations; the following check ensures that the valid proxified components // are accepted while the client proxies are ignored. Class<?> type = ClassHelper.getRealClassFromClass(applicationContext.getType(beanName)); if (Proxy.isProxyClass(type) && applicationContext.isSingleton(beanName)) { type = ClassHelper.getRealClass(applicationContext.getBean(beanName)); } if (Proxy.isProxyClass(type)) { LOG.fine("Can not determine the real class of the component '" + beanName + "'"); return false; } else { return true; } } protected boolean matchesComponentName(String beanName, Set<String> componentScanBeansSet) { return componentScanBeansSet == null || componentScanBeansSet.contains(beanName); } protected boolean matchesComponentPackage(String beanName, Set<String> componentScanPackagesSet) { return componentScanPackagesSet == null || !applicationContext.isSingleton(beanName) || componentScanPackagesSet.contains( PackageUtils.getPackageName(applicationContext.getBean(beanName).getClass())); } private static void warnIfDuplicatesAvailable(List<? extends Object> providers) { Set<String> classNames = new HashSet<>(); for (Object o : providers) { if (!classNames.add(o.getClass().getName())) { LOG.warning("Duplicate Provider " + o.getClass().getName() + " has been detected"); } } } private Object getProviderBean(String beanName) { return applicationContext.getBean(beanName); } protected void addCxfProvider(Object bean) { org.apache.cxf.annotations.Provider ann = bean.getClass().getAnnotation(org.apache.cxf.annotations.Provider.class); if (ann.scope() == Scope.Client) { return; } if (ann.value() == org.apache.cxf.annotations.Provider.Type.Feature) { cxfFeatures.add((Feature)bean); } else if (ann.value() == org.apache.cxf.annotations.Provider.Type.InInterceptor) { super.getInInterceptors().add((Interceptor<?>)bean); } else if (ann.value() == org.apache.cxf.annotations.Provider.Type.OutInterceptor) { super.getOutInterceptors().add((Interceptor<?>)bean); } else if (ann.value() == org.apache.cxf.annotations.Provider.Type.OutFaultInterceptor) { super.getOutFaultInterceptors().add((Interceptor<?>)bean); } } protected boolean matchesServiceAnnotation(String beanName) { return serviceAnnotation == null || isAnnotationAvailable(beanName, serviceAnnotation); } protected <A extends Annotation> boolean isAnnotationAvailable(String beanName, Class<A> annClass) { return applicationContext.findAnnotationOnBean(beanName, annClass) != null; } protected List<ResourceProvider> getResourceProviders() { return resourceProviders; } protected List<Object> getJaxrsProviders() { return jaxrsProviders; } @Override public List<Feature> getFeatures() { return cxfFeatures; } protected JAXRSServerFactoryBean createFactoryBeanFromApplication(Application app) { return ResourceUtils.createApplication(app, false, true, false, getBus()); } protected static Set<String> parseSetProperty(String componentScanProp) { return !StringUtils.isEmpty(componentScanProp) ? ClasspathScanner.parsePackages(componentScanProp) : null; } }