001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  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, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.webservices.builder;
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.lang.reflect.Method;
023    import java.math.BigDecimal;
024    import java.math.BigInteger;
025    import java.net.URI;
026    import java.net.URISyntaxException;
027    import java.net.URL;
028    import java.util.ArrayList;
029    import java.util.Calendar;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.jar.JarFile;
034    import java.util.zip.ZipEntry;
035    
036    import javax.wsdl.Definition;
037    import javax.wsdl.Operation;
038    import javax.wsdl.Port;
039    import javax.xml.namespace.QName;
040    import javax.xml.rpc.handler.HandlerInfo;
041    import javax.xml.rpc.holders.BigDecimalHolder;
042    import javax.xml.rpc.holders.BigIntegerHolder;
043    import javax.xml.rpc.holders.BooleanHolder;
044    import javax.xml.rpc.holders.BooleanWrapperHolder;
045    import javax.xml.rpc.holders.ByteArrayHolder;
046    import javax.xml.rpc.holders.ByteHolder;
047    import javax.xml.rpc.holders.ByteWrapperHolder;
048    import javax.xml.rpc.holders.CalendarHolder;
049    import javax.xml.rpc.holders.DoubleHolder;
050    import javax.xml.rpc.holders.DoubleWrapperHolder;
051    import javax.xml.rpc.holders.FloatHolder;
052    import javax.xml.rpc.holders.FloatWrapperHolder;
053    import javax.xml.rpc.holders.IntHolder;
054    import javax.xml.rpc.holders.IntegerWrapperHolder;
055    import javax.xml.rpc.holders.LongHolder;
056    import javax.xml.rpc.holders.LongWrapperHolder;
057    import javax.xml.rpc.holders.ObjectHolder;
058    import javax.xml.rpc.holders.QNameHolder;
059    import javax.xml.rpc.holders.ShortHolder;
060    import javax.xml.rpc.holders.ShortWrapperHolder;
061    import javax.xml.rpc.holders.StringHolder;
062    
063    import org.apache.geronimo.common.DeploymentException;
064    import org.apache.geronimo.kernel.ClassLoading;
065    import org.apache.geronimo.xbeans.j2ee.ExceptionMappingType;
066    import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingDocument;
067    import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
068    import org.apache.geronimo.xbeans.j2ee.PackageMappingType;
069    import org.apache.geronimo.xbeans.j2ee.ParamValueType;
070    import org.apache.geronimo.xbeans.j2ee.PortComponentHandlerType;
071    import org.apache.geronimo.xbeans.j2ee.PortComponentType;
072    import org.apache.geronimo.xbeans.j2ee.ServiceEndpointInterfaceMappingType;
073    import org.apache.geronimo.xbeans.j2ee.ServiceEndpointMethodMappingType;
074    import org.apache.geronimo.xbeans.j2ee.ServiceImplBeanType;
075    import org.apache.geronimo.xbeans.j2ee.WebserviceDescriptionType;
076    import org.apache.geronimo.xbeans.j2ee.WebservicesDocument;
077    import org.apache.geronimo.xbeans.j2ee.WebservicesType;
078    import org.apache.geronimo.xbeans.j2ee.XsdQNameType;
079    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
080    import org.apache.geronimo.webservices.builder.PortInfo;
081    import org.apache.geronimo.webservices.builder.SchemaInfoBuilder;
082    import org.apache.xmlbeans.XmlException;
083    
084    /**
085     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
086     */
087    public class WSDescriptorParser {
088    
089    
090        public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, URI jaxrpcMappingURI) throws DeploymentException {
091            String jaxrpcMappingPath = jaxrpcMappingURI.toString();
092            return readJaxrpcMapping(moduleFile, jaxrpcMappingPath);
093        }
094    
095        public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, String jaxrpcMappingPath) throws DeploymentException {
096            JavaWsdlMappingType mapping;
097            InputStream jaxrpcInputStream = null;
098            try {
099                ZipEntry zipEntry = moduleFile.getEntry(jaxrpcMappingPath);
100                if(zipEntry == null){
101                    throw new DeploymentException("The JAX-RPC mapping file "+jaxrpcMappingPath+" specified in webservices.xml for the ejb module could not be found.");
102                }
103                jaxrpcInputStream = moduleFile.getInputStream(zipEntry);
104            } catch (IOException e) {
105                throw new DeploymentException("Could not open stream to jaxrpc mapping document", e);
106            }
107            JavaWsdlMappingDocument mappingDocument = null;
108            try {
109                mappingDocument = JavaWsdlMappingDocument.Factory.parse(jaxrpcInputStream);
110            } catch (XmlException e) {
111                throw new DeploymentException("Could not parse jaxrpc mapping document", e);
112            } catch (IOException e) {
113                throw new DeploymentException("Could not read jaxrpc mapping document", e);
114            }
115            mapping = mappingDocument.getJavaWsdlMapping();
116            return mapping;
117        }
118    
119    
120        public static Map getExceptionMap(JavaWsdlMappingType mapping) {
121            Map exceptionMap = new HashMap();
122            if (mapping != null) {
123                ExceptionMappingType[] exceptionMappings = mapping.getExceptionMappingArray();
124                for (int i = 0; i < exceptionMappings.length; i++) {
125                    ExceptionMappingType exceptionMapping = exceptionMappings[i];
126                    QName exceptionMessageQName = exceptionMapping.getWsdlMessage().getQNameValue();
127                    exceptionMap.put(exceptionMessageQName, exceptionMapping);
128                }
129            }
130            return exceptionMap;
131        }
132    
133        public static String getPackageFromNamespace(String namespace, JavaWsdlMappingType mapping) throws DeploymentException {
134            PackageMappingType[] packageMappings = mapping.getPackageMappingArray();
135            for (int i = 0; i < packageMappings.length; i++) {
136                PackageMappingType packageMapping = packageMappings[i];
137                if (namespace.equals(packageMapping.getNamespaceURI().getStringValue().trim())) {
138                    return packageMapping.getPackageType().getStringValue().trim();
139                }
140            }
141            throw new DeploymentException("Namespace " + namespace + " was not mapped in jaxrpc mapping file");
142        }
143    
144        private static final Map rpcHolderClasses = new HashMap();
145    
146        static {
147            rpcHolderClasses.put(BigDecimal.class, BigDecimalHolder.class);
148            rpcHolderClasses.put(BigInteger.class, BigIntegerHolder.class);
149            rpcHolderClasses.put(boolean.class, BooleanHolder.class);
150            rpcHolderClasses.put(Boolean.class, BooleanWrapperHolder.class);
151            rpcHolderClasses.put(byte[].class, ByteArrayHolder.class);
152            rpcHolderClasses.put(byte.class, ByteHolder.class);
153            rpcHolderClasses.put(Byte.class, ByteWrapperHolder.class);
154            rpcHolderClasses.put(Calendar.class, CalendarHolder.class);
155            rpcHolderClasses.put(double.class, DoubleHolder.class);
156            rpcHolderClasses.put(Double.class, DoubleWrapperHolder.class);
157            rpcHolderClasses.put(float.class, FloatHolder.class);
158            rpcHolderClasses.put(Float.class, FloatWrapperHolder.class);
159            rpcHolderClasses.put(int.class, IntHolder.class);
160            rpcHolderClasses.put(Integer.class, IntegerWrapperHolder.class);
161            rpcHolderClasses.put(long.class, LongHolder.class);
162            rpcHolderClasses.put(Long.class, LongWrapperHolder.class);
163            rpcHolderClasses.put(Object.class, ObjectHolder.class);
164            rpcHolderClasses.put(QName.class, QNameHolder.class);
165            rpcHolderClasses.put(short.class, ShortHolder.class);
166            rpcHolderClasses.put(Short.class, ShortWrapperHolder.class);
167            rpcHolderClasses.put(String.class, StringHolder.class);
168        }
169    
170        public static Class getHolderType(String paramJavaTypeName, boolean isInOnly, QName typeQName, boolean isComplexType, JavaWsdlMappingType mapping, ClassLoader classLoader) throws DeploymentException {
171            Class paramJavaType = null;
172            if (isInOnly) {
173                //IN parameters just use their own type
174                try {
175                    paramJavaType = ClassLoading.loadClass(paramJavaTypeName, classLoader);
176                } catch (ClassNotFoundException e) {
177                    throw new DeploymentException("could not load parameter type", e);
178                }
179                return paramJavaType;
180            } else {
181                //INOUT and OUT parameters use holders.  See jaxrpc spec 4.3.5
182                String holderName;
183                if (isComplexType) {
184                    //complex types get mapped:
185                    //package is determined from the namespace to package map + ".holders"
186                    //class name is the complex type QNMAne local part + "Holder", with the initial character uppercased.
187                    String namespace = typeQName.getNamespaceURI();
188                    String packageName = WSDescriptorParser.getPackageFromNamespace(namespace, mapping);
189                    StringBuffer buf = new StringBuffer(packageName.length() + typeQName.getLocalPart().length() + 14);
190                    buf.append(packageName).append(".holders.").append(typeQName.getLocalPart()).append("Holder");
191                    buf.setCharAt(packageName.length() + 9, Character.toUpperCase(typeQName.getLocalPart().charAt(0)));
192                    holderName = buf.toString();
193                } else {
194                    //see if it is in the primitive type and simple type mapping
195                    try {
196                        paramJavaType = ClassLoading.loadClass(paramJavaTypeName, classLoader);
197                    } catch (ClassNotFoundException e) {
198                        throw new DeploymentException("could not load parameter type", e);
199                    }
200                    Class holder = (Class) rpcHolderClasses.get(paramJavaType);
201                    if (holder != null) {
202                        try {
203                            //TODO use class names in map or make sure we are in the correct classloader to start with.
204                            holder = ClassLoading.loadClass(holder.getName(), classLoader);
205                        } catch (ClassNotFoundException e) {
206                            throw new DeploymentException("could not load holder type in correct classloader", e);
207                        }
208                        return holder;
209                    }
210                    //Otherwise, the holder must be in:
211                    //package same as type's package + ".holders"
212                    //class name same as type name + "Holder"
213                    String paramTypeName = paramJavaType.getName();
214                    StringBuffer buf = new StringBuffer(paramTypeName.length() + 14);
215                    int dot = paramTypeName.lastIndexOf(".");
216                    //foo.Bar >>> foo.holders.BarHolder
217                    buf.append(paramTypeName.substring(0, dot)).append(".holders").append(paramTypeName.substring(dot)).append("Holder");
218                    holderName = buf.toString();
219                }
220                try {
221                    Class holder = ClassLoading.loadClass(holderName, classLoader);
222                    return holder;
223                } catch (ClassNotFoundException e) {
224                    throw new DeploymentException("Could not load holder class", e);
225                }
226            }
227        }
228    
229        public static ServiceEndpointMethodMappingType getMethodMappingForOperation(String operationName, ServiceEndpointMethodMappingType[] methodMappings) throws DeploymentException {
230            for (int i = 0; i < methodMappings.length; i++) {
231                ServiceEndpointMethodMappingType methodMapping = methodMappings[i];
232                if (operationName.equals(methodMapping.getWsdlOperation().getStringValue())) {
233                    return methodMapping;
234                }
235            }
236            // Build list of available operations for exception
237            StringBuffer availOps = new StringBuffer(128);
238            for (int i = 0; i < methodMappings.length; i++) {
239                if (i != 0) availOps.append(",");
240                availOps.append(methodMappings[i].getWsdlOperation().getStringValue());
241            }
242            throw new DeploymentException("No method found for operation named '" + operationName + "'. Available operations: " + availOps);
243        }
244    
245        public static ServiceEndpointInterfaceMappingType getServiceEndpointInterfaceMapping(ServiceEndpointInterfaceMappingType[] endpointMappings, QName portTypeQName) throws DeploymentException {
246            for (int i = 0; i < endpointMappings.length; i++) {
247                ServiceEndpointInterfaceMappingType endpointMapping = endpointMappings[i];
248                QName testPortQName = endpointMapping.getWsdlPortType().getQNameValue();
249                if (portTypeQName.equals(testPortQName)) {
250                    return endpointMapping;
251                }
252            }
253            throw new DeploymentException("Could not find service endpoint interface for port named " + portTypeQName);
254        }
255    
256        public static javax.wsdl.Service getService(QName serviceQName, Definition definition) throws DeploymentException {
257            javax.wsdl.Service service;
258            if (serviceQName != null) {
259                service = definition.getService(serviceQName);
260            } else {
261                Map services = definition.getServices();
262                if (services.size() != 1) {
263                    throw new DeploymentException("no serviceQName supplied, and there are " + services.size() + " services");
264                }
265                service = (javax.wsdl.Service) services.values().iterator().next();
266            }
267            if (service == null) {
268                throw new DeploymentException("No service wsdl for supplied service qname " + serviceQName);
269            }
270            return service;
271        }
272    
273        public static Method getMethodForOperation(Class serviceEndpointInterface, Operation operation) throws DeploymentException {
274            Method[] methods = serviceEndpointInterface.getMethods();
275            String opName = operation.getName();
276            Method found = null;
277            for (int i = 0; i < methods.length; i++) {
278                Method method = methods[i];
279                if (method.getName().equals(opName)) {
280                    if (found != null) {
281                        throw new DeploymentException("Overloaded methods are not allowed in lightweight mappings");
282                    }
283                    found = method;
284                }
285            }
286            if (found == null) {
287                throw new DeploymentException("No method found for operation named " + opName);
288            }
289            return found;
290        }
291    
292        /**
293         * Parses a webservice.xml file and returns a map PortInfo instances indexed by the
294         * corresponding ejb-link or servlet-link element .
295         *
296         * @param webservicesType
297         * @param moduleFile
298         * @param isEJB
299         * @param servletLocations
300         * @return
301         * @throws org.apache.geronimo.common.DeploymentException
302         */
303        public static Map parseWebServiceDescriptor(WebservicesType webservicesType, JarFile moduleFile, boolean isEJB, Map servletLocations) throws DeploymentException {
304            Map portMap = new HashMap();
305            WebserviceDescriptionType[] webserviceDescriptions = webservicesType.getWebserviceDescriptionArray();
306            for (int i = 0; i < webserviceDescriptions.length; i++) {
307                WebserviceDescriptionType webserviceDescription = webserviceDescriptions[i];
308                URI wsdlURI = null;
309                try {
310                    wsdlURI = new URI(webserviceDescription.getWsdlFile().getStringValue().trim());
311                } catch (URISyntaxException e) {
312                    throw new DeploymentException("could not construct wsdl uri from " + webserviceDescription.getWsdlFile().getStringValue(), e);
313                }
314                URI jaxrpcMappingURI = null;
315                try {
316                    jaxrpcMappingURI = new URI(webserviceDescription.getJaxrpcMappingFile().getStringValue().trim());
317                } catch (URISyntaxException e) {
318                    throw new DeploymentException("Could not construct jaxrpc mapping uri from " + webserviceDescription.getJaxrpcMappingFile(), e);
319                }
320                SchemaInfoBuilder schemaInfoBuilder =  new SchemaInfoBuilder(moduleFile, wsdlURI);
321                Map wsdlPortMap = schemaInfoBuilder.getPortMap();
322    
323                JavaWsdlMappingType javaWsdlMapping = readJaxrpcMapping(moduleFile, jaxrpcMappingURI);
324                HashMap seiMappings = new HashMap();
325                ServiceEndpointInterfaceMappingType[] mappings = javaWsdlMapping.getServiceEndpointInterfaceMappingArray();
326                for (int j = 0; j < mappings.length; j++) {
327                    ServiceEndpointInterfaceMappingType seiMapping = mappings[j];
328                    seiMappings.put(seiMapping.getServiceEndpointInterface().getStringValue(), seiMapping);
329                }
330    
331                PortComponentType[] portComponents = webserviceDescription.getPortComponentArray();
332                for (int j = 0; j < portComponents.length; j++) {
333                    PortComponentType portComponent = portComponents[j];
334                    String portComponentName = portComponent.getPortComponentName().getStringValue().trim();
335                    QName portQName = portComponent.getWsdlPort().getQNameValue();
336                    String seiInterfaceName = portComponent.getServiceEndpointInterface().getStringValue().trim();
337                    ServiceImplBeanType serviceImplBeanType = portComponent.getServiceImplBean();
338                    if (isEJB == serviceImplBeanType.isSetServletLink()) {
339                        throw new DeploymentException("Wrong kind of web service described in web service descriptor: expected " + (isEJB ? "EJB" : "POJO(Servlet)"));
340                    }
341                    String linkName;
342                    String servletLocation;
343                    if (serviceImplBeanType.isSetServletLink()) {
344                        linkName = serviceImplBeanType.getServletLink().getStringValue().trim();
345                        servletLocation = (String) servletLocations.get(linkName);
346                        if (servletLocation == null) {
347                            throw new DeploymentException("No servlet mapping for port " + portQName);
348                        }
349                        schemaInfoBuilder.movePortLocation(portQName.getLocalPart(), servletLocation);
350                    } else {
351                        linkName = serviceImplBeanType.getEjbLink().getStringValue().trim();
352                        servletLocation = (String) servletLocations.get(linkName);
353                        servletLocation = schemaInfoBuilder.movePortLocation(portQName.getLocalPart(), servletLocation);
354                    }
355                    PortComponentHandlerType[] handlers = portComponent.getHandlerArray();
356    
357                    Port port = (Port) wsdlPortMap.get(portQName.getLocalPart());
358                    if (port == null) {
359                        throw new DeploymentException("No WSDL Port definition for port-component " + portComponentName);
360                    }
361    
362                    ServiceEndpointInterfaceMappingType seiMapping = (ServiceEndpointInterfaceMappingType) seiMappings.get(seiInterfaceName);
363    
364                    String wsdlLocation = webserviceDescription.getWsdlFile().getStringValue().trim();
365                    URI contextURI = null;
366                    try {
367                        contextURI = new URI(servletLocation);
368                    } catch (URISyntaxException e) {
369                        throw new DeploymentException("Could not construct URI for web service location", e);
370                    }
371    
372                    PortInfo portInfo = new PortInfo(portComponentName, portQName, schemaInfoBuilder, javaWsdlMapping, seiInterfaceName, handlers, port, seiMapping, wsdlLocation, contextURI);
373    
374                    if (portMap.put(linkName, portInfo) != null) {
375                        throw new DeploymentException("Ambiguous description of port associated with j2ee component " + linkName);
376                    }
377                }
378            }
379            return portMap;
380        }
381    
382        public static Map parseWebServiceDescriptor(URL wsDDUrl, JarFile moduleFile, boolean isEJB, Map servletLocations) throws DeploymentException {
383            try {
384                WebservicesDocument webservicesDocument = WebservicesDocument.Factory.parse(wsDDUrl);
385                XmlBeansUtil.validateDD(webservicesDocument);
386                WebservicesType webservicesType = webservicesDocument.getWebservices();
387                return parseWebServiceDescriptor(webservicesType, moduleFile, isEJB, servletLocations);
388            } catch (XmlException e) {
389                throw new DeploymentException("Could not read descriptor document", e);
390            } catch (IOException e) {
391                return null;
392            }
393    
394        }
395    
396        public static List createHandlerInfoList(PortComponentHandlerType[] handlers, ClassLoader classLoader) throws DeploymentException {
397            List list = new ArrayList();
398            for (int i = 0; i < handlers.length; i++) {
399                PortComponentHandlerType handler = handlers[i];
400    
401                // Get handler class
402                Class handlerClass = null;
403                String className = handler.getHandlerClass().getStringValue().trim();
404                try {
405                    handlerClass = classLoader.loadClass(className);
406                } catch (ClassNotFoundException e) {
407                    throw new DeploymentException("Unable to load handler class: " + className, e);
408                }
409    
410                // config data for the handler
411                Map config = new HashMap();
412                ParamValueType[] paramValues = handler.getInitParamArray();
413                for (int j = 0; j < paramValues.length; j++) {
414                    ParamValueType paramValue = paramValues[j];
415                    String paramName = paramValue.getParamName().getStringValue().trim();
416                    String paramStringValue = paramValue.getParamValue().getStringValue().trim();
417                    config.put(paramName, paramStringValue);
418                }
419    
420                // QName array of headers it processes
421                XsdQNameType[] soapHeaderQNames = handler.getSoapHeaderArray();
422                QName[] headers = new QName[soapHeaderQNames.length];
423                for (int j = 0; j < soapHeaderQNames.length; j++) {
424                    XsdQNameType soapHeaderQName = soapHeaderQNames[j];
425                    headers[j] = soapHeaderQName.getQNameValue();
426                }
427    
428                list.add(new HandlerInfo(handlerClass, config, headers));
429            }
430            return list;
431        }
432    }