blob: 7b3b6a4e807d273eec4caba4ee591964961a3d81 [file] [log] [blame]
/**
* 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.ws;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_CONFIGS_SUPPORTED;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_INTENTS_SUPPORTED;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jws.WebService;
import org.apache.aries.rsa.spi.DistributionProvider;
import org.apache.aries.rsa.spi.Endpoint;
import org.apache.aries.rsa.spi.IntentUnsatisfiedException;
import org.apache.cxf.Bus;
import org.apache.cxf.aegis.databinding.AegisDatabinding;
import org.apache.cxf.binding.BindingConfiguration;
import org.apache.cxf.binding.soap.SoapBindingConfiguration;
import org.apache.cxf.databinding.DataBinding;
import org.apache.cxf.dosgi.common.api.IntentsProvider;
import org.apache.cxf.dosgi.common.endpoint.ServerEndpoint;
import org.apache.cxf.dosgi.common.handlers.BaseDistributionProvider;
import org.apache.cxf.dosgi.common.httpservice.HttpServiceManager;
import org.apache.cxf.dosgi.common.intent.IntentManager;
import org.apache.cxf.dosgi.common.proxy.ProxyFactory;
import org.apache.cxf.dosgi.common.util.PropertyHelper;
import org.apache.cxf.endpoint.AbstractEndpointFactory;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.feature.Feature;
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.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.service.remoteserviceadmin.RemoteConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(configurationPid = "cxf-dsw", property = //
{//
REMOTE_CONFIGS_SUPPORTED + "=" + WsConstants.WS_CONFIG_TYPE,
REMOTE_INTENTS_SUPPORTED + "="
})
public class WsProvider extends BaseDistributionProvider implements DistributionProvider {
private static final Logger LOG = LoggerFactory.getLogger(WsProvider.class);
protected BundleContext bundleContext;
@Reference
public void setHttpServiceManager(HttpServiceManager httpServiceManager) {
this.httpServiceManager = httpServiceManager;
}
@Reference
public void setIntentManager(IntentManager intentManager) {
this.intentManager = intentManager;
}
@Activate
public void activate(BundleContext context) {
this.bundleContext = context;
}
@Override
public String[] getSupportedTypes() {
return new String[] {WsConstants.WS_CONFIG_TYPE};
}
@Override
@SuppressWarnings("rawtypes")
public Object importEndpoint(ClassLoader consumerLoader,
BundleContext consumerContext,
Class[] interfaces,
EndpointDescription endpoint) throws IntentUnsatisfiedException {
if (interfaces.length > 1) {
throw new IllegalArgumentException("Multiple interfaces are not supported by this provider");
}
Class<?> iClass = interfaces[0];
Map<String, Object> sd = endpoint.getProperties();
String address = getClientAddress(sd);
LOG.info("Creating a " + iClass.getName() + " client, endpoint address is " + address);
try {
ClientProxyFactoryBean factory = createClientProxyFactoryBean(sd, iClass);
factory.setDataBinding(getDataBinding(sd, iClass));
factory.setBindingConfig(new SoapBindingConfiguration());
factory.setServiceClass(iClass);
factory.setAddress(address);
addContextProperties(factory.getClientFactoryBean(), sd, WsConstants.WS_CONTEXT_PROPS_PROP_KEY);
WsdlSupport.setWsdlProperties(factory.getClientFactoryBean(), bundleContext, sd);
applyIntents(sd, factory);
return ProxyFactory.create(factory.create(), iClass);
} catch (Exception e) {
throw new RuntimeException("proxy creation failed", e);
}
}
private void applyIntents(Map<String, Object> sd, ClientProxyFactoryBean factory) {
Set<String> intentNames = intentManager.getImported(sd);
List<Object> intents = intentManager.getRequiredIntents(intentNames);
List<Feature> features = intentManager.getIntents(Feature.class, intents);
factory.setFeatures(features);
DataBinding dataBinding = intentManager.getIntent(DataBinding.class, intents);
if (dataBinding != null) {
factory.setDataBinding(dataBinding);
}
BindingConfiguration binding = copy(intentManager.getIntent(BindingConfiguration.class, intents));
if (binding != null) {
factory.setBindingConfig(binding);
}
}
private BindingConfiguration copy(BindingConfiguration bindingCfg) {
return bindingCfg instanceof SoapBindingConfiguration
? copy((SoapBindingConfiguration)bindingCfg) : bindingCfg;
}
private SoapBindingConfiguration copy(SoapBindingConfiguration intent) {
SoapBindingConfiguration bindingCfg = new SoapBindingConfiguration();
bindingCfg.setVersion(intent.getVersion());
bindingCfg.setTransportURI(intent.getTransportURI());
bindingCfg.setUse(intent.getUse());
if (intent.isSetStyle()) {
bindingCfg.setStyle(intent.getStyle());
}
bindingCfg.setMtomEnabled(intent.isMtomEnabled());
bindingCfg.setBindingName(intent.getBindingName());
bindingCfg.setBindingNamePostfix(intent.getBindingNamePostfix());
return bindingCfg;
}
@Override
@SuppressWarnings("rawtypes")
public Endpoint exportService(Object serviceO,
BundleContext serviceContext,
Map<String, Object> endpointProps,
Class[] exportedInterfaces) throws IntentUnsatisfiedException {
if (!configTypeSupported(endpointProps, WsConstants.WS_CONFIG_TYPE)) {
return null;
}
Class<?> iClass = exportedInterfaces[0];
String address = getPojoAddress(endpointProps, iClass);
ServerFactoryBean factory = createServerFactoryBean(endpointProps, iClass);
String contextRoot = PropertyHelper.getProperty(endpointProps, WsConstants.WS_HTTP_SERVICE_CONTEXT);
final Long sid = (Long) endpointProps.get(RemoteConstants.ENDPOINT_SERVICE_ID);
Set<String> intentNames = intentManager.getExported(endpointProps);
List<Object> intents = intentManager.getRequiredIntents(intentNames);
intents.addAll(intentManager.getIntentsFromService(serviceO));
Bus bus = createBus(sid, serviceContext, contextRoot, endpointProps);
factory.setDataBinding(getDataBinding(endpointProps, iClass));
factory.setBindingConfig(new SoapBindingConfiguration());
factory.setBus(bus);
factory.setServiceClass(iClass);
factory.setServiceBean(serviceO);
factory.setAddress(address);
addContextProperties(factory, endpointProps, WsConstants.WS_CONTEXT_PROPS_PROP_KEY);
WsdlSupport.setWsdlProperties(factory, serviceContext, endpointProps);
if (serviceO instanceof IntentsProvider) {
intents.addAll(((IntentsProvider)serviceO).getIntents());
}
applyIntents(intents, factory);
String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address);
try {
EndpointDescription epd = createEndpointDesc(endpointProps,
new String[]{WsConstants.WS_CONFIG_TYPE},
completeEndpointAddress, intentNames);
return createServerFromFactory(factory, epd);
} catch (Exception e) {
throw new RuntimeException("Error exporting service with address " + completeEndpointAddress, e);
}
}
private void applyIntents(List<Object> intents, AbstractEndpointFactory factory) {
List<Feature> features = intentManager.getIntents(Feature.class, intents);
factory.setFeatures(features);
DataBinding dataBinding = intentManager.getIntent(DataBinding.class, intents);
if (dataBinding != null) {
factory.setDataBinding(dataBinding);
}
BindingConfiguration binding = intentManager.getIntent(BindingConfiguration.class, intents);
if (binding != null) {
factory.setBindingConfig(binding);
}
}
protected EndpointDescription createEndpointDesc(Map<String, Object> props, String[] importedConfigs,
String address, Collection<String> intents) {
return super.createEndpointDesc(props, importedConfigs, WsConstants.WS_ADDRESS_PROPERTY, address, intents);
}
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(WsConstants.WS_PORT_PROPERTY);
if (port == null) {
port = "9000";
}
address = "http://localhost:" + port + "/" + iClass.getName().replace('.', '/');
LOG.info("Using a default address: " + address);
return address;
}
protected String getClientAddress(Map<String, Object> sd) {
return PropertyHelper.getFirstNonEmptyStringProperty(sd, WsConstants.WS_ADDRESS_PROPERTY,
RemoteConstants.ENDPOINT_ID);
}
protected String getServerAddress(Map<String, Object> sd, Class<?> iClass) {
String address = getClientAddress(sd);
return address == null ? httpServiceManager.getDefaultAddress(iClass) : address;
}
protected Endpoint createServerFromFactory(ServerFactoryBean factory, EndpointDescription epd) {
Server server = factory.create();
return new ServerEndpoint(epd, server);
}
private DataBinding getDataBinding(Map<String, Object> sd, Class<?> iClass) {
return isJAXWS(sd, iClass) ? new JAXBDataBinding() : new AegisDatabinding();
}
// 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) {
return iClass.getAnnotation(WebService.class) != null;
}
}