001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License. You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied. See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.geronimo.cxf.ejb;
021    
022    import java.lang.reflect.Method;
023    import java.util.List;
024    
025    import javax.interceptor.AroundInvoke;
026    import javax.interceptor.InvocationContext;
027    import javax.xml.soap.SOAPMessage;
028    import javax.xml.stream.XMLStreamReader;
029    import javax.xml.transform.dom.DOMSource;
030    import javax.xml.ws.Binding;
031    
032    import org.apache.commons.logging.Log;
033    import org.apache.commons.logging.LogFactory;
034    import org.apache.cxf.Bus;
035    import org.apache.cxf.binding.soap.SoapMessage;
036    import org.apache.cxf.binding.soap.interceptor.MustUnderstandInterceptor;
037    import org.apache.cxf.endpoint.Endpoint;
038    import org.apache.cxf.interceptor.AbstractInDatabindingInterceptor;
039    import org.apache.cxf.interceptor.Interceptor;
040    import org.apache.cxf.interceptor.InterceptorChain;
041    import org.apache.cxf.interceptor.OutgoingChainInterceptor;
042    import org.apache.cxf.interceptor.ServiceInvokerInterceptor;
043    import org.apache.cxf.jaxws.handler.logical.LogicalHandlerInInterceptor;
044    import org.apache.cxf.jaxws.handler.soap.SOAPHandlerInterceptor;
045    import org.apache.cxf.jaxws.support.JaxWsEndpointImpl;
046    import org.apache.cxf.message.Exchange;
047    import org.apache.cxf.message.Message;
048    import org.apache.cxf.phase.PhaseInterceptorChain;
049    import org.apache.cxf.phase.PhaseManager;
050    import org.apache.cxf.service.Service;
051    import org.apache.cxf.staxutils.StaxUtils;
052    import org.w3c.dom.Element;
053    
054    public class EJBInterceptor {
055    
056        private static final Log LOG = LogFactory.getLog(EJBInterceptor.class);
057    
058        private Exchange exchange;
059        private Bus bus;
060        private EJBEndpoint ejbEndpoint;
061        private List<Object> params;
062        private Method method;
063    
064        public EJBInterceptor(List<Object> params,
065                              Method method,
066                              EJBEndpoint endpoint,
067                              Bus bus,
068                              Exchange exchange) {
069            this.params = params;
070            this.method = method;
071            this.ejbEndpoint = endpoint;
072            this.bus = bus;
073            this.exchange = exchange;
074        }
075    
076        private static void copyDataBindingInterceptors(PhaseInterceptorChain newChain,
077                                                        InterceptorChain oldChain) {
078            for (Interceptor interceptor : oldChain) {
079                if (interceptor instanceof AbstractInDatabindingInterceptor) {
080                    LOG.debug("Added data binding interceptor: " + interceptor);
081                    newChain.add(interceptor);
082                }
083            }
084        }
085    
086        @AroundInvoke
087        public Object intercept(InvocationContext context) throws Exception {
088            Endpoint endpoint = this.exchange.get(Endpoint.class);
089            Service service = endpoint.getService();
090            Binding binding = ((JaxWsEndpointImpl) endpoint).getJaxwsBinding();
091    
092            this.exchange.put(InvocationContext.class, context);
093    
094            if (binding.getHandlerChain() == null || binding.getHandlerChain().isEmpty()) {
095                // no handlers so let's just directly invoke the bean
096                LOG.debug("No handlers found.");
097                
098                EJBMethodInvoker invoker = (EJBMethodInvoker) service.getInvoker();
099                return invoker.directEjbInvoke(this.exchange, this.method, this.params);
100    
101            } else {
102                // have handlers so have to run handlers now and redo data binding
103                // as handlers can change the soap message
104                LOG.debug("Handlers found.");
105                            
106                // inject handlers (on first call only)
107                this.ejbEndpoint.injectHandlers();
108    
109                Message inMessage = this.exchange.getInMessage();
110                PhaseInterceptorChain chain = 
111                    new PhaseInterceptorChain(this.bus.getExtension(PhaseManager.class).getInPhases());
112    
113                chain.setFaultObserver(endpoint.getOutFaultObserver());
114    
115                /*
116                 * Since we have to re-do data binding and the XMLStreamReader
117                 * contents are already consumed by prior data binding step
118                 * we have to reinitialize the XMLStreamReader from the SOAPMessage
119                 * created by SAAJInInterceptor. 
120                 */
121                if (inMessage instanceof SoapMessage) {
122                    try {
123                        reserialize((SoapMessage)inMessage);
124                    } catch (Exception e) {
125                        throw new RuntimeException("Failed to reserialize soap message", e);
126                    }
127                } else {
128                    // TODO: how to handle XML/HTTP binding?
129                }
130                
131                this.exchange.setOutMessage(null);
132    
133                // install default interceptors
134                chain.add(new ServiceInvokerInterceptor());
135                chain.add(new OutgoingChainInterceptor());
136    
137                // install interceptors for handler processing
138                chain.add(new MustUnderstandInterceptor());
139                chain.add(new LogicalHandlerInInterceptor(binding));
140                chain.add(new SOAPHandlerInterceptor(binding));
141    
142                // install data binding interceptors
143                copyDataBindingInterceptors(chain, inMessage.getInterceptorChain());
144    
145                InterceptorChain oldChain = inMessage.getInterceptorChain();
146                inMessage.setInterceptorChain(chain);
147                try {
148                    chain.doIntercept(inMessage);
149                } finally {
150                    inMessage.setInterceptorChain(oldChain);
151                }
152    
153                // TODO: the result should be deserialized from SOAPMessage
154                Object result = getResult();
155    
156                return result;
157            }
158        }
159    
160        private Object getResult() {
161            Message outMessage = this.exchange.getOutMessage();
162            if (outMessage == null) {
163                return null;
164            } else {
165                List<?> result = outMessage.getContent(List.class);
166                if (result == null) {
167                    return outMessage.get(Object.class);
168                } else if (result.isEmpty()) {
169                    return null;
170                } else {
171                    return result.get(0);
172                }
173            }
174        }
175        
176        private void reserialize(SoapMessage message) throws Exception {
177            SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
178            if (soapMessage == null) {
179                return;
180            }
181                    
182            XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class);
183            StaxUtils.readDocElements(soapMessage.getSOAPBody(), xmlReader, true);
184            DOMSource bodySource = new DOMSource(soapMessage.getSOAPPart().getEnvelope().getBody());
185            xmlReader = StaxUtils.createXMLStreamReader(bodySource);
186            xmlReader.nextTag();
187            xmlReader.nextTag(); // move past body tag
188            message.setContent(XMLStreamReader.class, xmlReader);
189        }
190            
191    }