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 }