| /** |
| * 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.camel.component.cxf; |
| |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import javax.wsdl.Definition; |
| import javax.wsdl.WSDLException; |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.sax.SAXSource; |
| import javax.xml.transform.stax.StAXSource; |
| import javax.xml.transform.stream.StreamSource; |
| import javax.xml.ws.Provider; |
| import javax.xml.ws.WebServiceProvider; |
| import javax.xml.ws.handler.Handler; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.apache.camel.CamelContext; |
| import org.apache.camel.CamelException; |
| import org.apache.camel.Consumer; |
| import org.apache.camel.Processor; |
| import org.apache.camel.Producer; |
| import org.apache.camel.RuntimeCamelException; |
| import org.apache.camel.Service; |
| import org.apache.camel.component.cxf.common.header.CxfHeaderFilterStrategy; |
| import org.apache.camel.component.cxf.common.message.CxfConstants; |
| import org.apache.camel.component.cxf.feature.CXFMessageDataFormatFeature; |
| import org.apache.camel.component.cxf.feature.PayLoadDataFormatFeature; |
| import org.apache.camel.component.cxf.feature.RAWDataFormatFeature; |
| import org.apache.camel.impl.DefaultEndpoint; |
| import org.apache.camel.impl.SynchronousDelegateProducer; |
| import org.apache.camel.spi.HeaderFilterStrategy; |
| import org.apache.camel.spi.HeaderFilterStrategyAware; |
| import org.apache.camel.spi.UriEndpoint; |
| import org.apache.camel.spi.UriParam; |
| import org.apache.camel.spi.UriPath; |
| import org.apache.camel.util.CastUtils; |
| import org.apache.camel.util.EndpointHelper; |
| import org.apache.camel.util.ObjectHelper; |
| import org.apache.camel.util.UnsafeUriCharactersEncoder; |
| import org.apache.cxf.Bus; |
| import org.apache.cxf.BusFactory; |
| import org.apache.cxf.binding.BindingConfiguration; |
| import org.apache.cxf.common.classloader.ClassLoaderUtils; |
| import org.apache.cxf.common.injection.ResourceInjector; |
| import org.apache.cxf.common.util.ClassHelper; |
| import org.apache.cxf.common.util.ModCountCopyOnWriteArrayList; |
| import org.apache.cxf.common.util.ReflectionUtil; |
| import org.apache.cxf.configuration.security.AuthorizationPolicy; |
| import org.apache.cxf.databinding.DataBinding; |
| import org.apache.cxf.databinding.source.SourceDataBinding; |
| import org.apache.cxf.endpoint.Client; |
| import org.apache.cxf.endpoint.ClientImpl; |
| import org.apache.cxf.endpoint.Endpoint; |
| import org.apache.cxf.feature.Feature; |
| import org.apache.cxf.feature.LoggingFeature; |
| import org.apache.cxf.frontend.ClientFactoryBean; |
| import org.apache.cxf.frontend.ServerFactoryBean; |
| import org.apache.cxf.headers.Header; |
| import org.apache.cxf.interceptor.AbstractLoggingInterceptor; |
| import org.apache.cxf.interceptor.Interceptor; |
| import org.apache.cxf.jaxws.JaxWsClientFactoryBean; |
| import org.apache.cxf.jaxws.JaxWsServerFactoryBean; |
| import org.apache.cxf.jaxws.context.WebServiceContextResourceResolver; |
| import org.apache.cxf.jaxws.handler.AnnotationHandlerChainBuilder; |
| import org.apache.cxf.jaxws.support.JaxWsEndpointImpl; |
| import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean; |
| import org.apache.cxf.logging.FaultListener; |
| import org.apache.cxf.message.Attachment; |
| import org.apache.cxf.message.Message; |
| import org.apache.cxf.message.MessageContentsList; |
| import org.apache.cxf.resource.DefaultResourceManager; |
| import org.apache.cxf.resource.ResourceManager; |
| import org.apache.cxf.resource.ResourceResolver; |
| import org.apache.cxf.service.model.BindingOperationInfo; |
| import org.apache.cxf.service.model.MessagePartInfo; |
| import org.apache.cxf.staxutils.StaxSource; |
| import org.apache.cxf.staxutils.StaxUtils; |
| import org.apache.cxf.wsdl.WSDLManager; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The cxf component is used for SOAP WebServices using Apache CXF. |
| */ |
| @UriEndpoint(scheme = "cxf", title = "CXF", syntax = "cxf:beanId:address", consumerClass = CxfConsumer.class, label = "soap,webservice") |
| public class CxfEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware, Service, Cloneable { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(CxfEndpoint.class); |
| |
| @UriParam(label = "advanced") |
| protected Bus bus; |
| |
| private AtomicBoolean getBusHasBeenCalled = new AtomicBoolean(false); |
| private volatile boolean createBus; |
| |
| private BindingConfiguration bindingConfig; |
| private DataBinding dataBinding; |
| private Object serviceFactoryBean; |
| private List<Interceptor<? extends Message>> in = new ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>(); |
| private List<Interceptor<? extends Message>> out = new ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>(); |
| private List<Interceptor<? extends Message>> outFault = new ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>(); |
| private List<Interceptor<? extends Message>> inFault = new ModCountCopyOnWriteArrayList<Interceptor<? extends Message>>(); |
| private List<Feature> features = new ModCountCopyOnWriteArrayList<Feature>(); |
| private List<Handler> handlers; |
| private List<String> schemaLocations; |
| private String transportId; |
| |
| @UriPath(description = "To lookup an existing configured CxfEndpoint. Must used bean: as prefix.") |
| private String beanId; |
| @UriParam(defaultValue = "POJO") |
| private DataFormat dataFormat = DataFormat.POJO; |
| @UriPath(label = "service") |
| private String address; |
| @UriParam(label = "service") |
| private String wsdlURL; |
| @UriParam(label = "service") |
| private Class<?> serviceClass; |
| @UriParam(label = "service", name = "portName") |
| private String portNameString; |
| private QName portName; |
| @UriParam(label = "service", name = "serviceName") |
| private String serviceNameString; |
| private QName serviceName; |
| @UriParam(label = "service") |
| private String bindingId; |
| @UriParam(label = "service") |
| private String publishedEndpointUrl; |
| @UriParam(label = "producer") |
| private String defaultOperationName; |
| @UriParam(label = "producer") |
| private String defaultOperationNamespace; |
| @UriParam(label = "producer") |
| private boolean wrapped; |
| @UriParam |
| private Boolean wrappedStyle; |
| @UriParam(label = "advanced") |
| private Boolean allowStreaming; |
| @UriParam(label = "advanced") |
| private CxfBinding cxfBinding; |
| @UriParam(label = "advanced") |
| private HeaderFilterStrategy headerFilterStrategy; |
| @UriParam(label = "advanced") |
| private boolean defaultBus; |
| @UriParam(label = "logging") |
| private boolean loggingFeatureEnabled; |
| @UriParam(label = "logging", defaultValue = "" + AbstractLoggingInterceptor.DEFAULT_LIMIT) |
| private int loggingSizeLimit; |
| @UriParam(label = "advanced") |
| private boolean mtomEnabled; |
| @UriParam(label = "advanced") |
| private boolean skipPayloadMessagePartCheck; |
| @UriParam(label = "logging") |
| private boolean skipFaultLogging; |
| @UriParam(label = "advanced") |
| private boolean mergeProtocolHeaders; |
| @UriParam(label = "advanced") |
| private CxfEndpointConfigurer cxfEndpointConfigurer; |
| @UriParam(label = "advanced", defaultValue = "30000") |
| private long continuationTimeout = 30000; |
| @UriParam(label = "security") |
| private String username; |
| @UriParam(label = "security") |
| private String password; |
| @UriParam(label = "advanced", prefix = "properties.", multiValue = true) |
| private Map<String, Object> properties; |
| |
| public CxfEndpoint() { |
| } |
| |
| public CxfEndpoint(String remaining, CxfComponent cxfComponent) { |
| super(remaining, cxfComponent); |
| setAddress(remaining); |
| } |
| |
| @Deprecated |
| public CxfEndpoint(String remaining, CamelContext context) { |
| super(remaining, context); |
| setAddress(remaining); |
| } |
| |
| @Deprecated |
| public CxfEndpoint(String remaining) { |
| super(remaining); |
| setAddress(remaining); |
| } |
| |
| public CxfEndpoint copy() { |
| try { |
| return (CxfEndpoint)this.clone(); |
| } catch (CloneNotSupportedException e) { |
| throw new RuntimeCamelException(e); |
| } |
| } |
| |
| // This method is for CxfComponent setting the EndpointUri |
| protected void updateEndpointUri(String endpointUri) { |
| super.setEndpointUri(UnsafeUriCharactersEncoder.encodeHttpURI(endpointUri)); |
| } |
| |
| public Producer createProducer() throws Exception { |
| Producer answer = new CxfProducer(this); |
| if (isSynchronous()) { |
| return new SynchronousDelegateProducer(answer); |
| } else { |
| return answer; |
| } |
| } |
| |
| public Consumer createConsumer(Processor processor) throws Exception { |
| CxfConsumer answer = new CxfConsumer(this, processor); |
| configureConsumer(answer); |
| return answer; |
| } |
| |
| public boolean isSingleton() { |
| return true; |
| } |
| |
| /** |
| * Populate server factory bean |
| */ |
| protected void setupServerFactoryBean(ServerFactoryBean sfb, Class<?> cls) { |
| |
| // address |
| sfb.setAddress(getAddress()); |
| |
| sfb.setServiceClass(cls); |
| |
| sfb.setInInterceptors(in); |
| sfb.setOutInterceptors(out); |
| sfb.setOutFaultInterceptors(outFault); |
| sfb.setInFaultInterceptors(inFault); |
| sfb.setFeatures(features); |
| if (schemaLocations != null) { |
| sfb.setSchemaLocations(schemaLocations); |
| } |
| if (bindingConfig != null) { |
| sfb.setBindingConfig(bindingConfig); |
| } |
| |
| if (dataBinding != null) { |
| sfb.setDataBinding(dataBinding); |
| } |
| |
| if (serviceFactoryBean != null) { |
| setServiceFactory(sfb, serviceFactoryBean); |
| } |
| |
| if (sfb instanceof JaxWsServerFactoryBean && handlers != null) { |
| ((JaxWsServerFactoryBean)sfb).setHandlers(handlers); |
| } |
| if (getTransportId() != null) { |
| sfb.setTransportId(getTransportId()); |
| } |
| if (getBindingId() != null) { |
| sfb.setBindingId(getBindingId()); |
| } |
| |
| // wsdl url |
| if (getWsdlURL() != null) { |
| sfb.setWsdlURL(getWsdlURL()); |
| } |
| |
| // service name qname |
| if (getServiceName() != null) { |
| sfb.setServiceName(getServiceName()); |
| } |
| |
| // port qname |
| if (getPortName() != null) { |
| sfb.setEndpointName(getPortName()); |
| } |
| |
| // apply feature here |
| if (!CxfEndpointUtils.hasAnnotation(cls, WebServiceProvider.class)) { |
| if (getDataFormat() == DataFormat.PAYLOAD) { |
| sfb.getFeatures().add(new PayLoadDataFormatFeature(allowStreaming)); |
| } else if (getDataFormat().dealias() == DataFormat.CXF_MESSAGE) { |
| sfb.getFeatures().add(new CXFMessageDataFormatFeature()); |
| sfb.setDataBinding(new SourceDataBinding()); |
| } else if (getDataFormat().dealias() == DataFormat.RAW) { |
| RAWDataFormatFeature feature = new RAWDataFormatFeature(); |
| feature.addInIntercepters(getInInterceptors()); |
| feature.addOutInterceptors(getOutInterceptors()); |
| sfb.getFeatures().add(feature); |
| } |
| } else { |
| LOG.debug("Ignore DataFormat mode {} since SEI class is annotated with WebServiceProvider", getDataFormat()); |
| } |
| |
| if (isLoggingFeatureEnabled()) { |
| if (getLoggingSizeLimit() != 0) { |
| sfb.getFeatures().add(new LoggingFeature(getLoggingSizeLimit())); |
| } else { |
| sfb.getFeatures().add(new LoggingFeature()); |
| } |
| } |
| |
| if (getDataFormat() == DataFormat.PAYLOAD) { |
| sfb.setDataBinding(new HybridSourceDataBinding()); |
| } |
| |
| // set the document-literal wrapped style |
| if (getWrappedStyle() != null && getDataFormat().dealias() != DataFormat.CXF_MESSAGE) { |
| setWrapped(sfb, getWrappedStyle()); |
| } |
| |
| // any optional properties |
| if (getProperties() != null) { |
| if (sfb.getProperties() != null) { |
| // add to existing properties |
| sfb.getProperties().putAll(getProperties()); |
| } else { |
| sfb.setProperties(getProperties()); |
| } |
| LOG.debug("ServerFactoryBean: {} added properties: {}", sfb, getProperties()); |
| } |
| if (this.isSkipPayloadMessagePartCheck()) { |
| if (sfb.getProperties() == null) { |
| sfb.setProperties(new HashMap<String, Object>()); |
| } |
| sfb.getProperties().put("soap.no.validate.parts", Boolean.TRUE); |
| } |
| |
| if (this.isSkipFaultLogging()) { |
| if (sfb.getProperties() == null) { |
| sfb.setProperties(new HashMap<String, Object>()); |
| } |
| sfb.getProperties().put(FaultListener.class.getName(), new NullFaultListener()); |
| } |
| |
| sfb.setBus(getBus()); |
| sfb.setStart(false); |
| if (getCxfEndpointConfigurer() != null) { |
| getCxfEndpointConfigurer().configure(sfb); |
| } |
| } |
| |
| /** |
| * Create a client factory bean object. Notice that the serviceClass <b>must</b> be |
| * an interface. |
| */ |
| protected ClientFactoryBean createClientFactoryBean(Class<?> cls) throws CamelException { |
| if (CxfEndpointUtils.hasWebServiceAnnotation(cls)) { |
| return new JaxWsClientFactoryBean() { |
| @Override |
| protected Client createClient(Endpoint ep) { |
| return new CamelCxfClientImpl(getBus(), ep); |
| } |
| }; |
| } else { |
| return new ClientFactoryBean() { |
| @Override |
| protected Client createClient(Endpoint ep) { |
| return new CamelCxfClientImpl(getBus(), ep); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Create a client factory bean object without serviceClass interface. |
| */ |
| protected ClientFactoryBean createClientFactoryBean() { |
| ClientFactoryBean cf = new ClientFactoryBean() { |
| @Override |
| protected Client createClient(Endpoint ep) { |
| return new CamelCxfClientImpl(getBus(), ep); |
| } |
| |
| @Override |
| protected void initializeAnnotationInterceptors(Endpoint ep, Class<?> cls) { |
| // Do nothing here |
| } |
| }; |
| for (Method m : cf.getClass().getMethods()) { |
| if ("setServiceFactory".equals(m.getName())) { |
| try { |
| // Set Object class as the service class of WSDLServiceFactoryBean |
| ReflectionUtil.setAccessible(m).invoke(cf, new WSDLServiceFactoryBean(Object.class)); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| return cf; |
| } |
| |
| protected void setupHandlers(ClientFactoryBean factoryBean, Client client) |
| throws Exception { |
| |
| if (factoryBean instanceof JaxWsClientFactoryBean && handlers != null) { |
| AnnotationHandlerChainBuilder |
| builder = new AnnotationHandlerChainBuilder(); |
| Method m = factoryBean.getClass().getMethod("getServiceFactory"); |
| JaxWsServiceFactoryBean sf = (JaxWsServiceFactoryBean)m.invoke(factoryBean); |
| @SuppressWarnings("rawtypes") |
| List<Handler> chain = new ArrayList<Handler>(handlers); |
| |
| chain.addAll(builder.buildHandlerChainFromClass(sf.getServiceClass(), |
| sf.getEndpointInfo().getName(), |
| sf.getServiceQName(), |
| factoryBean.getBindingId())); |
| |
| if (!chain.isEmpty()) { |
| ResourceManager resourceManager = getBus().getExtension(ResourceManager.class); |
| List<ResourceResolver> resolvers = resourceManager.getResourceResolvers(); |
| resourceManager = new DefaultResourceManager(resolvers); |
| resourceManager.addResourceResolver(new WebServiceContextResourceResolver()); |
| ResourceInjector injector = new ResourceInjector(resourceManager); |
| for (Handler<?> h : chain) { |
| if (Proxy.isProxyClass(h.getClass()) && getServiceClass() != null) { |
| injector.inject(h, getServiceClass()); |
| injector.construct(h, getServiceClass()); |
| } else { |
| injector.inject(h); |
| injector.construct(h); |
| } |
| } |
| } |
| |
| ((JaxWsEndpointImpl)client.getEndpoint()).getJaxwsBinding().setHandlerChain(chain); |
| } |
| } |
| |
| protected void setupClientFactoryBean(ClientFactoryBean factoryBean, Class<?> cls) { |
| if (cls != null) { |
| factoryBean.setServiceClass(cls); |
| } |
| factoryBean.setInInterceptors(in); |
| factoryBean.setOutInterceptors(out); |
| factoryBean.setOutFaultInterceptors(outFault); |
| factoryBean.setInFaultInterceptors(inFault); |
| factoryBean.setFeatures(features); |
| factoryBean.setTransportId(transportId); |
| factoryBean.setBindingId(bindingId); |
| |
| if (bindingConfig != null) { |
| factoryBean.setBindingConfig(bindingConfig); |
| } |
| |
| if (dataBinding != null) { |
| factoryBean.setDataBinding(dataBinding); |
| } |
| |
| if (serviceFactoryBean != null) { |
| setServiceFactory(factoryBean, serviceFactoryBean); |
| } |
| |
| // address |
| factoryBean.setAddress(getAddress()); |
| |
| // wsdl url |
| if (getWsdlURL() != null) { |
| factoryBean.setWsdlURL(getWsdlURL()); |
| } |
| |
| // service name qname |
| if (getServiceName() != null) { |
| factoryBean.setServiceName(getServiceName()); |
| } |
| |
| |
| |
| // port name qname |
| if (getPortName() != null) { |
| factoryBean.setEndpointName(getPortName()); |
| } |
| |
| // apply feature here |
| if (getDataFormat().dealias() == DataFormat.RAW) { |
| RAWDataFormatFeature feature = new RAWDataFormatFeature(); |
| feature.addInIntercepters(getInInterceptors()); |
| feature.addOutInterceptors(getOutInterceptors()); |
| factoryBean.getFeatures().add(feature); |
| } else if (getDataFormat().dealias() == DataFormat.CXF_MESSAGE) { |
| factoryBean.getFeatures().add(new CXFMessageDataFormatFeature()); |
| factoryBean.setDataBinding(new SourceDataBinding()); |
| } else if (getDataFormat() == DataFormat.PAYLOAD) { |
| factoryBean.getFeatures().add(new PayLoadDataFormatFeature(allowStreaming)); |
| factoryBean.setDataBinding(new HybridSourceDataBinding()); |
| } |
| |
| if (isLoggingFeatureEnabled()) { |
| if (getLoggingSizeLimit() != 0) { |
| factoryBean.getFeatures().add(new LoggingFeature(getLoggingSizeLimit())); |
| } else { |
| factoryBean.getFeatures().add(new LoggingFeature()); |
| } |
| } |
| |
| // set the document-literal wrapped style |
| if (getWrappedStyle() != null) { |
| setWrapped(factoryBean, getWrappedStyle()); |
| } |
| |
| // any optional properties |
| if (getProperties() != null) { |
| if (factoryBean.getProperties() != null) { |
| // add to existing properties |
| factoryBean.getProperties().putAll(getProperties()); |
| } else { |
| factoryBean.setProperties(getProperties()); |
| } |
| LOG.debug("ClientFactoryBean: {} added properties: {}", factoryBean, getProperties()); |
| } |
| |
| // setup the basic authentication property |
| if (ObjectHelper.isNotEmpty(username)) { |
| AuthorizationPolicy authPolicy = new AuthorizationPolicy(); |
| authPolicy.setUserName(username); |
| authPolicy.setPassword(password); |
| factoryBean.getProperties().put(AuthorizationPolicy.class.getName(), authPolicy); |
| } |
| |
| if (this.isSkipPayloadMessagePartCheck()) { |
| if (factoryBean.getProperties() == null) { |
| factoryBean.setProperties(new HashMap<String, Object>()); |
| } |
| factoryBean.getProperties().put("soap.no.validate.parts", Boolean.TRUE); |
| } |
| |
| if (this.isSkipFaultLogging()) { |
| if (factoryBean.getProperties() == null) { |
| factoryBean.setProperties(new HashMap<String, Object>()); |
| } |
| factoryBean.getProperties().put(FaultListener.class.getName(), new NullFaultListener()); |
| } |
| |
| factoryBean.setBus(getBus()); |
| if (getCxfEndpointConfigurer() != null) { |
| getCxfEndpointConfigurer().configure(factoryBean); |
| } |
| } |
| |
| // Package private methods |
| // ------------------------------------------------------------------------- |
| |
| private void setWrapped(Object factoryBean, boolean wrapped) { |
| try { |
| Object sf = factoryBean.getClass().getMethod("getServiceFactory").invoke(factoryBean); |
| sf.getClass().getMethod("setWrapped", Boolean.TYPE).invoke(sf, wrapped); |
| } catch (Throwable t) { |
| throw new RuntimeException(t); |
| } |
| } |
| |
| private void setServiceFactory(Object factoryBean, Object serviceFactoryBean2) { |
| for (Method m : factoryBean.getClass().getMethods()) { |
| if ("setServiceFactory".equals(m.getName()) |
| && m.getParameterTypes()[0].isInstance(serviceFactoryBean2)) { |
| try { |
| ReflectionUtil.setAccessible(m).invoke(factoryBean, serviceFactoryBean2); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create a CXF client object |
| */ |
| Client createClient() throws Exception { |
| |
| // get service class |
| if (getDataFormat().equals(DataFormat.POJO)) { |
| ObjectHelper.notNull(getServiceClass(), CxfConstants.SERVICE_CLASS); |
| } |
| |
| if (getWsdlURL() == null && getServiceClass() == null) { |
| // no WSDL and serviceClass specified, set our default serviceClass |
| setServiceClass(org.apache.camel.component.cxf.DefaultSEI.class.getName()); |
| setDefaultOperationNamespace(CxfConstants.DISPATCH_NAMESPACE); |
| setDefaultOperationName(CxfConstants.DISPATCH_DEFAULT_OPERATION_NAMESPACE); |
| if (getDataFormat().equals(DataFormat.PAYLOAD)) { |
| setSkipPayloadMessagePartCheck(true); |
| } |
| } |
| |
| Class<?> cls = null; |
| if (getServiceClass() != null) { |
| cls = getServiceClass(); |
| // create client factory bean |
| ClientFactoryBean factoryBean = createClientFactoryBean(cls); |
| // setup client factory bean |
| setupClientFactoryBean(factoryBean, cls); |
| Client client = factoryBean.create(); |
| // setup the handlers |
| setupHandlers(factoryBean, client); |
| return client; |
| } else { |
| // create the client without service class |
| |
| checkName(portName, "endpoint/port name"); |
| checkName(serviceName, "service name"); |
| |
| ClientFactoryBean factoryBean = createClientFactoryBean(); |
| // setup client factory bean |
| setupClientFactoryBean(factoryBean, null); |
| return factoryBean.create(); |
| } |
| } |
| |
| void checkName(Object value, String name) { |
| if (ObjectHelper.isEmpty(value)) { |
| LOG.warn("The " + name + " of " + this.getEndpointUri() + " is empty, cxf will try to load the first one in wsdl for you."); |
| } |
| } |
| |
| /** |
| * Create a CXF server factory bean |
| */ |
| ServerFactoryBean createServerFactoryBean() throws Exception { |
| |
| Class<?> cls = null; |
| if (getDataFormat() == DataFormat.POJO) { |
| ObjectHelper.notNull(getServiceClass(), CxfConstants.SERVICE_CLASS); |
| } |
| |
| if (getWsdlURL() == null && getServiceClass() == null) { |
| // no WSDL and serviceClass specified, set our default serviceClass |
| if (getDataFormat().equals(DataFormat.PAYLOAD)) { |
| setServiceClass(org.apache.camel.component.cxf.DefaultPayloadProviderSEI.class.getName()); |
| } |
| } |
| |
| if (getServiceClass() != null) { |
| cls = getServiceClass(); |
| } |
| |
| // create server factory bean |
| // Shouldn't use CxfEndpointUtils.getServerFactoryBean(cls) as it is for |
| // CxfSoapComponent |
| ServerFactoryBean answer = null; |
| |
| if (cls == null) { |
| checkName(portName, " endpoint/port name"); |
| checkName(serviceName, " service name"); |
| answer = new JaxWsServerFactoryBean(new WSDLServiceFactoryBean()) { |
| { |
| doInit = false; |
| } |
| }; |
| cls = Provider.class; |
| } else if (CxfEndpointUtils.hasWebServiceAnnotation(cls)) { |
| answer = new JaxWsServerFactoryBean(); |
| } else { |
| answer = new ServerFactoryBean(); |
| } |
| |
| // setup server factory bean |
| setupServerFactoryBean(answer, cls); |
| return answer; |
| } |
| |
| protected String resolvePropertyPlaceholders(String str) { |
| try { |
| if (getCamelContext() != null) { |
| return getCamelContext().resolvePropertyPlaceholders(str); |
| } else { |
| return str; |
| } |
| } catch (Exception ex) { |
| throw ObjectHelper.wrapRuntimeCamelException(ex); |
| } |
| } |
| |
| // Properties |
| // ------------------------------------------------------------------------- |
| |
| |
| public String getBeanId() { |
| return beanId; |
| } |
| |
| public void setBeanId(String beanId) { |
| this.beanId = beanId; |
| } |
| |
| public DataFormat getDataFormat() { |
| return dataFormat; |
| } |
| |
| /** |
| * The data type messages supported by the CXF endpoint. |
| */ |
| public void setDataFormat(DataFormat format) { |
| dataFormat = format; |
| } |
| |
| public String getPublishedEndpointUrl() { |
| return resolvePropertyPlaceholders(publishedEndpointUrl); |
| } |
| |
| /** |
| * This option can override the endpointUrl that published from the WSDL which can be accessed with service address url plus ?wsd |
| */ |
| public void setPublishedEndpointUrl(String url) { |
| publishedEndpointUrl = url; |
| } |
| |
| public String getWsdlURL() { |
| return resolvePropertyPlaceholders(wsdlURL); |
| } |
| |
| /** |
| * The location of the WSDL. Can be on the classpath, file system, or be hosted remotely. |
| */ |
| public void setWsdlURL(String url) { |
| wsdlURL = url; |
| } |
| |
| public Class<?> getServiceClass() { |
| return serviceClass; |
| } |
| |
| /** |
| * The class name of the SEI (Service Endpoint Interface) class which could have JSR181 annotation or not. |
| */ |
| public void setServiceClass(Class<?> cls) { |
| serviceClass = cls; |
| } |
| |
| /** |
| * The class name of the SEI (Service Endpoint Interface) class which could have JSR181 annotation or not. |
| */ |
| public void setServiceClass(Object instance) { |
| serviceClass = ClassHelper.getRealClass(instance); |
| } |
| |
| /** |
| * The class name of the SEI (Service Endpoint Interface) class which could have JSR181 annotation or not. |
| */ |
| public void setServiceClass(String type) throws ClassNotFoundException { |
| if (ObjectHelper.isEmpty(type)) { |
| throw new IllegalArgumentException("The serviceClass option can neither be null nor an empty String."); |
| } |
| serviceClass = ClassLoaderUtils.loadClass(resolvePropertyPlaceholders(type), getClass()); |
| } |
| |
| /** |
| * The service name this service is implementing, it maps to the wsdl:service@name. |
| */ |
| public void setServiceNameString(String service) { |
| serviceNameString = service; |
| } |
| |
| /** |
| * The service name this service is implementing, it maps to the wsdl:service@name. |
| */ |
| public void setServiceName(QName service) { |
| serviceName = service; |
| } |
| |
| /** |
| * The service name this service is implementing, it maps to the wsdl:service@name. |
| */ |
| public void setService(String service) { |
| serviceNameString = service; |
| } |
| |
| public QName getServiceName() { |
| if (serviceName == null && serviceNameString != null) { |
| serviceName = QName.valueOf(resolvePropertyPlaceholders(serviceNameString)); |
| } |
| //if not specify the service name and if the wsdlUrl is available, |
| //parse the wsdl to see if only one service in it, if so set the only service |
| //from wsdl to avoid ambiguity |
| if (serviceName == null && getWsdlURL() != null) { |
| // use wsdl manager to parse wsdl or get cached |
| // definition |
| try { |
| Definition definition = getBus().getExtension(WSDLManager.class) |
| .getDefinition(getWsdlURL()); |
| if (definition.getServices().size() == 1) { |
| serviceName = (QName) definition.getServices().keySet() |
| .iterator().next(); |
| |
| } |
| } catch (WSDLException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| return serviceName; |
| } |
| |
| public QName getPortName() { |
| if (portName == null && portNameString != null) { |
| portName = QName.valueOf(resolvePropertyPlaceholders(portNameString)); |
| } |
| return portName; |
| } |
| |
| /** |
| * The endpoint name this service is implementing, it maps to the wsdl:port@name. In the format of ns:PORT_NAME where ns is a namespace prefix valid at this scope. |
| */ |
| public void setPortName(QName port) { |
| portName = port; |
| } |
| |
| /** |
| * The endpoint name this service is implementing, it maps to the wsdl:port@name. In the format of ns:PORT_NAME where ns is a namespace prefix valid at this scope. |
| */ |
| public void setPortNameString(String portNameString) { |
| this.portNameString = portNameString; |
| } |
| |
| public void setPortName(String portName) { |
| portNameString = portName; |
| } |
| |
| /** |
| * The port name this service is implementing, it maps to the wsdl:port@name. |
| */ |
| public void setEndpointNameString(String port) { |
| portNameString = port; |
| } |
| |
| /** |
| * The port name this service is implementing, it maps to the wsdl:port@name. |
| */ |
| public void setEndpointName(QName port) { |
| portName = port; |
| } |
| |
| public String getDefaultOperationName() { |
| return resolvePropertyPlaceholders(defaultOperationName); |
| } |
| |
| /** |
| * This option will set the default operationName that will be used by the CxfProducer which invokes the remote service. |
| */ |
| public void setDefaultOperationName(String name) { |
| defaultOperationName = name; |
| } |
| |
| public String getDefaultOperationNamespace() { |
| return resolvePropertyPlaceholders(defaultOperationNamespace); |
| } |
| |
| /** |
| * This option will set the default operationNamespace that will be used by the CxfProducer which invokes the remote service. |
| */ |
| public void setDefaultOperationNamespace(String namespace) { |
| defaultOperationNamespace = namespace; |
| } |
| |
| public boolean isWrapped() { |
| return wrapped; |
| } |
| |
| /** |
| * Which kind of operation that CXF endpoint producer will invoke |
| */ |
| public void setWrapped(boolean wrapped) { |
| this.wrapped = wrapped; |
| } |
| |
| public Boolean getWrappedStyle() { |
| return wrappedStyle; |
| } |
| |
| /** |
| * The WSDL style that describes how parameters are represented in the SOAP body. |
| * If the value is false, CXF will chose the document-literal unwrapped style, |
| * If the value is true, CXF will chose the document-literal wrapped style |
| */ |
| public void setWrappedStyle(Boolean wrapped) { |
| wrappedStyle = wrapped; |
| } |
| |
| /** |
| * This option controls whether the CXF component, when running in PAYLOAD mode, will DOM parse the incoming messages |
| * into DOM Elements or keep the payload as a javax.xml.transform.Source object that would allow streaming in some cases. |
| */ |
| public void setAllowStreaming(Boolean allowStreaming) { |
| this.allowStreaming = allowStreaming; |
| } |
| |
| public Boolean getAllowStreaming() { |
| return allowStreaming; |
| } |
| |
| /** |
| * To use a custom CxfBinding to control the binding between Camel Message and CXF Message. |
| */ |
| public void setCxfBinding(CxfBinding cxfBinding) { |
| this.cxfBinding = cxfBinding; |
| } |
| |
| public CxfBinding getCxfBinding() { |
| return cxfBinding; |
| } |
| |
| /** |
| * To use a custom HeaderFilterStrategy to filter header to and from Camel message. |
| */ |
| public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { |
| this.headerFilterStrategy = headerFilterStrategy; |
| if (cxfBinding instanceof HeaderFilterStrategyAware) { |
| ((HeaderFilterStrategyAware) cxfBinding).setHeaderFilterStrategy(headerFilterStrategy); |
| } |
| } |
| |
| public HeaderFilterStrategy getHeaderFilterStrategy() { |
| return headerFilterStrategy; |
| } |
| |
| /** |
| * To use a custom configured CXF Bus. |
| */ |
| public void setBus(Bus bus) { |
| this.bus = bus; |
| this.createBus = false; |
| } |
| |
| public Bus getBus() { |
| if (bus == null) { |
| bus = CxfEndpointUtils.createBus(getCamelContext()); |
| this.createBus = true; |
| LOG.debug("Using DefaultBus {}", bus); |
| } |
| |
| if (!getBusHasBeenCalled.getAndSet(true) && defaultBus) { |
| BusFactory.setDefaultBus(bus); |
| LOG.debug("Set bus {} as thread default bus", bus); |
| } |
| return bus; |
| } |
| |
| /** |
| * Will set the default bus when CXF endpoint create a bus by itself |
| */ |
| public void setDefaultBus(boolean defaultBus) { |
| this.defaultBus = defaultBus; |
| } |
| |
| public boolean isDefaultBus() { |
| return defaultBus; |
| } |
| |
| /** |
| * This option enables CXF Logging Feature which writes inbound and outbound SOAP messages to log. |
| */ |
| public void setLoggingFeatureEnabled(boolean loggingFeatureEnabled) { |
| this.loggingFeatureEnabled = loggingFeatureEnabled; |
| } |
| |
| public boolean isLoggingFeatureEnabled() { |
| return loggingFeatureEnabled; |
| } |
| |
| public int getLoggingSizeLimit() { |
| return loggingSizeLimit; |
| } |
| |
| /** |
| * To limit the total size of number of bytes the logger will output when logging feature has been enabled and -1 for no limit. |
| */ |
| public void setLoggingSizeLimit(int loggingSizeLimit) { |
| if (loggingSizeLimit < -1) { |
| throw new IllegalArgumentException("LoggingSizeLimit must be greater or equal to -1."); |
| } |
| this.loggingSizeLimit = loggingSizeLimit; |
| } |
| |
| public boolean isSkipPayloadMessagePartCheck() { |
| return skipPayloadMessagePartCheck; |
| } |
| |
| /** |
| * Sets whether SOAP message validation should be disabled. |
| */ |
| public void setSkipPayloadMessagePartCheck(boolean skipPayloadMessagePartCheck) { |
| this.skipPayloadMessagePartCheck = skipPayloadMessagePartCheck; |
| } |
| |
| public Map<String, Object> getProperties() { |
| return properties; |
| } |
| |
| public void setCamelContext(CamelContext c) { |
| super.setCamelContext(c); |
| if (this.properties != null) { |
| try { |
| EndpointHelper.setReferenceProperties(getCamelContext(), |
| this, |
| this.properties); |
| EndpointHelper.setProperties(getCamelContext(), |
| this, |
| this.properties); |
| } catch (Throwable e) { |
| // TODO: Why dont't we rethrown this exception |
| LOG.warn("Error setting CamelContext. This exception will be ignored.", e); |
| } |
| } |
| } |
| |
| /** |
| * To set additional CXF options using the key/value pairs from the Map. |
| * For example to turn on stacktraces in SOAP faults, <tt>properties.faultStackTraceEnabled=true</tt> |
| */ |
| public void setProperties(Map<String, Object> properties) { |
| if (this.properties == null) { |
| this.properties = properties; |
| } else { |
| this.properties.putAll(properties); |
| } |
| if (getCamelContext() != null && this.properties != null) { |
| try { |
| EndpointHelper.setReferenceProperties(getCamelContext(), |
| this, |
| this.properties); |
| EndpointHelper.setProperties(getCamelContext(), |
| this, |
| this.properties); |
| } catch (Throwable e) { |
| // TODO: Why dont't we rethrown this exception |
| LOG.warn("Error setting properties. This exception will be ignored.", e); |
| } |
| } |
| } |
| |
| @Override |
| protected void doStart() throws Exception { |
| if (headerFilterStrategy == null) { |
| headerFilterStrategy = new CxfHeaderFilterStrategy(); |
| } |
| if (cxfBinding == null) { |
| cxfBinding = new DefaultCxfBinding(); |
| } |
| if (cxfBinding instanceof HeaderFilterStrategyAware) { |
| ((HeaderFilterStrategyAware) cxfBinding).setHeaderFilterStrategy(getHeaderFilterStrategy()); |
| } |
| } |
| |
| @Override |
| protected void doStop() throws Exception { |
| // we should consider to shutdown the bus if the bus is created by cxfEndpoint |
| if (createBus && bus != null) { |
| LOG.info("shutdown the bus ... " + bus); |
| getBus().shutdown(false); |
| // clean up the bus to create a new one if the endpoint is started again |
| bus = null; |
| } |
| } |
| |
| /** |
| * The service publish address. |
| */ |
| public void setAddress(String address) { |
| super.setEndpointUri(UnsafeUriCharactersEncoder.encodeHttpURI(address)); |
| this.address = address; |
| } |
| |
| public String getAddress() { |
| return resolvePropertyPlaceholders(address); |
| } |
| |
| /** |
| * To enable MTOM (attachments). This requires to use POJO or PAYLOAD data format mode. |
| */ |
| public void setMtomEnabled(boolean mtomEnabled) { |
| this.mtomEnabled = mtomEnabled; |
| } |
| |
| public boolean isMtomEnabled() { |
| return mtomEnabled; |
| } |
| |
| public String getPassword() { |
| return password; |
| } |
| |
| /** |
| * This option is used to set the basic authentication information of password for the CXF client. |
| */ |
| public void setPassword(String password) { |
| this.password = password; |
| } |
| |
| public String getUsername() { |
| return username; |
| } |
| |
| /** |
| * This option is used to set the basic authentication information of username for the CXF client. |
| */ |
| public void setUsername(String username) { |
| this.username = username; |
| } |
| |
| /** |
| * We need to override the {@link ClientImpl#setParameters} method |
| * to insert parameters into CXF Message for {@link DataFormat#PAYLOAD} mode. |
| */ |
| class CamelCxfClientImpl extends ClientImpl { |
| |
| public CamelCxfClientImpl(Bus bus, Endpoint ep) { |
| super(bus, ep); |
| } |
| |
| public Bus getBus() { |
| return bus; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| protected void setParameters(Object[] params, Message message) { |
| |
| Object attachments = message.get(CxfConstants.CAMEL_CXF_ATTACHMENTS); |
| if (attachments != null) { |
| message.setAttachments((Collection<Attachment>) attachments); |
| message.remove(CxfConstants.CAMEL_CXF_ATTACHMENTS); |
| } |
| |
| // Don't try to reset the parameters if the parameter is not CxfPayload instance |
| // as the setParameter will be called more than once when using the fail over feature |
| if (DataFormat.PAYLOAD == message.get(DataFormat.class) && params[0] instanceof CxfPayload) { |
| |
| CxfPayload<?> payload = (CxfPayload<?>) params[0]; |
| List<Source> elements = payload.getBodySources(); |
| |
| BindingOperationInfo boi = message.get(BindingOperationInfo.class); |
| MessageContentsList content = new MessageContentsList(); |
| int i = 0; |
| |
| for (MessagePartInfo partInfo : boi.getInput().getMessageParts()) { |
| if (elements.size() > i) { |
| if (isSkipPayloadMessagePartCheck()) { |
| content.put(partInfo, elements.get(i++)); |
| } else { |
| String name = findName(elements, i); |
| if (partInfo.getConcreteName().getLocalPart().equals(name)) { |
| content.put(partInfo, elements.get(i++)); |
| } |
| } |
| } |
| } |
| |
| if (elements != null && content.size() < elements.size()) { |
| throw new IllegalArgumentException("The PayLoad elements cannot fit with the message parts of the BindingOperation. Please check the BindingOperation and PayLoadMessage."); |
| } |
| |
| message.setContent(List.class, content); |
| // merge header list from request context with header list from CXF payload |
| List<Object> headerListOfRequestContxt = (List<Object>)message.get(Header.HEADER_LIST); |
| List<Object> headerListOfPayload = CastUtils.cast(payload.getHeaders()); |
| if (headerListOfRequestContxt == headerListOfPayload) { |
| // == is correct, we want to compare the object instances |
| // nothing to do, this can happen when the CXF payload is already created in the from-cxf-endpoint and then forwarded to a to-cxf-endpoint |
| } else { |
| if (headerListOfRequestContxt == null) { |
| message.put(Header.HEADER_LIST, payload.getHeaders()); |
| } else { |
| headerListOfRequestContxt.addAll(headerListOfPayload); |
| } |
| } |
| } else { |
| super.setParameters(params, message); |
| } |
| |
| message.remove(DataFormat.class); |
| } |
| |
| private String findName(List<Source> sources, int i) { |
| Source source = sources.get(i); |
| XMLStreamReader r = null; |
| if (source instanceof DOMSource) { |
| Node nd = ((DOMSource)source).getNode(); |
| if (nd instanceof Document) { |
| nd = ((Document)nd).getDocumentElement(); |
| } |
| return ((Element)nd).getLocalName(); |
| } else if (source instanceof StaxSource) { |
| StaxSource s = (StaxSource)source; |
| r = s.getXMLStreamReader(); |
| } else if (source instanceof StAXSource) { |
| StAXSource s = (StAXSource)source; |
| r = s.getXMLStreamReader(); |
| } else if (source instanceof StreamSource || source instanceof SAXSource) { |
| //flip to stax so we can get the name |
| r = StaxUtils.createXMLStreamReader(source); |
| StaxSource src2 = new StaxSource(r); |
| sources.set(i, src2); |
| } |
| if (r != null) { |
| try { |
| if (r.getEventType() == XMLStreamConstants.START_DOCUMENT) { |
| r.next(); |
| } |
| if (r.getEventType() != XMLStreamConstants.START_ELEMENT) { |
| r.nextTag(); |
| } |
| } catch (XMLStreamException e) { |
| //ignore |
| LOG.warn("Error finding the start element.", e); |
| return null; |
| } |
| return r.getLocalName(); |
| } |
| return null; |
| } |
| } |
| |
| |
| public List<Interceptor<? extends Message>> getOutFaultInterceptors() { |
| return outFault; |
| } |
| |
| public List<Interceptor<? extends Message>> getInFaultInterceptors() { |
| return inFault; |
| } |
| |
| public List<Interceptor<? extends Message>> getInInterceptors() { |
| return in; |
| } |
| |
| public List<Interceptor<? extends Message>> getOutInterceptors() { |
| return out; |
| } |
| |
| public void setInInterceptors(List<Interceptor<? extends Message>> interceptors) { |
| in = interceptors; |
| } |
| |
| public void setInFaultInterceptors(List<Interceptor<? extends Message>> interceptors) { |
| inFault = interceptors; |
| } |
| |
| public void setOutInterceptors(List<Interceptor<? extends Message>> interceptors) { |
| out = interceptors; |
| } |
| |
| public void setOutFaultInterceptors(List<Interceptor<? extends Message>> interceptors) { |
| outFault = interceptors; |
| } |
| |
| public void setFeatures(List<Feature> f) { |
| features = f; |
| } |
| |
| public List<Feature> getFeatures() { |
| return features; |
| } |
| |
| @SuppressWarnings("rawtypes") |
| public void setHandlers(List<Handler> h) { |
| handlers = h; |
| } |
| |
| @SuppressWarnings("rawtypes") |
| public List<Handler> getHandlers() { |
| return handlers; |
| } |
| |
| public void setSchemaLocations(List<String> sc) { |
| schemaLocations = sc; |
| } |
| |
| public List<String> getSchemaLocations() { |
| return schemaLocations; |
| } |
| |
| public String getTransportId() { |
| return resolvePropertyPlaceholders(transportId); |
| } |
| |
| public void setTransportId(String transportId) { |
| this.transportId = transportId; |
| } |
| |
| public String getBindingId() { |
| return resolvePropertyPlaceholders(bindingId); |
| } |
| |
| /** |
| * The bindingId for the service model to use. |
| */ |
| public void setBindingId(String bindingId) { |
| this.bindingId = bindingId; |
| } |
| |
| public BindingConfiguration getBindingConfig() { |
| return bindingConfig; |
| } |
| |
| public boolean isSkipFaultLogging() { |
| return skipFaultLogging; |
| } |
| |
| /** |
| * This option controls whether the PhaseInterceptorChain skips logging the Fault that it catches. |
| */ |
| public void setSkipFaultLogging(boolean skipFaultLogging) { |
| this.skipFaultLogging = skipFaultLogging; |
| } |
| |
| public Boolean getMergeProtocolHeaders() { |
| return mergeProtocolHeaders; |
| } |
| |
| /** |
| * Whether to merge protocol headers. If enabled then propagating headers between Camel and CXF becomes more consistent and similar. For more details see CAMEL-6393. |
| */ |
| public void setMergeProtocolHeaders(boolean mergeProtocolHeaders) { |
| this.mergeProtocolHeaders = mergeProtocolHeaders; |
| } |
| |
| public void setBindingConfig(BindingConfiguration bindingConfig) { |
| this.bindingConfig = bindingConfig; |
| } |
| |
| public DataBinding getDataBinding() { |
| return dataBinding; |
| } |
| |
| public void setDataBinding(DataBinding dataBinding) { |
| this.dataBinding = dataBinding; |
| } |
| |
| public Object getServiceFactoryBean() { |
| return serviceFactoryBean; |
| } |
| |
| public void setServiceFactoryBean(Object serviceFactoryBean) { |
| this.serviceFactoryBean = serviceFactoryBean; |
| } |
| |
| public void setServiceFactory(Object serviceFactoryBean) { |
| // needed a setter with this name as the cxf namespace parser expects this name |
| this.serviceFactoryBean = serviceFactoryBean; |
| } |
| |
| public CxfEndpointConfigurer getCxfEndpointConfigurer() { |
| return cxfEndpointConfigurer; |
| } |
| |
| /** |
| * This option could apply the implementation of org.apache.camel.component.cxf.CxfEndpointConfigurer which supports to configure the CXF endpoint |
| * in programmatic way. User can configure the CXF server and client by implementing configure{Server|Client} method of CxfEndpointConfigurer. |
| */ |
| public void setCxfEndpointConfigurer(CxfEndpointConfigurer configurer) { |
| this.cxfEndpointConfigurer = configurer; |
| } |
| |
| public long getContinuationTimeout() { |
| return continuationTimeout; |
| } |
| |
| /** |
| * This option is used to set the CXF continuation timeout which could be used in CxfConsumer by default when the CXF server is using Jetty or Servlet transport. |
| */ |
| public void setContinuationTimeout(long continuationTimeout) { |
| this.continuationTimeout = continuationTimeout; |
| } |
| |
| } |