/**
* 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;
}
}