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    
018    package org.apache.geronimo.axis2.builder;
019    
020    import java.io.FileNotFoundException;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.StringWriter;
024    import java.net.URI;
025    import java.net.URL;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.Map;
029    import java.util.jar.JarFile;
030    
031    import javax.xml.namespace.QName;
032    import javax.xml.ws.http.HTTPBinding;
033    
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    import org.apache.geronimo.axis2.client.Axis2ServiceReference;
037    import org.apache.geronimo.axis2.pojo.POJOWebServiceContainerFactoryGBean;
038    import org.apache.geronimo.common.DeploymentException;
039    import org.apache.geronimo.deployment.DeploymentContext;
040    import org.apache.geronimo.gbean.GBeanData;
041    import org.apache.geronimo.gbean.GBeanInfo;
042    import org.apache.geronimo.gbean.GBeanInfoBuilder;
043    import org.apache.geronimo.j2ee.deployment.Module;
044    import org.apache.geronimo.j2ee.deployment.WebModule;
045    import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
046    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
047    import org.apache.geronimo.jaxws.JAXWSUtils;
048    import org.apache.geronimo.jaxws.PortInfo;
049    import org.apache.geronimo.jaxws.builder.EndpointInfoBuilder;
050    import org.apache.geronimo.jaxws.builder.JAXWSServiceBuilder;
051    import org.apache.geronimo.jaxws.builder.WsdlGenerator;
052    import org.apache.geronimo.jaxws.client.EndpointInfo;
053    import org.apache.geronimo.kernel.repository.Environment;
054    import org.apache.geronimo.xbeans.geronimo.naming.GerServiceRefType;
055    import org.apache.geronimo.xbeans.javaee.PortComponentRefType;
056    import org.apache.geronimo.xbeans.javaee.PortComponentType;
057    import org.apache.geronimo.xbeans.javaee.ServiceImplBeanType;
058    import org.apache.geronimo.xbeans.javaee.ServiceRefHandlerChainsType;
059    import org.apache.geronimo.xbeans.javaee.WebserviceDescriptionType;
060    import org.apache.geronimo.xbeans.javaee.WebservicesDocument;
061    import org.apache.geronimo.xbeans.javaee.WebservicesType;
062    import org.apache.xmlbeans.XmlCursor;
063    import org.apache.xmlbeans.XmlObject;
064    import org.apache.xmlbeans.XmlOptions;
065    
066    /**
067     * @version $Rev$ $Date$
068     */
069    public class Axis2Builder extends JAXWSServiceBuilder {
070    
071        private static final Log log = LogFactory.getLog(Axis2Builder.class);
072            
073        public Axis2Builder(Environment defaultEnviroment) {
074            super(defaultEnviroment);
075        }
076        
077        public Axis2Builder(){
078            super(null);
079        }
080        
081        protected GBeanInfo getContainerFactoryGBeanInfo() {
082            return POJOWebServiceContainerFactoryGBean.GBEAN_INFO;
083        }
084        
085        protected Map<String, PortInfo> parseWebServiceDescriptor(InputStream in,
086                                                                  URL wsDDUrl,
087                                                                  JarFile moduleFile,
088                                                                  boolean isEJB,
089                                                                  Map correctedPortLocations)
090                throws DeploymentException {
091    
092            log.debug("Parsing descriptor " + wsDDUrl);
093    
094            Map<String, PortInfo> map = null;
095            XmlCursor cursor = null;
096    
097            try {
098                XmlObject xobj = XmlObject.Factory.parse(in);
099               
100                cursor = xobj.newCursor();
101                cursor.toStartDoc();
102                cursor.toFirstChild();
103                //the checking is needed as we also send JAX-RPC based webservices.xml here
104                if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) {
105                    WebservicesDocument wd = (WebservicesDocument)xobj.changeType(WebservicesDocument.type);
106                    WebservicesType wst = wd.getWebservices();
107    
108                    for (WebserviceDescriptionType desc : wst.getWebserviceDescriptionArray()) {
109                        String wsdlFile = null;
110                        if (desc.getWsdlFile() != null) {
111                            wsdlFile = getString(desc.getWsdlFile().getStringValue());
112                        }
113    
114                        String serviceName = desc.getWebserviceDescriptionName().getStringValue();
115    
116                        for (PortComponentType port : desc.getPortComponentArray()) {
117    
118                            PortInfo portInfo = new PortInfo();
119                            String serviceLink = null;
120                            ServiceImplBeanType beanType = port.getServiceImplBean();
121                            if (beanType.getEjbLink() != null) {
122                                serviceLink = beanType.getEjbLink().getStringValue();
123                            } else if (beanType.getServletLink().getStringValue() != null) {
124                                serviceLink = beanType.getServletLink().getStringValue();
125                            }
126                            portInfo.setServiceLink(serviceLink);
127    
128                            if (port.getServiceEndpointInterface() != null) {
129                                String sei = port.getServiceEndpointInterface().getStringValue();
130                                portInfo.setServiceEndpointInterfaceName(sei);
131                            }
132    
133                            String portName = port.getPortComponentName().getStringValue();
134                            portInfo.setPortName(portName);
135    
136                            portInfo.setProtocolBinding(port.getProtocolBinding());
137                            portInfo.setServiceName(serviceName);
138                            portInfo.setWsdlFile(wsdlFile);
139    
140                            if (port.getEnableMtom() != null) {
141                                portInfo.setEnableMTOM(port.getEnableMtom().getBooleanValue());
142                            }
143    
144                            if (port.getHandlerChains() != null) {
145                                StringBuffer chains = new StringBuffer("<handler-chains xmlns=\"http://java.sun.com/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
146                                chains.append(port.getHandlerChains().xmlText());
147                                chains.append("</handler-chains>");
148                                portInfo.setHandlersAsXML(chains.toString());
149                            }
150    
151                            if (port.getWsdlPort() != null) {
152                                portInfo.setWsdlPort(port.getWsdlPort().getQNameValue());
153                            }
154    
155                            if (port.getWsdlService() != null) {
156                                portInfo.setWsdlService(port.getWsdlService().getQNameValue());
157                            }
158    
159                            String location = (String) correctedPortLocations.get(serviceLink);
160                            portInfo.setLocation(location);
161    
162                            if (map == null) {
163                                map = new HashMap<String, PortInfo>();
164                            }
165    
166                            map.put(serviceLink, portInfo);
167                        }
168                    }
169                } else {
170                    log.debug("Descriptor ignored (not a Java EE 5 descriptor)");
171                }
172                
173                return map;
174            } catch (FileNotFoundException e) {
175                return Collections.emptyMap();
176            } catch (IOException ex) {
177                throw new DeploymentException("Unable to read " + wsDDUrl, ex);
178            } catch (Exception ex) {
179                throw new DeploymentException("Unknown deployment error", ex);
180            } finally {
181                try {
182                    cursor.dispose();
183                    in.close();
184                } catch (IOException e) {
185                    // ignore
186                }
187            }
188        }
189    
190        public boolean configurePOJO(GBeanData targetGBean,
191                                     String servletName,
192                                     Module module,
193                                     String seiClassName,
194                                     DeploymentContext context)
195            throws DeploymentException {
196                    
197            boolean status = super.configurePOJO(targetGBean, servletName, module, seiClassName, context);
198            if(!status) {
199                return false;
200            }       
201                    
202            //change the URL
203            Map sharedContext = ((WebModule) module).getSharedContext();
204            String contextRoot = ((WebModule) module).getContextRoot();
205            Map portInfoMap = (Map) sharedContext.get(getKey());
206            PortInfo portInfo;
207            
208            if(portInfoMap != null && portInfoMap.get(servletName) != null){
209                portInfo = (PortInfo) portInfoMap.get(servletName);
210                processURLPattern(contextRoot, portInfo);
211            }
212            
213            return status;
214        }
215    
216        public Object createService(Class serviceInterface,
217                                    Class serviceReference,
218                                    URI wsdlURI,
219                                    QName serviceQName,
220                                    Map<Class, PortComponentRefType> portComponentRefMap,
221                                    ServiceRefHandlerChainsType handlerChains,
222                                    GerServiceRefType serviceRefType,
223                                    Module module,
224                                    ClassLoader cl) throws DeploymentException {
225            EndpointInfoBuilder builder = new EndpointInfoBuilder(serviceInterface,
226                    serviceRefType, portComponentRefMap, module.getModuleFile(),
227                    wsdlURI, serviceQName);
228            builder.build();
229    
230            wsdlURI = builder.getWsdlURI();
231            serviceQName = builder.getServiceQName();
232            Map<Object, EndpointInfo> seiInfoMap = builder.getEndpointInfo();
233    
234            String handlerChainsXML = null;
235            try {
236                handlerChainsXML = getHandlerChainAsString(handlerChains);
237            } catch (IOException e) {
238                // this should not happen
239                log.warn("Failed to serialize handler chains", e);
240            }
241    
242            String serviceReferenceName = (serviceReference == null) ? null : serviceReference.getName();
243            return new Axis2ServiceReference(serviceInterface.getName(), serviceReferenceName,  wsdlURI,
244                    serviceQName, module.getModuleName(), handlerChainsXML, seiInfoMap);
245        }
246    
247        private static String getHandlerChainAsString(ServiceRefHandlerChainsType handlerChains)
248            throws IOException {
249            String xml = null;
250            if (handlerChains != null) {
251                StringWriter w = new StringWriter();
252                XmlOptions options = new XmlOptions();
253                options.setSaveSyntheticDocumentElement(new QName("http://java.sun.com/xml/ns/javaee", "handler-chains")); 
254                handlerChains.save(w, options);
255                xml = w.toString();
256            }
257            return xml;
258        }
259        
260        private static String getString(String in) {
261            if (in != null) {
262                in = in.trim();
263                if (in.length() == 0) {
264                    return null;
265                }
266            }
267            return in;
268        }
269    
270        private void processURLPattern(String contextRoot, PortInfo portInfo) throws DeploymentException {
271            //if the user specifies a url-pattern, set it here. 
272            String oldup = portInfo.getLocation();
273            if (oldup == null || oldup.length() == 0) { 
274                //if we cannot grab a valid urlpattern, default it to the port-component-name.
275                oldup = portInfo.getPortName();   
276            } else {
277                int i = oldup.indexOf(contextRoot);
278                oldup = oldup.substring(i + contextRoot.length() + 1);
279                oldup = oldup.trim();
280                if (oldup.indexOf("*") > 0) {
281                    //uncomment this before we fix this issue.  workarond by assuming * is at the end.
282                    //throw new DeploymentException("Per JSR 109, the url-pattern should not contain an asterisk.");
283                    oldup = oldup.substring(0, oldup.length() - 1);
284                } 
285                //trim the forward slashes at the beginning or end.
286                if (oldup.substring(0, 1).equalsIgnoreCase("/")) {
287                    oldup = oldup.substring(1);
288                } 
289                if (oldup.substring(oldup.length()-1).equalsIgnoreCase("/")) {
290                    oldup = oldup.substring(0, oldup.length() - 1);
291                }
292            
293            } 
294            portInfo.setLocation(oldup);
295        }
296            
297        @Override
298        protected void initialize(GBeanData targetGBean, Class serviceClass, PortInfo portInfo, Module module) 
299            throws DeploymentException {
300            if (isWsdlSet(portInfo, serviceClass)) {
301                log.debug("Service " + portInfo.getServiceName() + " has WSDL.");
302                return;
303            }
304            
305            if (isHTTPBinding(portInfo, serviceClass)) {
306                log.debug("Service " + portInfo.getServiceName() + " is HTTPBinding.  Only SOAP 1.1 or 1.2 is supported.");
307                return;
308            }
309            
310            log.debug("Service " + portInfo.getServiceName() + " does not have WSDL. Generating WSDL...");
311    
312            WsdlGenerator generator = new WsdlGenerator();
313            generator.setAxis2SAAJ();
314            
315            // set wsdl service
316            if (portInfo.getWsdlService() == null) {
317                generator.setWsdlService(JAXWSUtils.getServiceQName(serviceClass));
318            } else {
319                generator.setWsdlService(portInfo.getWsdlService());
320            }
321            
322            // set wsdl port
323            if (portInfo.getWsdlPort() != null) {
324                generator.setWsdlPort(portInfo.getWsdlPort());
325            }
326                    
327            String wsdlFile = generator.generateWsdl(module, serviceClass.getName(), module.getEarContext(), portInfo);
328            portInfo.setWsdlFile(wsdlFile);
329            
330            log.debug("Generated " + wsdlFile + " for service " + portInfo.getServiceName());        
331        }
332        
333        private boolean isWsdlSet(PortInfo portInfo, Class serviceClass) {
334            return (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().trim().equals(""))
335                    || JAXWSUtils.containsWsdlLocation(serviceClass, serviceClass.getClassLoader());
336        }
337        
338        private boolean isHTTPBinding(PortInfo portInfo, Class serviceClass) {
339            String bindingURI = "";
340            String bindingURIFromAnnot;
341            
342            if (portInfo.getProtocolBinding() != null) {
343                bindingURI = JAXWSUtils.getBindingURI(portInfo.getProtocolBinding());
344            }        
345            bindingURIFromAnnot = JAXWSUtils.getBindingURIFromAnnot(serviceClass, serviceClass.getClassLoader());
346            
347            if (bindingURI != null && !bindingURI.trim().equals("")) {
348                return bindingURI.equals(HTTPBinding.HTTP_BINDING);
349            } else if (bindingURIFromAnnot != null && !bindingURIFromAnnot.trim().equals("")) {
350                return bindingURIFromAnnot.equals(HTTPBinding.HTTP_BINDING);
351            } 
352            
353            return false;  
354        }
355            
356        public static final GBeanInfo GBEAN_INFO;
357    
358        static {
359            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(Axis2Builder.class, NameFactory.MODULE_BUILDER);
360            infoBuilder.addInterface(WebServiceBuilder.class);
361            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
362            infoBuilder.setConstructor(new String[]{"defaultEnvironment"});
363            GBEAN_INFO = infoBuilder.getBeanInfo();
364        }
365    
366        public static GBeanInfo getGBeanInfo() {
367            return GBEAN_INFO;
368        }
369    }