| /** |
| * 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.dosgi.dsw.handlers.rest; |
| |
| import static org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_CONFIGS_SUPPORTED; |
| import static org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_INTENTS_SUPPORTED; |
| |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.ws.rs.ext.ContextResolver; |
| 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 org.apache.aries.rsa.spi.DistributionProvider; |
| import org.apache.aries.rsa.spi.Endpoint; |
| import org.apache.aries.rsa.spi.IntentUnsatisfiedException; |
| import org.apache.cxf.Bus; |
| import org.apache.cxf.binding.BindingConfiguration; |
| import org.apache.cxf.databinding.DataBinding; |
| import org.apache.cxf.dosgi.common.endpoint.ServerEndpoint; |
| import org.apache.cxf.dosgi.common.handlers.BaseDistributionProvider; |
| import org.apache.cxf.dosgi.common.httpservice.HttpServiceManager; |
| import org.apache.cxf.dosgi.common.intent.IntentManager; |
| import org.apache.cxf.dosgi.common.proxy.ProxyFactory; |
| import org.apache.cxf.dosgi.common.util.PropertyHelper; |
| import org.apache.cxf.endpoint.Server; |
| import org.apache.cxf.feature.Feature; |
| import org.apache.cxf.jaxrs.AbstractJAXRSFactoryBean; |
| import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; |
| import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; |
| import org.apache.cxf.jaxrs.ext.ContextProvider; |
| import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.remoteserviceadmin.EndpointDescription; |
| import org.osgi.service.remoteserviceadmin.RemoteConstants; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| @Component(property = // |
| {// |
| REMOTE_CONFIGS_SUPPORTED + "=" + RsConstants.RS_CONFIG_TYPE, |
| REMOTE_INTENTS_SUPPORTED + "=" |
| }) |
| public class RsProvider extends BaseDistributionProvider implements DistributionProvider { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(RsProvider.class); |
| |
| @Reference |
| public void setHttpServiceManager(HttpServiceManager httpServiceManager) { |
| this.httpServiceManager = httpServiceManager; |
| } |
| |
| @Reference |
| public void setIntentManager(IntentManager intentManager) { |
| this.intentManager = intentManager; |
| } |
| |
| @Override |
| public String[] getSupportedTypes() { |
| return new String[] {RsConstants.RS_CONFIG_TYPE}; |
| } |
| |
| @Override |
| @SuppressWarnings("rawtypes") |
| public Object importEndpoint(ClassLoader consumerLoader, |
| BundleContext consumerContext, |
| Class[] interfaces, |
| EndpointDescription endpoint) { |
| if (interfaces.length > 1) { |
| throw new IllegalArgumentException("Multiple interfaces are not supported by this provider"); |
| } |
| Set<String> intentNames = intentManager.getImported(endpoint.getProperties()); |
| List<Object> intents = intentManager.getRequiredIntents(intentNames); |
| Class<?> iClass = interfaces[0]; |
| String address = PropertyHelper.getProperty(endpoint.getProperties(), RsConstants.RS_ADDRESS_PROPERTY); |
| if (address == null) { |
| LOG.warn("Remote address is unavailable"); |
| return null; |
| } |
| return createJaxrsProxy(address, iClass, null, endpoint, intents); |
| } |
| |
| private Object createJaxrsProxy(String address, |
| Class<?> iClass, |
| ClassLoader loader, |
| EndpointDescription endpoint, |
| List<Object> intents) { |
| JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean(); |
| factory.setAddress(address); |
| if (loader != null) { |
| factory.setClassLoader(loader); |
| } |
| addContextProperties(factory, endpoint.getProperties(), RsConstants.RS_CONTEXT_PROPS_PROP_KEY); |
| factory.setServiceClass(iClass); |
| applyIntents(intents, factory); |
| return ProxyFactory.create(factory.create(), iClass); |
| } |
| |
| @Override |
| @SuppressWarnings("rawtypes") |
| public Endpoint exportService(Object serviceBean, |
| BundleContext callingContext, |
| Map<String, Object> endpointProps, |
| Class[] exportedInterfaces) throws IntentUnsatisfiedException { |
| if (!configTypeSupported(endpointProps, RsConstants.RS_CONFIG_TYPE)) { |
| return null; |
| } |
| String contextRoot = PropertyHelper.getProperty(endpointProps, RsConstants.RS_HTTP_SERVICE_CONTEXT); |
| Class<?> iClass = exportedInterfaces[0]; |
| String address = PropertyHelper.getProperty(endpointProps, RsConstants.RS_ADDRESS_PROPERTY); |
| if (address == null) { |
| address = httpServiceManager.getDefaultAddress(iClass); |
| } |
| final Long sid = (Long) endpointProps.get(RemoteConstants.ENDPOINT_SERVICE_ID); |
| Set<String> intentNames = intentManager.getExported(endpointProps); |
| List<Object> intents = intentManager.getRequiredIntents(intentNames); |
| intents.addAll(intentManager.getIntentsFromService(serviceBean)); |
| |
| Bus bus = createBus(sid, callingContext, contextRoot, endpointProps); |
| LOG.info("Creating JAXRS endpoint for " + iClass.getName() + " with address " + address); |
| |
| JAXRSServerFactoryBean factory = createServerFactory(callingContext, endpointProps, |
| iClass, serviceBean, address, bus); |
| applyIntents(intents, factory); |
| String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address); |
| EndpointDescription epd = createEndpointDesc(endpointProps, // |
| new String[] {RsConstants.RS_CONFIG_TYPE}, |
| completeEndpointAddress, |
| intentNames); |
| return createServerFromFactory(factory, epd); |
| } |
| |
| private void applyIntents(List<Object> intents, AbstractJAXRSFactoryBean factory) { |
| List<Feature> features = intentManager.getIntents(Feature.class, intents); |
| factory.setFeatures(features); |
| DataBinding dataBinding = intentManager.getIntent(DataBinding.class, intents); |
| if (dataBinding != null) { |
| factory.setDataBinding(dataBinding); |
| } |
| BindingConfiguration binding = intentManager.getIntent(BindingConfiguration.class, intents); |
| if (binding != null) { |
| factory.setBindingConfig(binding); |
| } |
| |
| List<Object> providers = new ArrayList<>(); |
| for (Object intent : intents) { |
| if (isProvider(intent)) { |
| providers.add(intent); |
| } |
| } |
| factory.setProviders(providers); |
| } |
| |
| private boolean isProvider(Object intent) { |
| return intent.getClass().getAnnotation(Provider.class) != null // |
| || (intent instanceof ExceptionMapper) // |
| || (intent instanceof MessageBodyReader) // |
| || (intent instanceof MessageBodyWriter) // |
| || (intent instanceof ContextResolver) // |
| || (intent instanceof ContextProvider); |
| } |
| |
| private Endpoint createServerFromFactory(JAXRSServerFactoryBean factory, |
| EndpointDescription epd) { |
| ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); |
| try { |
| Thread.currentThread().setContextClassLoader(JAXRSServerFactoryBean.class.getClassLoader()); |
| Server server = factory.create(); |
| return new ServerEndpoint(epd, server); |
| } finally { |
| Thread.currentThread().setContextClassLoader(oldClassLoader); |
| } |
| } |
| |
| private JAXRSServerFactoryBean createServerFactory(BundleContext callingContext, |
| Map<String, Object> sd, |
| Class<?> iClass, |
| Object serviceBean, |
| String address, |
| Bus bus) { |
| JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean(); |
| factory.setBus(bus); |
| factory.setServiceClass(iClass); |
| factory.setResourceProvider(iClass, new SingletonResourceProvider(serviceBean)); |
| factory.setAddress(address); |
| addContextProperties(factory, sd, RsConstants.RS_CONTEXT_PROPS_PROP_KEY); |
| String location = PropertyHelper.getProperty(sd, RsConstants.RS_WADL_LOCATION); |
| setWadlLocation(callingContext, factory, location); |
| return factory; |
| } |
| |
| private void setWadlLocation(BundleContext callingContext, JAXRSServerFactoryBean factory, |
| String location) { |
| if (location != null) { |
| URL wadlURL = callingContext.getBundle().getResource(location); |
| if (wadlURL != null) { |
| factory.setDocLocation(wadlURL.toString()); |
| } |
| } |
| } |
| |
| protected EndpointDescription createEndpointDesc(Map<String, Object> props, String[] importedConfigs, |
| String address, Collection<String> intents) { |
| return super.createEndpointDesc(props, importedConfigs, RsConstants.RS_ADDRESS_PROPERTY, address, intents); |
| } |
| } |