Thu, 31 Aug 2017 15:18:52 +0800
merge
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 */
26 package com.sun.xml.internal.ws.api;
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;
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;
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;
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 {
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 }
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 }
114 public final @NotNull WSBinding createBinding(WebServiceFeature... features) {
115 return BindingImpl.create(this, features);
116 }
118 public final @NotNull WSBinding createBinding(WSFeatureList features) {
119 return createBinding(features.toArray());
120 }
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();
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);
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();
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 }
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 }
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 }
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 }
227 @Override
228 public int hashCode() {
229 return toString().hashCode();
230 }
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);
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 }
265 // nobody understood this value
266 throw new WebServiceException("Wrong binding ID: "+lexical);
267 }
269 private static boolean belongsTo(String lexical, String id) {
270 return lexical.equals(id) || lexical.startsWith(id+'?');
271 }
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;
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 }
301 return r;
302 }
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 }
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);
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);
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);
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 };
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 };
373 private static abstract class Impl extends BindingID {
374 final SOAPVersion version;
375 private final String lexical;
376 private final boolean canGenerateWSDL;
378 public Impl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
379 this.version = version;
380 this.lexical = lexical;
381 this.canGenerateWSDL = canGenerateWSDL;
382 }
384 @Override
385 public SOAPVersion getSOAPVersion() {
386 return version;
387 }
389 @Override
390 public String toString() {
391 return lexical;
392 }
394 @Deprecated
395 @Override
396 public boolean canGenerateWSDL() {
397 return canGenerateWSDL;
398 }
399 }
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>();
407 static final String MTOM_PARAM = "mtom";
409 public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
410 super(version, lexical, canGenerateWSDL);
411 }
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 }
420 public @NotNull @Override Codec createEncoder(WSBinding binding) {
421 return new SOAPBindingCodec(binding.getFeatures());
422 }
424 private Boolean isMTOMEnabled() {
425 String mtom = parameters.get(MTOM_PARAM);
426 return mtom==null?null:Boolean.valueOf(mtom);
427 }
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 }
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 }
445 @Override
446 public SOAPHTTPImpl clone() throws CloneNotSupportedException {
447 return (SOAPHTTPImpl) super.clone();
448 }
449 }
450 }