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