View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.webservices.builder;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.lang.reflect.Method;
23  import java.math.BigDecimal;
24  import java.math.BigInteger;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.util.ArrayList;
29  import java.util.Calendar;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.jar.JarFile;
34  import java.util.zip.ZipEntry;
35  
36  import javax.wsdl.Definition;
37  import javax.wsdl.Operation;
38  import javax.wsdl.Port;
39  import javax.xml.namespace.QName;
40  import javax.xml.rpc.handler.HandlerInfo;
41  import javax.xml.rpc.holders.BigDecimalHolder;
42  import javax.xml.rpc.holders.BigIntegerHolder;
43  import javax.xml.rpc.holders.BooleanHolder;
44  import javax.xml.rpc.holders.BooleanWrapperHolder;
45  import javax.xml.rpc.holders.ByteArrayHolder;
46  import javax.xml.rpc.holders.ByteHolder;
47  import javax.xml.rpc.holders.ByteWrapperHolder;
48  import javax.xml.rpc.holders.CalendarHolder;
49  import javax.xml.rpc.holders.DoubleHolder;
50  import javax.xml.rpc.holders.DoubleWrapperHolder;
51  import javax.xml.rpc.holders.FloatHolder;
52  import javax.xml.rpc.holders.FloatWrapperHolder;
53  import javax.xml.rpc.holders.IntHolder;
54  import javax.xml.rpc.holders.IntegerWrapperHolder;
55  import javax.xml.rpc.holders.LongHolder;
56  import javax.xml.rpc.holders.LongWrapperHolder;
57  import javax.xml.rpc.holders.ObjectHolder;
58  import javax.xml.rpc.holders.QNameHolder;
59  import javax.xml.rpc.holders.ShortHolder;
60  import javax.xml.rpc.holders.ShortWrapperHolder;
61  import javax.xml.rpc.holders.StringHolder;
62  
63  import org.apache.geronimo.common.DeploymentException;
64  import org.apache.geronimo.kernel.ClassLoading;
65  import org.apache.geronimo.xbeans.j2ee.ExceptionMappingType;
66  import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingDocument;
67  import org.apache.geronimo.xbeans.j2ee.JavaWsdlMappingType;
68  import org.apache.geronimo.xbeans.j2ee.PackageMappingType;
69  import org.apache.geronimo.xbeans.j2ee.ParamValueType;
70  import org.apache.geronimo.xbeans.j2ee.PortComponentHandlerType;
71  import org.apache.geronimo.xbeans.j2ee.PortComponentType;
72  import org.apache.geronimo.xbeans.j2ee.ServiceEndpointInterfaceMappingType;
73  import org.apache.geronimo.xbeans.j2ee.ServiceEndpointMethodMappingType;
74  import org.apache.geronimo.xbeans.j2ee.ServiceImplBeanType;
75  import org.apache.geronimo.xbeans.j2ee.WebserviceDescriptionType;
76  import org.apache.geronimo.xbeans.j2ee.WebservicesDocument;
77  import org.apache.geronimo.xbeans.j2ee.WebservicesType;
78  import org.apache.geronimo.xbeans.j2ee.XsdQNameType;
79  import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
80  import org.apache.geronimo.webservices.builder.PortInfo;
81  import org.apache.geronimo.webservices.builder.SchemaInfoBuilder;
82  import org.apache.xmlbeans.XmlException;
83  
84  /**
85   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
86   */
87  public class WSDescriptorParser {
88  
89  
90      public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, URI jaxrpcMappingURI) throws DeploymentException {
91          String jaxrpcMappingPath = jaxrpcMappingURI.toString();
92          return readJaxrpcMapping(moduleFile, jaxrpcMappingPath);
93      }
94  
95      public static JavaWsdlMappingType readJaxrpcMapping(JarFile moduleFile, String jaxrpcMappingPath) throws DeploymentException {
96          JavaWsdlMappingType mapping;
97          InputStream jaxrpcInputStream = null;
98          try {
99              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 }