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