ohair@286: /* alanb@368: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.api; ohair@286: ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.xml.internal.ws.api.message.Message; ohair@286: import com.sun.xml.internal.ws.api.pipe.Codec; ohair@286: import com.sun.xml.internal.ws.api.pipe.Tube; ohair@286: import com.sun.xml.internal.ws.binding.BindingImpl; ohair@286: import com.sun.xml.internal.ws.binding.SOAPBindingImpl; ohair@286: import com.sun.xml.internal.ws.binding.WebServiceFeatureList; ohair@286: import com.sun.xml.internal.ws.encoding.SOAPBindingCodec; ohair@286: import com.sun.xml.internal.ws.encoding.XMLHTTPBindingCodec; ohair@286: import com.sun.xml.internal.ws.encoding.soap.streaming.SOAPNamespaceConstants; ohair@286: import com.sun.xml.internal.ws.util.ServiceFinder; ohair@286: import com.sun.xml.internal.ws.developer.JAXWSProperties; ohair@286: ohair@286: import javax.xml.ws.BindingType; ohair@286: import javax.xml.ws.WebServiceException; ohair@286: import javax.xml.ws.WebServiceFeature; ohair@286: import javax.xml.ws.handler.Handler; ohair@286: import javax.xml.ws.http.HTTPBinding; ohair@286: import javax.xml.ws.soap.MTOMFeature; ohair@286: import javax.xml.ws.soap.SOAPBinding; ohair@286: ohair@286: import java.io.UnsupportedEncodingException; ohair@286: import java.net.URL; ohair@286: import java.net.URLDecoder; ohair@286: import java.util.HashMap; ohair@286: import java.util.Map; ohair@286: ohair@286: /** ohair@286: * Parsed binding ID string. ohair@286: * ohair@286: *

ohair@286: * {@link BindingID} is an immutable object that represents a binding ID, ohair@286: * much like how {@link URL} is a representation of an URL. ohair@286: * Like {@link URL}, this class offers a bunch of methods that let you ohair@286: * query various traits/properties of a binding ID. ohair@286: * ohair@286: *

ohair@286: * {@link BindingID} is extensible; one can plug in a parser from ohair@286: * {@link String} to {@link BindingID} to interpret binding IDs that ohair@286: * the JAX-WS RI does no a-priori knowledge of. ohair@286: * Technologies such as Tango uses this to make the JAX-WS RI understand ohair@286: * binding IDs defined in their world. ohair@286: * ohair@286: * Such technologies are free to extend this class and expose more characterstics. ohair@286: * ohair@286: *

ohair@286: * Even though this class defines a few well known constants, {@link BindingID} ohair@286: * instances do not necessarily have singleton semantics. Use {@link #equals(Object)} ohair@286: * for the comparison. ohair@286: * ohair@286: *

{@link BindingID} and {@link WSBinding}

ohair@286: *

ohair@286: * {@link WSBinding} is mutable and represents a particular "use" of a {@link BindingID}. ohair@286: * As such, it has state like a list of {@link Handler}s, which are inherently local ohair@286: * to a particular usage. For example, if you have two proxies, you need two instances. ohair@286: * ohair@286: * {@link BindingID}, OTOH, is immutable and thus the single instance ohair@286: * that represents "SOAP1.2/HTTP" can be shared and reused by all proxies in the same VM. ohair@286: * ohair@286: * @author Kohsuke Kawaguchi ohair@286: */ ohair@286: public abstract class BindingID { ohair@286: ohair@286: /** ohair@286: * Creates an instance of {@link WSBinding} (which is conceptually an "use" ohair@286: * of {@link BindingID}) from a {@link BindingID}. ohair@286: * ohair@286: * @return ohair@286: * Always a new instance. ohair@286: */ ohair@286: public final @NotNull WSBinding createBinding() { ohair@286: return BindingImpl.create(this); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns wsdl:binding@transport attribute. Sub classes ohair@286: * are expected to override this method to provide their transport ohair@286: * attribute. ohair@286: * ohair@286: * @return wsdl:binding@transport attribute ohair@286: * @since JAX-WS RI 2.1.6 ohair@286: */ ohair@286: public @NotNull String getTransport() { ohair@286: return SOAPNamespaceConstants.TRANSPORT_HTTP; ohair@286: } ohair@286: ohair@286: public final @NotNull WSBinding createBinding(WebServiceFeature... features) { ohair@286: return BindingImpl.create(this, features); ohair@286: } ohair@286: ohair@286: public final @NotNull WSBinding createBinding(WSFeatureList features) { ohair@286: return createBinding(features.toArray()); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the SOAP version of this binding. ohair@286: * ohair@286: * TODO: clarify what to do with XML/HTTP binding ohair@286: * ohair@286: * @return ohair@286: * If the binding is using SOAP, this method returns ohair@286: * a {@link SOAPVersion} constant. ohair@286: * ohair@286: * If the binding is not based on SOAP, this method ohair@286: * returns null. See {@link Message} for how a non-SOAP ohair@286: * binding shall be handled by {@link Tube}s. ohair@286: */ ohair@286: public abstract SOAPVersion getSOAPVersion(); ohair@286: ohair@286: /** ohair@286: * Creates a new {@link Codec} for this binding. ohair@286: * ohair@286: * @param binding ohair@286: * Ocassionally some aspects of binding can be overridden by ohair@286: * {@link WSBinding} at runtime by users, so some {@link Codec}s ohair@286: * need to have access to {@link WSBinding} that it's working for. ohair@286: */ ohair@286: public abstract @NotNull Codec createEncoder(@NotNull WSBinding binding); ohair@286: ohair@286: /** ohair@286: * Gets the binding ID, which uniquely identifies the binding. ohair@286: * ohair@286: *

ohair@286: * The relevant specs define the binding IDs and what they mean. ohair@286: * The ID is used in many places to identify the kind of binding ohair@286: * (such as SOAP1.1, SOAP1.2, REST, ...) ohair@286: * ohair@286: * @return ohair@286: * Always non-null same value. ohair@286: */ alanb@368: @Override ohair@286: public abstract String toString(); ohair@286: ohair@286: /** ohair@286: * Returna a new {@link WebServiceFeatureList} instance ohair@286: * that represents the features that are built into this binding ID. ohair@286: * ohair@286: *

ohair@286: * For example, {@link BindingID} for ohair@286: * "{@value SOAPBinding#SOAP11HTTP_MTOM_BINDING}" ohair@286: * would always return a list that has {@link MTOMFeature} enabled. ohair@286: */ ohair@286: public WebServiceFeatureList createBuiltinFeatureList() { ohair@286: return new WebServiceFeatureList(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns true if this binding can generate WSDL. ohair@286: * ohair@286: *

ohair@286: * For e.g.: SOAP 1.1 and "XSOAP 1.2" is supposed to return true ohair@286: * from this method. For SOAP1.2, there is no standard WSDL, so the ohair@286: * runtime is not generating one and it expects the WSDL is packaged. ohair@286: * ohair@286: */ ohair@286: public boolean canGenerateWSDL() { ohair@286: return false; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns a parameter of this binding ID. ohair@286: * ohair@286: *

ohair@286: * Some binding ID, such as those for SOAP/HTTP, uses the URL ohair@286: * query syntax (like ?mtom=true) to control ohair@286: * the optional part of the binding. This method obtains ohair@286: * the value for such optional parts. ohair@286: * ohair@286: *

ohair@286: * For implementors of the derived classes, if your binding ID ohair@286: * does not define such optional parts (such as the XML/HTTP binding ID), ohair@286: * then you should simply return the specified default value ohair@286: * (which is what this implementation does.) ohair@286: * ohair@286: * @param parameterName ohair@286: * The parameter name, such as "mtom" in the above example. ohair@286: * @param defaultValue ohair@286: * If this binding ID doesn't have the specified parameter explicitly, ohair@286: * this value will be returned. ohair@286: * ohair@286: * @return ohair@286: * the value of the parameter, if it's present (such as "true" ohair@286: * in the above example.) If not present, this method returns ohair@286: * the {@code defaultValue}. ohair@286: */ ohair@286: public String getParameter(String parameterName, String defaultValue) { ohair@286: return defaultValue; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Compares the equality based on {@link #toString()}. ohair@286: */ alanb@368: @Override ohair@286: public boolean equals(Object obj) { ohair@286: if(!(obj instanceof BindingID)) ohair@286: return false; ohair@286: return toString().equals(obj.toString()); ohair@286: } ohair@286: alanb@368: @Override ohair@286: public int hashCode() { ohair@286: return toString().hashCode(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Parses a binding ID string into a {@link BindingID} object. ohair@286: * ohair@286: *

ohair@286: * This method first checks for a few known values and then delegate ohair@286: * the parsing to {@link BindingIDFactory}. ohair@286: * ohair@286: *

ohair@286: * If parsing succeeds this method returns a value. Otherwise ohair@286: * throws {@link WebServiceException}. ohair@286: * ohair@286: * @throws WebServiceException ohair@286: * If the binding ID is not understood. ohair@286: */ ohair@286: public static @NotNull BindingID parse(String lexical) { ohair@286: if(lexical.equals(XML_HTTP.toString())) ohair@286: return XML_HTTP; ohair@286: if(lexical.equals(REST_HTTP.toString())) ohair@286: return REST_HTTP; ohair@286: if(belongsTo(lexical,SOAP11_HTTP.toString())) ohair@286: return customize(lexical,SOAP11_HTTP); ohair@286: if(belongsTo(lexical,SOAP12_HTTP.toString())) ohair@286: return customize(lexical,SOAP12_HTTP); ohair@286: if(belongsTo(lexical,SOAPBindingImpl.X_SOAP12HTTP_BINDING)) ohair@286: return customize(lexical,X_SOAP12_HTTP); ohair@286: ohair@286: // OK, it's none of the values JAX-WS understands. ohair@286: for( BindingIDFactory f : ServiceFinder.find(BindingIDFactory.class) ) { ohair@286: BindingID r = f.parse(lexical); ohair@286: if(r!=null) ohair@286: return r; ohair@286: } ohair@286: ohair@286: // nobody understood this value ohair@286: throw new WebServiceException("Wrong binding ID: "+lexical); ohair@286: } ohair@286: ohair@286: private static boolean belongsTo(String lexical, String id) { ohair@286: return lexical.equals(id) || lexical.startsWith(id+'?'); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Parses parameter portion and returns appropriately populated {@link SOAPHTTPImpl} ohair@286: */ ohair@286: private static SOAPHTTPImpl customize(String lexical, SOAPHTTPImpl base) { ohair@286: if(lexical.equals(base.toString())) ohair@286: return base; ohair@286: ohair@286: // otherwise we must have query parameter ohair@286: // we assume the spec won't define any tricky parameters that require ohair@286: // complicated handling (such as %HH or non-ASCII char), so this parser ohair@286: // is quite simple-minded. ohair@286: SOAPHTTPImpl r = new SOAPHTTPImpl(base.getSOAPVersion(), lexical, base.canGenerateWSDL()); ohair@286: try { ohair@286: // With X_SOAP12_HTTP, base != lexical and lexical does n't have any query string ohair@286: if(lexical.indexOf('?') == -1) { ohair@286: return r; ohair@286: } ohair@286: String query = URLDecoder.decode(lexical.substring(lexical.indexOf('?')+1),"UTF-8"); ohair@286: for( String token : query.split("&") ) { ohair@286: int idx = token.indexOf('='); ohair@286: if(idx<0) ohair@286: throw new WebServiceException("Malformed binding ID (no '=' in "+token+")"); ohair@286: r.parameters.put(token.substring(0,idx),token.substring(idx+1)); ohair@286: } ohair@286: } catch (UnsupportedEncodingException e) { ohair@286: throw new AssertionError(e); // UTF-8 is supported everywhere ohair@286: } ohair@286: ohair@286: return r; ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * Figures out the binding from {@link BindingType} annotation. ohair@286: * ohair@286: * @return ohair@286: * default to {@link BindingID#SOAP11_HTTP}, if no such annotation is present. ohair@286: * @see #parse(String) ohair@286: */ ohair@286: public static @NotNull BindingID parse(Class implClass) { ohair@286: BindingType bindingType = implClass.getAnnotation(BindingType.class); ohair@286: if (bindingType != null) { ohair@286: String bindingId = bindingType.value(); ohair@286: if (bindingId.length() > 0) { ohair@286: return BindingID.parse(bindingId); ohair@286: } ohair@286: } ohair@286: return SOAP11_HTTP; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Constant that represents implementation specific SOAP1.2/HTTP which is ohair@286: * used to generate non-standard WSDLs ohair@286: */ ohair@286: public static final SOAPHTTPImpl X_SOAP12_HTTP = new SOAPHTTPImpl( ohair@286: SOAPVersion.SOAP_12, SOAPBindingImpl.X_SOAP12HTTP_BINDING, true); ohair@286: ohair@286: /** ohair@286: * Constant that represents SOAP1.2/HTTP. ohair@286: */ ohair@286: public static final SOAPHTTPImpl SOAP12_HTTP = new SOAPHTTPImpl( ohair@286: SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_BINDING, true); ohair@286: /** ohair@286: * Constant that represents SOAP1.1/HTTP. ohair@286: */ ohair@286: public static final SOAPHTTPImpl SOAP11_HTTP = new SOAPHTTPImpl( ohair@286: SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_BINDING, true); ohair@286: ohair@286: /** ohair@286: * Constant that represents SOAP1.2/HTTP. ohair@286: */ ohair@286: public static final SOAPHTTPImpl SOAP12_HTTP_MTOM = new SOAPHTTPImpl( ohair@286: SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_MTOM_BINDING, true, true); ohair@286: /** ohair@286: * Constant that represents SOAP1.1/HTTP. ohair@286: */ ohair@286: public static final SOAPHTTPImpl SOAP11_HTTP_MTOM = new SOAPHTTPImpl( ohair@286: SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_MTOM_BINDING, true, true); ohair@286: ohair@286: ohair@286: /** ohair@286: * Constant that represents REST. ohair@286: */ ohair@286: public static final BindingID XML_HTTP = new Impl(SOAPVersion.SOAP_11, HTTPBinding.HTTP_BINDING,false) { alanb@368: @Override ohair@286: public Codec createEncoder(WSBinding binding) { ohair@286: return new XMLHTTPBindingCodec(binding.getFeatures()); ohair@286: } ohair@286: }; ohair@286: ohair@286: /** ohair@286: * Constant that represents REST. ohair@286: */ ohair@286: private static final BindingID REST_HTTP = new Impl(SOAPVersion.SOAP_11, JAXWSProperties.REST_BINDING,true) { alanb@368: @Override ohair@286: public Codec createEncoder(WSBinding binding) { ohair@286: return new XMLHTTPBindingCodec(binding.getFeatures()); ohair@286: } ohair@286: }; ohair@286: ohair@286: private static abstract class Impl extends BindingID { ohair@286: final SOAPVersion version; ohair@286: private final String lexical; ohair@286: private final boolean canGenerateWSDL; ohair@286: ohair@286: public Impl(SOAPVersion version, String lexical, boolean canGenerateWSDL) { ohair@286: this.version = version; ohair@286: this.lexical = lexical; ohair@286: this.canGenerateWSDL = canGenerateWSDL; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public SOAPVersion getSOAPVersion() { ohair@286: return version; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public String toString() { ohair@286: return lexical; ohair@286: } ohair@286: ohair@286: @Deprecated alanb@368: @Override ohair@286: public boolean canGenerateWSDL() { ohair@286: return canGenerateWSDL; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Internal implementation for SOAP/HTTP. ohair@286: */ ohair@286: private static final class SOAPHTTPImpl extends Impl implements Cloneable { ohair@286: /*final*/ Map parameters = new HashMap(); ohair@286: ohair@286: static final String MTOM_PARAM = "mtom"; ohair@286: ohair@286: public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL) { ohair@286: super(version, lexical, canGenerateWSDL); ohair@286: } ohair@286: ohair@286: public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL, ohair@286: boolean mtomEnabled) { ohair@286: this(version, lexical, canGenerateWSDL); ohair@286: String mtomStr = mtomEnabled ? "true" : "false"; ohair@286: parameters.put(MTOM_PARAM, mtomStr); ohair@286: } ohair@286: alanb@368: public @NotNull @Override Codec createEncoder(WSBinding binding) { ohair@286: return new SOAPBindingCodec(binding.getFeatures()); ohair@286: } ohair@286: ohair@286: private Boolean isMTOMEnabled() { ohair@286: String mtom = parameters.get(MTOM_PARAM); ohair@286: return mtom==null?null:Boolean.valueOf(mtom); ohair@286: } ohair@286: alanb@368: @Override ohair@286: public WebServiceFeatureList createBuiltinFeatureList() { ohair@286: WebServiceFeatureList r=super.createBuiltinFeatureList(); ohair@286: Boolean mtom = isMTOMEnabled(); ohair@286: if(mtom != null) ohair@286: r.add(new MTOMFeature(mtom)); ohair@286: return r; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public String getParameter(String parameterName, String defaultValue) { ohair@286: if (parameters.get(parameterName) == null) ohair@286: return super.getParameter(parameterName, defaultValue); ohair@286: return parameters.get(parameterName); ohair@286: } alanb@368: alanb@368: @Override alanb@368: public SOAPHTTPImpl clone() throws CloneNotSupportedException { alanb@368: return (SOAPHTTPImpl) super.clone(); alanb@368: } ohair@286: } ohair@286: }