001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.cxfse;
018    
019    import java.lang.reflect.Field;
020    import java.lang.reflect.Method;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.concurrent.CopyOnWriteArrayList;
024    
025    import javax.annotation.PostConstruct;
026    import javax.annotation.PreDestroy;
027    import javax.jbi.component.ComponentContext;
028    import javax.jbi.management.DeploymentException;
029    import javax.jbi.messaging.DeliveryChannel;
030    import javax.jbi.messaging.ExchangeStatus;
031    import javax.jbi.messaging.MessageExchange;
032    import javax.wsdl.WSDLException;
033    import javax.wsdl.factory.WSDLFactory;
034    import javax.xml.namespace.QName;
035    import javax.xml.ws.WebServiceRef;
036    
037    import org.apache.cxf.Bus;
038    import org.apache.cxf.interceptor.Fault;
039    import org.apache.cxf.interceptor.Interceptor;
040    import org.apache.cxf.interceptor.InterceptorProvider;
041    import org.apache.cxf.jaxws.EndpointImpl;
042    import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
043    import org.apache.cxf.jaxws.ServiceImpl;
044    import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
045    import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
046    import org.apache.cxf.service.model.EndpointInfo;
047    import org.apache.cxf.transport.ConduitInitiatorManager;
048    import org.apache.cxf.transport.jbi.JBIDestination;
049    import org.apache.cxf.transport.jbi.JBIDispatcherUtil;
050    import org.apache.cxf.transport.jbi.JBITransportFactory;
051    import org.apache.cxf.wsdl11.ServiceWSDLBuilder;
052    import org.apache.servicemix.common.endpoints.ProviderEndpoint;
053    import org.apache.servicemix.cxfse.interceptors.AttachmentInInterceptor;
054    import org.apache.servicemix.cxfse.interceptors.AttachmentOutInterceptor;
055    import org.apache.servicemix.cxfse.support.ReflectionUtils;
056    import org.apache.servicemix.id.IdGenerator;
057    import org.springframework.util.ReflectionUtils.FieldCallback;
058    
059    /**
060     * 
061     * @author gnodet
062     * @org.apache.xbean.XBean element="endpoint" description="an endpoint using CXF's JAX-WS frontend"
063     */
064    public class CxfSeEndpoint extends ProviderEndpoint implements
065            InterceptorProvider {
066    
067        private static final IdGenerator ID_GENERATOR = new IdGenerator();
068    
069        private Object pojo;
070    
071        private EndpointImpl endpoint;
072    
073        private String address;
074    
075        private List<Interceptor> in = new CopyOnWriteArrayList<Interceptor>();
076    
077        private List<Interceptor> out = new CopyOnWriteArrayList<Interceptor>();
078    
079        private List<Interceptor> outFault = new CopyOnWriteArrayList<Interceptor>();
080    
081        private List<Interceptor> inFault = new CopyOnWriteArrayList<Interceptor>();
082        
083        private Map properties;
084        
085        private boolean mtomEnabled;
086        
087        private boolean useJBIWrapper = true;
088        
089        
090        /**
091            * Returns the object implementing the endpoint's functionality. It is 
092            * returned as a generic Java <code>Object</code> that can be cast to 
093            * the proper type.
094            *
095         * @return the pojo
096         */
097        public Object getPojo() {
098            return pojo;
099        }
100    
101        /**
102            * Specifies the object implementing the endpoint's functionality. This 
103            * object should use the JAX-WS annotations.
104            *
105         * @param pojo  a JAX-WS annotated object
106         * @org.apache.xbean.Property description="a bean configuring the JAX-WS annotated implementation for the endpoint"
107         */
108        public void setPojo(Object pojo) {
109            this.pojo = pojo;
110        }
111    
112        /**
113            * Returns the list of interceptors used to process fault messages being
114            * sent back to the consumer.
115            *
116            * @return a list of <code>Interceptor</code> objects
117            * */
118        public List<Interceptor> getOutFaultInterceptors() {
119            return outFault;
120        }
121    
122        /**
123            * Returns the list of interceptors used to process fault messages being
124            * recieved by the endpoint.
125            *
126            * @return a list of <code>Interceptor</code> objects
127            * */
128        public List<Interceptor> getInFaultInterceptors() {
129            return inFault;
130        }
131    
132        /**
133            * Returns the list of interceptors used to process messages being 
134            * recieved by the endpoint.
135            *
136            * @return a list of <code>Interceptor</code> objects
137            * */
138        public List<Interceptor> getInInterceptors() {
139            return in;
140        }
141    
142        /**
143            * Returns the list of interceptors used to process responses being
144            * sent back to the consumer.
145            *
146            * @return a list of <code>Interceptor</code> objects
147            * */
148        public List<Interceptor> getOutInterceptors() {
149            return out;
150        }
151    
152        /**
153            * Specifies a list of interceptors used to process requests recieved
154            * by the endpoint.
155            *
156            * @param interceptors   a list of <code>Interceptor</code> objects
157            * @org.apache.xbean.Property description="a list of beans configuring interceptors that process incoming requests"
158            * */
159        public void setInInterceptors(List<Interceptor> interceptors) {
160            in = interceptors;
161        }
162    
163        /**
164            * Specifies a list of interceptors used to process faults recieved by
165             * the endpoint.
166            *
167            * @param interceptors   a list of <code>Interceptor</code> objects
168            * @org.apache.xbean.Property description="a list of beans configuring interceptors that process incoming faults"
169            * */
170        public void setInFaultInterceptors(List<Interceptor> interceptors) {
171            inFault = interceptors;
172        }
173    
174        /**
175            * Specifies a list of interceptors used to process responses sent by 
176            * the endpoint.
177            *
178            * @param interceptors   a list of <code>Interceptor</code> objects
179            * @org.apache.xbean.Property description="a list of beans configuring interceptors that process response messages"
180            * */
181        public void setOutInterceptors(List<Interceptor> interceptors) {
182            out = interceptors;
183        }
184    
185        /**
186            * Specifies a list of interceptors used to process faults sent by 
187            * the endpoint.
188            *
189            * @param interceptors   a list of <code>Interceptor</code> objects
190            * @org.apache.xbean.Property description="a list of beans configuring interceptors that process fault messages being returned to the consumer"
191            * */
192        public void setOutFaultInterceptors(List<Interceptor> interceptors) {
193            outFault = interceptors;
194        }
195    
196        public Map getProperties() {
197            return properties;
198        }
199    
200        public void setProperties(Map properties) {
201            this.properties = properties;
202        }
203    
204    
205        /*
206         * (non-Javadoc)
207         * 
208         * @see org.apache.servicemix.common.Endpoint#validate()
209         */
210        @Override
211        public void validate() throws DeploymentException {
212            if (getPojo() == null) {
213                throw new DeploymentException("pojo must be set");
214            }
215            JaxWsServiceFactoryBean serviceFactory = new JaxWsServiceFactoryBean();
216            serviceFactory.setPopulateFromClass(true);
217            endpoint = new EndpointImpl(getBus(), getPojo(),
218                    new JaxWsServerFactoryBean(serviceFactory));
219            if (isUseJBIWrapper()) {
220                endpoint.setBindingUri(org.apache.cxf.binding.jbi.JBIConstants.NS_JBI_BINDING);
221            }
222            endpoint.setInInterceptors(getInInterceptors());
223            endpoint.setInFaultInterceptors(getInFaultInterceptors());
224            endpoint.setOutInterceptors(getOutInterceptors());
225            endpoint.setOutFaultInterceptors(getOutFaultInterceptors());
226            if (isMtomEnabled()) {
227                endpoint.getInInterceptors().add(new AttachmentInInterceptor());
228                endpoint.getOutInterceptors().add(new AttachmentOutInterceptor());
229            }
230            JaxWsImplementorInfo implInfo = new JaxWsImplementorInfo(getPojo()
231                    .getClass());
232            setService(implInfo.getServiceName());
233            setInterfaceName(implInfo.getInterfaceName());
234            setEndpoint(implInfo.getEndpointName().getLocalPart());
235            super.validate();
236        }
237    
238        /*
239         * (non-Javadoc)
240         * 
241         * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#process(javax.jbi.messaging.MessageExchange)
242         */
243        @Override
244        public void process(MessageExchange exchange) throws Exception {
245            JBIContext.setMessageExchange(exchange);
246            try {
247                QName opeName = exchange.getOperation();
248                EndpointInfo ei = endpoint.getServer().getEndpoint().getEndpointInfo();
249                if (opeName == null) {
250                    // if interface only have one operation, may not specify the opeName in MessageExchange
251                    if (ei.getBinding().getOperations().size() == 1) {
252                        opeName = ei.getBinding().getOperations().iterator().next().getName();
253                        exchange.setOperation(opeName);
254                    } else {
255                        throw new Fault(
256                                    new Exception("Operation not bound on this MessageExchange"));
257    
258                    }
259                }
260    
261                JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
262                        .getExtension(ConduitInitiatorManager.class)
263                        .getConduitInitiator(JBITransportFactory.TRANSPORT_ID);
264    
265                QName serviceName = exchange.getService();
266                if (serviceName == null) {
267                    serviceName = getService();
268                    exchange.setService(serviceName);
269                }
270                QName interfaceName = exchange.getInterfaceName();
271                if (interfaceName == null) {
272                    interfaceName = getInterfaceName();
273                    exchange.setInterfaceName(interfaceName);
274                }
275                JBIDestination jbiDestination = jbiTransportFactory
276                        .getDestination(serviceName.toString()
277                                + interfaceName.toString());
278                DeliveryChannel dc = getContext().getDeliveryChannel();
279                jbiTransportFactory.setDeliveryChannel(dc);
280    
281                jbiDestination.setDeliveryChannel(dc);
282                if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
283                    jbiDestination.getJBIDispatcherUtil().dispatch(exchange);
284                }
285            } finally {
286                JBIContext.setMessageExchange(null);
287            }
288        }
289    
290        /*
291         * (non-Javadoc)
292         * 
293         * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#start()
294         */
295        @Override
296        public void start() throws Exception {
297            super.start();
298            address = "jbi://" + ID_GENERATOR.generateSanitizedId();
299            try {
300                endpoint.publish(address);
301            } catch (Exception e) {
302                e.printStackTrace();
303            }
304            
305            setService(endpoint.getServer().getEndpoint().getService().getName());
306            setEndpoint(endpoint.getServer().getEndpoint().getEndpointInfo()
307                    .getName().getLocalPart());
308            try {
309                definition = new ServiceWSDLBuilder(getBus(), endpoint.getServer()
310                        .getEndpoint().getService().getServiceInfos().iterator()
311                        .next()).build();
312                description = WSDLFactory.newInstance().newWSDLWriter().getDocument(definition);
313            } catch (WSDLException e) {
314                throw new DeploymentException(e);
315            }
316            ReflectionUtils.doWithFields(getPojo().getClass(), new FieldCallback() {
317                public void doWith(Field field) throws IllegalArgumentException,
318                        IllegalAccessException {
319                    if (field.getAnnotation(WebServiceRef.class) != null) {
320                        ServiceImpl s = new ServiceImpl(getBus(), null, null, field
321                                .getType());
322                        s.addPort(new QName("port"),
323                                JBITransportFactory.TRANSPORT_ID, "jbi://"
324                                        + ID_GENERATOR.generateSanitizedId());
325                        Object o = s.getPort(new QName("port"), field.getType());
326                        field.setAccessible(true);
327                        field.set(getPojo(), o);
328                    }
329                }
330            });
331            ReflectionUtils.callLifecycleMethod(getPojo(), PostConstruct.class);
332            injectPojo();
333        }
334    
335    
336        /*
337         * (non-Javadoc)
338         * 
339         * @see org.apache.servicemix.common.endpoints.ProviderEndpoint#stop()
340         */
341        @Override
342        public void stop() throws Exception {
343            endpoint.stop();
344            ReflectionUtils.callLifecycleMethod(getPojo(), PreDestroy.class);
345            JBIDispatcherUtil.clean();
346            JBITransportFactory jbiTransportFactory = (JBITransportFactory) getBus()
347                .getExtension(ConduitInitiatorManager.class)
348                .getConduitInitiator(JBITransportFactory.TRANSPORT_ID);
349            jbiTransportFactory.setDeliveryChannel(null);
350            jbiTransportFactory.removeDestination(getService().toString()
351                        + getInterfaceName().toString());
352            super.stop();
353        }
354    
355        protected Bus getBus() {
356            return ((CxfSeComponent) getServiceUnit().getComponent()).getBus();
357        }
358    
359        
360        @PostConstruct
361        protected void injectPojo() {
362            try {
363                ComponentContext context = getContext();
364                Method mth = pojo.getClass().getMethod("setContext", new Class[] {ComponentContext.class });
365                if (mth != null) {
366                    mth.invoke(pojo, new Object[] {context});
367                }
368            } catch (Exception e) {
369                logger.debug("Unable to inject ComponentContext: " + e.getMessage());
370            }
371            
372        }
373    
374        /**
375            * Specifies if the endpoint can process messages with binary data.
376            *
377            * @param    mtomEnabled     a <code>boolean</code>
378            * @org.apache.xbean.Property description="Specifies if the service can  consume MTOM formatted binary data. The  default is <code>false</code>."
379            * */
380        public void setMtomEnabled(boolean mtomEnabled) {
381            this.mtomEnabled = mtomEnabled;
382        }
383    
384        public boolean isMtomEnabled() {
385            return mtomEnabled;
386        }
387    
388        /**
389            * Specifies if the endpoint expects messages that are encased in the 
390            * JBI wrapper used for SOAP messages.
391            *
392            * @param    mtomEnabled     a <code>boolean</code>
393            * @org.apache.xbean.Property description="Specifies if the endpoint expects to receive the JBI wrapper in the message received from the NMR. The  default is <code>true</code>."
394            * */
395        public void setUseJBIWrapper(boolean useJBIWrapper) {
396            this.useJBIWrapper = useJBIWrapper;
397        }
398    
399        public boolean isUseJBIWrapper() {
400            return useJBIWrapper;
401        }
402    }