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 = 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 = 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 = 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 = 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            QName opeName = exchange.getOperation();
324            EndpointInfo ei = server.getEndpoint().getEndpointInfo();
325                   
326            if (opeName == null) {
327                // if interface only have one operation, may not specify the opeName
328                // in MessageExchange
329                if (ei.getBinding().getOperations().size() == 1) {
330                    opeName = ei.getBinding().getOperations().iterator().next()
331                            .getName();
332                    exchange.setOperation(opeName);
333                } else {
334                    throw new Fault(new Exception(
335                            "Operation not bound on this MessageExchange"));
336    
337                }
338            }
339            JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
340                    .getExtension(ConduitInitiatorManager.class)
341                    .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
342    
343            QName serviceName = exchange.getService();
344            if (serviceName == null) {
345                serviceName = getService();
346                exchange.setService(serviceName);
347            }
348            QName interfaceName = exchange.getInterfaceName();
349            if (interfaceName == null) {
350                interfaceName = getInterfaceName();
351                exchange.setInterfaceName(interfaceName);
352            }
353            JBIDestination jbiDestination = jbiTransportFactory
354                    .getDestination(serviceName.toString()
355                            + interfaceName.toString());
356            DeliveryChannel dc = getContext().getDeliveryChannel();
357            jbiTransportFactory.setDeliveryChannel(dc);
358    
359            jbiDestination.setDeliveryChannel(dc);
360            if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
361                jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
362            }
363            if (exchange instanceof InOnly || exchange instanceof RobustInOnly) {
364                exchange.setStatus(ExchangeStatus.DONE);
365                dc.send(exchange);
366            }
367        }
368    
369        /*
370         * (non-Javadoc)
371         * 
372         * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
373         */
374        @Override
375        public void start() throws Exception {
376            super.start();
377            address = "jbi://" + ID_GENERATOR.generateSanitizedId();
378            try {
379                if (isUseAegis()) {
380                    server.start();
381                } else {
382                    endpoint.publish(address);
383                    server = endpoint.getServer();
384                }
385            } catch (Exception e) {
386                e.printStackTrace();
387            }
388    
389            setService(server.getEndpoint().getService().getName());
390            setEndpoint(server.getEndpoint().getEndpointInfo()
391                    .getName().getLocalPart());
392            if (!isUseJBIWrapper() && !isUseSOAPEnvelope()) {
393                removeInterceptor(server.getEndpoint().getBinding()
394                        .getInInterceptors(), "ReadHeadersInterceptor");
395                removeInterceptor(server.getEndpoint().getBinding()
396                        .getInFaultInterceptors(), "ReadHeadersInterceptor");
397                removeInterceptor(server.getEndpoint().getBinding()
398                        .getOutInterceptors(), "SoapOutInterceptor");
399                removeInterceptor(server.getEndpoint().getBinding()
400                        .getOutFaultInterceptors(), "SoapOutInterceptor");
401                removeInterceptor(server.getEndpoint().getBinding()
402                        .getOutInterceptors(), "StaxOutInterceptor");
403            }
404    
405            try {
406                definition = new ServiceWSDLBuilder(getBus(), server
407                        .getEndpoint().getService().getServiceInfos().iterator()
408                        .next()).build();
409                description = WSDLFactory.newInstance().newWSDLWriter()
410                        .getDocument(definition);
411            } catch (WSDLException e) {
412                throw new DeploymentException(e);
413            }
414            ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
415                public void doWith(Field field) throws IllegalArgumentException,
416                        IllegalAccessException {
417                    if (field.getAnnotation(WebServiceRef.class) != null) {
418                        ServiceImpl s = new ServiceImpl(getBus(), null, null, field
419                                .getType());
420                        s.addPort(new QName("port"),
421                                JBITransportFactory.TRANSPORT_ID, "jbi://"
422                                        + ID_GENERATOR.generateSanitizedId());
423                        Object o = s.getPort(new QName("port"), field.getType());
424                        field.setAccessible(true);
425                        field.set(getPojo(), o);
426                    }
427                }
428            });
429            ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
430            injectPojo();
431        }
432    
433        /*
434         * (non-Javadoc)
435         * 
436         * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
437         */
438        @Override
439        public void stop() throws Exception {
440            if (isUseAegis()) {
441                server.stop();
442            } else {
443                endpoint.stop();
444            }
445            ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
446            JBIDispatcherUtil.clean();
447            JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
448                    .getExtension(ConduitInitiatorManager.class)
449                    .getConduitInitiator(CxfSeComponent.JBI_TRANSPORT_ID);
450            jbiTransportFactory.setDeliveryChannel(null);
451            jbiTransportFactory.removeDestination(getService().toString()
452                    + getInterfaceName().toString());
453            super.stop();
454        }
455    
456        protected Bus getBus() {
457            return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
458        }
459    
460        @PostConstruct
461        protected void injectPojo() {
462            try {
463                ComponentContext context = getContext();
464                Method mth = pojo.getClass().getMethod("setContext",
465                        new Class[] {ComponentContext.class});
466                if (mth != null) {
467                    mth.invoke(pojo, new Object[] {context});
468                }
469            } catch (Exception e) {
470                logger
471                        .debug("Unable to inject ComponentContext: "
472                                + e.getMessage());
473            }
474    
475        }
476    
477        /**
478         * Specifies if the endpoint can process messages with binary data.
479         * 
480         * @param mtomEnabled
481         *            a <code>boolean</code>
482         * @org.apache.xbean.Property description="Specifies if the service can
483         *                            consume MTOM formatted binary data. The
484         *                            default is <code>false</code>."
485         */
486        public void setMtomEnabled(boolean mtomEnabled) {
487            this.mtomEnabled = mtomEnabled;
488        }
489    
490        public boolean isMtomEnabled() {
491            return mtomEnabled;
492        }
493    
494        public boolean isSchemaValidationEnabled() {
495            return schemaValidationEnabled;
496        }
497    
498        public void setSchemaValidationEnabled(boolean schemaValidationEnabled) {
499            this.schemaValidationEnabled = schemaValidationEnabled;
500        }
501    
502        /**
503         * Specifies if the endpoint expects messages that are encased in the JBI
504         * wrapper used for SOAP messages.
505         * 
506         * @param mtomEnabled
507         *            a <code>boolean</code>
508         * @org.apache.xbean.Property description="Specifies if the endpoint expects
509         *                            to receive the JBI wrapper in the message
510         *                            received from the NMR. The default is
511         *                            <code>true</code>."
512         */
513        public void setUseJBIWrapper(boolean useJBIWrapper) {
514            this.useJBIWrapper = useJBIWrapper;
515        }
516    
517        public boolean isUseJBIWrapper() {
518            return useJBIWrapper;
519        }
520    
521        /**
522         * Specifies if the endpoint expects soap messages when useJBIWrapper is
523         * false, if useJBIWrapper is true then ignore useSOAPEnvelope
524         * 
525         * @org.apache.xbean.Property description="Specifies if the endpoint expects
526         *                            soap messages when useJBIWrapper is false, if
527         *                            useJBIWrapper is true then ignore
528         *                            useSOAPEnvelope. The default is
529         *                            <code>true</code>.
530         */
531        public void setUseSOAPEnvelope(boolean useSOAPEnvelope) {
532            this.useSOAPEnvelope = useSOAPEnvelope;
533        }
534    
535        public boolean isUseSOAPEnvelope() {
536            return useSOAPEnvelope;
537        }
538    
539        /**
540         * Specifies if the endpoint use aegis databinding to marshell/unmarshell message
541         * 
542         * @org.apache.xbean.Property description="Specifies if the endpoint use aegis databinding to marshell/unmarshell message. 
543         * The default is <code>false</code>.
544         */
545        public void setUseAegis(boolean useAegis) {
546            this.useAegis = useAegis;
547        }
548    
549        
550        public boolean isUseAegis() {
551            return useAegis;
552        }
553    
554    }