http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java new file mode 100644 index 0000000..eca5460 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/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.handlers; + +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); + } + } + + protected String getDefaultAddress(Class<?> type) { + return "/" + type.getName().replace('.', '/'); + } + + protected 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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java new file mode 100644 index 0000000..8f4f152 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java @@ -0,0 +1,201 @@ +/** + * 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.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.Constants; +import org.apache.cxf.dosgi.dsw.qos.IntentManager; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java new file mode 100644 index 0000000..6e78744 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java @@ -0,0 +1,121 @@ +/** + * 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.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.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.Constants.RS_DATABINDING_PROP_KEY))) { + providers.add(new AegisElementProvider()); + } + + providers.addAll(ClassUtils.loadProviderClasses(callingContext, + sd, + org.apache.cxf.dosgi.dsw.Constants.RS_PROVIDER_PROP_KEY)); + + Object globalQueryProp = sd.get(org.apache.cxf.dosgi.dsw.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.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.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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/LocalHostUtil.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/LocalHostUtil.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/LocalHostUtil.java new file mode 100644 index 0000000..50e2127 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/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.handlers; + +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. + */ +public 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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java new file mode 100644 index 0000000..bf92141 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java @@ -0,0 +1,172 @@ +/** + * 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.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.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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java new file mode 100644 index 0000000..7e9710b --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/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.handlers; + +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> + */ +public 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; + + public 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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServerWrapper.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServerWrapper.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServerWrapper.java new file mode 100644 index 0000000..bcc4963 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServerWrapper.java @@ -0,0 +1,49 @@ +/** + * 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.io.IOException; + +import org.apache.aries.rsa.spi.Endpoint; +import org.apache.cxf.endpoint.Server; +import org.osgi.service.remoteserviceadmin.EndpointDescription; + +public class ServerWrapper implements Endpoint { + private EndpointDescription desc; + private Server server; + + public ServerWrapper(EndpointDescription desc, Server server) { + this.desc = desc; + this.server = server; + } + + public Server getServer() { + return this.server; + } + + @Override + public void close() throws IOException { + this.server.destroy(); + } + + @Override + public EndpointDescription description() { + return this.desc; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/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 new file mode 100644 index 0000000..6171a53 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/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; + +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/1425743f/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 new file mode 100644 index 0000000..5bf16d7 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java @@ -0,0 +1,160 @@ +/** + * 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/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java new file mode 100644 index 0000000..7e0dd2e --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java @@ -0,0 +1,52 @@ +/** + * 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.qos; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.cxf.binding.soap.Soap11; +import org.apache.cxf.binding.soap.Soap12; +import org.apache.cxf.binding.soap.SoapBindingConfiguration; +import org.apache.cxf.binding.soap.SoapVersion; +import org.apache.cxf.feature.LoggingFeature; + +public class DefaultIntentMapFactory { + + public Map<String, Object> create() { + Map<String, Object> intentMap = new HashMap<String, Object>(); + intentMap.put("logging", getLoggingFeature()); + Object soap11 = getSoapBinding(Soap11.getInstance()); + intentMap.put("SOAP", soap11); + intentMap.put("SOAP.1_1", soap11); + intentMap.put("SOAP.1_2", getSoapBinding(Soap12.getInstance())); + intentMap.put("HTTP", "PROVIDED"); + return intentMap; + } + + private Object getLoggingFeature() { + return new LoggingFeature(); + } + + private Object getSoapBinding(SoapVersion soapVersion) { + SoapBindingConfiguration soapBindingConfig = new SoapBindingConfiguration(); + soapBindingConfig.setVersion(soapVersion); + return soapBindingConfig; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java new file mode 100644 index 0000000..ecaf070 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java @@ -0,0 +1,31 @@ +/** + * 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.qos; + +import java.util.List; +import java.util.Map; + +import org.apache.cxf.endpoint.AbstractEndpointFactory; +import org.apache.cxf.feature.Feature; + +public interface IntentManager { + + String[] applyIntents(List<Feature> features, AbstractEndpointFactory factory, Map<String, Object> props); + void assertAllIntentsSupported(Map<String, Object> serviceProperties); +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java new file mode 100644 index 0000000..e161ef0 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java @@ -0,0 +1,155 @@ +/** + * 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.qos; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.aries.rsa.spi.IntentUnsatisfiedException; +import org.apache.cxf.binding.BindingConfiguration; +import org.apache.cxf.endpoint.AbstractEndpointFactory; +import org.apache.cxf.feature.Feature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class IntentManagerImpl implements IntentManager { + + static final Logger LOG = LoggerFactory.getLogger(IntentManagerImpl.class); + private static final String PROVIDED_INTENT_VALUE = "PROVIDED"; + + private final IntentMap intentMap; + private final long maxIntentWaitTime; + + public IntentManagerImpl(IntentMap intentMap) { + this(intentMap, 0); + } + + public IntentManagerImpl(IntentMap intentMap, int maxIntentWaitTime) { + this.intentMap = intentMap; + this.maxIntentWaitTime = maxIntentWaitTime; + } + + public String[] applyIntents(List<Feature> features, AbstractEndpointFactory factory, + Map<String, Object> props) throws IntentUnsatisfiedException { + Set<String> requestedIntents = IntentUtils.getRequestedIntents(props); + Set<String> appliedIntents = new HashSet<String>(); + appliedIntents.addAll(reverseLookup(intentMap, PROVIDED_INTENT_VALUE)); + boolean bindingApplied = false; + for (String intentName : requestedIntents) { + bindingApplied |= processIntent(features, factory, intentName, intentMap.get(intentName)); + appliedIntents.add(intentName); + } + if (!bindingApplied) { + String defaultBindingName = "SOAP"; + processIntent(features, factory, defaultBindingName, intentMap.get(defaultBindingName)); + appliedIntents.add(defaultBindingName); + } + appliedIntents.addAll(addSynonymIntents(appliedIntents, intentMap)); + return appliedIntents.toArray(new String[appliedIntents.size()]); + } + + private boolean processIntent(List<Feature> features, AbstractEndpointFactory factory, + String intentName, Object intent) throws IntentUnsatisfiedException { + if (intent instanceof String) { + if (PROVIDED_INTENT_VALUE.equalsIgnoreCase((String) intent)) { + return false; + } + } else if (intent instanceof BindingConfiguration) { + BindingConfiguration bindingCfg = (BindingConfiguration)intent; + LOG.info("Applying intent: " + intentName + " via binding config: " + bindingCfg); + factory.setBindingConfig(bindingCfg); + return true; + } else if (intent instanceof Feature) { + Feature feature = (Feature) intent; + LOG.info("Applying intent: " + intentName + " via feature: " + feature); + features.add(feature); + return false; + } else { + LOG.info("No mapping for intent: " + intentName); + throw new IntentUnsatisfiedException(intentName); + } + return false; + } + + private static Collection<String> addSynonymIntents(Collection<String> appliedIntents, + IntentMap map) { + // E.g. SOAP and SOAP.1_1 are synonyms + List<Object> values = new ArrayList<Object>(); + for (String key : appliedIntents) { + values.add(map.get(key)); + } + return reverseLookup(map, values); + } + + private static Collection<String> reverseLookup(IntentMap im, Object obj) { + return reverseLookup(im, Collections.singleton(obj)); + } + + /** + * Retrieves all keys whose mapped values are found in the given collection. + * + * @param im an intent map + * @param values a collection of potential values + * @return all keys whose mapped values are found in the given collection + */ + private static Collection<String> reverseLookup(IntentMap im, Collection<?> values) { + Set<String> intentsFound = new HashSet<String>(); + for (Map.Entry<String, Object> entry : im.entrySet()) { + if (values.contains(entry.getValue())) { + intentsFound.add(entry.getKey()); + } + } + return intentsFound; + } + + public void assertAllIntentsSupported(Map<String, Object> serviceProperties) { + long endTime = System.currentTimeMillis() + maxIntentWaitTime; + Set<String> requiredIntents = IntentUtils.getRequestedIntents(serviceProperties); + List<String> unsupportedIntents = new ArrayList<String>(); + do { + unsupportedIntents.clear(); + for (String ri : requiredIntents) { + if (!intentMap.containsKey(ri)) { + unsupportedIntents.add(ri); + } + } + long remainingSeconds = (endTime - System.currentTimeMillis()) / 1000; + if (!unsupportedIntents.isEmpty() && remainingSeconds > 0) { + LOG.debug("Waiting for custom intents " + unsupportedIntents + " timeout in " + remainingSeconds); + try { + synchronized (intentMap) { + intentMap.wait(1000); + } + } catch (InterruptedException e) { + LOG.warn(e.getMessage(), e); + } + } + } while (!unsupportedIntents.isEmpty() && System.currentTimeMillis() < endTime); + + if (!unsupportedIntents.isEmpty()) { + throw new RuntimeException("service cannot be exported because the following " + + "intents are not supported by this RSA: " + unsupportedIntents); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java new file mode 100644 index 0000000..5ed4ef6 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java @@ -0,0 +1,62 @@ +/** + * 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.qos; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Maps intent names to intent objects + * An intent object can be a Feature, a BindingConfiguration or a String + * + * Also supports a default intent map. Custom intents can override the defaults + */ +public class IntentMap extends ConcurrentHashMap<String, Object> { + + private static final long serialVersionUID = 2606460607920520767L; + private Map<String, Object> defaultMap; + + public IntentMap() { + this(new HashMap<String, Object>()); + } + + public IntentMap(Map<String, Object> defaultMap) { + this.defaultMap = defaultMap; + putAll(defaultMap); + } + + @Override + public Object put(String key, Object value) { + Object result = super.put(key, value); + synchronized (this) { + notifyAll(); + } + return result; + } + + @Override + public Object remove(Object key) { + Object old = super.remove(key); + if (defaultMap.containsKey(key)) { + put((String)key, defaultMap.get(key)); + } + return old; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java new file mode 100644 index 0000000..e1ceaaa --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java @@ -0,0 +1,62 @@ +/** + * 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.qos; + +import org.apache.cxf.dosgi.dsw.Constants; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class IntentTracker extends ServiceTracker { + + private final IntentMap intentMap; + + + public IntentTracker(BundleContext context, IntentMap intentMap) { + super(context, getFilter(context), null); + this.intentMap = intentMap; + } + + static Filter getFilter(BundleContext context) { + try { + return context.createFilter("(" + Constants.INTENT_NAME_PROP + "=*)"); + } catch (InvalidSyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + @Override + public Object addingService(ServiceReference reference) { + String intentName = (String) reference.getProperty(Constants.INTENT_NAME_PROP); + Object intent = super.addingService(reference); + IntentManagerImpl.LOG.info("Adding custom intent " + intentName); + intentMap.put(intentName, intent); + return intent; + } + + @Override + public void removedService(ServiceReference reference, Object service) { + String intentName = (String) reference.getProperty(Constants.INTENT_NAME_PROP); + intentMap.remove(intentName); + super.removedService(reference, service); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java new file mode 100644 index 0000000..31b1e42 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java @@ -0,0 +1,86 @@ +/** + * 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.qos; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.cxf.dosgi.dsw.Constants; +import org.apache.cxf.dosgi.dsw.util.OsgiUtils; +import org.osgi.service.remoteserviceadmin.RemoteConstants; + +public final class IntentUtils { + + private IntentUtils() { + // never constructed + } + + public static String[] mergeArrays(String[] a1, String[] a2) { + if (a1 == null) { + return a2; + } + if (a2 == null) { + return a1; + } + + List<String> list = new ArrayList<String>(a1.length + a2.length); + Collections.addAll(list, a1); + for (String s : a2) { + if (!list.contains(s)) { + list.add(s); + } + } + + return list.toArray(new String[list.size()]); + } + + public static Set<String> getRequestedIntents(Map<String, Object> sd) { + Collection<String> intents = OsgiUtils.getMultiValueProperty(sd.get(RemoteConstants.SERVICE_EXPORTED_INTENTS)); + Collection<String> intents2 + = OsgiUtils.getMultiValueProperty(sd.get(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA)); + @SuppressWarnings("deprecation") + Collection<String> oldIntents = OsgiUtils.getMultiValueProperty(sd.get(Constants.EXPORTED_INTENTS_OLD)); + Set<String> allIntents = new HashSet<String>(); + if (intents != null) { + allIntents.addAll(parseIntents(intents)); + } + if (intents2 != null) { + allIntents.addAll(parseIntents(intents2)); + } + if (oldIntents != null) { + allIntents.addAll(parseIntents(oldIntents)); + } + + return allIntents; + } + + private static Collection<String> parseIntents(Collection<String> intents) { + List<String> parsed = new ArrayList<String>(); + for (String intent : intents) { + parsed.addAll(Arrays.asList(intent.split("[ ]"))); + } + return parsed; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java new file mode 100644 index 0000000..9acd0f0 --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java @@ -0,0 +1,132 @@ +/** + * 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.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("deprecation") +public final class OsgiUtils { + + public static final Logger LOG = LoggerFactory.getLogger(OsgiUtils.class); + + private OsgiUtils() { + } + + public static boolean getBooleanProperty(Map<String, Object> sd, String name) { + return toBoolean(sd.get(name)); + } + + public static boolean toBoolean(Object value) { + return value instanceof Boolean && (Boolean) value + || value instanceof String && Boolean.parseBoolean((String)value); + } + + @SuppressWarnings("unchecked") + public static Collection<String> getMultiValueProperty(Object property) { + if (property == null) { + return null; + } else if (property instanceof Collection) { + return (Collection<String>)property; + } else if (property instanceof String[]) { + return Arrays.asList((String[])property); + } else { + return Collections.singleton(property.toString()); + } + } + + public static String getProperty(EndpointDescription endpoint, String name) { + return getProperty(endpoint.getProperties(), name); + } + + public static String getProperty(Map<String, Object> dict, String name) { + Object value = dict.get(name); + return value instanceof String ? (String) value : null; + } + + public static String getFirstNonEmptyStringProperty(Map<String, Object> dict, String ... keys) { + for (String key : keys) { + String value = getProperty(dict, key); + if (value != null) { + return value; + } + } + return null; + } + + /** + * Tries to retrieve the version of iClass via the PackageAdmin. + * + * @param iClass tThe interface for which the version should be found + * @param bc any valid BundleContext + * @return the version of the interface or "0.0.0" if no version information could be found or an error + * occurred during the retrieval + */ + public static String getVersion(Class<?> iClass, BundleContext bc) { + ServiceReference<PackageAdmin> paRef = bc.getServiceReference(PackageAdmin.class); + if (paRef != null) { + PackageAdmin pa = bc.getService(paRef); + try { + Bundle b = pa.getBundle(iClass); + if (b == null) { + LOG.info("Unable to find interface version for interface " + iClass.getName() + + ". Falling back to 0.0.0"); + return "0.0.0"; + } + LOG.debug("Interface source bundle: {}", b.getSymbolicName()); + + ExportedPackage[] ep = pa.getExportedPackages(b); + LOG.debug("Exported Packages of the source bundle: {}", (Object)ep); + + String pack = iClass.getPackage().getName(); + LOG.debug("Looking for Package: {}", pack); + if (ep != null) { + for (ExportedPackage p : ep) { + if (p != null + && pack.equals(p.getName())) { + LOG.debug("found package -> Version: {}", p.getVersion()); + return p.getVersion().toString(); + } + } + } + } finally { + if (pa != null) { + bc.ungetService(paRef); + } + } + } else { + LOG.error("Was unable to obtain the package admin service -> can't resolve interface versions"); + } + + LOG.info("Unable to find interface version for interface " + iClass.getName() + + ". Falling back to 0.0.0"); + return "0.0.0"; + } +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/StringPlus.java ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/StringPlus.java b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/StringPlus.java new file mode 100644 index 0000000..dbb4cda --- /dev/null +++ b/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/StringPlus.java @@ -0,0 +1,72 @@ +/** + * 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.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class StringPlus { + + private static final Logger LOG = LoggerFactory.getLogger(StringPlus.class); + + private StringPlus() { + // never constructed + } + + @SuppressWarnings("rawtypes") + public static String[] normalize(Object object) { + if (object instanceof String) { + String s = (String)object; + String[] values = s.split(","); + List<String> list = new ArrayList<String>(); + for (String val : values) { + String actualValue = val.trim(); + if (!actualValue.isEmpty()) { + list.add(actualValue); + } + } + return list.toArray(new String[list.size()]); + } + + if (object instanceof String[]) { + return (String[])object; + } + + if (object instanceof Collection) { + Collection col = (Collection)object; + List<String> ar = new ArrayList<String>(col.size()); + for (Object o : col) { + if (o instanceof String) { + String s = (String)o; + ar.add(s); + } else { + LOG.warn("stringPlus contained non string element in list! Element was skipped"); + } + } + return ar.toArray(new String[ar.size()]); + } + + return null; + } + +} http://git-wip-us.apache.org/repos/asf/cxf-dosgi/blob/1425743f/cxf-dsw/src/main/resources/service-decoration.xsd ---------------------------------------------------------------------- diff --git a/cxf-dsw/src/main/resources/service-decoration.xsd b/cxf-dsw/src/main/resources/service-decoration.xsd new file mode 100644 index 0000000..66e8d30 --- /dev/null +++ b/cxf-dsw/src/main/resources/service-decoration.xsd @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<schema targetNamespace="http://cxf.apache.org/xmlns/service-decoration/1.0.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cxf.apache.org/xmlns/service-decoration/1.0.0"> + <element name="service-decorations" type="tns:ServiceDecorationsType"></element> + + <complexType name="ServiceDecorationsType"> + <sequence> + <element maxOccurs="unbounded" minOccurs="0" + ref="tns:service-decoration"> + </element> + </sequence> + </complexType> + + <complexType name="ServiceDecorationType"> + <sequence> + <element maxOccurs="unbounded" minOccurs="0" + ref="tns:match"> + </element> + </sequence> + </complexType> + + <complexType name="MatchType"> + <sequence> + <element maxOccurs="unbounded" minOccurs="0" + ref="tns:match-property"> + </element> + <element maxOccurs="unbounded" minOccurs="0" + ref="tns:add-property"> + </element> + </sequence> + <attribute name="interface" type="string"></attribute> + </complexType> + + <complexType name="MatchPropertyType"> + <attribute name="name" type="string"></attribute> + <attribute name="value" type="string"></attribute> + </complexType> + + <complexType name="AddPropertyType"> + <attribute name="name" type="string"></attribute> + <attribute name="value" type="string"></attribute> + <attribute name="type" type="string"></attribute> + </complexType> + <element name="service-decoration" + type="tns:ServiceDecorationType"> + </element> + <element name="match" type="tns:MatchType"></element> + <element name="match-property" type="tns:MatchPropertyType"></element> + <element name="add-property" type="tns:AddPropertyType"></element> +</schema> \ No newline at end of file