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.jaxws.builder; 018 019 import java.io.IOException; 020 import java.io.InputStream; 021 import java.net.MalformedURLException; 022 import java.net.URI; 023 import java.net.URISyntaxException; 024 import java.net.URL; 025 import java.util.ArrayList; 026 import java.util.HashMap; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.jar.JarFile; 031 import java.util.zip.ZipEntry; 032 033 import javax.wsdl.Binding; 034 import javax.wsdl.Definition; 035 import javax.wsdl.Port; 036 import javax.wsdl.PortType; 037 import javax.wsdl.Service; 038 import javax.wsdl.WSDLException; 039 import javax.wsdl.extensions.ExtensibilityElement; 040 import javax.wsdl.extensions.soap.SOAPAddress; 041 import javax.wsdl.factory.WSDLFactory; 042 import javax.wsdl.xml.WSDLLocator; 043 import javax.wsdl.xml.WSDLReader; 044 import javax.xml.namespace.QName; 045 import javax.xml.ws.WebServiceClient; 046 047 import org.apache.commons.logging.Log; 048 import org.apache.commons.logging.LogFactory; 049 import org.apache.geronimo.common.DeploymentException; 050 import org.apache.geronimo.jaxws.JAXWSUtils; 051 import org.apache.geronimo.jaxws.client.EndpointInfo; 052 import org.apache.geronimo.xbeans.geronimo.naming.GerPortType; 053 import org.apache.geronimo.xbeans.geronimo.naming.GerServiceRefType; 054 import org.apache.geronimo.xbeans.javaee.PortComponentRefType; 055 import org.xml.sax.InputSource; 056 057 public class EndpointInfoBuilder { 058 059 private static final Log LOG = LogFactory.getLog(EndpointInfoBuilder.class); 060 061 private JarFile moduleFile; 062 063 private URI wsdlURI; 064 065 private QName serviceQName; 066 067 private Class serviceClass; 068 069 private GerServiceRefType serviceRefType; 070 071 private Map<Object, EndpointInfo> portInfoMap = new HashMap<Object, EndpointInfo>(); 072 073 private Map<Class, PortComponentRefType> portComponentRefMap; 074 075 public EndpointInfoBuilder(Class serviceClass, 076 GerServiceRefType serviceRefType, 077 Map<Class, PortComponentRefType> portComponentRefMap, 078 JarFile moduleFile, 079 URI wsdlURI, 080 QName serviceQName) { 081 this.serviceClass = serviceClass; 082 this.serviceRefType = serviceRefType; 083 this.portComponentRefMap = portComponentRefMap; 084 this.moduleFile = moduleFile; 085 this.wsdlURI = wsdlURI; 086 this.serviceQName = serviceQName; 087 } 088 089 public URI getWsdlURI() { 090 return this.wsdlURI; 091 } 092 093 public QName getServiceQName() { 094 return this.serviceQName; 095 } 096 097 public Map<Object, EndpointInfo> getEndpointInfo() { 098 return this.portInfoMap; 099 } 100 101 public void build() throws DeploymentException { 102 if (this.wsdlURI == null) { 103 // wsdl was not explicitly specified 104 if (javax.xml.ws.Service.class.equals(this.serviceClass)) { 105 // Generic Service class specified. 106 // Service API requires a service qname so create a dummy one 107 this.serviceQName = new QName("http://noservice", "noservice"); 108 return; 109 } else { 110 // Generated Service class specified. 111 // Get the wsdl and service qname from the WebServiceClient annotation 112 // of the generated Service class 113 WebServiceClient webServiceClient = 114 (WebServiceClient) this.serviceClass.getAnnotation(WebServiceClient.class); 115 if (webServiceClient != null) { 116 this.wsdlURI = getWSDLLocation(webServiceClient); 117 this.serviceQName = getServiceQName(webServiceClient); 118 } 119 120 // wsdl really shouldn't be null at this point 121 if (this.wsdlURI == null) { 122 return; 123 } 124 } 125 } 126 127 JarWSDLLocator wsdlLocator = null; 128 URL wsdlURL = null; 129 try { 130 wsdlURL = new URL(this.wsdlURI.toString()); 131 } catch (MalformedURLException e1) { 132 // not a URL, assume it's a local reference 133 wsdlLocator = new JarWSDLLocator(this.wsdlURI); 134 } 135 136 Definition definition; 137 WSDLFactory wsdlFactory; 138 try { 139 wsdlFactory = WSDLFactory.newInstance(); 140 } catch (WSDLException e) { 141 throw new DeploymentException("Could not create WSDLFactory", e); 142 } 143 WSDLReader wsdlReader = wsdlFactory.newWSDLReader(); 144 wsdlReader.setFeature("javax.wsdl.importDocuments", true); 145 wsdlReader.setFeature("javax.wsdl.verbose", false); 146 try { 147 if (wsdlURL != null) { 148 definition = wsdlReader.readWSDL(wsdlURL.toString()); 149 } else if (wsdlLocator != null) { 150 definition = wsdlReader.readWSDL(wsdlLocator); 151 } else { 152 throw new DeploymentException("unknown"); 153 } 154 } catch (WSDLException e) { 155 throw new DeploymentException("Failed to read wsdl document", e); 156 } catch (RuntimeException e) { 157 throw new DeploymentException(e.getMessage(), e); 158 } 159 160 verifyPortComponentList(definition); 161 162 Map services = definition.getServices(); 163 if (services.size() == 0) { 164 // partial wsdl, return as is 165 166 if (this.serviceRefType != null && this.serviceRefType.isSetServiceCompletion()) { 167 LOG.warn("Service completion is not supported with partial wsdl"); 168 } 169 } else { 170 // full wsdl 171 172 if (this.serviceRefType != null && this.serviceRefType.isSetServiceCompletion()) { 173 throw new DeploymentException("Full wsdl, but service completion supplied"); 174 } 175 176 Service service = null; 177 if (this.serviceQName != null) { 178 service = definition.getService(this.serviceQName); 179 if (service == null) { 180 throw new DeploymentException( 181 "No service wsdl for supplied service qname " 182 + this.serviceQName); 183 } 184 } else if (services.size() == 1) { 185 service = (Service) services.values().iterator().next(); 186 this.serviceQName = service.getQName(); 187 } else { 188 throw new DeploymentException( 189 "No service qname supplied, and there are " 190 + services.size() + " services"); 191 } 192 193 // organize the extra port info 194 Map<String, GerPortType> portMap = new HashMap<String, GerPortType>(); 195 if (serviceRefType != null) { 196 GerPortType[] ports = serviceRefType.getPortArray(); 197 for (int i = 0; i < ports.length; i++) { 198 GerPortType port = ports[i]; 199 String portName = port.getPortName().trim(); 200 portMap.put(portName, port); 201 } 202 } 203 204 Map wsdlPortMap = service.getPorts(); 205 for (Iterator iterator = wsdlPortMap.entrySet().iterator(); iterator.hasNext();) { 206 Map.Entry entry = (Map.Entry) iterator.next(); 207 String portName = (String) entry.getKey(); 208 Port port = (Port) entry.getValue(); 209 210 GerPortType gerPort = portMap.get(portName); 211 212 URL location = (gerPort == null) ? getAddressLocation(port) : getLocation(gerPort); 213 // skip non-soap ports 214 if (location == null) { 215 continue; 216 } 217 String credentialsName = (gerPort == null) ? null : getCredentialsName(gerPort); 218 219 Binding binding = port.getBinding(); 220 if (binding == null) { 221 throw new DeploymentException("No binding for port: " + portName); 222 } 223 224 PortType portType = binding.getPortType(); 225 if (portType == null) { 226 throw new DeploymentException("No portType for binding: " + binding.getQName()); 227 } 228 229 boolean mtomEnabled = isMTOMEnabled(portType.getQName()); 230 231 EndpointInfo info = new EndpointInfo(location, credentialsName, mtomEnabled); 232 this.portInfoMap.put(portName, info); 233 // prefer first binding listed in wsdl 234 if (!this.portInfoMap.containsKey(portType.getQName())) { 235 this.portInfoMap.put(portType.getQName(), info); 236 } 237 } 238 } 239 } 240 241 private QName getServiceQName(WebServiceClient webServiceClient) { 242 if (webServiceClient.targetNamespace() != null && webServiceClient.name() != null) { 243 return new QName(webServiceClient.targetNamespace(), webServiceClient.name()); 244 } else { 245 return null; 246 } 247 } 248 249 private URI getWSDLLocation(WebServiceClient webServiceClient) throws DeploymentException { 250 String wsdlLocation = webServiceClient.wsdlLocation(); 251 if (wsdlLocation != null && wsdlLocation.trim().length() > 0) { 252 try { 253 return new URI(wsdlLocation.trim()); 254 } catch (URISyntaxException e) { 255 throw new DeploymentException( 256 "Invalid wsdl location in annotation: " + wsdlLocation, e); 257 } 258 } 259 260 return null; 261 } 262 263 private String getCredentialsName(GerPortType port) { 264 String credentialsName = port.getCredentialsName(); 265 return (credentialsName == null) ? null : credentialsName.trim(); 266 } 267 268 private URL getLocation(GerPortType port) throws DeploymentException { 269 String protocol = port.getProtocol().trim(); 270 String host = port.getHost().trim(); 271 int portNum = port.getPort(); 272 String uri = port.getUri().trim(); 273 String locationURIString = protocol + "://" + host + ":" + portNum + uri; 274 URL location = getURL(locationURIString); 275 return location; 276 } 277 278 private URL getAddressLocation(Port port) throws DeploymentException { 279 SOAPAddress soapAddress = 280 (SOAPAddress) getExtensibilityElement(SOAPAddress.class, port.getExtensibilityElements()); 281 URL location = null; 282 if (soapAddress != null) { 283 String locationURIString = soapAddress.getLocationURI(); 284 location = getURL(locationURIString); 285 } 286 return location; 287 } 288 289 private URL getURL(String locationURIString) throws DeploymentException { 290 try { 291 return new URL(locationURIString); 292 } catch (MalformedURLException e) { 293 throw new DeploymentException( 294 "Could not construct web service location URL from " 295 + locationURIString, e); 296 } 297 } 298 299 public static ExtensibilityElement getExtensibilityElement(Class clazz, 300 List extensibilityElements) { 301 for (Iterator iterator = extensibilityElements.iterator(); iterator 302 .hasNext();) { 303 ExtensibilityElement extensibilityElement = (ExtensibilityElement) iterator 304 .next(); 305 if (clazz.isAssignableFrom(extensibilityElement.getClass())) { 306 return extensibilityElement; 307 } 308 } 309 return null; 310 } 311 312 private void verifyPortComponentList(Definition wsdl) throws DeploymentException { 313 if (this.portComponentRefMap == null) { 314 return; 315 } 316 for (Class sei : this.portComponentRefMap.keySet()) { 317 QName portType = JAXWSUtils.getPortType(sei); 318 if (portType == null) { 319 continue; 320 } 321 if (wsdl.getPortType(portType) == null) { 322 throw new DeploymentException("No portType found in WSDL for SEI: " + sei.getName()); 323 } 324 } 325 } 326 327 private boolean isMTOMEnabled(QName portType) { 328 boolean mtomEnabled = false; 329 PortComponentRefType portRef = getPortComponentRef(portType); 330 if (portRef != null && portRef.isSetEnableMtom()) { 331 mtomEnabled = portRef.getEnableMtom().getBooleanValue(); 332 } 333 return mtomEnabled; 334 } 335 336 private PortComponentRefType getPortComponentRef(QName portType) { 337 if (this.portComponentRefMap == null) { 338 return null; 339 } 340 for (Class sei : this.portComponentRefMap.keySet()) { 341 QName seiPortType = JAXWSUtils.getPortType(sei); 342 if (seiPortType == null) { 343 continue; 344 } 345 if (portType.equals(seiPortType)) { 346 return this.portComponentRefMap.get(sei); 347 } 348 } 349 return null; 350 } 351 352 private class JarWSDLLocator implements WSDLLocator { 353 354 private final List<InputStream> streams = new ArrayList<InputStream>(); 355 356 private final URI wsdlURI; 357 358 private URI latestImportURI; 359 360 public JarWSDLLocator(URI wsdlURI) { 361 this.wsdlURI = wsdlURI; 362 } 363 364 public InputSource getBaseInputSource() { 365 InputStream wsdlInputStream; 366 ZipEntry entry = moduleFile.getEntry(wsdlURI.toString()); 367 if (entry == null) { 368 throw new RuntimeException( 369 "WSDL file does not exist in the module " + wsdlURI.toString()); 370 } 371 try { 372 wsdlInputStream = moduleFile.getInputStream(entry); 373 streams.add(wsdlInputStream); 374 } catch (Exception e) { 375 throw new RuntimeException( 376 "Could not open stream to wsdl file", e); 377 } 378 return new InputSource(wsdlInputStream); 379 } 380 381 public String getBaseURI() { 382 return wsdlURI.toString(); 383 } 384 385 public InputSource getImportInputSource(String parentLocation, 386 String relativeLocation) { 387 URI parentURI = URI.create(parentLocation); 388 latestImportURI = parentURI.resolve(relativeLocation); 389 InputStream importInputStream; 390 ZipEntry entry = moduleFile.getEntry(latestImportURI.toString()); 391 if (entry == null) { 392 throw new RuntimeException( 393 "File does not exist in the module " + latestImportURI.toString()); 394 } 395 try { 396 importInputStream = moduleFile.getInputStream(entry); 397 streams.add(importInputStream); 398 } catch (Exception e) { 399 throw new RuntimeException( 400 "Could not open stream to import file", e); 401 } 402 InputSource inputSource = new InputSource(importInputStream); 403 inputSource.setSystemId(getLatestImportURI()); 404 return inputSource; 405 } 406 407 public String getLatestImportURI() { 408 return latestImportURI.toString(); 409 } 410 411 public void close() { 412 for (InputStream inputStream : this.streams) { 413 try { 414 inputStream.close(); 415 } catch (IOException e) { 416 // ignore 417 } 418 } 419 streams.clear(); 420 } 421 } 422 }