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.geronimo.cxf.builder;
018    
019    import java.io.FileNotFoundException;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.StringWriter;
023    import java.net.URI;
024    import java.net.URL;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.Map;
028    import java.util.jar.JarFile;
029    
030    import javax.xml.bind.JAXBContext;
031    import javax.xml.bind.JAXBElement;
032    import javax.xml.bind.JAXBException;
033    import javax.xml.bind.Unmarshaller;
034    import javax.xml.namespace.QName;
035    import javax.xml.transform.stream.StreamSource;
036    
037    import org.apache.commons.logging.Log;
038    import org.apache.commons.logging.LogFactory;
039    import org.apache.cxf.jaxws.javaee.HandlerChainsType;
040    import org.apache.cxf.jaxws.javaee.PortComponentType;
041    import org.apache.cxf.jaxws.javaee.ServiceImplBeanType;
042    import org.apache.cxf.jaxws.javaee.WebserviceDescriptionType;
043    import org.apache.cxf.jaxws.javaee.WebservicesType;
044    import org.apache.cxf.jaxws.support.JaxWsImplementorInfo;
045    import org.apache.geronimo.common.DeploymentException;
046    import org.apache.geronimo.cxf.client.CXFServiceReference;
047    import org.apache.geronimo.cxf.pojo.POJOWebServiceContainerFactoryGBean;
048    import org.apache.geronimo.gbean.GBeanData;
049    import org.apache.geronimo.gbean.GBeanInfo;
050    import org.apache.geronimo.gbean.GBeanInfoBuilder;
051    import org.apache.geronimo.j2ee.deployment.Module;
052    import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
053    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
054    import org.apache.geronimo.jaxws.JAXWSUtils;
055    import org.apache.geronimo.jaxws.PortInfo;
056    import org.apache.geronimo.jaxws.builder.EndpointInfoBuilder;
057    import org.apache.geronimo.jaxws.builder.JAXWSServiceBuilder;
058    import org.apache.geronimo.jaxws.builder.WsdlGenerator;
059    import org.apache.geronimo.jaxws.client.EndpointInfo;
060    import org.apache.geronimo.kernel.repository.Environment;
061    import org.apache.geronimo.xbeans.geronimo.naming.GerServiceRefType;
062    import org.apache.geronimo.xbeans.javaee.PortComponentRefType;
063    import org.apache.geronimo.xbeans.javaee.ServiceRefHandlerChainsType;
064    import org.apache.xmlbeans.XmlOptions;
065    
066    public class CXFBuilder extends JAXWSServiceBuilder {
067        private static final Log LOG = LogFactory.getLog(CXFBuilder.class);
068        
069        /**
070         * This property if enabled will cause the Sun wsgen tool to be used to 
071         * generate the WSDL for servies without WSDL. By default CXF tooling
072         * will be used the generate the WSDL.
073         */
074        private static final String USE_WSGEN_PROPERTY = 
075            "org.apache.geronimo.cxf.use.wsgen";
076    
077        public CXFBuilder() {
078            this(null);
079        }
080    
081        public CXFBuilder(Environment defaultEnvironment) {
082            super(defaultEnvironment);
083        }
084    
085        protected GBeanInfo getContainerFactoryGBeanInfo() {
086            return POJOWebServiceContainerFactoryGBean.GBEAN_INFO;
087        }
088    
089        protected Map<String, PortInfo> parseWebServiceDescriptor(InputStream in,
090                                                                  URL wsDDUrl,
091                                                                  JarFile moduleFile,
092                                                                  boolean isEJB,
093                                                                  Map correctedPortLocations)
094                throws DeploymentException {
095    
096            LOG.debug("Parsing descriptor " + wsDDUrl);
097    
098            Map<String, PortInfo> map = null;
099    
100            try {
101                JAXBContext ctx = JAXBContext.newInstance(WebservicesType.class);
102                Unmarshaller unmarshaller = ctx.createUnmarshaller();
103                Object obj = unmarshaller.unmarshal(new StreamSource(in), WebservicesType.class);
104    
105                if (obj instanceof JAXBElement) {
106                    obj = ((JAXBElement) obj).getValue();
107                }
108    
109                if (!(obj instanceof WebservicesType)) {
110                    return map;
111                }
112                WebservicesType wst = (WebservicesType) obj;
113    
114                for (WebserviceDescriptionType desc : wst.getWebserviceDescription()) {
115                    String wsdlFile = null;
116                    if (desc.getWsdlFile() != null) {
117                        wsdlFile = getString(desc.getWsdlFile().getValue());
118                    }
119    
120                    String serviceName = desc.getWebserviceDescriptionName().getValue();
121    
122                    for (PortComponentType port : desc.getPortComponent()) {
123    
124                        PortInfo portInfo = new PortInfo();
125    
126                        String serviceLink = null;
127                        ServiceImplBeanType beanType = port.getServiceImplBean();
128                        if (beanType.getEjbLink() != null) {
129                            serviceLink = beanType.getEjbLink().getValue();
130                        } else if (beanType.getServletLink().getValue() != null) {
131                            serviceLink = beanType.getServletLink().getValue();
132                        }
133                        portInfo.setServiceLink(serviceLink);
134    
135                        if (port.getServiceEndpointInterface() != null) {
136                            String sei = port.getServiceEndpointInterface().getValue();
137                            portInfo.setServiceEndpointInterfaceName(sei);
138                        }
139    
140                        String portName = port.getPortComponentName().getValue();
141                        portInfo.setPortName(portName);
142    
143                        portInfo.setProtocolBinding(port.getProtocolBinding());
144                        portInfo.setServiceName(serviceName);
145                        portInfo.setWsdlFile(wsdlFile);
146    
147                        if (port.getEnableMtom() != null) {
148                            portInfo.setEnableMTOM(port.getEnableMtom().isValue());
149                        }
150    
151                        portInfo.setHandlers(HandlerChainsType.class, port.getHandlerChains());
152    
153                        if (port.getWsdlPort() != null) {
154                            portInfo.setWsdlPort(port.getWsdlPort().getValue());
155                        }
156    
157                        if (port.getWsdlService() != null) {
158                            portInfo.setWsdlService(port.getWsdlService().getValue());
159                        }
160    
161                        String location = (String) correctedPortLocations.get(serviceLink);
162                        portInfo.setLocation(location);
163    
164                        if (map == null) {
165                            map = new HashMap<String, PortInfo>();
166                        }
167                        map.put(serviceLink, portInfo);
168                    }
169                }
170    
171                return map;
172            } catch (FileNotFoundException e) {
173                return Collections.emptyMap();
174            } catch (IOException ex) {
175                throw new DeploymentException("Unable to read " + wsDDUrl, ex);
176            } catch (JAXBException ex) {
177                throw new DeploymentException("Unable to parse " + wsDDUrl, ex);
178            } catch (Exception ex) {
179                throw new DeploymentException("Unknown deployment error", ex);
180            } finally {
181                try {
182                    in.close();
183                } catch (IOException e) {
184                    // ignore
185                }
186            }
187        }
188    
189        public Object createService(Class serviceInterface,
190                                    Class serviceReference,
191                                    URI wsdlURI,
192                                    QName serviceQName,
193                                    Map<Class, PortComponentRefType> portComponentRefMap,
194                                    ServiceRefHandlerChainsType handlerChains,
195                                    GerServiceRefType serviceRefType,
196                                    Module module,
197                                    ClassLoader cl) throws DeploymentException {     
198            EndpointInfoBuilder builder = new EndpointInfoBuilder(serviceInterface,
199                    serviceRefType, portComponentRefMap, module.getModuleFile(),
200                    wsdlURI, serviceQName);
201            builder.build();
202    
203            wsdlURI = builder.getWsdlURI();
204            serviceQName = builder.getServiceQName();
205            Map<Object, EndpointInfo> seiInfoMap = builder.getEndpointInfo();
206    
207            String handlerChainsXML = null;
208            try {
209                handlerChainsXML = getHandlerChainAsString(handlerChains);
210            } catch (IOException e) {
211                // this should not happen
212                LOG.warn("Failed to serialize handler chains", e);
213            }
214    
215            String serviceReferenceName = (serviceReference == null) ? null : serviceReference.getName();
216            
217            return new CXFServiceReference(serviceInterface.getName(), serviceReferenceName,  wsdlURI,
218                    serviceQName, module.getModuleName(), handlerChainsXML, seiInfoMap);
219        }
220        
221        private static String getHandlerChainAsString(ServiceRefHandlerChainsType handlerChains)
222                throws IOException {
223            String xml = null;
224            if (handlerChains != null) {
225                StringWriter w = new StringWriter();
226                XmlOptions options = new XmlOptions();
227                options.setSaveSyntheticDocumentElement(new QName("http://java.sun.com/xml/ns/javaee", "handler-chains")); 
228                handlerChains.save(w, options);
229                xml = w.toString();
230            }
231            return xml;
232        }
233        
234        private static String getString(String in) {
235            if (in != null) {
236                in = in.trim();
237                if (in.length() == 0) {
238                    return null;
239                }
240            }
241            return in;
242        }
243            
244        @Override
245        protected void initialize(GBeanData targetGBean, Class serviceClass, PortInfo portInfo, Module module) 
246            throws DeploymentException {  
247            if (Boolean.getBoolean(USE_WSGEN_PROPERTY)) {
248                generateWSDL(serviceClass, portInfo, module);
249            }
250        }
251        
252        private void generateWSDL(Class serviceClass, PortInfo portInfo, Module module) 
253            throws DeploymentException {
254            if (isWsdlSet(portInfo, serviceClass)) {
255                LOG.debug("Service " + portInfo.getServiceName() + " has WSDL.");
256                return;
257            }        
258            LOG.debug("Service " + portInfo.getServiceName() + " does not have WSDL. Generating WSDL...");
259    
260            WsdlGenerator generator = new WsdlGenerator();
261            generator.setSunSAAJ();
262            
263            JaxWsImplementorInfo serviceInfo = new JaxWsImplementorInfo(serviceClass);
264            
265            // set wsdl service
266            if (portInfo.getWsdlService() == null) {
267                generator.setWsdlService(serviceInfo.getServiceName());
268            } else {
269                generator.setWsdlService(portInfo.getWsdlService());
270            }
271            
272            // set wsdl port
273            if (portInfo.getWsdlPort() != null) {
274                generator.setWsdlPort(portInfo.getWsdlPort());
275            }
276                            
277            String wsdlFile = generator.generateWsdl(module, serviceClass.getName(), module.getEarContext(), portInfo);
278            portInfo.setWsdlFile(wsdlFile);
279            
280            LOG.debug("Generated " + wsdlFile + " for service " + portInfo.getServiceName()); 
281        }   
282        
283        private boolean isWsdlSet(PortInfo portInfo, Class serviceClass) {
284            return (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().trim().equals(""))
285                    || JAXWSUtils.containsWsdlLocation(serviceClass, serviceClass.getClassLoader());
286        }    
287        
288        public static final GBeanInfo GBEAN_INFO;
289    
290        static {
291            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(CXFBuilder.class, NameFactory.MODULE_BUILDER);
292            infoBuilder.addInterface(WebServiceBuilder.class);
293            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
294    
295            infoBuilder.setConstructor(new String[]{"defaultEnvironment"});
296    
297            GBEAN_INFO = infoBuilder.getBeanInfo();
298        }
299    
300        public static GBeanInfo getGBeanInfo() {
301            return GBEAN_INFO;
302        }
303    
304    }