View Javadoc
1   /*
2    * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.xml.internal.ws.api;
27  
28  import com.sun.istack.internal.NotNull;
29  import com.sun.xml.internal.ws.api.message.Message;
30  import com.sun.xml.internal.ws.api.pipe.Codec;
31  import com.sun.xml.internal.ws.api.pipe.Tube;
32  import com.sun.xml.internal.ws.binding.BindingImpl;
33  import com.sun.xml.internal.ws.binding.SOAPBindingImpl;
34  import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
35  import com.sun.xml.internal.ws.encoding.SOAPBindingCodec;
36  import com.sun.xml.internal.ws.encoding.XMLHTTPBindingCodec;
37  import com.sun.xml.internal.ws.encoding.soap.streaming.SOAPNamespaceConstants;
38  import com.sun.xml.internal.ws.util.ServiceFinder;
39  import com.sun.xml.internal.ws.developer.JAXWSProperties;
40  
41  import javax.xml.ws.BindingType;
42  import javax.xml.ws.WebServiceException;
43  import javax.xml.ws.WebServiceFeature;
44  import javax.xml.ws.handler.Handler;
45  import javax.xml.ws.http.HTTPBinding;
46  import javax.xml.ws.soap.MTOMFeature;
47  import javax.xml.ws.soap.SOAPBinding;
48  
49  import java.io.UnsupportedEncodingException;
50  import java.net.URL;
51  import java.net.URLDecoder;
52  import java.util.HashMap;
53  import java.util.Map;
54  
55  /**
56   * Parsed binding ID string.
57   *
58   * <p>
59   * {@link BindingID} is an immutable object that represents a binding ID,
60   * much like how {@link URL} is a representation of an URL.
61   * Like {@link URL}, this class offers a bunch of methods that let you
62   * query various traits/properties of a binding ID.
63   *
64   * <p>
65   * {@link BindingID} is extensible; one can plug in a parser from
66   * {@link String} to {@link BindingID} to interpret binding IDs that
67   * the JAX-WS RI does no a-priori knowledge of.
68   * Technologies such as Tango uses this to make the JAX-WS RI understand
69   * binding IDs defined in their world.
70   *
71   * Such technologies are free to extend this class and expose more characterstics.
72   *
73   * <p>
74   * Even though this class defines a few well known constants, {@link BindingID}
75   * instances do not necessarily have singleton semantics. Use {@link #equals(Object)}
76   * for the comparison.
77   *
78   * <h3>{@link BindingID} and {@link WSBinding}</h3>
79   * <p>
80   * {@link WSBinding} is mutable and represents a particular "use" of a {@link BindingID}.
81   * As such, it has state like a list of {@link Handler}s, which are inherently local
82   * to a particular usage. For example, if you have two proxies, you need two instances.
83   *
84   * {@link BindingID}, OTOH, is immutable and thus the single instance
85   * that represents "SOAP1.2/HTTP" can be shared and reused by all proxies in the same VM.
86   *
87   * @author Kohsuke Kawaguchi
88   */
89  public abstract class BindingID {
90  
91      /**
92       * Creates an instance of {@link WSBinding} (which is conceptually an "use"
93       * of {@link BindingID}) from a {@link BindingID}.
94       *
95       * @return
96       *      Always a new instance.
97       */
98      public final @NotNull WSBinding createBinding() {
99          return BindingImpl.create(this);
100     }
101 
102     /**
103      * Returns wsdl:binding@transport attribute. Sub classes
104      * are expected to override this method to provide their transport
105      * attribute.
106      *
107      * @return wsdl:binding@transport attribute
108      * @since JAX-WS RI 2.1.6
109      */
110     public @NotNull String getTransport() {
111         return SOAPNamespaceConstants.TRANSPORT_HTTP;
112     }
113 
114     public final @NotNull WSBinding createBinding(WebServiceFeature... features) {
115         return BindingImpl.create(this, features);
116     }
117 
118     public final @NotNull WSBinding createBinding(WSFeatureList features) {
119         return createBinding(features.toArray());
120     }
121 
122     /**
123      * Gets the SOAP version of this binding.
124      *
125      * TODO: clarify what to do with XML/HTTP binding
126      *
127      * @return
128      *      If the binding is using SOAP, this method returns
129      *      a {@link SOAPVersion} constant.
130      *
131      *      If the binding is not based on SOAP, this method
132      *      returns null. See {@link Message} for how a non-SOAP
133      *      binding shall be handled by {@link Tube}s.
134      */
135     public abstract SOAPVersion getSOAPVersion();
136 
137     /**
138      * Creates a new {@link Codec} for this binding.
139      *
140      * @param binding
141      *      Ocassionally some aspects of binding can be overridden by
142      *      {@link WSBinding} at runtime by users, so some {@link Codec}s
143      *      need to have access to {@link WSBinding} that it's working for.
144      */
145     public abstract @NotNull Codec createEncoder(@NotNull WSBinding binding);
146 
147     /**
148      * Gets the binding ID, which uniquely identifies the binding.
149      *
150      * <p>
151      * The relevant specs define the binding IDs and what they mean.
152      * The ID is used in many places to identify the kind of binding
153      * (such as SOAP1.1, SOAP1.2, REST, ...)
154      *
155      * @return
156      *      Always non-null same value.
157      */
158     @Override
159     public abstract String toString();
160 
161     /**
162      * Returna a new {@link WebServiceFeatureList} instance
163      * that represents the features that are built into this binding ID.
164      *
165      * <p>
166      * For example, {@link BindingID} for
167      * <tt>"{@value SOAPBinding#SOAP11HTTP_MTOM_BINDING}"</tt>
168      * would always return a list that has {@link MTOMFeature} enabled.
169      */
170     public WebServiceFeatureList createBuiltinFeatureList() {
171         return new WebServiceFeatureList();
172     }
173 
174     /**
175      * Returns true if this binding can generate WSDL.
176      *
177      * <p>
178      * For e.g.: SOAP 1.1 and "XSOAP 1.2" is supposed to return true
179      * from this method. For SOAP1.2, there is no standard WSDL, so the
180      * runtime is not generating one and it expects the WSDL is packaged.
181      *
182      */
183     public boolean canGenerateWSDL() {
184         return false;
185     }
186 
187     /**
188      * Returns a parameter of this binding ID.
189      *
190      * <p>
191      * Some binding ID, such as those for SOAP/HTTP, uses the URL
192      * query syntax (like <tt>?mtom=true</tt>) to control
193      * the optional part of the binding. This method obtains
194      * the value for such optional parts.
195      *
196      * <p>
197      * For implementors of the derived classes, if your binding ID
198      * does not define such optional parts (such as the XML/HTTP binding ID),
199      * then you should simply return the specified default value
200      * (which is what this implementation does.)
201      *
202      * @param parameterName
203      *      The parameter name, such as "mtom" in the above example.
204      * @param defaultValue
205      *      If this binding ID doesn't have the specified parameter explicitly,
206      *      this value will be returned.
207      *
208      * @return
209      *      the value of the parameter, if it's present (such as "true"
210      *      in the above example.) If not present, this method returns
211      *      the {@code defaultValue}.
212      */
213     public String getParameter(String parameterName, String defaultValue) {
214         return defaultValue;
215     }
216 
217     /**
218      * Compares the equality based on {@link #toString()}.
219      */
220     @Override
221     public boolean equals(Object obj) {
222         if(!(obj instanceof BindingID))
223             return false;
224         return toString().equals(obj.toString());
225     }
226 
227     @Override
228     public int hashCode() {
229         return toString().hashCode();
230     }
231 
232     /**
233      * Parses a binding ID string into a {@link BindingID} object.
234      *
235      * <p>
236      * This method first checks for a few known values and then delegate
237      * the parsing to {@link BindingIDFactory}.
238      *
239      * <p>
240      * If parsing succeeds this method returns a value. Otherwise
241      * throws {@link WebServiceException}.
242      *
243      * @throws WebServiceException
244      *      If the binding ID is not understood.
245      */
246     public static @NotNull BindingID parse(String lexical) {
247         if(lexical.equals(XML_HTTP.toString()))
248             return XML_HTTP;
249         if(lexical.equals(REST_HTTP.toString()))
250             return REST_HTTP;
251         if(belongsTo(lexical,SOAP11_HTTP.toString()))
252             return customize(lexical,SOAP11_HTTP);
253         if(belongsTo(lexical,SOAP12_HTTP.toString()))
254             return customize(lexical,SOAP12_HTTP);
255         if(belongsTo(lexical,SOAPBindingImpl.X_SOAP12HTTP_BINDING))
256             return customize(lexical,X_SOAP12_HTTP);
257 
258         // OK, it's none of the values JAX-WS understands.
259         for( BindingIDFactory f : ServiceFinder.find(BindingIDFactory.class) ) {
260             BindingID r = f.parse(lexical);
261             if(r!=null)
262                 return r;
263         }
264 
265         // nobody understood this value
266         throw new WebServiceException("Wrong binding ID: "+lexical);
267     }
268 
269     private static boolean belongsTo(String lexical, String id) {
270         return lexical.equals(id) || lexical.startsWith(id+'?');
271     }
272 
273     /**
274      * Parses parameter portion and returns appropriately populated {@link SOAPHTTPImpl}
275      */
276     private static SOAPHTTPImpl customize(String lexical, SOAPHTTPImpl base) {
277         if(lexical.equals(base.toString()))
278             return base;
279 
280         // otherwise we must have query parameter
281         // we assume the spec won't define any tricky parameters that require
282         // complicated handling (such as %HH or non-ASCII char), so this parser
283         // is quite simple-minded.
284         SOAPHTTPImpl r = new SOAPHTTPImpl(base.getSOAPVersion(), lexical, base.canGenerateWSDL());
285         try {
286             // With X_SOAP12_HTTP, base != lexical and lexical does n't have any query string
287             if(lexical.indexOf('?') == -1) {
288                 return r;
289             }
290             String query = URLDecoder.decode(lexical.substring(lexical.indexOf('?')+1),"UTF-8");
291             for( String token : query.split("&") ) {
292                 int idx = token.indexOf('=');
293                 if(idx<0)
294                     throw new WebServiceException("Malformed binding ID (no '=' in "+token+")");
295                 r.parameters.put(token.substring(0,idx),token.substring(idx+1));
296             }
297         } catch (UnsupportedEncodingException e) {
298             throw new AssertionError(e);    // UTF-8 is supported everywhere
299         }
300 
301         return r;
302     }
303 
304 
305     /**
306      * Figures out the binding from {@link BindingType} annotation.
307      *
308      * @return
309      *      default to {@link BindingID#SOAP11_HTTP}, if no such annotation is present.
310      * @see #parse(String)
311      */
312     public static @NotNull BindingID parse(Class<?> implClass) {
313         BindingType bindingType = implClass.getAnnotation(BindingType.class);
314         if (bindingType != null) {
315             String bindingId = bindingType.value();
316             if (bindingId.length() > 0) {
317                 return BindingID.parse(bindingId);
318             }
319         }
320         return SOAP11_HTTP;
321     }
322 
323     /**
324      * Constant that represents implementation specific SOAP1.2/HTTP which is
325      * used to generate non-standard WSDLs
326      */
327     public static final SOAPHTTPImpl X_SOAP12_HTTP = new SOAPHTTPImpl(
328         SOAPVersion.SOAP_12, SOAPBindingImpl.X_SOAP12HTTP_BINDING, true);
329 
330     /**
331      * Constant that represents SOAP1.2/HTTP.
332      */
333     public static final SOAPHTTPImpl SOAP12_HTTP = new SOAPHTTPImpl(
334         SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_BINDING, true);
335     /**
336      * Constant that represents SOAP1.1/HTTP.
337      */
338     public static final SOAPHTTPImpl SOAP11_HTTP = new SOAPHTTPImpl(
339         SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_BINDING, true);
340 
341     /**
342      * Constant that represents SOAP1.2/HTTP.
343      */
344     public static final SOAPHTTPImpl SOAP12_HTTP_MTOM = new SOAPHTTPImpl(
345         SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_MTOM_BINDING, true, true);
346     /**
347      * Constant that represents SOAP1.1/HTTP.
348      */
349     public static final SOAPHTTPImpl SOAP11_HTTP_MTOM = new SOAPHTTPImpl(
350         SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_MTOM_BINDING, true, true);
351 
352 
353     /**
354      * Constant that represents REST.
355      */
356     public static final BindingID XML_HTTP = new Impl(SOAPVersion.SOAP_11, HTTPBinding.HTTP_BINDING,false) {
357         @Override
358         public Codec createEncoder(WSBinding binding) {
359             return new XMLHTTPBindingCodec(binding.getFeatures());
360         }
361     };
362 
363     /**
364      * Constant that represents REST.
365      */
366     private static final BindingID REST_HTTP = new Impl(SOAPVersion.SOAP_11, JAXWSProperties.REST_BINDING,true) {
367         @Override
368         public Codec createEncoder(WSBinding binding) {
369             return new XMLHTTPBindingCodec(binding.getFeatures());
370         }
371     };
372 
373     private static abstract class Impl extends BindingID {
374         final SOAPVersion version;
375         private final String lexical;
376         private final boolean canGenerateWSDL;
377 
378         public Impl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
379             this.version = version;
380             this.lexical = lexical;
381             this.canGenerateWSDL = canGenerateWSDL;
382         }
383 
384         @Override
385         public SOAPVersion getSOAPVersion() {
386             return version;
387         }
388 
389         @Override
390         public String toString() {
391             return lexical;
392         }
393 
394         @Deprecated
395         @Override
396         public boolean canGenerateWSDL() {
397             return canGenerateWSDL;
398         }
399     }
400 
401     /**
402      * Internal implementation for SOAP/HTTP.
403      */
404     private static final class SOAPHTTPImpl extends Impl implements Cloneable {
405         /*final*/ Map<String,String> parameters = new HashMap<String,String>();
406 
407         static final String MTOM_PARAM = "mtom";
408 
409         public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
410             super(version, lexical, canGenerateWSDL);
411         }
412 
413         public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL,
414                            boolean mtomEnabled) {
415             this(version, lexical, canGenerateWSDL);
416             String mtomStr = mtomEnabled ? "true" : "false";
417             parameters.put(MTOM_PARAM, mtomStr);
418         }
419 
420         public @NotNull @Override Codec createEncoder(WSBinding binding) {
421             return new SOAPBindingCodec(binding.getFeatures());
422         }
423 
424         private Boolean isMTOMEnabled() {
425             String mtom = parameters.get(MTOM_PARAM);
426             return mtom==null?null:Boolean.valueOf(mtom);
427         }
428 
429         @Override
430         public WebServiceFeatureList createBuiltinFeatureList() {
431             WebServiceFeatureList r=super.createBuiltinFeatureList();
432             Boolean mtom = isMTOMEnabled();
433             if(mtom != null)
434                 r.add(new MTOMFeature(mtom));
435             return r;
436         }
437 
438         @Override
439         public String getParameter(String parameterName, String defaultValue) {
440             if (parameters.get(parameterName) == null)
441                 return super.getParameter(parameterName, defaultValue);
442             return parameters.get(parameterName);
443         }
444 
445         @Override
446         public SOAPHTTPImpl clone() throws CloneNotSupportedException {
447             return (SOAPHTTPImpl) super.clone();
448         }
449     }
450 }