001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.cxfse;
018
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Method;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.concurrent.CopyOnWriteArrayList;
024
025 import javax.annotation.PostConstruct;
026 import javax.annotation.PreDestroy;
027 import javax.jbi.component.ComponentContext;
028 import javax.jbi.management.DeploymentException;
029 import javax.jbi.messaging.DeliveryChannel;
030 import javax.jbi.messaging.ExchangeStatus;
031 import javax.jbi.messaging.MessageExchange;
032 import javax.wsdl.WSDLException;
033 import javax.wsdl.factory.WSDLFactory;
034 import javax.xml.namespace.QName;
035 import javax.xml.ws.WebServiceRef;
036
037 import org.apache.cxf.Bus;
038 import org.apache.cxf.interceptor.Fault;
039 import org.apache.cxf.interceptor.Interceptor;
040 import org.apache.cxf.interceptor.InterceptorProvider;
041 import org.apache.cxf.jaxws.EndpointImpl;
042 import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
043 import org.apache.cxf.jaxws.ServiceImpl;
044 import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
045 import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
046 import org.apache.cxf.service.model.EndpointInfo;
047 import org.apache.cxf.transport.ConduitInitiatorManager;
048 import org.apache.cxf.transport.jbi.JBIDestination;
049 import org.apache.cxf.transport.jbi.JBIDispatcherUtil;
050 import org.apache.cxf.transport.jbi.JBITransportFactory;
051 import org.apache.cxf.wsdl11.ServiceWSDLBuilder;
052 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
053 import org.apache.servicemix.cxfse.interceptors.AttachmentInInterceptor;
054 import org.apache.servicemix.cxfse.interceptors.AttachmentOutInterceptor;
055 import org.apache.servicemix.cxfse.support.ReflectionUtils;
056 import org.apache.servicemix.id.IdGenerator;
057 import org.springframework.util.ReflectionUtils.FieldCallback;
058
059 /**
060 *
061 * @author gnodet
062 * @org.apache.xbean.XBean element="endpoint" description="an endpoint using CXF's JAX-WS frontend"
063 */
064 public class CxfSeEndpoint extends ProviderEndpoint implements
065 InterceptorProvider {
066
067 private static final IdGenerator ID_GENERATOR = new IdGenerator();
068
069 private Object pojo;
070
071 private EndpointImpl endpoint;
072
073 private String address;
074
075 private List<Interceptor> in = new CopyOnWriteArrayList<Interceptor>();
076
077 private List<Interceptor> out = new CopyOnWriteArrayList<Interceptor>();
078
079 private List<Interceptor> outFault = new CopyOnWriteArrayList<Interceptor>();
080
081 private List<Interceptor> inFault = new CopyOnWriteArrayList<Interceptor>();
082
083 private Map properties;
084
085 private boolean mtomEnabled;
086
087 private boolean useJBIWrapper = true;
088
089
090 /**
091 * Returns the object implementing the endpoint's functionality. It is
092 * returned as a generic Java <code>Object</code> that can be cast to
093 * the proper type.
094 *
095 * @return the pojo
096 */
097 public Object getPojo() {
098 return pojo;
099 }
100
101 /**
102 * Specifies the object implementing the endpoint's functionality. This
103 * object should use the JAX-WS annotations.
104 *
105 * @param pojo a JAX-WS annotated object
106 * @org.apache.xbean.Property description="a bean configuring the JAX-WS annotated implementation for the endpoint"
107 */
108 public void setPojo(Object pojo) {
109 this.pojo = pojo;
110 }
111
112 /**
113 * Returns the list of interceptors used to process fault messages being
114 * sent back to the consumer.
115 *
116 * @return a list of <code>Interceptor</code> objects
117 * */
118 public List<Interceptor> getOutFaultInterceptors() {
119 return outFault;
120 }
121
122 /**
123 * Returns the list of interceptors used to process fault messages being
124 * recieved by the endpoint.
125 *
126 * @return a list of <code>Interceptor</code> objects
127 * */
128 public List<Interceptor> getInFaultInterceptors() {
129 return inFault;
130 }
131
132 /**
133 * Returns the list of interceptors used to process messages being
134 * recieved by the endpoint.
135 *
136 * @return a list of <code>Interceptor</code> objects
137 * */
138 public List<Interceptor> getInInterceptors() {
139 return in;
140 }
141
142 /**
143 * Returns the list of interceptors used to process responses being
144 * sent back to the consumer.
145 *
146 * @return a list of <code>Interceptor</code> objects
147 * */
148 public List<Interceptor> getOutInterceptors() {
149 return out;
150 }
151
152 /**
153 * Specifies a list of interceptors used to process requests recieved
154 * by the endpoint.
155 *
156 * @param interceptors a list of <code>Interceptor</code> objects
157 * @org.apache.xbean.Property description="a list of beans configuring interceptors that process incoming requests"
158 * */
159 public void setInInterceptors(List<Interceptor> interceptors) {
160 in = interceptors;
161 }
162
163 /**
164 * Specifies a list of interceptors used to process faults recieved by
165 * the endpoint.
166 *
167 * @param interceptors a list of <code>Interceptor</code> objects
168 * @org.apache.xbean.Property description="a list of beans configuring interceptors that process incoming faults"
169 * */
170 public void setInFaultInterceptors(List<Interceptor> interceptors) {
171 inFault = interceptors;
172 }
173
174 /**
175 * Specifies a list of interceptors used to process responses sent by
176 * the endpoint.
177 *
178 * @param interceptors a list of <code>Interceptor</code> objects
179 * @org.apache.xbean.Property description="a list of beans configuring interceptors that process response messages"
180 * */
181 public void setOutInterceptors(List<Interceptor> interceptors) {
182 out = interceptors;
183 }
184
185 /**
186 * Specifies a list of interceptors used to process faults sent by
187 * the endpoint.
188 *
189 * @param interceptors a list of <code>Interceptor</code> objects
190 * @org.apache.xbean.Property description="a list of beans configuring interceptors that process fault messages being returned to the consumer"
191 * */
192 public void setOutFaultInterceptors(List<Interceptor> interceptors) {
193 outFault = interceptors;
194 }
195
196 public Map getProperties() {
197 return properties;
198 }
199
200 public void setProperties(Map properties) {
201 this.properties = properties;
202 }
203
204
205 /*
206 * (non-Javadoc)
207 *
208 * @see org.apache.servicemix.common.Endpoint#validate()
209 */
210 @Override
211 public void validate() throws DeploymentException {
212 if (getPojo() == null) {
213 throw new DeploymentException("pojo must be set");
214 }
215 JaxWsServiceFactoryBean serviceFactory = new JaxWsServiceFactoryBean();
216 serviceFactory.setPopulateFromClass(true);
217 endpoint = new EndpointImpl(getBus(), getPojo(),
218 new JaxWsServerFactoryBean(serviceFactory));
219 if (isUseJBIWrapper()) {
220 endpoint.setBindingUri(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
221 }
222 endpoint.setInInterceptors(getInInterceptors());
223 endpoint.setInFaultInterceptors(getInFaultInterceptors());
224 endpoint.setOutInterceptors(getOutInterceptors());
225 endpoint.setOutFaultInterceptors(getOutFaultInterceptors());
226 if (isMtomEnabled()) {
227 endpoint.getInInterceptors().add(new AttachmentInInterceptor());
228 endpoint.getOutInterceptors().add(new AttachmentOutInterceptor());
229 }
230 JaxWsImplementorInfo implInfo = new JaxWsImplementorInfo(getPojo()
231 .getClass());
232 setService(implInfo.getServiceName());
233 setInterfaceName(implInfo.getInterfaceName());
234 setEndpoint(implInfo.getEndpointName().getLocalPart());
235 super.validate();
236 }
237
238 /*
239 * (non-Javadoc)
240 *
241 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(javax.jbi.messaging.MessageExchange)
242 */
243 @Override
244 public void process(MessageExchange exchange) throws Exception {
245 JBIContext.setMessageExchange(exchange);
246 try {
247 QName opeName = exchange.getOperation();
248 EndpointInfo ei = endpoint.getServer().getEndpoint().getEndpointInfo();
249 if (opeName == null) {
250 // if interface only have one operation, may not specify the opeName in MessageExchange
251 if (ei.getBinding().getOperations().size() == 1) {
252 opeName = ei.getBinding().getOperations().iterator().next().getName();
253 exchange.setOperation(opeName);
254 } else {
255 throw new Fault(
256 new Exception("Operation not bound on this MessageExchange"));
257
258 }
259 }
260
261 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
262 .getExtension(ConduitInitiatorManager.class)
263 .getConduitInitiator(JBITransportFactory.TRANSPORT_ID);
264
265 QName serviceName = exchange.getService();
266 if (serviceName == null) {
267 serviceName = getService();
268 exchange.setService(serviceName);
269 }
270 QName interfaceName = exchange.getInterfaceName();
271 if (interfaceName == null) {
272 interfaceName = getInterfaceName();
273 exchange.setInterfaceName(interfaceName);
274 }
275 JBIDestination jbiDestination = jbiTransportFactory
276 .getDestination(serviceName.toString()
277 + interfaceName.toString());
278 DeliveryChannel dc = getContext().getDeliveryChannel();
279 jbiTransportFactory.setDeliveryChannel(dc);
280
281 jbiDestination.setDeliveryChannel(dc);
282 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
283 jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
284 }
285 } finally {
286 JBIContext.setMessageExchange(null);
287 }
288 }
289
290 /*
291 * (non-Javadoc)
292 *
293 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
294 */
295 @Override
296 public void start() throws Exception {
297 super.start();
298 address = "jbi://" + ID_GENERATOR.generateSanitizedId();
299 try {
300 endpoint.publish(address);
301 } catch (Exception e) {
302 e.printStackTrace();
303 }
304
305 setService(endpoint.getServer().getEndpoint().getService().getName());
306 setEndpoint(endpoint.getServer().getEndpoint().getEndpointInfo()
307 .getName().getLocalPart());
308 try {
309 definition = new ServiceWSDLBuilder(getBus(), endpoint.getServer()
310 .getEndpoint().getService().getServiceInfos().iterator()
311 .next()).build();
312 description = WSDLFactory.newInstance().newWSDLWriter().getDocument(definition);
313 } catch (WSDLException e) {
314 throw new DeploymentException(e);
315 }
316 ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
317 public void doWith(Field field) throws IllegalArgumentException,
318 IllegalAccessException {
319 if (field.getAnnotation(WebServiceRef.class) != null) {
320 ServiceImpl s = new ServiceImpl(getBus(), null, null, field
321 .getType());
322 s.addPort(new QName("port"),
323 JBITransportFactory.TRANSPORT_ID, "jbi://"
324 + ID_GENERATOR.generateSanitizedId());
325 Object o = s.getPort(new QName("port"), field.getType());
326 field.setAccessible(true);
327 field.set(getPojo(), o);
328 }
329 }
330 });
331 ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
332 injectPojo();
333 }
334
335
336 /*
337 * (non-Javadoc)
338 *
339 * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
340 */
341 @Override
342 public void stop() throws Exception {
343 endpoint.stop();
344 ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
345 JBIDispatcherUtil.clean();
346 JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
347 .getExtension(ConduitInitiatorManager.class)
348 .getConduitInitiator(JBITransportFactory.TRANSPORT_ID);
349 jbiTransportFactory.setDeliveryChannel(null);
350 jbiTransportFactory.removeDestination(getService().toString()
351 + getInterfaceName().toString());
352 super.stop();
353 }
354
355 protected Bus getBus() {
356 return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
357 }
358
359
360 @PostConstruct
361 protected void injectPojo() {
362 try {
363 ComponentContext context = getContext();
364 Method mth = pojo.getClass().getMethod("setContext", new Class[] {ComponentContext.class });
365 if (mth != null) {
366 mth.invoke(pojo, new Object[] {context});
367 }
368 } catch (Exception e) {
369 logger.debug("Unable to inject ComponentContext: " + e.getMessage());
370 }
371
372 }
373
374 /**
375 * Specifies if the endpoint can process messages with binary data.
376 *
377 * @param mtomEnabled a <code>boolean</code>
378 * @org.apache.xbean.Property description="Specifies if the service can consume MTOM formatted binary data. The default is <code>false</code>."
379 * */
380 public void setMtomEnabled(boolean mtomEnabled) {
381 this.mtomEnabled = mtomEnabled;
382 }
383
384 public boolean isMtomEnabled() {
385 return mtomEnabled;
386 }
387
388 /**
389 * Specifies if the endpoint expects messages that are encased in the
390 * JBI wrapper used for SOAP messages.
391 *
392 * @param mtomEnabled a <code>boolean</code>
393 * @org.apache.xbean.Property description="Specifies if the endpoint expects to receive the JBI wrapper in the message received from the NMR. The default is <code>true</code>."
394 * */
395 public void setUseJBIWrapper(boolean useJBIWrapper) {
396 this.useJBIWrapper = useJBIWrapper;
397 }
398
399 public boolean isUseJBIWrapper() {
400 return useJBIWrapper;
401 }
402 }