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