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                if (cursor != null) {
182                    cursor.dispose();
183                }
184                try {
185                    in.close();
186                } catch (IOException e) {
187                    // ignore
188                }
189            }
190        }
191    
192        public boolean configurePOJO(GBeanData targetGBean,
193                                     String servletName,
194                                     Module module,
195                                     String seiClassName,
196                                     DeploymentContext context)
197            throws DeploymentException {
198                    
199            boolean status = super.configurePOJO(targetGBean, servletName, module, seiClassName, context);
200            if(!status) {
201                return false;
202            }       
203                    
204            //change the URL
205            Map sharedContext = ((WebModule) module).getSharedContext();
206            String contextRoot = ((WebModule) module).getContextRoot();
207            Map portInfoMap = (Map) sharedContext.get(getKey());
208            PortInfo portInfo;
209            
210            if(portInfoMap != null && portInfoMap.get(servletName) != null){
211                portInfo = (PortInfo) portInfoMap.get(servletName);
212                processURLPattern(contextRoot, portInfo);
213            }
214            
215            return status;
216        }
217    
218        public Object createService(Class serviceInterface,
219                                    Class serviceReference,
220                                    URI wsdlURI,
221                                    QName serviceQName,
222                                    Map<Class, PortComponentRefType> portComponentRefMap,
223                                    ServiceRefHandlerChainsType handlerChains,
224                                    GerServiceRefType serviceRefType,
225                                    Module module,
226                                    ClassLoader cl) throws DeploymentException {
227            EndpointInfoBuilder builder = new EndpointInfoBuilder(serviceInterface,
228                    serviceRefType, portComponentRefMap, module.getModuleFile(),
229                    wsdlURI, serviceQName);
230            builder.build();
231    
232            wsdlURI = builder.getWsdlURI();
233            serviceQName = builder.getServiceQName();
234            Map<Object, EndpointInfo> seiInfoMap = builder.getEndpointInfo();
235    
236            String handlerChainsXML = null;
237            try {
238                handlerChainsXML = getHandlerChainAsString(handlerChains);
239            } catch (IOException e) {
240                // this should not happen
241                log.warn("Failed to serialize handler chains", e);
242            }
243    
244            String serviceReferenceName = (serviceReference == null) ? null : serviceReference.getName();
245            return new Axis2ServiceReference(serviceInterface.getName(), serviceReferenceName,  wsdlURI,
246                    serviceQName, module.getModuleName(), handlerChainsXML, seiInfoMap);
247        }
248    
249        private static String getHandlerChainAsString(ServiceRefHandlerChainsType handlerChains)
250            throws IOException {
251            String xml = null;
252            if (handlerChains != null) {
253                StringWriter w = new StringWriter();
254                XmlOptions options = new XmlOptions();
255                options.setSaveSyntheticDocumentElement(new QName("http://java.sun.com/xml/ns/javaee", "handler-chains")); 
256                handlerChains.save(w, options);
257                xml = w.toString();
258            }
259            return xml;
260        }
261        
262        private static String getString(String in) {
263            if (in != null) {
264                in = in.trim();
265                if (in.length() == 0) {
266                    return null;
267                }
268            }
269            return in;
270        }
271    
272        private void processURLPattern(String contextRoot, PortInfo portInfo) throws DeploymentException {
273            //if the user specifies a url-pattern, set it here. 
274            String oldup = portInfo.getLocation();
275            if (oldup == null || oldup.length() == 0) { 
276                //if we cannot grab a valid urlpattern, default it to the port-component-name.
277                oldup = portInfo.getPortName();   
278            } else {
279                int i = oldup.indexOf(contextRoot);
280                oldup = oldup.substring(i + contextRoot.length() + 1);
281                oldup = oldup.trim();
282                if (oldup.indexOf("*") > 0) {
283                    //uncomment this before we fix this issue.  workarond by assuming * is at the end.
284                    //throw new DeploymentException("Per JSR 109, the url-pattern should not contain an asterisk.");
285                    oldup = oldup.substring(0, oldup.length() - 1);
286                } 
287                //trim the forward slashes at the beginning or end.
288                if (oldup.substring(0, 1).equalsIgnoreCase("/")) {
289                    oldup = oldup.substring(1);
290                } 
291                if (oldup.substring(oldup.length()-1).equalsIgnoreCase("/")) {
292                    oldup = oldup.substring(0, oldup.length() - 1);
293                }
294            
295            } 
296            portInfo.setLocation(oldup);
297        }
298            
299        @Override
300        protected void initialize(GBeanData targetGBean, Class serviceClass, PortInfo portInfo, Module module) 
301            throws DeploymentException {
302            if (isWsdlSet(portInfo, serviceClass)) {
303                log.debug("Service " + portInfo.getServiceName() + " has WSDL.");
304                return;
305            }
306            
307            if (isHTTPBinding(portInfo, serviceClass)) {
308                log.debug("Service " + portInfo.getServiceName() + " is HTTPBinding.  Only SOAP 1.1 or 1.2 is supported.");
309                return;
310            }
311            
312            log.debug("Service " + portInfo.getServiceName() + " does not have WSDL. Generating WSDL...");
313    
314            WsdlGenerator generator = new WsdlGenerator();
315            generator.setAxis2SAAJ();
316            
317            // set wsdl service
318            if (portInfo.getWsdlService() == null) {
319                generator.setWsdlService(JAXWSUtils.getServiceQName(serviceClass));
320            } else {
321                generator.setWsdlService(portInfo.getWsdlService());
322            }
323            
324            // set wsdl port
325            if (portInfo.getWsdlPort() != null) {
326                generator.setWsdlPort(portInfo.getWsdlPort());
327            }
328                    
329            String wsdlFile = generator.generateWsdl(module, serviceClass.getName(), module.getEarContext(), portInfo);
330            portInfo.setWsdlFile(wsdlFile);
331            
332            log.debug("Generated " + wsdlFile + " for service " + portInfo.getServiceName());        
333        }
334        
335        private boolean isWsdlSet(PortInfo portInfo, Class serviceClass) {
336            return (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().trim().equals(""))
337                    || JAXWSUtils.containsWsdlLocation(serviceClass, serviceClass.getClassLoader());
338        }
339        
340        private boolean isHTTPBinding(PortInfo portInfo, Class serviceClass) {
341            String bindingURI = "";
342            String bindingURIFromAnnot;
343            
344            if (portInfo.getProtocolBinding() != null) {
345                bindingURI = JAXWSUtils.getBindingURI(portInfo.getProtocolBinding());
346            }        
347            bindingURIFromAnnot = JAXWSUtils.getBindingURIFromAnnot(serviceClass, serviceClass.getClassLoader());
348            
349            if (bindingURI != null && !bindingURI.trim().equals("")) {
350                return bindingURI.equals(HTTPBinding.HTTP_BINDING);
351            } else if (bindingURIFromAnnot != null && !bindingURIFromAnnot.trim().equals("")) {
352                return bindingURIFromAnnot.equals(HTTPBinding.HTTP_BINDING);
353            } 
354            
355            return false;  
356        }
357            
358        public static final GBeanInfo GBEAN_INFO;
359    
360        static {
361            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(Axis2Builder.class, NameFactory.MODULE_BUILDER);
362            infoBuilder.addInterface(WebServiceBuilder.class);
363            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
364            infoBuilder.setConstructor(new String[]{"defaultEnvironment"});
365            GBEAN_INFO = infoBuilder.getBeanInfo();
366        }
367    
368        public static GBeanInfo getGBeanInfo() {
369            return GBEAN_INFO;
370        }
371    }