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 }