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 }