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