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 }