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.wsn.component;
018    
019    import java.io.StringWriter;
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Method;
022    import java.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.List;
025    
026    import javax.jbi.component.ComponentContext;
027    import javax.jbi.messaging.DeliveryChannel;
028    import javax.jbi.messaging.ExchangeStatus;
029    import javax.jbi.messaging.Fault;
030    import javax.jbi.messaging.InOnly;
031    import javax.jbi.messaging.MessageExchange;
032    import javax.jbi.messaging.MessageExchange.Role;
033    import javax.jbi.messaging.NormalizedMessage;
034    import javax.jbi.servicedesc.ServiceEndpoint;
035    import javax.jws.Oneway;
036    import javax.jws.WebMethod;
037    import javax.jws.WebService;
038    import javax.xml.bind.JAXBContext;
039    import javax.xml.bind.JAXBException;
040    import javax.xml.bind.annotation.XmlRootElement;
041    import javax.xml.namespace.QName;
042    import javax.xml.ws.WebFault;
043    
044    import org.apache.servicemix.common.Endpoint;
045    import org.apache.servicemix.common.ExchangeProcessor;
046    import org.apache.servicemix.jbi.jaxp.StringSource;
047    import org.apache.servicemix.jbi.resolver.URIResolver;
048    import org.oasis_open.docs.wsrf.bf_2.BaseFaultType;
049    
050    public class WSNEndpoint extends Endpoint implements ExchangeProcessor {
051    
052        protected ServiceEndpoint activated;
053    
054        protected String address;
055    
056        protected Object pojo;
057    
058        protected DeliveryChannel channel;
059    
060        protected JAXBContext jaxbContext;
061    
062        protected Class endpointInterface;
063    
064        public WSNEndpoint(String address, Object pojo) {
065            this.address = address;
066            this.pojo = pojo;
067            String[] parts = URIResolver.split3(address);
068            service = new QName(parts[0], parts[1]);
069            endpoint = parts[2];
070        }
071    
072        @Override
073        public Role getRole() {
074            return Role.PROVIDER;
075        }
076    
077        @Override
078        public void activate() throws Exception {
079            logger = this.serviceUnit.getComponent().getLogger();
080            WebService ws = getWebServiceAnnotation(pojo.getClass());
081            if (ws == null) {
082                throw new IllegalStateException("Unable to find WebService annotation");
083            }
084            endpointInterface = Class.forName(ws.endpointInterface());
085            jaxbContext = createJAXBContext(endpointInterface);
086            ws = getWebServiceAnnotation(endpointInterface);
087            if (ws != null) {
088                interfaceName = new QName(ws.targetNamespace(), ws.name());
089            }
090            ComponentContext ctx = this.serviceUnit.getComponent().getComponentContext();
091            activated = ctx.activateEndpoint(service, endpoint);
092            channel = ctx.getDeliveryChannel();
093        }
094    
095        public static JAXBContext createJAXBContext(Class interfaceClass) throws JAXBException {
096            List<Class> classes = new ArrayList<Class>();
097            classes.add(JbiFault.class);
098            for (Method mth : interfaceClass.getMethods()) {
099                WebMethod wm = (WebMethod) mth.getAnnotation(WebMethod.class);
100                if (wm != null) {
101                    classes.add(mth.getReturnType());
102                    classes.addAll(Arrays.asList(mth.getParameterTypes()));
103                }
104            }
105            return JAXBContext.newInstance(classes.toArray(new Class[classes.size()]));
106        }
107    
108        @Override
109        public void deactivate() throws Exception {
110            ServiceEndpoint ep = activated;
111            activated = null;
112            ComponentContext ctx = this.serviceUnit.getComponent().getComponentContext();
113            ctx.deactivateEndpoint(ep);
114        }
115    
116        @Override
117        public ExchangeProcessor getProcessor() {
118            return this;
119        }
120    
121        @SuppressWarnings("unchecked")
122        public void process(MessageExchange exchange) throws Exception {
123            if (exchange.getStatus() == ExchangeStatus.DONE) {
124                return;
125            } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
126                return;
127            }
128            Object input = jaxbContext.createUnmarshaller().unmarshal(exchange.getMessage("in").getContent());
129            Method webMethod = null;
130            for (Method mth : endpointInterface.getMethods()) {
131                Class[] params = mth.getParameterTypes();
132                if (params.length == 1 && params[0].isAssignableFrom(input.getClass())) {
133                    webMethod = mth;
134                    break;
135                }
136            }
137            if (webMethod == null) {
138                throw new IllegalStateException("Could not determine invoked web method");
139            }
140            boolean oneWay = webMethod.getAnnotation(Oneway.class) != null;
141            Object output;
142            try {
143                output = webMethod.invoke(pojo, new Object[] {input });
144            } catch (InvocationTargetException e) {
145                if (e.getCause() instanceof Exception) {
146                    WebFault fa = (WebFault) e.getCause().getClass().getAnnotation(WebFault.class);
147                    if (!(exchange instanceof InOnly) && fa != null) {
148                        BaseFaultType info = (BaseFaultType) e.getCause().getClass().getMethod("getFaultInfo").invoke(
149                                e.getCause());
150                        Fault fault = exchange.createFault();
151                        exchange.setFault(fault);
152                        exchange.setError((Exception) e.getCause());
153                        StringWriter writer = new StringWriter();
154                        jaxbContext.createMarshaller().marshal(new JbiFault(info), writer);
155                        fault.setContent(new StringSource(writer.toString()));
156                        channel.send(exchange);
157                        return;
158                    } else {
159                        throw (Exception) e.getCause();
160                    }
161                } else if (e.getCause() instanceof Error) {
162                    throw (Error) e.getCause();
163                } else {
164                    throw new RuntimeException(e.getCause());
165                }
166            }
167            if (oneWay) {
168                exchange.setStatus(ExchangeStatus.DONE);
169                channel.send(exchange);
170            } else {
171                NormalizedMessage msg = exchange.createMessage();
172                exchange.setMessage(msg, "out");
173                StringWriter writer = new StringWriter();
174                jaxbContext.createMarshaller().marshal(output, writer);
175                msg.setContent(new StringSource(writer.toString()));
176                channel.send(exchange);
177            }
178        }
179    
180        @XmlRootElement(name = "Fault")
181        public static class JbiFault {
182            private BaseFaultType info;
183    
184            public JbiFault() {
185            }
186    
187            public JbiFault(BaseFaultType info) {
188                this.info = info;
189            }
190    
191            public BaseFaultType getInfo() {
192                return info;
193            }
194    
195            public void setInfo(BaseFaultType info) {
196                this.info = info;
197            }
198        }
199    
200        protected Method getWebServiceMethod(QName interfaceName, QName operation) throws Exception {
201            WebService ws = getWebServiceAnnotation(pojo.getClass());
202            if (ws == null) {
203                throw new IllegalStateException("Unable to find WebService annotation");
204            }
205            Class itf = Class.forName(ws.endpointInterface());
206            for (Method mth : itf.getMethods()) {
207                WebMethod wm = (WebMethod) mth.getAnnotation(WebMethod.class);
208                if (wm != null) {
209                    // TODO: get name ?
210                }
211            }
212            return null;
213        }
214    
215        @SuppressWarnings("unchecked")
216        protected WebService getWebServiceAnnotation(Class clazz) {
217            for (Class cl = clazz; cl != null; cl = cl.getSuperclass()) {
218                WebService ws = (WebService) cl.getAnnotation(WebService.class);
219                if (ws != null) {
220                    return ws;
221                }
222            }
223            return null;
224        }
225    
226        public void start() throws Exception {
227            // Nothing to do
228        }
229    
230        public void stop() throws Exception {
231            // Nothing to do
232        }
233    
234    }