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 explitely not specified, try to get it from 104 // the service class annotation 105 WebServiceClient webServiceClient = 106 (WebServiceClient) this.serviceClass.getAnnotation(WebServiceClient.class); 107 if (webServiceClient != null) { 108 this.wsdlURI = getWSDLLocation(webServiceClient); 109 this.serviceQName = getServiceQName(webServiceClient); 110 } 111 112 if (this.wsdlURI == null) { 113 return; 114 } 115 } 116 117 JarWSDLLocator wsdlLocator = null; 118 URL wsdlURL = null; 119 try { 120 wsdlURL = new URL(this.wsdlURI.toString()); 121 } catch (MalformedURLException e1) { 122 // not a URL, assume it's a local reference 123 wsdlLocator = new JarWSDLLocator(this.wsdlURI); 124 } 125 126 Definition definition; 127 WSDLFactory wsdlFactory; 128 try { 129 wsdlFactory = WSDLFactory.newInstance(); 130 } catch (WSDLException e) { 131 throw new DeploymentException("Could not create WSDLFactory", e); 132 } 133 WSDLReader wsdlReader = wsdlFactory.newWSDLReader(); 134 wsdlReader.setFeature("javax.wsdl.importDocuments", true); 135 wsdlReader.setFeature("javax.wsdl.verbose", false); 136 try { 137 if (wsdlURL != null) { 138 definition = wsdlReader.readWSDL(wsdlURL.toString()); 139 } else if (wsdlLocator != null) { 140 definition = wsdlReader.readWSDL(wsdlLocator); 141 } else { 142 throw new DeploymentException("unknown"); 143 } 144 } catch (WSDLException e) { 145 throw new DeploymentException("Failed to read wsdl document", e); 146 } catch (RuntimeException e) { 147 throw new DeploymentException(e.getMessage(), e); 148 } 149 150 verifyPortComponentList(definition); 151 152 Map services = definition.getServices(); 153 if (services.size() == 0) { 154 // partial wsdl, return as is 155 156 if (this.serviceRefType != null && this.serviceRefType.isSetServiceCompletion()) { 157 LOG.warn("Service completion is not supported with partial wsdl"); 158 } 159 } else { 160 // full wsdl 161 162 if (this.serviceRefType != null && this.serviceRefType.isSetServiceCompletion()) { 163 throw new DeploymentException("Full wsdl, but service completion supplied"); 164 } 165 166 Service service = null; 167 if (this.serviceQName != null) { 168 service = definition.getService(this.serviceQName); 169 if (service == null) { 170 throw new DeploymentException( 171 "No service wsdl for supplied service qname " 172 + this.serviceQName); 173 } 174 } else if (services.size() == 1) { 175 service = (Service) services.values().iterator().next(); 176 this.serviceQName = service.getQName(); 177 } else { 178 throw new DeploymentException( 179 "No service qname supplied, and there are " 180 + services.size() + " services"); 181 } 182 183 // organize the extra port info 184 Map<String, GerPortType> portMap = new HashMap<String, GerPortType>(); 185 if (serviceRefType != null) { 186 GerPortType[] ports = serviceRefType.getPortArray(); 187 for (int i = 0; i < ports.length; i++) { 188 GerPortType port = ports[i]; 189 String portName = port.getPortName().trim(); 190 portMap.put(portName, port); 191 } 192 } 193 194 Map wsdlPortMap = service.getPorts(); 195 for (Iterator iterator = wsdlPortMap.entrySet().iterator(); iterator.hasNext();) { 196 Map.Entry entry = (Map.Entry) iterator.next(); 197 String portName = (String) entry.getKey(); 198 Port port = (Port) entry.getValue(); 199 200 GerPortType gerPort = portMap.get(portName); 201 202 URL location = (gerPort == null) ? getAddressLocation(port) : getLocation(gerPort); 203 // skip non-soap ports 204 if (location == null) { 205 continue; 206 } 207 String credentialsName = (gerPort == null) ? null : getCredentialsName(gerPort); 208 209 Binding binding = port.getBinding(); 210 if (binding == null) { 211 throw new DeploymentException("No binding for port: " + portName); 212 } 213 214 PortType portType = binding.getPortType(); 215 if (portType == null) { 216 throw new DeploymentException("No portType for binding: " + binding.getQName()); 217 } 218 219 boolean mtomEnabled = isMTOMEnabled(portType.getQName()); 220 221 EndpointInfo info = new EndpointInfo(location, credentialsName, mtomEnabled); 222 this.portInfoMap.put(portName, info); 223 // prefer first binding listed in wsdl 224 if (!this.portInfoMap.containsKey(portType.getQName())) { 225 this.portInfoMap.put(portType.getQName(), info); 226 } 227 } 228 } 229 } 230 231 private QName getServiceQName(WebServiceClient webServiceClient) { 232 if (webServiceClient.targetNamespace() != null && webServiceClient.name() != null) { 233 return new QName(webServiceClient.targetNamespace(), webServiceClient.name()); 234 } else { 235 return null; 236 } 237 } 238 239 private URI getWSDLLocation(WebServiceClient webServiceClient) throws DeploymentException { 240 String wsdlLocation = webServiceClient.wsdlLocation(); 241 if (wsdlLocation != null && wsdlLocation.trim().length() > 0) { 242 try { 243 return new URI(wsdlLocation.trim()); 244 } catch (URISyntaxException e) { 245 throw new DeploymentException( 246 "Invalid wsdl location in annotation: " + wsdlLocation, e); 247 } 248 } 249 250 return null; 251 } 252 253 private String getCredentialsName(GerPortType port) { 254 String credentialsName = port.getCredentialsName(); 255 return (credentialsName == null) ? null : credentialsName.trim(); 256 } 257 258 private URL getLocation(GerPortType port) throws DeploymentException { 259 String protocol = port.getProtocol().trim(); 260 String host = port.getHost().trim(); 261 int portNum = port.getPort(); 262 String uri = port.getUri().trim(); 263 String locationURIString = protocol + "://" + host + ":" + portNum + uri; 264 URL location = getURL(locationURIString); 265 return location; 266 } 267 268 private URL getAddressLocation(Port port) throws DeploymentException { 269 SOAPAddress soapAddress = 270 (SOAPAddress) getExtensibilityElement(SOAPAddress.class, port.getExtensibilityElements()); 271 URL location = null; 272 if (soapAddress != null) { 273 String locationURIString = soapAddress.getLocationURI(); 274 location = getURL(locationURIString); 275 } 276 return location; 277 } 278 279 private URL getURL(String locationURIString) throws DeploymentException { 280 try { 281 return new URL(locationURIString); 282 } catch (MalformedURLException e) { 283 throw new DeploymentException( 284 "Could not construct web service location URL from " 285 + locationURIString, e); 286 } 287 } 288 289 public static ExtensibilityElement getExtensibilityElement(Class clazz, 290 List extensibilityElements) { 291 for (Iterator iterator = extensibilityElements.iterator(); iterator 292 .hasNext();) { 293 ExtensibilityElement extensibilityElement = (ExtensibilityElement) iterator 294 .next(); 295 if (clazz.isAssignableFrom(extensibilityElement.getClass())) { 296 return extensibilityElement; 297 } 298 } 299 return null; 300 } 301 302 private void verifyPortComponentList(Definition wsdl) throws DeploymentException { 303 if (this.portComponentRefMap == null) { 304 return; 305 } 306 for (Class sei : this.portComponentRefMap.keySet()) { 307 QName portType = JAXWSUtils.getPortType(sei); 308 if (portType == null) { 309 continue; 310 } 311 if (wsdl.getPortType(portType) == null) { 312 throw new DeploymentException("No portType found in WSDL for SEI: " + sei.getName()); 313 } 314 } 315 } 316 317 private boolean isMTOMEnabled(QName portType) { 318 boolean mtomEnabled = false; 319 PortComponentRefType portRef = getPortComponentRef(portType); 320 if (portRef != null && portRef.isSetEnableMtom()) { 321 mtomEnabled = portRef.getEnableMtom().getBooleanValue(); 322 } 323 return mtomEnabled; 324 } 325 326 private PortComponentRefType getPortComponentRef(QName portType) { 327 if (this.portComponentRefMap == null) { 328 return null; 329 } 330 for (Class sei : this.portComponentRefMap.keySet()) { 331 QName seiPortType = JAXWSUtils.getPortType(sei); 332 if (seiPortType == null) { 333 continue; 334 } 335 if (portType.equals(seiPortType)) { 336 return this.portComponentRefMap.get(sei); 337 } 338 } 339 return null; 340 } 341 342 private class JarWSDLLocator implements WSDLLocator { 343 344 private final List<InputStream> streams = new ArrayList<InputStream>(); 345 346 private final URI wsdlURI; 347 348 private URI latestImportURI; 349 350 public JarWSDLLocator(URI wsdlURI) { 351 this.wsdlURI = wsdlURI; 352 } 353 354 public InputSource getBaseInputSource() { 355 InputStream wsdlInputStream; 356 ZipEntry entry = moduleFile.getEntry(wsdlURI.toString()); 357 if (entry == null) { 358 throw new RuntimeException( 359 "WSDL file does not exist in the module " + wsdlURI.toString()); 360 } 361 try { 362 wsdlInputStream = moduleFile.getInputStream(entry); 363 streams.add(wsdlInputStream); 364 } catch (Exception e) { 365 throw new RuntimeException( 366 "Could not open stream to wsdl file", e); 367 } 368 return new InputSource(wsdlInputStream); 369 } 370 371 public String getBaseURI() { 372 return wsdlURI.toString(); 373 } 374 375 public InputSource getImportInputSource(String parentLocation, 376 String relativeLocation) { 377 URI parentURI = URI.create(parentLocation); 378 latestImportURI = parentURI.resolve(relativeLocation); 379 InputStream importInputStream; 380 ZipEntry entry = moduleFile.getEntry(latestImportURI.toString()); 381 if (entry == null) { 382 throw new RuntimeException( 383 "File does not exist in the module " + latestImportURI.toString()); 384 } 385 try { 386 importInputStream = moduleFile.getInputStream(entry); 387 streams.add(importInputStream); 388 } catch (Exception e) { 389 throw new RuntimeException( 390 "Could not open stream to import file", e); 391 } 392 InputSource inputSource = new InputSource(importInputStream); 393 inputSource.setSystemId(getLatestImportURI()); 394 return inputSource; 395 } 396 397 public String getLatestImportURI() { 398 return latestImportURI.toString(); 399 } 400 401 public void close() { 402 for (InputStream inputStream : this.streams) { 403 try { 404 inputStream.close(); 405 } catch (IOException e) { 406 // ignore 407 } 408 } 409 streams.clear(); 410 } 411 } 412 }