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