http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java deleted file mode 100644 index 6171a53..0000000 --- a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * 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; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.security.AccessController; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.osgi.framework.ServiceException; - -public class ServiceInvocationHandler implements InvocationHandler { - - private static final String REMOTE_EXCEPTION_TYPE = "REMOTE"; - private static final Collection<Method> OBJECT_METHODS = Arrays.asList(Object.class.getMethods()); - - private Map<Method, List<Class<?>>> exceptionsMap = new HashMap<Method, List<Class<?>>>(); - private Object serviceObject; - - public ServiceInvocationHandler(Object serviceObject, Class<?> iType) { - this.serviceObject = serviceObject; - introspectType(iType); - } - - public Object invoke(Object proxy, final Method m, Object[] params) throws Throwable { - if (OBJECT_METHODS.contains(m)) { - if (m.getName().equals("equals")) { - params = new Object[] {Proxy.getInvocationHandler(params[0])}; - } - return m.invoke(this, params); - } - - ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - final Object[] paramsFinal = params; - return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { - public Object run() throws Exception { - return m.invoke(serviceObject, paramsFinal); - } - }); - } catch (Throwable ex) { - Throwable theCause = ex.getCause() == null ? ex : ex.getCause(); - Throwable theCauseCause = theCause.getCause() == null ? theCause : theCause.getCause(); - List<Class<?>> excTypes = exceptionsMap.get(m); - if (excTypes != null) { - for (Class<?> type : excTypes) { - if (type.isAssignableFrom(theCause.getClass())) { - throw theCause; - } - if (type.isAssignableFrom(theCauseCause.getClass())) { - throw theCauseCause; - } - } - } - - throw new ServiceException(REMOTE_EXCEPTION_TYPE, theCause); - } finally { - Thread.currentThread().setContextClassLoader(oldCl); - } - } - - private void introspectType(Class<?> iType) { - for (Method m : iType.getDeclaredMethods()) { - for (Class<?> excType : m.getExceptionTypes()) { - if (Exception.class.isAssignableFrom(excType)) { - List<Class<?>> types = exceptionsMap.get(m); - if (types == null) { - types = new ArrayList<Class<?>>(); - exceptionsMap.put(m, types); - } - types.add(excType); - } - } - } - } -}
http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java deleted file mode 100644 index 5bf16d7..0000000 --- a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * 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; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.ws.Service; - -import org.apache.aries.rsa.spi.Endpoint; -import org.apache.cxf.Bus; -import org.apache.cxf.common.util.PackageUtils; -import org.apache.cxf.databinding.DataBinding; -import org.apache.cxf.dosgi.dsw.Constants; -import org.apache.cxf.dosgi.dsw.qos.IntentManager; -import org.apache.cxf.dosgi.dsw.util.OsgiUtils; -import org.apache.cxf.jaxb.JAXBDataBinding; -import org.apache.cxf.jaxws.JaxWsServerFactoryBean; -import org.osgi.framework.BundleContext; -import org.osgi.service.remoteserviceadmin.EndpointDescription; -import org.osgi.service.remoteserviceadmin.RemoteConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class WsdlConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler { - - private static final Logger LOG = LoggerFactory.getLogger(WsdlConfigurationTypeHandler.class); - - public WsdlConfigurationTypeHandler(BundleContext dswBC, - IntentManager intentManager, - HttpServiceManager httpServiceManager) { - super(dswBC, intentManager, httpServiceManager); - } - - public String[] getSupportedTypes() { - return new String[] {Constants.WSDL_CONFIG_TYPE}; - } - - @SuppressWarnings("rawtypes") - public Object importEndpoint(ClassLoader consumerLoader, - BundleContext consumerContext, - Class[] interfaces, - EndpointDescription endpoint) { - Class<?> iClass = interfaces[0]; - String wsdlAddressProp = getWsdlAddress(endpoint, iClass); - if (wsdlAddressProp == null) { - LOG.warn("WSDL address is unavailable"); - return null; - } - - URL wsdlAddress; - try { - wsdlAddress = new URL(wsdlAddressProp); - } catch (MalformedURLException ex) { - LOG.warn("WSDL address is malformed"); - return null; - } - - LOG.info("Creating a " + endpoint.getInterfaces().toArray()[0] + " client, wsdl address is " - + OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX)); - - String serviceNs = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAMESPACE); - if (serviceNs == null) { - serviceNs = PackageUtils.getNamespace(PackageUtils.getPackageName(iClass)); - } - String serviceName = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAME); - if (serviceName == null) { - serviceName = iClass.getSimpleName(); - } - QName serviceQname = getServiceQName(iClass, endpoint.getProperties(), - Constants.WSDL_SERVICE_NAMESPACE, - Constants.WSDL_SERVICE_NAME); - QName portQname = getPortQName(serviceQname.getNamespaceURI(), - endpoint.getProperties(), Constants.WSDL_PORT_NAME); - Service service = createWebService(wsdlAddress, serviceQname); - Object proxy = getProxy(portQname == null ? service.getPort(iClass) : service.getPort(portQname, iClass), - iClass); - // MARC: FIXME!!!! getDistributionProvider().addRemoteService(serviceReference); - return proxy; - } - - // Isolated so that it can be overridden for test purposes. - Service createWebService(URL wsdlAddress, QName serviceQname) { - return Service.create(wsdlAddress, serviceQname); - } - - @SuppressWarnings("rawtypes") - public Endpoint exportService(Object serviceO, - BundleContext serviceContext, - Map<String, Object> sd, - Class[] exportedInterfaces) { - Class<?> iClass = exportedInterfaces[0]; - String location = OsgiUtils.getProperty(sd, Constants.WSDL_LOCATION); - if (location == null) { - throw new RuntimeException("WSDL location property is unavailable"); - } - URL wsdlURL = serviceContext.getBundle().getResource(location); - if (wsdlURL == null) { - throw new RuntimeException("WSDL resource at " + location + " is unavailable"); - } - - String address = getServerAddress(sd, iClass); - String contextRoot = getServletContextRoot(sd); - if (address == null && contextRoot == null) { - throw new RuntimeException("Remote address is unavailable"); - } - - LOG.info("Creating a " + iClass.getName() + " endpoint from CXF PublishHook, address is " + address); - - DataBinding databinding = new JAXBDataBinding(); - JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean(); - final Long sid = (Long) sd.get(RemoteConstants.ENDPOINT_SERVICE_ID); - Bus bus = createBus(sid, serviceContext, contextRoot); - factory.setBus(bus); - factory.setServiceClass(iClass); - factory.setAddress(address != null ? address : "/"); - factory.getServiceFactory().setDataBinding(databinding); - factory.setServiceBean(serviceO); - - addWsInterceptorsFeaturesProps(factory, serviceContext, sd); - - setWsdlProperties(factory, serviceContext, sd, true); - - String[] intents = intentManager.applyIntents(factory.getFeatures(), factory, sd); - - EndpointDescription epd = createEndpointDesc(sd, - new String[]{Constants.WS_CONFIG_TYPE}, - address, intents); - return createServerFromFactory(factory, epd); - } - - private String getWsdlAddress(EndpointDescription endpoint, Class<?> iClass) { - String address = OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX); - if (address == null) { - address = httpServiceManager.getDefaultAddress(iClass); - if (address != null) { - address += "?wsdl"; - } - } - return address; - } -} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/AbstractPojoConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/AbstractPojoConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/AbstractPojoConfigurationTypeHandler.java new file mode 100644 index 0000000..aa543e0 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/AbstractPojoConfigurationTypeHandler.java @@ -0,0 +1,246 @@ +/** + * 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.pojo; + +import java.lang.reflect.Proxy; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.QName; + +import org.apache.aries.rsa.spi.DistributionProvider; +import org.apache.aries.rsa.spi.Endpoint; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.common.util.PackageUtils; +import org.apache.cxf.dosgi.dsw.httpservice.HttpServiceManager; +import org.apache.cxf.dosgi.dsw.osgi.Constants; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.dosgi.dsw.qos.IntentUtils; +import org.apache.cxf.dosgi.dsw.util.ClassUtils; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +import org.apache.cxf.dosgi.dsw.util.ServerWrapper; +import org.apache.cxf.dosgi.dsw.util.StringPlus; +import org.apache.cxf.endpoint.AbstractEndpointFactory; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.feature.AbstractFeature; +import org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory; +import org.apache.cxf.frontend.ClientFactoryBean; +import org.apache.cxf.frontend.ServerFactoryBean; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.interceptor.Interceptor; +import org.osgi.framework.BundleContext; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteConstants; + +public abstract class AbstractPojoConfigurationTypeHandler implements DistributionProvider { + protected BundleContext bundleContext; + protected IntentManager intentManager; + protected HttpServiceManager httpServiceManager; + + public AbstractPojoConfigurationTypeHandler(BundleContext dswBC, IntentManager intentManager, + HttpServiceManager httpServiceManager) { + this.bundleContext = dswBC; + this.intentManager = intentManager; + this.httpServiceManager = httpServiceManager; + } + + protected Object getProxy(Object serviceProxy, Class<?> iType) { + return Proxy.newProxyInstance(iType.getClassLoader(), new Class[] { + iType + }, new ServiceInvocationHandler(serviceProxy, iType)); + } + + protected EndpointDescription createEndpointDesc(Map<String, Object> props, + String[] importedConfigs, + String address, + String[] intents) { + props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, importedConfigs); + for (String configurationType : importedConfigs) { + if (Constants.WS_CONFIG_TYPE.equals(configurationType)) { + props.put(Constants.WS_ADDRESS_PROPERTY, address); + } else if (Constants.RS_CONFIG_TYPE.equals(configurationType)) { + props.put(Constants.RS_ADDRESS_PROPERTY, address); + } else if (Constants.WS_CONFIG_TYPE_OLD.equals(configurationType)) { + props.put(Constants.WS_ADDRESS_PROPERTY_OLD, address); + props.put(Constants.WS_ADDRESS_PROPERTY, address); + } + } + String[] sIntents = StringPlus.normalize(props.get(RemoteConstants.SERVICE_INTENTS)); + String[] allIntents = IntentUtils.mergeArrays(intents, sIntents); + props.put(RemoteConstants.SERVICE_INTENTS, allIntents); + props.put(RemoteConstants.ENDPOINT_ID, address); + return new EndpointDescription(props); + } + + protected void setCommonWsdlProperties(AbstractWSDLBasedEndpointFactory factory, BundleContext context, + Map<String, Object> sd, boolean wsdlType) { + String location = OsgiUtils.getProperty(sd, wsdlType ? Constants.WSDL_LOCATION : Constants.WS_WSDL_LOCATION); + if (location != null) { + URL wsdlURL = context.getBundle().getResource(location); + if (wsdlURL != null) { + factory.setWsdlURL(wsdlURL.toString()); + } + QName serviceName = getServiceQName(null, sd, + wsdlType ? Constants.WSDL_SERVICE_NAMESPACE : Constants.WS_WSDL_SERVICE_NAMESPACE, + wsdlType ? Constants.WSDL_SERVICE_NAME : Constants.WS_WSDL_SERVICE_NAME); + if (serviceName != null) { + factory.setServiceName(serviceName); + QName portName = getPortQName(serviceName.getNamespaceURI(), sd, + wsdlType ? Constants.WSDL_PORT_NAME : Constants.WS_WSDL_PORT_NAME); + if (portName != null) { + factory.setEndpointName(portName); + } + } + } + } + + protected void setWsdlProperties(ServerFactoryBean factory, BundleContext callingContext, Map<String, Object> sd, + boolean wsdlType) { + setCommonWsdlProperties(factory, callingContext, sd, wsdlType); + } + + protected void setClientWsdlProperties(ClientFactoryBean factory, BundleContext dswContext, Map<String, Object> sd, + boolean wsdlType) { + setCommonWsdlProperties(factory, dswContext, sd, wsdlType); + } + + protected static QName getServiceQName(Class<?> iClass, Map<String, Object> sd, String nsPropName, + String namePropName) { + String serviceNs = OsgiUtils.getProperty(sd, nsPropName); + String serviceName = OsgiUtils.getProperty(sd, namePropName); + if (iClass == null && (serviceNs == null || serviceName == null)) { + return null; + } + if (serviceNs == null) { + serviceNs = PackageUtils.getNamespace(PackageUtils.getPackageName(iClass)); + } + if (serviceName == null) { + serviceName = iClass.getSimpleName(); + } + return new QName(serviceNs, serviceName); + } + + protected static QName getPortQName(String ns, Map<String, Object> sd, String propName) { + String portName = OsgiUtils.getProperty(sd, propName); + if (portName == null) { + return null; + } + return new QName(ns, portName); + } + + protected String getClientAddress(Map<String, Object> sd) { + return OsgiUtils.getFirstNonEmptyStringProperty(sd, RemoteConstants.ENDPOINT_ID, + Constants.WS_ADDRESS_PROPERTY, + Constants.WS_ADDRESS_PROPERTY_OLD, + Constants.RS_ADDRESS_PROPERTY); + } + + protected String getServerAddress(Map<String, Object> sd, Class<?> iClass) { + String address = getClientAddress(sd); + return address == null ? httpServiceManager.getDefaultAddress(iClass) : address; + } + + public String getServletContextRoot(Map<String, Object> sd) { + return OsgiUtils.getFirstNonEmptyStringProperty(sd, + Constants.WS_HTTP_SERVICE_CONTEXT, + Constants.WS_HTTP_SERVICE_CONTEXT_OLD, + Constants.WSDL_HTTP_SERVICE_CONTEXT, + Constants.RS_HTTP_SERVICE_CONTEXT); + } + + + protected Bus createBus(Long sid, BundleContext callingContext, String contextRoot) { + Bus bus = BusFactory.newInstance().createBus(); + if (contextRoot != null) { + httpServiceManager.registerServlet(bus, contextRoot, callingContext, sid); + } + return bus; + } + + protected Endpoint createServerFromFactory(ServerFactoryBean factory, EndpointDescription epd) { + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(ServerFactoryBean.class.getClassLoader()); + Server server = factory.create(); + return new ServerWrapper(epd, server); + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + protected static void addWsInterceptorsFeaturesProps(AbstractEndpointFactory factory, BundleContext callingContext, + Map<String, Object> sd) { + addInterceptors(factory, callingContext, sd, Constants.WS_IN_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.WS_OUT_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.WS_OUT_FAULT_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.WS_IN_FAULT_INTERCEPTORS_PROP_KEY); + addFeatures(factory, callingContext, sd, Constants.WS_FEATURES_PROP_KEY); + addContextProperties(factory, sd, Constants.WS_CONTEXT_PROPS_PROP_KEY); + } + + protected static void addRsInterceptorsFeaturesProps(AbstractEndpointFactory factory, BundleContext callingContext, + Map<String, Object> sd) { + addInterceptors(factory, callingContext, sd, Constants.RS_IN_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.RS_OUT_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.RS_OUT_FAULT_INTERCEPTORS_PROP_KEY); + addInterceptors(factory, callingContext, sd, Constants.RS_IN_FAULT_INTERCEPTORS_PROP_KEY); + addFeatures(factory, callingContext, sd, Constants.RS_FEATURES_PROP_KEY); + addContextProperties(factory, sd, Constants.RS_CONTEXT_PROPS_PROP_KEY); + } + + private static void addInterceptors(AbstractEndpointFactory factory, BundleContext callingContext, + Map<String, Object> sd, String propName) { + List<Object> providers = ClassUtils.loadProviderClasses(callingContext, sd, propName); + boolean in = propName.contains("in.interceptors"); + boolean out = propName.contains("out.interceptors"); + boolean inFault = propName.contains("in.fault.interceptors"); + boolean outFault = propName.contains("out.fault.interceptors"); + for (Object provider : providers) { + Interceptor<?> interceptor = (Interceptor<?>) provider; + if (in) { + factory.getInInterceptors().add(interceptor); + } else if (out) { + factory.getOutInterceptors().add(interceptor); + } else if (inFault) { + factory.getInFaultInterceptors().add(interceptor); + } else if (outFault) { + factory.getOutFaultInterceptors().add(interceptor); + } + } + } + + private static void addFeatures(AbstractEndpointFactory factory, BundleContext callingContext, + Map<String, Object> sd, String propName) { + List<Object> providers = ClassUtils.loadProviderClasses(callingContext, sd, propName); + if (!providers.isEmpty()) { + factory.getFeatures().addAll(CastUtils.cast(providers, AbstractFeature.class)); + } + } + + private static void addContextProperties(AbstractEndpointFactory factory, Map<String, Object> sd, String propName) { + @SuppressWarnings("unchecked") + Map<String, Object> props = (Map<String, Object>)sd.get(propName); + if (props != null) { + factory.getProperties(true).putAll(props); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/PojoConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/PojoConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/PojoConfigurationTypeHandler.java new file mode 100644 index 0000000..a0784fb --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/PojoConfigurationTypeHandler.java @@ -0,0 +1,173 @@ +/** + * 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.pojo; + +import java.util.Map; + +import javax.jws.WebService; + +import org.apache.aries.rsa.spi.Endpoint; +import org.apache.aries.rsa.spi.IntentUnsatisfiedException; +import org.apache.cxf.Bus; +import org.apache.cxf.aegis.databinding.AegisDatabinding; +import org.apache.cxf.databinding.DataBinding; +import org.apache.cxf.dosgi.dsw.httpservice.HttpServiceManager; +import org.apache.cxf.dosgi.dsw.osgi.Constants; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.frontend.ClientProxyFactoryBean; +import org.apache.cxf.frontend.ServerFactoryBean; +import org.apache.cxf.jaxb.JAXBDataBinding; +import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; +import org.apache.cxf.jaxws.JaxWsServerFactoryBean; +import org.osgi.framework.BundleContext; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PojoConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler { + + private static final Logger LOG = LoggerFactory.getLogger(PojoConfigurationTypeHandler.class); + + public PojoConfigurationTypeHandler(BundleContext dswBC, + IntentManager intentManager, + HttpServiceManager httpServiceManager) { + super(dswBC, intentManager, httpServiceManager); + } + + public String[] getSupportedTypes() { + return new String[] {Constants.WS_CONFIG_TYPE, Constants.WS_CONFIG_TYPE_OLD}; + } + + @SuppressWarnings("rawtypes") + public Object importEndpoint(ClassLoader consumerLoader, + BundleContext consumerContext, + Class[] interfaces, + EndpointDescription endpoint) throws IntentUnsatisfiedException { + Class<?> iClass = interfaces[0]; + Map<String, Object> sd = endpoint.getProperties(); + String address = getClientAddress(sd); + if (address == null) { + LOG.warn("Remote address is unavailable"); + // TODO: fire Event + return null; + } + + LOG.info("Creating a " + iClass.getName() + " client, endpoint address is " + address); + + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + ClientProxyFactoryBean factory = createClientProxyFactoryBean(sd, iClass); + factory.getServiceFactory().setDataBinding(getDataBinding(sd, iClass)); + factory.setServiceClass(iClass); + factory.setAddress(address); + addWsInterceptorsFeaturesProps(factory.getClientFactoryBean(), consumerContext, sd); + setClientWsdlProperties(factory.getClientFactoryBean(), bundleContext, sd, false); + + intentManager.applyIntents(factory.getFeatures(), factory.getClientFactoryBean(), sd); + + Thread.currentThread().setContextClassLoader(ClientProxyFactoryBean.class.getClassLoader()); + return getProxy(factory.create(), iClass); + } catch (Exception e) { + LOG.warn("proxy creation failed", e); + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + return null; + } + + @SuppressWarnings("rawtypes") + public Endpoint exportService(Object serviceO, + BundleContext serviceContext, + Map<String, Object> endpointProps, + Class[] exportedInterfaces) throws IntentUnsatisfiedException { + Class<?> iClass = exportedInterfaces[0]; + String address = getPojoAddress(endpointProps, iClass); + ServerFactoryBean factory = createServerFactoryBean(endpointProps, iClass); + factory.setDataBinding(getDataBinding(endpointProps, iClass)); + String contextRoot = getServletContextRoot(endpointProps); + + final Long sid = (Long) endpointProps.get(RemoteConstants.ENDPOINT_SERVICE_ID); + Bus bus = createBus(sid, serviceContext, contextRoot); + factory.setBus(bus); + factory.setServiceClass(iClass); + factory.setAddress(address); + + factory.setServiceBean(serviceO); + addWsInterceptorsFeaturesProps(factory, serviceContext, endpointProps); + setWsdlProperties(factory, serviceContext, endpointProps, false); + String[] intents = intentManager.applyIntents(factory.getFeatures(), factory, endpointProps); + + String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address); + EndpointDescription epd = createEndpointDesc(endpointProps, + new String[]{Constants.WS_CONFIG_TYPE}, + completeEndpointAddress, intents); + return createServerFromFactory(factory, epd); + } + + private String getPojoAddress(Map<String, Object> sd, Class<?> iClass) { + String address = getClientAddress(sd); + if (address != null) { + return address; + } + + // If the property is not of type string this will cause an ClassCastException which + // will be propagated to the ExportRegistration exception property. + Object port = sd.get(Constants.WS_PORT_PROPERTY); + if (port == null) { + port = "9000"; + } + + address = "http://localhost:" + port + "/" + iClass.getName().replace('.', '/'); + LOG.info("Using a default address: " + address); + return address; + } + + private DataBinding getDataBinding(Map<String, Object> sd, Class<?> iClass) { + Object dataBindingBeanProp = sd.get(Constants.WS_DATABINDING_BEAN_PROP_KEY); + if (dataBindingBeanProp instanceof DataBinding) { + return (DataBinding)dataBindingBeanProp; + } + return isJAXB(sd, iClass) ? new JAXBDataBinding() : new AegisDatabinding(); + } + + private boolean isJAXB(Map<String, Object> sd, Class<?> iClass) { + String dataBindingName = (String)sd.get(Constants.WS_DATABINDING_PROP_KEY); + return (iClass.getAnnotation(WebService.class) != null + || Constants.WS_DATA_BINDING_JAXB.equals(dataBindingName)) + && !Constants.WS_DATA_BINDING_AEGIS.equals(dataBindingName); + } + + // Isolated so that it can be substituted for testing + protected ClientProxyFactoryBean createClientProxyFactoryBean(Map<String, Object> sd, Class<?> iClass) { + return isJAXWS(sd, iClass) ? new JaxWsProxyFactoryBean() : new ClientProxyFactoryBean(); + } + + // Isolated so that it can be substituted for testing + protected ServerFactoryBean createServerFactoryBean(Map<String, Object> sd, Class<?> iClass) { + return isJAXWS(sd, iClass) ? new JaxWsServerFactoryBean() : new ServerFactoryBean(); + } + + private boolean isJAXWS(Map<String, Object> sd, Class<?> iClass) { + String frontEnd = (String)sd.get(Constants.WS_FRONTEND_PROP_KEY); + return (iClass.getAnnotation(WebService.class) != null + || Constants.WS_FRONTEND_JAXWS.equals(frontEnd)) + && !Constants.WS_FRONTEND_SIMPLE.equals(frontEnd); + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/ServiceInvocationHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/ServiceInvocationHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/ServiceInvocationHandler.java new file mode 100644 index 0000000..3928c7d --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/ServiceInvocationHandler.java @@ -0,0 +1,100 @@ +/** + * 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.pojo; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.ServiceException; + +public class ServiceInvocationHandler implements InvocationHandler { + + private static final String REMOTE_EXCEPTION_TYPE = "REMOTE"; + private static final Collection<Method> OBJECT_METHODS = Arrays.asList(Object.class.getMethods()); + + private Map<Method, List<Class<?>>> exceptionsMap = new HashMap<Method, List<Class<?>>>(); + private Object serviceObject; + + public ServiceInvocationHandler(Object serviceObject, Class<?> iType) { + this.serviceObject = serviceObject; + introspectType(iType); + } + + public Object invoke(Object proxy, final Method m, Object[] params) throws Throwable { + if (OBJECT_METHODS.contains(m)) { + if (m.getName().equals("equals")) { + params = new Object[] {Proxy.getInvocationHandler(params[0])}; + } + return m.invoke(this, params); + } + + ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + final Object[] paramsFinal = params; + return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { + public Object run() throws Exception { + return m.invoke(serviceObject, paramsFinal); + } + }); + } catch (Throwable ex) { + Throwable theCause = ex.getCause() == null ? ex : ex.getCause(); + Throwable theCauseCause = theCause.getCause() == null ? theCause : theCause.getCause(); + List<Class<?>> excTypes = exceptionsMap.get(m); + if (excTypes != null) { + for (Class<?> type : excTypes) { + if (type.isAssignableFrom(theCause.getClass())) { + throw theCause; + } + if (type.isAssignableFrom(theCauseCause.getClass())) { + throw theCauseCause; + } + } + } + + throw new ServiceException(REMOTE_EXCEPTION_TYPE, theCause); + } finally { + Thread.currentThread().setContextClassLoader(oldCl); + } + } + + private void introspectType(Class<?> iType) { + for (Method m : iType.getDeclaredMethods()) { + for (Class<?> excType : m.getExceptionTypes()) { + if (Exception.class.isAssignableFrom(excType)) { + List<Class<?>> types = exceptionsMap.get(m); + if (types == null) { + types = new ArrayList<Class<?>>(); + exceptionsMap.put(m, types); + } + types.add(excType); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/WsdlConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/WsdlConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/WsdlConfigurationTypeHandler.java new file mode 100644 index 0000000..b818354 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/pojo/WsdlConfigurationTypeHandler.java @@ -0,0 +1,161 @@ +/** + * 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.pojo; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; + +import org.apache.aries.rsa.spi.Endpoint; +import org.apache.cxf.Bus; +import org.apache.cxf.common.util.PackageUtils; +import org.apache.cxf.databinding.DataBinding; +import org.apache.cxf.dosgi.dsw.httpservice.HttpServiceManager; +import org.apache.cxf.dosgi.dsw.osgi.Constants; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +import org.apache.cxf.jaxb.JAXBDataBinding; +import org.apache.cxf.jaxws.JaxWsServerFactoryBean; +import org.osgi.framework.BundleContext; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WsdlConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler { + + private static final Logger LOG = LoggerFactory.getLogger(WsdlConfigurationTypeHandler.class); + + public WsdlConfigurationTypeHandler(BundleContext dswBC, + IntentManager intentManager, + HttpServiceManager httpServiceManager) { + super(dswBC, intentManager, httpServiceManager); + } + + public String[] getSupportedTypes() { + return new String[] {Constants.WSDL_CONFIG_TYPE}; + } + + @SuppressWarnings("rawtypes") + public Object importEndpoint(ClassLoader consumerLoader, + BundleContext consumerContext, + Class[] interfaces, + EndpointDescription endpoint) { + Class<?> iClass = interfaces[0]; + String wsdlAddressProp = getWsdlAddress(endpoint, iClass); + if (wsdlAddressProp == null) { + LOG.warn("WSDL address is unavailable"); + return null; + } + + URL wsdlAddress; + try { + wsdlAddress = new URL(wsdlAddressProp); + } catch (MalformedURLException ex) { + LOG.warn("WSDL address is malformed"); + return null; + } + + LOG.info("Creating a " + endpoint.getInterfaces().toArray()[0] + " client, wsdl address is " + + OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX)); + + String serviceNs = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAMESPACE); + if (serviceNs == null) { + serviceNs = PackageUtils.getNamespace(PackageUtils.getPackageName(iClass)); + } + String serviceName = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAME); + if (serviceName == null) { + serviceName = iClass.getSimpleName(); + } + QName serviceQname = getServiceQName(iClass, endpoint.getProperties(), + Constants.WSDL_SERVICE_NAMESPACE, + Constants.WSDL_SERVICE_NAME); + QName portQname = getPortQName(serviceQname.getNamespaceURI(), + endpoint.getProperties(), Constants.WSDL_PORT_NAME); + Service service = createWebService(wsdlAddress, serviceQname); + Object proxy = getProxy(portQname == null ? service.getPort(iClass) : service.getPort(portQname, iClass), + iClass); + // MARC: FIXME!!!! getDistributionProvider().addRemoteService(serviceReference); + return proxy; + } + + // Isolated so that it can be overridden for test purposes. + Service createWebService(URL wsdlAddress, QName serviceQname) { + return Service.create(wsdlAddress, serviceQname); + } + + @SuppressWarnings("rawtypes") + public Endpoint exportService(Object serviceO, + BundleContext serviceContext, + Map<String, Object> sd, + Class[] exportedInterfaces) { + Class<?> iClass = exportedInterfaces[0]; + String location = OsgiUtils.getProperty(sd, Constants.WSDL_LOCATION); + if (location == null) { + throw new RuntimeException("WSDL location property is unavailable"); + } + URL wsdlURL = serviceContext.getBundle().getResource(location); + if (wsdlURL == null) { + throw new RuntimeException("WSDL resource at " + location + " is unavailable"); + } + + String address = getServerAddress(sd, iClass); + String contextRoot = getServletContextRoot(sd); + if (address == null && contextRoot == null) { + throw new RuntimeException("Remote address is unavailable"); + } + + LOG.info("Creating a " + iClass.getName() + " endpoint from CXF PublishHook, address is " + address); + + DataBinding databinding = new JAXBDataBinding(); + JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean(); + final Long sid = (Long) sd.get(RemoteConstants.ENDPOINT_SERVICE_ID); + Bus bus = createBus(sid, serviceContext, contextRoot); + factory.setBus(bus); + factory.setServiceClass(iClass); + factory.setAddress(address != null ? address : "/"); + factory.getServiceFactory().setDataBinding(databinding); + factory.setServiceBean(serviceO); + + addWsInterceptorsFeaturesProps(factory, serviceContext, sd); + + setWsdlProperties(factory, serviceContext, sd, true); + + String[] intents = intentManager.applyIntents(factory.getFeatures(), factory, sd); + + EndpointDescription epd = createEndpointDesc(sd, + new String[]{Constants.WS_CONFIG_TYPE}, + address, intents); + return createServerFromFactory(factory, epd); + } + + private String getWsdlAddress(EndpointDescription endpoint, Class<?> iClass) { + String address = OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX); + if (address == null) { + address = httpServiceManager.getDefaultAddress(iClass); + if (address != null) { + address += "?wsdl"; + } + } + return address; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSPojoConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSPojoConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSPojoConfigurationTypeHandler.java new file mode 100644 index 0000000..49e9d39 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSPojoConfigurationTypeHandler.java @@ -0,0 +1,204 @@ +/** + * 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 java.net.URL; +import java.util.List; +import java.util.Map; + +import org.apache.aries.rsa.spi.Endpoint; +import org.apache.aries.rsa.spi.IntentUnsatisfiedException; +import org.apache.cxf.Bus; +import org.apache.cxf.common.util.ProxyClassLoader; +import org.apache.cxf.dosgi.dsw.handlers.pojo.AbstractPojoConfigurationTypeHandler; +import org.apache.cxf.dosgi.dsw.httpservice.HttpServiceManager; +import org.apache.cxf.dosgi.dsw.osgi.Constants; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +import org.apache.cxf.dosgi.dsw.util.ServerWrapper; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.client.Client; +import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; +import org.apache.cxf.jaxrs.model.UserResource; +import org.osgi.framework.BundleContext; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JaxRSPojoConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler { + + private static final Logger LOG = LoggerFactory.getLogger(JaxRSPojoConfigurationTypeHandler.class); + + public JaxRSPojoConfigurationTypeHandler(BundleContext dswBC, + IntentManager intentManager, + HttpServiceManager httpServiceManager) { + super(dswBC, intentManager, httpServiceManager); + } + + public String[] getSupportedTypes() { + return new String[] {Constants.RS_CONFIG_TYPE}; + } + + @SuppressWarnings("rawtypes") + public Object importEndpoint(ClassLoader consumerLoader, + BundleContext consumerContext, + Class[] interfaces, + EndpointDescription endpoint) { + Class<?> iClass = interfaces[0]; + String address = getPojoAddress(endpoint, iClass); + if (address == null) { + LOG.warn("Remote address is unavailable"); + return null; + } + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + return createJaxrsProxy(address, consumerContext, iClass, null, endpoint); + } catch (Throwable e) { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + + try { + ProxyClassLoader cl = new ProxyClassLoader(iClass.getClassLoader()); + cl.addLoader(Client.class.getClassLoader()); + return createJaxrsProxy(address, consumerContext, iClass, cl, endpoint); + } catch (Throwable e) { + LOG.warn("proxy creation failed", e); + } + + return null; + } + + protected Object createJaxrsProxy(String address, + BundleContext callingContext, + Class<?> iClass, + ClassLoader loader, + EndpointDescription endpoint) { + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setAddress(address); + if (loader != null) { + bean.setClassLoader(loader); + } + + addRsInterceptorsFeaturesProps(bean, callingContext, endpoint.getProperties()); + + List<UserResource> resources = JaxRSUtils.getModel(callingContext, iClass); + if (resources != null) { + bean.setModelBeansWithServiceClass(resources, iClass); + } else { + bean.setServiceClass(iClass); + } + List<Object> providers = JaxRSUtils.getProviders(callingContext, endpoint.getProperties()); + if (providers != null && !providers.isEmpty()) { + bean.setProviders(providers); + } + Thread.currentThread().setContextClassLoader(JAXRSClientFactoryBean.class.getClassLoader()); + return getProxy(bean.create(), iClass); + } + + @SuppressWarnings("rawtypes") + public Endpoint exportService(Object serviceBean, + BundleContext callingContext, + Map<String, Object> endpointProps, + Class[] exportedInterfaces) throws IntentUnsatisfiedException { + String contextRoot = getServletContextRoot(endpointProps); + String address; + Class<?> iClass = exportedInterfaces[0]; + if (contextRoot == null) { + address = getServerAddress(endpointProps, iClass); + } else { + address = getClientAddress(endpointProps); + if (address == null) { + address = "/"; + } + } + final Long sid = (Long) endpointProps.get(RemoteConstants.ENDPOINT_SERVICE_ID); + Bus bus = createBus(sid, callingContext, contextRoot); + + LOG.info("Creating a " + iClass.getName() + + " endpoint via JaxRSPojoConfigurationTypeHandler, address is " + address); + + JAXRSServerFactoryBean factory = createServerFactory(callingContext, endpointProps, + iClass, serviceBean, address, bus); + String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address); + + EndpointDescription epd = createEndpointDesc(endpointProps, new String[] {Constants.RS_CONFIG_TYPE}, + completeEndpointAddress, new String[] {"HTTP"}); + + return createServerFromFactory(factory, epd); + } + + 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 ServerWrapper(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); + List<UserResource> resources = JaxRSUtils.getModel(callingContext, iClass); + if (resources != null) { + factory.setModelBeansWithServiceClass(resources, iClass); + factory.setServiceBeanObjects(serviceBean); + } else { + factory.setServiceClass(iClass); + factory.setResourceProvider(iClass, new SingletonResourceProvider(serviceBean)); + } + factory.setAddress(address); + List<Object> providers = JaxRSUtils.getProviders(callingContext, sd); + if (providers != null && !providers.isEmpty()) { + factory.setProviders(providers); + } + addRsInterceptorsFeaturesProps(factory, callingContext, sd); + String location = OsgiUtils.getProperty(sd, Constants.RS_WADL_LOCATION); + if (location != null) { + URL wadlURL = callingContext.getBundle().getResource(location); + if (wadlURL != null) { + factory.setDocLocation(wadlURL.toString()); + } + } + return factory; + } + + protected String getPojoAddress(EndpointDescription endpoint, Class<?> iClass) { + String address = OsgiUtils.getProperty(endpoint, Constants.RS_ADDRESS_PROPERTY); + + if (address == null) { + address = httpServiceManager.getDefaultAddress(iClass); + if (address != null) { + LOG.info("Using a default address: " + address); + } + } + return address; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSUtils.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSUtils.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSUtils.java new file mode 100644 index 0000000..c03a29f --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/rest/JaxRSUtils.java @@ -0,0 +1,122 @@ +/** + * 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 java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.dosgi.dsw.util.ClassUtils; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +import org.apache.cxf.jaxrs.model.UserResource; +import org.apache.cxf.jaxrs.provider.aegis.AegisElementProvider; +import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class JaxRSUtils { + + public static final String MODEL_FOLDER = "/OSGI-INF/cxf/jaxrs/"; + public static final String DEFAULT_MODEL = "/OSGI-INF/cxf/jaxrs/model.xml"; + public static final String PROVIDERS_FILTER = "(|" + + "(objectClass=javax.ws.rs.ext.MessageBodyReader)" + + "(objectClass=javax.ws.rs.ext.MessageBodyWriter)" + + "(objectClass=javax.ws.rs.ext.ExceptionMapper)" + + "(objectClass=org.apache.cxf.jaxrs.ext.RequestHandler)" + + "(objectClass=org.apache.cxf.jaxrs.ext.ResponseHandler)" + + "(objectClass=org.apache.cxf.jaxrs.ext.ParameterHandler)" + + "(objectClass=org.apache.cxf.jaxrs.ext.ResponseExceptionMapper)" + + ")"; + private static final Logger LOG = LoggerFactory.getLogger(JaxRSUtils.class); + + private JaxRSUtils() { + // never constructed + } + + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + static List<Object> getProviders(BundleContext callingContext, Map<String, Object> sd) { + List<Object> providers = new ArrayList<Object>(); + if ("aegis".equals(sd.get(org.apache.cxf.dosgi.dsw.osgi.Constants.RS_DATABINDING_PROP_KEY))) { + providers.add(new AegisElementProvider()); + } + + providers.addAll(ClassUtils.loadProviderClasses(callingContext, + sd, + org.apache.cxf.dosgi.dsw.osgi.Constants.RS_PROVIDER_PROP_KEY)); + + Object globalQueryProp = sd.get(org.apache.cxf.dosgi.dsw.osgi.Constants.RS_PROVIDER_GLOBAL_PROP_KEY); + boolean globalQueryRequired = globalQueryProp == null || OsgiUtils.toBoolean(globalQueryProp); + if (!globalQueryRequired) { + return providers; + } + + boolean cxfProvidersOnly = OsgiUtils.getBooleanProperty(sd, + org.apache.cxf.dosgi.dsw.osgi.Constants.RS_PROVIDER_EXPECTED_PROP_KEY); + + try { + ServiceReference[] refs = callingContext.getServiceReferences((String)null, PROVIDERS_FILTER); + if (refs != null) { + for (ServiceReference ref : refs) { + if (!cxfProvidersOnly + || OsgiUtils.toBoolean(ref + .getProperty(org.apache.cxf.dosgi.dsw.osgi.Constants.RS_PROVIDER_PROP_KEY))) { + providers.add(callingContext.getService(ref)); + } + } + } + } catch (Exception ex) { + LOG.debug("Problems finding JAXRS providers " + ex.getMessage(), ex); + } + return providers; + } + + static List<UserResource> getModel(BundleContext callingContext, Class<?> iClass) { + String classModel = MODEL_FOLDER + iClass.getSimpleName() + "-model.xml"; + List<UserResource> list = getModel(callingContext, iClass, classModel); + return list != null ? list : getModel(callingContext, iClass, DEFAULT_MODEL); + } + + private static List<UserResource> getModel(BundleContext callingContext, Class<?> iClass, String name) { + InputStream r = iClass.getClassLoader().getResourceAsStream(name); + if (r == null) { + URL u = callingContext.getBundle().getResource(name); + if (u != null) { + try { + r = u.openStream(); + } catch (Exception ex) { + LOG.info("Problems opening a user model resource at " + u.toString()); + } + } + } + if (r != null) { + try { + return ResourceUtils.getUserResources(r); + } catch (Exception ex) { + LOG.info("Problems reading a user model, it will be ignored"); + } + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/HttpServiceManager.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/HttpServiceManager.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/HttpServiceManager.java new file mode 100644 index 0000000..f6ebda1 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/HttpServiceManager.java @@ -0,0 +1,176 @@ +/** + * 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.httpservice; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.cxf.Bus; +import org.apache.cxf.transport.http.DestinationRegistry; +import org.apache.cxf.transport.http.DestinationRegistryImpl; +import org.apache.cxf.transport.servlet.CXFNonSpringServlet; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceException; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpContext; +import org.osgi.service.http.HttpService; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HttpServiceManager { + + private static final Logger LOG = LoggerFactory.getLogger(HttpServiceManager.class); + private static final long SERVICE_LOOKUP_TIMEOUT = 10000; + private ServiceTracker<HttpService, HttpService> tracker; + private BundleContext bundleContext; + private Map<Long, String> exportedAliases = Collections.synchronizedMap(new HashMap<Long, String>()); + private String httpBase; + private String cxfServletAlias; + + public HttpServiceManager(BundleContext bundleContext, String httpBase, String cxfServletAlias) { + this(bundleContext, httpBase, cxfServletAlias, + new ServiceTracker<HttpService, HttpService>(bundleContext, HttpService.class, null)); + this.tracker.open(); + } + + // Only for tests + public HttpServiceManager(BundleContext bundleContext, + String httpBase, String cxfServletAlias, + ServiceTracker<HttpService, HttpService> tracker) { + this.bundleContext = bundleContext; + this.tracker = tracker; + this.httpBase = getWithDefault(httpBase, "http://" + LocalHostUtil.getLocalIp() + ":8181"); + this.cxfServletAlias = getWithDefault(cxfServletAlias, "/cxf"); + } + + private String getWithDefault(String value, String defaultValue) { + return value == null ? defaultValue : value; + } + + public Bus registerServlet(Bus bus, String contextRoot, BundleContext callingContext, Long sid) { + bus.setExtension(new DestinationRegistryImpl(), DestinationRegistry.class); + CXFNonSpringServlet cxf = new CXFNonSpringServlet(); + cxf.setBus(bus); + try { + HttpService httpService = getHttpService(); + httpService.registerServlet(contextRoot, cxf, new Hashtable<String, String>(), + getHttpContext(callingContext, httpService)); + + registerUnexportHook(sid, contextRoot); + + LOG.info("Successfully registered CXF DOSGi servlet at " + contextRoot); + } catch (Exception e) { + throw new ServiceException("CXF DOSGi: problem registering CXF HTTP Servlet", e); + } + return bus; + } + + protected HttpService getHttpService() { + HttpService service = null; + try { + service = tracker.waitForService(SERVICE_LOOKUP_TIMEOUT); + } catch (InterruptedException ex) { + LOG.warn("waitForService interrupeted", ex); + } + if (service == null) { + throw new RuntimeException("No HTTPService found"); + } + return service; + } + + private HttpContext getHttpContext(BundleContext bc, HttpService httpService) { + HttpContext httpContext = httpService.createDefaultHttpContext(); + return new SecurityDelegatingHttpContext(bc, httpContext); + } + + /** + * This listens for service removal events and "un-exports" the service + * from the HttpService. + * + * @param sref the service reference to track + * @param alias the HTTP servlet context alias + */ + private void registerUnexportHook(Long sid, String alias) { + LOG.debug("Registering service listener for service with ID {}", sid); + + String previous = exportedAliases.put(sid, alias); + if (previous != null) { + LOG.warn("Overwriting service export for service with ID {}", sid); + } + + try { + Filter f = bundleContext.createFilter("(" + org.osgi.framework.Constants.SERVICE_ID + "=" + sid + ")"); + if (f != null) { + bundleContext.addServiceListener(new UnregisterListener(), f.toString()); + } else { + LOG.warn("Service listener could not be started. The service will not be automatically unexported."); + } + } catch (InvalidSyntaxException e) { + LOG.warn("Service listener could not be started. The service will not be automatically unexported.", e); + } + } + + public String getDefaultAddress(Class<?> type) { + return "/" + type.getName().replace('.', '/'); + } + + public String getAbsoluteAddress(String contextRoot, String relativeEndpointAddress) { + if (relativeEndpointAddress.startsWith("http")) { + return relativeEndpointAddress; + } + String effContextRoot = contextRoot == null ? cxfServletAlias : contextRoot; + return this.httpBase + effContextRoot + relativeEndpointAddress; + } + + public void close() { + tracker.close(); + } + + private final class UnregisterListener implements ServiceListener { + + public void serviceChanged(ServiceEvent event) { + if (!(event.getType() == ServiceEvent.UNREGISTERING)) { + return; + } + final ServiceReference<?> sref = event.getServiceReference(); + final Long sid = (Long) sref.getProperty(org.osgi.framework.Constants.SERVICE_ID); + final String alias = exportedAliases.remove(sid); + if (alias == null) { + LOG.error("Unable to unexport HTTP servlet for service class '{}'," + + " service-id {}: no servlet alias found", + sref.getProperty(org.osgi.framework.Constants.OBJECTCLASS), sid); + return; + } + LOG.debug("Unexporting HTTP servlet for alias '{}'", alias); + try { + HttpService http = getHttpService(); + http.unregister(alias); + } catch (Exception e) { + LOG.warn("An exception occurred while unregistering service for HTTP servlet alias '{}'", alias, e); + } + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/LocalHostUtil.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/LocalHostUtil.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/LocalHostUtil.java new file mode 100644 index 0000000..f2fefe9 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/LocalHostUtil.java @@ -0,0 +1,92 @@ +/** + * 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.httpservice; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * Utility methods to get the local address even on a linux host. + */ +final class LocalHostUtil { + + private LocalHostUtil() { + // Util Class + } + + /** + * Returns an InetAddress representing the address of the localhost. Every + * attempt is made to find an address for this host that is not the loopback + * address. If no other address can be found, the loopback will be returned. + * + * @return InetAddress the address of localhost + * @throws UnknownHostException if there is a problem determining the address + */ + public static InetAddress getLocalHost() throws UnknownHostException { + InetAddress localHost = InetAddress.getLocalHost(); + if (!localHost.isLoopbackAddress()) { + return localHost; + } + InetAddress[] addrs = getAllLocalUsingNetworkInterface(); + for (InetAddress addr : addrs) { + if (!addr.isLoopbackAddress() && !addr.getHostAddress().contains(":")) { + return addr; + } + } + return localHost; + } + + /** + * Utility method that delegates to the methods of NetworkInterface to + * determine addresses for this machine. + * + * @return all addresses found from the NetworkInterfaces + * @throws UnknownHostException if there is a problem determining addresses + */ + private static InetAddress[] getAllLocalUsingNetworkInterface() throws UnknownHostException { + try { + List<InetAddress> addresses = new ArrayList<InetAddress>(); + Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + for (Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements();) { + addresses.add(e2.nextElement()); + } + } + return addresses.toArray(new InetAddress[] {}); + } catch (SocketException ex) { + throw new UnknownHostException("127.0.0.1"); + } + } + + public static String getLocalIp() { + String localIP; + try { + localIP = getLocalHost().getHostAddress(); + } catch (Exception e) { + localIP = "localhost"; + } + return localIP; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/SecurityDelegatingHttpContext.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/SecurityDelegatingHttpContext.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/SecurityDelegatingHttpContext.java new file mode 100644 index 0000000..042f2d5 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/httpservice/SecurityDelegatingHttpContext.java @@ -0,0 +1,133 @@ +/** + * 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.httpservice; + +import java.io.IOException; +import java.net.URL; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * <p> + * An HttpContext that delegates to another HttpContext for all things other than security. This implementation handles + * security by delegating to a {@link FilterChain} based on the set of {@link Filter}s registered with a + * {@link #FILTER_PROP} property. + * </p> + * <p> + * If the {@link BundleContext} contains a {@link #FILTER_REQUIRED_PROP} property with value "true", requests will not + * be allowed until at least one {@link Filter} with a {@link #FILTER_PROP} property is registered. + * </p> + */ +class SecurityDelegatingHttpContext implements HttpContext { + + public static final String FILTER_PROP = "org.apache.cxf.httpservice.filter"; + public static final String FILTER_REQUIRED_PROP = "org.apache.cxf.httpservice.requirefilter"; + private static final Logger LOG = LoggerFactory.getLogger(SecurityDelegatingHttpContext.class); + private static final String FILTER_FILTER = "(" + FILTER_PROP + "=*)"; + + BundleContext bundleContext; + HttpContext delegate; + boolean requireFilter; + + SecurityDelegatingHttpContext(BundleContext bundleContext, HttpContext delegate) { + this.bundleContext = bundleContext; + this.delegate = delegate; + requireFilter = Boolean.TRUE.toString().equalsIgnoreCase(bundleContext.getProperty(FILTER_REQUIRED_PROP)); + } + + public String getMimeType(String name) { + return delegate.getMimeType(name); + } + + public URL getResource(String name) { + return delegate.getResource(name); + } + + @SuppressWarnings({ + "unchecked", "rawtypes" + }) + public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { + ServiceReference[] refs; + try { + refs = bundleContext.getServiceReferences(Filter.class.getName(), FILTER_FILTER); + } catch (InvalidSyntaxException e) { + LOG.warn(e.getMessage(), e); + return false; + } + if (refs == null || refs.length == 0) { + LOG.info("No filter registered."); + return !requireFilter; + } + Filter[] filters = new Filter[refs.length]; + try { + for (int i = 0; i < refs.length; i++) { + filters[i] = (Filter)bundleContext.getService(refs[i]); + } + try { + new Chain(filters).doFilter(request, response); + return !response.isCommitted(); + } catch (ServletException e) { + LOG.warn(e.getMessage(), e); + return false; + } + } finally { + for (int i = 0; i < refs.length; i++) { + if (filters[i] != null) { + bundleContext.ungetService(refs[i]); + } + } + } + } +} + +/** + * A {@link FilterChain} composed of {@link Filter}s with the + */ +class Chain implements FilterChain { + + private static final Logger LOG = LoggerFactory.getLogger(Chain.class); + + int current; + final Filter[] filters; + + Chain(Filter[] filters) { + this.filters = filters; + } + + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + if (current < filters.length && !response.isCommitted()) { + Filter filter = filters[current++]; + LOG.info("doFilter() on {}", filter); + filter.doFilter(request, response, this); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/79bb9a43/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/osgi/Activator.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/osgi/Activator.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/osgi/Activator.java new file mode 100644 index 0000000..13ef30f --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/osgi/Activator.java @@ -0,0 +1,153 @@ +/** + * 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.osgi; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.aries.rsa.spi.DistributionProvider; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.dosgi.dsw.handlers.CXFDistributionProvider; +import org.apache.cxf.dosgi.dsw.httpservice.HttpServiceManager; +import org.apache.cxf.dosgi.dsw.qos.DefaultIntentMapFactory; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.dosgi.dsw.qos.IntentManagerImpl; +import org.apache.cxf.dosgi.dsw.qos.IntentMap; +import org.apache.cxf.dosgi.dsw.qos.IntentTracker; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// registered as spring bean -> start / stop called accordingly +public class Activator implements ManagedService, BundleActivator { + + private static final Logger LOG = LoggerFactory.getLogger(Activator.class); + private static final int DEFAULT_INTENT_TIMEOUT = 30000; + private static final String CONFIG_SERVICE_PID = "cxf-dsw"; + private ServiceRegistration<?> rsaFactoryReg; + private ServiceRegistration<?> decoratorReg; + private IntentTracker intentTracker; + private HttpServiceManager httpServiceManager; + private BundleContext bc; + private BundleListener bundleListener; + private Dictionary<String, Object> curConfiguration; + private Bus bus; + + public void start(BundleContext bundlecontext) throws Exception { + LOG.debug("RemoteServiceAdmin Implementation is starting up"); + this.bc = bundlecontext; + // Disable the fast infoset as it's not compatible (yet) with OSGi + System.setProperty("org.apache.cxf.nofastinfoset", "true"); + curConfiguration = getDefaultConfig(); + init(curConfiguration); + registerManagedService(bc); + } + + private Dictionary<String, Object> getDefaultConfig() { + return new Hashtable<String, Object>(); + } + + private synchronized void init(Dictionary<String, Object> config) { + bus = BusFactory.newInstance().createBus(); + + String httpBase = (String) config.get(org.apache.cxf.dosgi.dsw.osgi.Constants.HTTP_BASE); + String cxfServletAlias = (String) config.get(org.apache.cxf.dosgi.dsw.osgi.Constants.CXF_SERVLET_ALIAS); + + IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create()); + intentTracker = new IntentTracker(bc, intentMap); + intentTracker.open(); + IntentManager intentManager = new IntentManagerImpl(intentMap, DEFAULT_INTENT_TIMEOUT); + httpServiceManager = new HttpServiceManager(bc, httpBase, cxfServletAlias); + DistributionProvider cxfProvider + = new CXFDistributionProvider(bc, intentManager, httpServiceManager); + Dictionary<String, Object> props = new Hashtable<String, Object>(); + String[] supportedIntents = intentMap.keySet().toArray(new String[] {}); + props.put(Constants.REMOTE_INTENTS_SUPPORTED, supportedIntents); + props.put(Constants.REMOTE_CONFIGS_SUPPORTED, cxfProvider.getSupportedTypes()); + rsaFactoryReg = bc.registerService(DistributionProvider.class.getName(), cxfProvider, props); + } + + private synchronized void uninit() { + if (decoratorReg != null) { + decoratorReg.unregister(); + decoratorReg = null; + } + if (bundleListener != null) { + bc.removeBundleListener(bundleListener); + bundleListener = null; + } + if (rsaFactoryReg != null) { + // This also triggers the unimport and unexport of the remote services + rsaFactoryReg.unregister(); + rsaFactoryReg = null; + } + if (httpServiceManager != null) { + httpServiceManager.close(); + httpServiceManager = null; + } + if (intentTracker != null) { + intentTracker.close(); + intentTracker = null; + } + } + + private void registerManagedService(BundleContext bundlecontext) { + Dictionary<String, String> props = new Hashtable<String, String>(); + props.put(Constants.SERVICE_PID, CONFIG_SERVICE_PID); + // No need to store the registration. Will be unregistered in stop by framework + bundlecontext.registerService(ManagedService.class.getName(), this, props); + } + + public void stop(BundleContext context) throws Exception { + LOG.debug("RemoteServiceAdmin Implementation is shutting down now"); + uninit(); + shutdownCXFBus(); + } + + /** + * Causes also the shutdown of the embedded HTTP server + */ + private void shutdownCXFBus() { + if (bus != null) { + LOG.debug("Shutting down the CXF Bus"); + bus.shutdown(true); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public synchronized void updated(Dictionary config) throws ConfigurationException { + LOG.debug("RemoteServiceAdmin Implementation configuration is updated with {}", config); + // config is null if it doesn't exist, is being deleted or has not yet been loaded + // in which case we run with defaults (just like we do manually when bundle is first started) + Dictionary<String, Object> configMap = config == null ? getDefaultConfig() : config; + if (!configMap.equals(curConfiguration)) { // only if something actually changed + curConfiguration = configMap; + uninit(); + init(configMap); + } + } + +}
