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.aegis.databinding.AegisDatabinding;
039    import org.apache.cxf.endpoint.Server;
040    import org.apache.cxf.frontend.ServerFactoryBean;
041    import org.apache.cxf.interceptor.Fault;
042    import org.apache.cxf.interceptor.Interceptor;
043    import org.apache.cxf.interceptor.InterceptorProvider;
044    import org.apache.cxf.jaxws.EndpointImpl;
045    import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
046    import org.apache.cxf.jaxws.ServiceImpl;
047    import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
048    import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
049    import org.apache.cxf.service.model.EndpointInfo;
050    import org.apache.cxf.transport.ConduitInitiatorManager;
051    import org.apache.cxf.transport.jbi.JBIDestination;
052    import org.apache.cxf.transport.jbi.JBIDispatcherUtil;
053    import org.apache.cxf.transport.jbi.JBITransportFactory;
054    import org.apache.cxf.wsdl11.ServiceWSDLBuilder;
055    import org.apache.servicemix.common.endpoints.ProviderEndpoint;
056    import org.apache.servicemix.cxfse.interceptors.AttachmentInInterceptor;
057    import org.apache.servicemix.cxfse.interceptors.AttachmentOutInterceptor;
058    import org.apache.servicemix.cxfse.support.ReflectionUtils;
059    import org.apache.servicemix.id.IdGenerator;
060    import org.springframework.util.ReflectionUtils.FieldCallback;
061    
062    /**
063     * 
064     * @author gnodet
065     * @org.apache.xbean.XBean element="endpoint" description="an endpoint using
066     *                         CXF's JAX-WS frontend"
067     */
068    public class CxfSeEndpoint extends ProviderEndpoint implements
069                    InterceptorProvider {
070    
071            private static final IdGenerator ID_GENERATOR = new IdGenerator();
072    
073            private Object pojo;
074    
075            private EndpointImpl endpoint;
076    
077            private String address;
078    
079            private ServerFactoryBean sf;
080    
081            private Server server;
082    
083            private List<Interceptor> in = new CopyOnWriteArrayList<Interceptor>();
084    
085            private List<Interceptor> out = new CopyOnWriteArrayList<Interceptor>();
086    
087            private List<Interceptor> outFault = new CopyOnWriteArrayList<Interceptor>();
088    
089            private List<Interceptor> inFault = new CopyOnWriteArrayList<Interceptor>();
090    
091            private Map properties;
092    
093            private boolean mtomEnabled;
094    
095            private boolean useJBIWrapper = true;
096    
097            private boolean useSOAPEnvelope = true;
098    
099            private boolean useAegis;
100    
101            /**
102             * Returns the object implementing the endpoint's functionality. It is
103             * returned as a generic Java <code>Object</code> that can be cast to the
104             * proper type.
105             * 
106             * @return the pojo
107             */
108            public Object getPojo() {
109                    return pojo;
110            }
111    
112            /**
113             * Specifies the object implementing the endpoint's functionality. This
114             * object should use the JAX-WS annotations.
115             * 
116             * @param pojo
117             *            a JAX-WS annotated object
118             * @org.apache.xbean.Property description="a bean configuring the JAX-WS
119             *                            annotated implementation for the endpoint"
120             */
121            public void setPojo(Object pojo) {
122                    this.pojo = pojo;
123            }
124    
125            /**
126             * Returns the list of interceptors used to process fault messages being
127             * sent back to the consumer.
128             * 
129             * @return a list of <code>Interceptor</code> objects
130             */
131            public List<Interceptor> getOutFaultInterceptors() {
132                    return outFault;
133            }
134    
135            /**
136             * Returns the list of interceptors used to process fault messages being
137             * recieved by the endpoint.
138             * 
139             * @return a list of <code>Interceptor</code> objects
140             */
141            public List<Interceptor> getInFaultInterceptors() {
142                    return inFault;
143            }
144    
145            /**
146             * Returns the list of interceptors used to process messages being recieved
147             * by the endpoint.
148             * 
149             * @return a list of <code>Interceptor</code> objects
150             */
151            public List<Interceptor> getInInterceptors() {
152                    return in;
153            }
154    
155            /**
156             * Returns the list of interceptors used to process responses being sent
157             * back to the consumer.
158             * 
159             * @return a list of <code>Interceptor</code> objects
160             */
161            public List<Interceptor> getOutInterceptors() {
162                    return out;
163            }
164    
165            /**
166             * Specifies a list of interceptors used to process requests recieved by the
167             * endpoint.
168             * 
169             * @param interceptors
170             *            a list of <code>Interceptor</code> objects
171             * @org.apache.xbean.Property description="a list of beans configuring
172             *                            interceptors that process incoming requests"
173             */
174            public void setInInterceptors(List<Interceptor> interceptors) {
175                    in = interceptors;
176            }
177    
178            /**
179             * Specifies a list of interceptors used to process faults recieved by the
180             * endpoint.
181             * 
182             * @param interceptors
183             *            a list of <code>Interceptor</code> objects
184             * @org.apache.xbean.Property description="a list of beans configuring
185             *                            interceptors that process incoming faults"
186             */
187            public void setInFaultInterceptors(List<Interceptor> interceptors) {
188                    inFault = interceptors;
189            }
190    
191            /**
192             * Specifies a list of interceptors used to process responses sent by the
193             * endpoint.
194             * 
195             * @param interceptors
196             *            a list of <code>Interceptor</code> objects
197             * @org.apache.xbean.Property description="a list of beans configuring
198             *                            interceptors that process response messages"
199             */
200            public void setOutInterceptors(List<Interceptor> interceptors) {
201                    out = interceptors;
202            }
203    
204            /**
205             * Specifies a list of interceptors used to process faults sent by the
206             * endpoint.
207             * 
208             * @param interceptors
209             *            a list of <code>Interceptor</code> objects
210             * @org.apache.xbean.Property description="a list of beans configuring
211             *                            interceptors that process fault messages being
212             *                            returned to the consumer"
213             */
214            public void setOutFaultInterceptors(List<Interceptor> interceptors) {
215                    outFault = interceptors;
216            }
217    
218            public Map getProperties() {
219                    return properties;
220            }
221    
222            public void setProperties(Map properties) {
223                    this.properties = properties;
224            }
225    
226            /*
227             * (non-Javadoc)
228             * 
229             * @see org.apache.servicemix.common.Endpoint#validate()
230             */
231            @Override
232            public void validate() throws DeploymentException {
233                    if (getPojo() == null) {
234                            throw new DeploymentException("pojo must be set");
235                    }
236                    if (isUseAegis()) {
237                            sf = new ServerFactoryBean();
238                            sf.setServiceBean(getPojo());
239                            sf.setAddress("jbi://" + ID_GENERATOR.generateSanitizedId());
240                            sf.getServiceFactory().setDataBinding(new AegisDatabinding());
241                            sf.getServiceFactory().setPopulateFromClass(true);
242                            sf.setStart(false);
243                            if (isUseJBIWrapper()) {
244                                    sf
245                                                    .setBindingId(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
246                            }
247                            server = sf.create();
248                            server.getEndpoint().getInInterceptors()
249                                            .addAll(getInInterceptors());
250                            server.getEndpoint().getInFaultInterceptors().addAll(
251                                            getInFaultInterceptors());
252                            server.getEndpoint().getOutInterceptors().addAll(
253                                            getOutInterceptors());
254                            server.getEndpoint().getOutFaultInterceptors().addAll(
255                                            getOutFaultInterceptors());
256                            if (isMtomEnabled()) {
257                                    server.getEndpoint().getInInterceptors().add(
258                                                    new AttachmentInInterceptor());
259                                    server.getEndpoint().getOutInterceptors().add(
260                                                    new AttachmentOutInterceptor());
261                            }
262                            if (sf.getServiceFactory().getServiceQName() != null) {
263                                    setService(sf.getServiceFactory().getServiceQName());
264                            }
265                            if (sf.getServiceFactory().getEndpointInfo().getName() != null) {
266                                    setEndpoint(sf.getServiceFactory().getEndpointInfo().getName()
267                                                    .getLocalPart());
268                            }
269                            if (sf.getServiceFactory().getInterfaceName() != null) {
270                                    setInterfaceName(sf.getServiceFactory().getInterfaceName());
271                            }
272                    } else {
273                            JaxWsServiceFactoryBean serviceFactory = new JaxWsServiceFactoryBean();
274                            serviceFactory.setPopulateFromClass(true);
275                            endpoint = new EndpointImpl(getBus(), getPojo(),
276                                            new JaxWsServerFactoryBean(serviceFactory));
277                            if (isUseJBIWrapper()) {
278                                    endpoint
279                                                    .setBindingUri(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
280                            }
281                            endpoint.setInInterceptors(getInInterceptors());
282                            endpoint.setInFaultInterceptors(getInFaultInterceptors());
283                            endpoint.setOutInterceptors(getOutInterceptors());
284                            endpoint.setOutFaultInterceptors(getOutFaultInterceptors());
285                            if (isMtomEnabled()) {
286                                    endpoint.getInInterceptors().add(new AttachmentInInterceptor());
287                                    endpoint.getOutInterceptors().add(
288                                                    new AttachmentOutInterceptor());
289                            }
290                            JaxWsImplementorInfo implInfo = new JaxWsImplementorInfo(getPojo()
291                                            .getClass());
292                            setService(implInfo.getServiceName());
293    
294                            setInterfaceName(implInfo.getInterfaceName());
295                            setEndpoint(implInfo.getEndpointName().getLocalPart());
296    
297                    }
298                    super.validate();
299    
300            }
301    
302            private void removeInterceptor(List<Interceptor> interceptors,
303                            String whichInterceptor) {
304                    for (Interceptor interceptor : interceptors) {
305                            if (interceptor.getClass().getName().endsWith(whichInterceptor)) {
306                                    interceptors.remove(interceptor);
307                            }
308                    }
309            }
310    
311            /*
312             * (non-Javadoc)
313             * 
314             * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(javax.jbi.messaging.MessageExchange)
315             */
316            @Override
317            public void process(MessageExchange exchange) throws Exception {
318                    JBIContext.setMessageExchange(exchange);
319                    try {
320                            QName opeName = exchange.getOperation();
321                            EndpointInfo ei = server.getEndpoint().getEndpointInfo();
322    
323                            if (opeName == null) {
324                                    // if interface only have one operation, may not specify the
325                                    // opeName
326                                    // in MessageExchange
327                                    if (ei.getBinding().getOperations().size() == 1) {
328                                            opeName = ei.getBinding().getOperations().iterator().next()
329                                                            .getName();
330                                            exchange.setOperation(opeName);
331                                    } else {
332                                            throw new Fault(new Exception(
333                                                            "Operation not bound on this MessageExchange"));
334    
335                                    }
336                            }
337                            JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
338                                            .getExtension(ConduitInitiatorManager.class)
339                                            .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
340    
341                            QName serviceName = exchange.getService();
342                            if (serviceName == null) {
343                                    serviceName = getService();
344                                    exchange.setService(serviceName);
345                            }
346                            QName interfaceName = exchange.getInterfaceName();
347                            if (interfaceName == null) {
348                                    interfaceName = getInterfaceName();
349                                    exchange.setInterfaceName(interfaceName);
350                            }
351                            JBIDestination jbiDestination = jbiTransportFactory
352                                            .getDestination(serviceName.toString()
353                                                            + interfaceName.toString());
354                            DeliveryChannel dc = getContext().getDeliveryChannel();
355                            jbiTransportFactory.setDeliveryChannel(dc);
356    
357                            jbiDestination.setDeliveryChannel(dc);
358                            if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
359                                    jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
360                            }
361                    } finally {
362                            JBIContext.setMessageExchange(null);
363                    }
364            }
365    
366            /*
367             * (non-Javadoc)
368             * 
369             * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
370             */
371            @Override
372            public void start() throws Exception {
373                    super.start();
374                    address = "jbi://" + ID_GENERATOR.generateSanitizedId();
375                    try {
376                            if (isUseAegis()) {
377                                    server.start();
378                            } else {
379                                    endpoint.publish(address);
380                                    server = endpoint.getServer();
381                            }
382                    } catch (Exception e) {
383                            e.printStackTrace();
384                    }
385    
386                    setService(server.getEndpoint().getService().getName());
387                    setEndpoint(server.getEndpoint().getEndpointInfo().getName()
388                                    .getLocalPart());
389                    if (!isUseJBIWrapper() && !isUseSOAPEnvelope()) {
390                            removeInterceptor(server.getEndpoint().getBinding()
391                                            .getInInterceptors(), "ReadHeadersInterceptor");
392                            removeInterceptor(server.getEndpoint().getBinding()
393                                            .getInFaultInterceptors(), "ReadHeadersInterceptor");
394                            removeInterceptor(server.getEndpoint().getBinding()
395                                            .getOutInterceptors(), "SoapOutInterceptor");
396                            removeInterceptor(server.getEndpoint().getBinding()
397                                            .getOutFaultInterceptors(), "SoapOutInterceptor");
398                            removeInterceptor(server.getEndpoint().getBinding()
399                                            .getOutInterceptors(), "StaxOutInterceptor");
400                    }
401    
402                    try {
403                            definition = new ServiceWSDLBuilder(getBus(), server.getEndpoint()
404                                            .getService().getServiceInfos().iterator().next()).build();
405                            description = WSDLFactory.newInstance().newWSDLWriter()
406                                            .getDocument(definition);
407                    } catch (WSDLException e) {
408                            throw new DeploymentException(e);
409                    }
410                    ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
411                            public void doWith(Field field) throws IllegalArgumentException,
412                                            IllegalAccessException {
413                                    if (field.getAnnotation(WebServiceRef.class) != null) {
414                                            ServiceImpl s = new ServiceImpl(getBus(), null, null, field
415                                                            .getType());
416                                            s.addPort(new QName("port"),
417                                                            JBITransportFactory.TRANSPORT_ID, "jbi://"
418                                                                            + ID_GENERATOR.generateSanitizedId());
419                                            Object o = s.getPort(new QName("port"), field.getType());
420                                            field.setAccessible(true);
421                                            field.set(getPojo(), o);
422                                    }
423                            }
424                    });
425                    ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
426                    injectPojo();
427            }
428    
429            /*
430             * (non-Javadoc)
431             * 
432             * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
433             */
434            @Override
435        public void stop() throws Exception {
436            if (isUseAegis()) {
437                server.stop();
438            } else {
439                endpoint.stop();
440            }
441            ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
442            JBIDispatcherUtil.clean();
443            JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
444                    .getExtension(ConduitInitiatorManager.class)
445                    .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
446            jbiTransportFactory.setDeliveryChannel(null);
447            jbiTransportFactory.removeDestination(getService().toString()
448                    + getInterfaceName().toString());
449            super.stop();
450        }
451    
452            protected Bus getBus() {
453                    return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
454            }
455    
456            @PostConstruct
457            protected void injectPojo() {
458                    try {
459                            ComponentContext context = getContext();
460                            Method mth = pojo.getClass().getMethod("setContext",
461                                            new Class[] { ComponentContext.class });
462                            if (mth != null) {
463                                    mth.invoke(pojo, new Object[] { context });
464                            }
465                    } catch (Exception e) {
466                            logger
467                                            .debug("Unable to inject ComponentContext: "
468                                                            + e.getMessage());
469                    }
470    
471            }
472    
473            /**
474             * Specifies if the endpoint can process messages with binary data.
475             * 
476             * @param mtomEnabled
477             *            a <code>boolean</code>
478             * @org.apache.xbean.Property description="Specifies if the service can
479             *                            consume MTOM formatted binary data. The
480             *                            default is <code>false</code>."
481             */
482            public void setMtomEnabled(boolean mtomEnabled) {
483                    this.mtomEnabled = mtomEnabled;
484            }
485    
486            public boolean isMtomEnabled() {
487                    return mtomEnabled;
488            }
489    
490            /**
491             * Specifies if the endpoint expects messages that are encased in the JBI
492             * wrapper used for SOAP messages. Ignore the value of useSOAPEnvelope if
493             * useJBIWrapper is true
494             * 
495             * @org.apache.xbean.Property description="Specifies if the endpoint expects
496             *                            to receive the JBI wrapper in the message
497             *                            received from the NMR. The default is
498             *                            <code>true</code>. Ignore the value of
499             *                            useSOAPEnvelope if useJBIWrapper is true"
500             */
501            public void setUseJBIWrapper(boolean useJBIWrapper) {
502                    this.useJBIWrapper = useJBIWrapper;
503            }
504    
505            public boolean isUseJBIWrapper() {
506                    return useJBIWrapper;
507            }
508    
509            /**
510             * Specifies if the endpoint expects soap messages when useJBIWrapper is
511             * false, if useJBIWrapper is true then ignore useSOAPEnvelope
512             * 
513             * @org.apache.xbean.Property description="Specifies if the endpoint expects
514             *                            soap messages when useJBIWrapper is false, if
515             *                            useJBIWrapper is true then ignore
516             *                            useSOAPEnvelope. The default is
517             *                            <code>true</code>.
518             */
519            public void setUseSOAPEnvelope(boolean useSOAPEnvelope) {
520                    this.useSOAPEnvelope = useSOAPEnvelope;
521            }
522    
523            public boolean isUseSOAPEnvelope() {
524                    return useSOAPEnvelope;
525            }
526    
527            /**
528             * Specifies if the endpoint use aegis databinding to marshell/unmarshell
529             * message
530             * 
531             * @org.apache.xbean.Property description="Specifies if the endpoint use
532             *                            aegis databinding to marshell/unmarshell
533             *                            message. The default is <code>false</code>.
534             */
535            public void setUseAegis(boolean useAegis) {
536                    this.useAegis = useAegis;
537            }
538    
539            public boolean isUseAegis() {
540                    return useAegis;
541            }
542    
543    }