View Javadoc

1   /*
2    *  Copyright (c) 2008 Rodrigo Ruiz
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.apache.axis.message.addressing.util;
17  
18  import java.io.StringReader;
19  import java.io.StringWriter;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import javax.wsdl.Input;
29  import javax.wsdl.Operation;
30  import javax.wsdl.Output;
31  import javax.wsdl.extensions.AttributeExtensible;
32  import javax.xml.namespace.QName;
33  import javax.xml.rpc.handler.MessageContext;
34  import javax.xml.soap.Name;
35  import javax.xml.soap.SOAPElement;
36  import javax.xml.soap.SOAPException;
37  import javax.xml.soap.SOAPHeader;
38  import javax.xml.soap.SOAPHeaderElement;
39  
40  import org.apache.axis.AxisEngine;
41  import org.apache.axis.message.addressing.AddressingHeaders;
42  import org.apache.axis.message.addressing.AddressingVersion;
43  import org.apache.axis.message.addressing.Constants;
44  import org.apache.axis.message.addressing.EndpointReference;
45  import org.apache.axis.utils.DOM2Writer;
46  import org.apache.axis.utils.XMLUtils;
47  import org.w3c.dom.Document;
48  import org.w3c.dom.Element;
49  import org.xml.sax.InputSource;
50  
51  /**
52   * A set of WS-Addressing-related utility methods.
53   *
54   * @author Jarek Gawor
55   * @author Rodrigo Ruiz
56   * @version $Revision: 14 $
57   */
58  public abstract class AddressingUtils implements Constants {
59  
60    /**
61     * A set that contains the namespace URIs (as Strings) for the various
62     * versions of WSA that we support.
63     */
64    private static final Set<String> ADDRESSING_NS_URI_SET = new HashSet<String>();
65  
66    /**
67     * A set that contains the name-space URIs (as Strings) for the various
68     * versions of W3C WS-Addressing supported by this library.
69     */
70    private static final Set<String> W3C_ADDRESSING_NS_URI_SET = new HashSet<String>();
71  
72    /**
73     * Maps name-spaces to AddressingVersion instances.
74     */
75    private static final Map<String, AddressingVersion> VERSIONS
76      = new HashMap<String, AddressingVersion>();
77  
78    /**
79     * Default addressing namespace. This namespace is used only if none is
80     * configured at MessageContext level.
81     */
82    private static final String DEFAULT_ADDRESSING_NS;
83  
84    static {
85      addVersion(NS_URI_ADDRESSING_2003_03, null, RESPONSE, false, false, true, false);
86      addVersion(NS_URI_ADDRESSING_2004_03, null, REPLY, false, false, true, false);
87      addVersion(NS_URI_ADDRESSING_2004_08, null, REPLY, false, true, true, false);
88  
89      addVersion(NS_URI_ADDRESSING_2005_08, URI_ANONYMOUS_W3C_CR, REPLY,
90                 true, true, true, true);
91  
92      // Determine default addressing namespace once
93      DEFAULT_ADDRESSING_NS = System.getProperty(
94        Constants.ENV_ADDRESSING_NAMESPACE_URI,
95        Constants.NS_URI_ADDRESSING_DEFAULT);
96    }
97  
98    /**
99     * Registers a WS-Addressing version.
100    *
101    * @param ns       Namespace
102    * @param anonURI  Anonymous target URI
103    * @param response Response/Reply local name
104    * @param w3c      W3C compliant?
105    * @param params   Supports reference parameters?
106    * @param props    Supports reference properties?
107    * @param mdata    Supports meta-data?
108    */
109   private static void addVersion(String ns, String anonURI, String response,
110     boolean w3c, boolean params, boolean props, boolean mdata) {
111     if (w3c) {
112       W3C_ADDRESSING_NS_URI_SET.add(ns);
113     } else {
114       ADDRESSING_NS_URI_SET.add(ns);
115     }
116 
117     if (anonURI == null) {
118       anonURI = ns + "/role/anonymous";
119     }
120     VERSIONS.put(ns, new WsaVersion(ns, anonURI, new QName(ns, response),
121                  w3c, params, props, mdata));
122   }
123 
124   /**
125    * Hidden constructor.
126    */
127   protected AddressingUtils() {
128   }
129 
130   /**
131    * Removes all instances of a given header name in WSA namespace for a given
132    * actor.
133    *
134    * @param soapHeader Header containing the element to remove
135    * @param actorURI   Actor URI
136    * @param headerName element name
137    */
138   public static void removeHeader(SOAPHeader soapHeader, String actorURI,
139     String headerName) {
140     if (soapHeader == null) {
141       return;
142     }
143     Iterator<?> headers = soapHeader.examineHeaderElements(actorURI);
144     List<SOAPHeaderElement> existingElements = new ArrayList<SOAPHeaderElement>();
145     while (headers.hasNext()) {
146       SOAPHeaderElement hElement = (SOAPHeaderElement) headers.next();
147       Name hName = hElement.getElementName();
148       if (isAddressingNamespaceURI(hName.getURI())
149         && hName.getLocalName().equals(headerName)) {
150         existingElements.add(hElement);
151       }
152     }
153     for (int i = 0; i < existingElements.size(); i++) {
154       SOAPHeaderElement el = (SOAPHeaderElement) existingElements.get(i);
155       el.detachNode();
156     }
157   }
158 
159   /**
160    * Gets an object describing the WS-Addressing specification for which the
161    * specified message context is configured.
162    *
163    * @param ctx Message context
164    * @return An AddressingVersion instance, or null
165    */
166   public static AddressingVersion getAddressingVersion(MessageContext ctx) {
167     return VERSIONS.get(getAddressingNamespaceURI(ctx));
168   }
169 
170   /**
171    * Gets an object describing the WS-Addressing specification represented
172    * by the specified namespace.
173    *
174    * @param ns Addressing namespace URI
175    * @return An AddressingVersion instance, or null
176    */
177   public static AddressingVersion getAddressingVersion(String ns) {
178     return VERSIONS.get(ns);
179   }
180 
181   /**
182    * Gets an object describing the WS-Addressing specification represented
183    * by the namespace configured at the MessageContext or System level.
184    *
185    * @return An AddressingVersion instance, or null
186    */
187   public static AddressingVersion getAddressingVersion() {
188     String ns = getAddressingNamespaceURI(AxisEngine.getCurrentMessageContext());
189     return VERSIONS.get(ns);
190   }
191 
192   /**
193    * Returns true if the specified URI is corresponds to the namespace URI of a
194    * supported version of the WSA specification, or false otherwise.
195    *
196    * @param uri a URI
197    * @return true if the specified URI is a valid addressing namespace
198    * @deprecated
199    */
200   public static boolean isAddressingNamespaceURI(String uri) {
201     return ADDRESSING_NS_URI_SET.contains(uri);
202   }
203 
204   /**
205    * Returns true if the specified URI is corresponds to the namespace URI of a
206    * supported version of the WSA specification using the W3C specs, or false
207    * otherwise.
208    *
209    * @param uri a URI
210    * @return true if the specified URI is a valid W3C addressing namespace
211    * @deprecated
212    */
213   public static boolean isW3CAddressingNamespaceURI(String uri) {
214     return W3C_ADDRESSING_NS_URI_SET.contains(uri);
215   }
216 
217   /**
218    * Removes all instances of all WSA headers for a given actor.
219    *
220    * @param soapHeader header container
221    * @param actorURI   Actor URI
222    */
223   public static void removeHeaders(SOAPHeader soapHeader, String actorURI) {
224     if (soapHeader == null) {
225       return;
226     }
227     Iterator<?> headers = soapHeader.examineHeaderElements(actorURI);
228     List<SOAPHeaderElement> existingElements = new ArrayList<SOAPHeaderElement>();
229     while (headers.hasNext()) {
230       SOAPHeaderElement hElement = (SOAPHeaderElement) headers.next();
231       Name hName = hElement.getElementName();
232       AddressingVersion version = getAddressingVersion(hName.getURI());
233       if (version != null) {
234         existingElements.add(hElement);
235       }
236     }
237     for (int i = 0; i < existingElements.size(); i++) {
238       SOAPHeaderElement el = (SOAPHeaderElement) existingElements.get(i);
239       el.detachNode();
240     }
241   }
242 
243   /**
244    * Gets response <code>AddressingHeaders</code> associated with the
245    * specified context. If there are no <code>AddressingHeaders</code>
246    * associated with the context, new reponse <code>AddressingHeaders</code>
247    * are created and associated with the context.
248    *
249    * @param msgCtx Context information
250    * @return The addressing headers container
251    */
252   public static AddressingHeaders getResponseHeaders(MessageContext msgCtx) {
253     return getHeaders(msgCtx, Constants.ENV_ADDRESSING_RESPONSE_HEADERS);
254   }
255 
256   /**
257    * Gets request <code>AddressingHeaders</code> associated with the specified
258    * context. If there are no <code>AddressingHeaders</code> associated with
259    * the context, new request <code>AddressingHeaders</code> are created and
260    * associated with the context.
261    *
262    * @param msgCtx Context information
263    * @return The headers container
264    */
265   public static AddressingHeaders getRequestHeaders(MessageContext msgCtx) {
266     return getHeaders(msgCtx, Constants.ENV_ADDRESSING_REQUEST_HEADERS);
267   }
268 
269   /**
270    * Gets input action. See section 3.3 of the 2004/08 WSA specification.
271    *
272    * @param portTypeQName Name of the port type
273    * @param operation     Operation in the specified port type
274    * @return The operation input action URI
275    */
276   public static String getInputAction(QName portTypeQName, Operation operation) {
277     if (portTypeQName == null || operation == null) {
278       throw new IllegalArgumentException("Null parameters are not allowed.");
279     }
280     Input input = operation.getInput();
281     if (input == null) {
282       return null;
283     }
284     return getAction(input, input.getName(), portTypeQName, operation, "Request");
285   }
286 
287   /**
288    * Gets output action. See section 3.3 of the 2004/08 WSA specification.
289    *
290    * @param portTypeQName Name of the port type
291    * @param operation     Operation in the specified port type
292    * @return The operation output action URI
293    */
294   public static String getOutputAction(QName portTypeQName, Operation operation) {
295     if (portTypeQName == null || operation == null) {
296       throw new IllegalArgumentException("Null parameters are not allowed.");
297     }
298     Output output = operation.getOutput();
299     if (output == null) {
300       return null;
301     }
302     return getAction(output, output.getName(), portTypeQName, operation, "Response");
303   }
304 
305   /**
306    * Gets prefix for a given namespace. If the prefix is already defined for the
307    * namespace given, it is returned. Otherwise, a new prefix is automatically
308    * registered with the given namespace and returned.
309    *
310    * @param element   Element where the prefix will be look up
311    * @param namespace Namespace to look up
312    * @return A namespace prefix
313    */
314   public static String getNamespacePrefix(SOAPElement element, String namespace)
315     throws SOAPException {
316     if (element == null || namespace == null) {
317       throw new IllegalArgumentException("Null parameters are not allowed.");
318     }
319     Iterator<?> iter = element.getVisibleNamespacePrefixes();
320     String prefix;
321     String ns;
322     while (iter.hasNext()) {
323       prefix = (String) iter.next();
324       ns = element.getNamespaceURI(prefix);
325       if (namespace.equals(ns)) {
326         return prefix;
327       }
328     }
329 
330     int i = 0;
331     prefix = "ns" + i;
332     while (element.getNamespaceURI(prefix) != null) {
333       prefix = "ns" + i++;
334     }
335 
336     element.addNamespaceDeclaration(prefix, namespace);
337 
338     return prefix;
339   }
340 
341   /**
342    * Returns the namespace URI of the WSA version being used by the current
343    * request.
344    *
345    * @return The Addressing namespace URI
346    * @deprecated
347    */
348   public static String getAddressingNamespaceURI() {
349     // NOTE: the below call is Axis-specific :-(
350     MessageContext ctx = AxisEngine.getCurrentMessageContext();
351     return getAddressingNamespaceURI(ctx);
352   }
353 
354   /**
355    * Returns the namespace URI of the WSA version being used by the request
356    * in the specified message context.
357    *
358    * @param ctx The message context
359    * @return The Addressing namespace URI
360    */
361   public static String getAddressingNamespaceURI(MessageContext ctx) {
362     String nsURI = null;
363     if (ctx != null) {
364       nsURI = (String) ctx.getProperty(Constants.ENV_ADDRESSING_NAMESPACE_URI);
365     }
366     if (nsURI == null) {
367       nsURI = DEFAULT_ADDRESSING_NS;
368     }
369     return nsURI;
370   }
371 
372   /**
373    * Returns the fault wsa:Action URI that is appropriate for the WSA version
374    * being used.
375    *
376    * @return The fault Action URI
377    * @deprecated
378    */
379   public static String getFaultActionURI() {
380     return getAddressingVersion().getFaultActionURI();
381   }
382 
383   /**
384    * Returns the anonymous target URI that is appropriate for the WSA version
385    * being used.
386    *
387    * @return The anonymous target URI
388    * @deprecated
389    */
390   public static String getAnonymousRoleURI() {
391     AddressingVersion version = getAddressingVersion();
392     return version.getAnonymousRoleURI();
393   }
394 
395   /**
396    * Returns the anonymous target URI that is appropriate for the WSA version
397    * being used.
398    *
399    * @param ctx Message Context
400    * @return The anonymous target URI
401    */
402   public static String getAnonymousRoleURI(MessageContext ctx) {
403     return getAddressingVersion(ctx).getAnonymousRoleURI();
404   }
405 
406   /**
407    * Returns the response/reply relationship QName that is appropriate for the
408    * WSA version being used.
409    *
410    * @return The response/reply QName
411    */
412   public static QName getResponseRelationshipType() {
413     return getAddressingVersion().getResponseRelationshipType();
414   }
415 
416   /**
417    * Returns the response/reply relationship QName that is appropriate for the
418    * WSA version being used.
419    *
420    * @param ctx Message context
421    * @return The response/reply QName
422    */
423   public static QName getResponseRelationshipType(MessageContext ctx) {
424     return getAddressingVersion(ctx).getResponseRelationshipType();
425   }
426 
427 
428   /**
429    * Parses a string containing an End-point reference in XML format, and
430    * translates it into an {@link EndpointReference} bean.
431    *
432    * @param epr End-point reference in XML format
433    * @return Bean instance
434    * @throws Exception If the XML parser fails to initialize, a syntax error
435    *                   is detected in the input string, or a general error
436    *                   occurs while constructing the bean
437    */
438   public static EndpointReference parseReference(String epr) throws Exception {
439     Document doc = XMLUtils.newDocument(new InputSource(new StringReader(epr)));
440     Element elem = doc.getDocumentElement();
441     return new EndpointReference(elem);
442   }
443 
444   /**
445    * Converts an End-Point Reference into an XML String.
446    *
447    * @param epr End-point reference
448    * @param prettyPrint Whether to beautify the generated XML text
449    * @return XML String
450    * @throws Exception If an error occurs
451    */
452   public static String toString(EndpointReference epr, boolean prettyPrint)
453     throws Exception {
454 
455     if (epr == null) {
456       return null;
457     } else {
458       Document doc = XMLUtils.newDocument();
459       Element elem = epr.toDOM(doc, "wsa:EndpointReference");
460 
461       StringWriter writer = new StringWriter();
462       DOM2Writer.serializeAsXML(elem, writer, true, prettyPrint);
463       return writer.toString();
464     }
465   }
466 
467   /**
468    * Gets the addressing headers from the context information.
469    *
470    * @param ctx Context information
471    * @param type       Property name
472    * @return Headers container
473    */
474   private static AddressingHeaders getHeaders(MessageContext ctx, String type) {
475     AddressingHeaders headers = (AddressingHeaders) ctx.getProperty(type);
476 
477     if (headers == null) {
478       headers = new AddressingHeaders();
479       headers.isW3CVersion = getAddressingVersion(ctx).isW3C();
480 
481       // set property so other handlers might have a chance to use/modify
482       ctx.setProperty(type, headers);
483     }
484 
485     return headers;
486   }
487 
488   /**
489    * Gets an Action URI.
490    *
491    * @param inputOutput     Attribute containing the service WSDL
492    * @param inputOutputName Name of the input/output element
493    * @param portTypeQName   Port type name
494    * @param operation       Operation name
495    * @param defaultName     prefix to add to the operation name if inputOutputName is null
496    * @return An Action URI
497    */
498   private static String getAction(AttributeExtensible inputOutput,
499     String inputOutputName, QName portTypeQName, Operation operation,
500     String defaultName) {
501 
502     Object value = inputOutput.getExtensionAttribute(new QName(
503       getAddressingNamespaceURI(), Constants.WSDL_ATTRIB_ACTION));
504     if (value != null) {
505       // wsdl4j returns a qname by default?
506       if (value instanceof QName) {
507         return ((QName) value).getLocalPart();
508       } else {
509         return value.toString();
510       }
511     }
512     String name = inputOutputName;
513     if (name == null) {
514       name = operation.getName() + defaultName;
515     }
516     StringBuffer buf = new StringBuffer();
517     buf.append(portTypeQName.getNamespaceURI());
518     if (!portTypeQName.getNamespaceURI().endsWith("/")) {
519       buf.append("/");
520     }
521     buf.append(portTypeQName.getLocalPart()).append("/").append(name);
522     return buf.toString();
523   }
524 
525 }