blob: 9ef2aea857b9513a0824a10a867844bae37c6a05 [file] [log] [blame]
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.xml.internal.ws.api;
import com.sun.istack.internal.NotNull;
import com.sun.xml.internal.ws.api.message.Message;
import com.sun.xml.internal.ws.api.pipe.Codec;
import com.sun.xml.internal.ws.api.pipe.Tube;
import com.sun.xml.internal.ws.binding.BindingImpl;
import com.sun.xml.internal.ws.binding.SOAPBindingImpl;
import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
import com.sun.xml.internal.ws.encoding.SOAPBindingCodec;
import com.sun.xml.internal.ws.encoding.XMLHTTPBindingCodec;
import com.sun.xml.internal.ws.encoding.soap.streaming.SOAPNamespaceConstants;
import com.sun.xml.internal.ws.util.ServiceFinder;
import com.sun.xml.internal.ws.developer.JAXWSProperties;
import javax.xml.ws.BindingType;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.soap.MTOMFeature;
import javax.xml.ws.soap.SOAPBinding;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
/**
* Parsed binding ID string.
*
* <p>
* {@link BindingID} is an immutable object that represents a binding ID,
* much like how {@link URL} is a representation of an URL.
* Like {@link URL}, this class offers a bunch of methods that let you
* query various traits/properties of a binding ID.
*
* <p>
* {@link BindingID} is extensible; one can plug in a parser from
* {@link String} to {@link BindingID} to interpret binding IDs that
* the JAX-WS RI does no a-priori knowledge of.
* Technologies such as Tango uses this to make the JAX-WS RI understand
* binding IDs defined in their world.
*
* Such technologies are free to extend this class and expose more characterstics.
*
* <p>
* Even though this class defines a few well known constants, {@link BindingID}
* instances do not necessarily have singleton semantics. Use {@link #equals(Object)}
* for the comparison.
*
* <h3>{@link BindingID} and {@link WSBinding}</h3>
* <p>
* {@link WSBinding} is mutable and represents a particular "use" of a {@link BindingID}.
* As such, it has state like a list of {@link Handler}s, which are inherently local
* to a particular usage. For example, if you have two proxies, you need two instances.
*
* {@link BindingID}, OTOH, is immutable and thus the single instance
* that represents "SOAP1.2/HTTP" can be shared and reused by all proxies in the same VM.
*
* @author Kohsuke Kawaguchi
*/
public abstract class BindingID {
/**
* Creates an instance of {@link WSBinding} (which is conceptually an "use"
* of {@link BindingID}) from a {@link BindingID}.
*
* @return
* Always a new instance.
*/
public final @NotNull WSBinding createBinding() {
return BindingImpl.create(this);
}
/**
* Returns wsdl:binding@transport attribute. Sub classes
* are expected to override this method to provide their transport
* attribute.
*
* @return wsdl:binding@transport attribute
* @since JAX-WS RI 2.1.6
*/
public @NotNull String getTransport() {
return SOAPNamespaceConstants.TRANSPORT_HTTP;
}
public final @NotNull WSBinding createBinding(WebServiceFeature... features) {
return BindingImpl.create(this, features);
}
public final @NotNull WSBinding createBinding(WSFeatureList features) {
return createBinding(features.toArray());
}
/**
* Gets the SOAP version of this binding.
*
* TODO: clarify what to do with XML/HTTP binding
*
* @return
* If the binding is using SOAP, this method returns
* a {@link SOAPVersion} constant.
*
* If the binding is not based on SOAP, this method
* returns null. See {@link Message} for how a non-SOAP
* binding shall be handled by {@link Tube}s.
*/
public abstract SOAPVersion getSOAPVersion();
/**
* Creates a new {@link Codec} for this binding.
*
* @param binding
* Ocassionally some aspects of binding can be overridden by
* {@link WSBinding} at runtime by users, so some {@link Codec}s
* need to have access to {@link WSBinding} that it's working for.
*/
public abstract @NotNull Codec createEncoder(@NotNull WSBinding binding);
/**
* Gets the binding ID, which uniquely identifies the binding.
*
* <p>
* The relevant specs define the binding IDs and what they mean.
* The ID is used in many places to identify the kind of binding
* (such as SOAP1.1, SOAP1.2, REST, ...)
*
* @return
* Always non-null same value.
*/
@Override
public abstract String toString();
/**
* Returna a new {@link WebServiceFeatureList} instance
* that represents the features that are built into this binding ID.
*
* <p>
* For example, {@link BindingID} for
* <tt>"{@value SOAPBinding#SOAP11HTTP_MTOM_BINDING}"</tt>
* would always return a list that has {@link MTOMFeature} enabled.
*/
public WebServiceFeatureList createBuiltinFeatureList() {
return new WebServiceFeatureList();
}
/**
* Returns true if this binding can generate WSDL.
*
* <p>
* For e.g.: SOAP 1.1 and "XSOAP 1.2" is supposed to return true
* from this method. For SOAP1.2, there is no standard WSDL, so the
* runtime is not generating one and it expects the WSDL is packaged.
*
*/
public boolean canGenerateWSDL() {
return false;
}
/**
* Returns a parameter of this binding ID.
*
* <p>
* Some binding ID, such as those for SOAP/HTTP, uses the URL
* query syntax (like <tt>?mtom=true</tt>) to control
* the optional part of the binding. This method obtains
* the value for such optional parts.
*
* <p>
* For implementors of the derived classes, if your binding ID
* does not define such optional parts (such as the XML/HTTP binding ID),
* then you should simply return the specified default value
* (which is what this implementation does.)
*
* @param parameterName
* The parameter name, such as "mtom" in the above example.
* @param defaultValue
* If this binding ID doesn't have the specified parameter explicitly,
* this value will be returned.
*
* @return
* the value of the parameter, if it's present (such as "true"
* in the above example.) If not present, this method returns
* the {@code defaultValue}.
*/
public String getParameter(String parameterName, String defaultValue) {
return defaultValue;
}
/**
* Compares the equality based on {@link #toString()}.
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof BindingID))
return false;
return toString().equals(obj.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Parses a binding ID string into a {@link BindingID} object.
*
* <p>
* This method first checks for a few known values and then delegate
* the parsing to {@link BindingIDFactory}.
*
* <p>
* If parsing succeeds this method returns a value. Otherwise
* throws {@link WebServiceException}.
*
* @throws WebServiceException
* If the binding ID is not understood.
*/
public static @NotNull BindingID parse(String lexical) {
if(lexical.equals(XML_HTTP.toString()))
return XML_HTTP;
if(lexical.equals(REST_HTTP.toString()))
return REST_HTTP;
if(belongsTo(lexical,SOAP11_HTTP.toString()))
return customize(lexical,SOAP11_HTTP);
if(belongsTo(lexical,SOAP12_HTTP.toString()))
return customize(lexical,SOAP12_HTTP);
if(belongsTo(lexical,SOAPBindingImpl.X_SOAP12HTTP_BINDING))
return customize(lexical,X_SOAP12_HTTP);
// OK, it's none of the values JAX-WS understands.
for( BindingIDFactory f : ServiceFinder.find(BindingIDFactory.class) ) {
BindingID r = f.parse(lexical);
if(r!=null)
return r;
}
// nobody understood this value
throw new WebServiceException("Wrong binding ID: "+lexical);
}
private static boolean belongsTo(String lexical, String id) {
return lexical.equals(id) || lexical.startsWith(id+'?');
}
/**
* Parses parameter portion and returns appropriately populated {@link SOAPHTTPImpl}
*/
private static SOAPHTTPImpl customize(String lexical, SOAPHTTPImpl base) {
if(lexical.equals(base.toString()))
return base;
// otherwise we must have query parameter
// we assume the spec won't define any tricky parameters that require
// complicated handling (such as %HH or non-ASCII char), so this parser
// is quite simple-minded.
SOAPHTTPImpl r = new SOAPHTTPImpl(base.getSOAPVersion(), lexical, base.canGenerateWSDL());
try {
// With X_SOAP12_HTTP, base != lexical and lexical does n't have any query string
if(lexical.indexOf('?') == -1) {
return r;
}
String query = URLDecoder.decode(lexical.substring(lexical.indexOf('?')+1),"UTF-8");
for( String token : query.split("&") ) {
int idx = token.indexOf('=');
if(idx<0)
throw new WebServiceException("Malformed binding ID (no '=' in "+token+")");
r.parameters.put(token.substring(0,idx),token.substring(idx+1));
}
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e); // UTF-8 is supported everywhere
}
return r;
}
/**
* Figures out the binding from {@link BindingType} annotation.
*
* @return
* default to {@link BindingID#SOAP11_HTTP}, if no such annotation is present.
* @see #parse(String)
*/
public static @NotNull BindingID parse(Class<?> implClass) {
BindingType bindingType = implClass.getAnnotation(BindingType.class);
if (bindingType != null) {
String bindingId = bindingType.value();
if (bindingId.length() > 0) {
return BindingID.parse(bindingId);
}
}
return SOAP11_HTTP;
}
/**
* Constant that represents implementation specific SOAP1.2/HTTP which is
* used to generate non-standard WSDLs
*/
public static final SOAPHTTPImpl X_SOAP12_HTTP = new SOAPHTTPImpl(
SOAPVersion.SOAP_12, SOAPBindingImpl.X_SOAP12HTTP_BINDING, true);
/**
* Constant that represents SOAP1.2/HTTP.
*/
public static final SOAPHTTPImpl SOAP12_HTTP = new SOAPHTTPImpl(
SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_BINDING, true);
/**
* Constant that represents SOAP1.1/HTTP.
*/
public static final SOAPHTTPImpl SOAP11_HTTP = new SOAPHTTPImpl(
SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_BINDING, true);
/**
* Constant that represents SOAP1.2/HTTP.
*/
public static final SOAPHTTPImpl SOAP12_HTTP_MTOM = new SOAPHTTPImpl(
SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_MTOM_BINDING, true, true);
/**
* Constant that represents SOAP1.1/HTTP.
*/
public static final SOAPHTTPImpl SOAP11_HTTP_MTOM = new SOAPHTTPImpl(
SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_MTOM_BINDING, true, true);
/**
* Constant that represents REST.
*/
public static final BindingID XML_HTTP = new Impl(SOAPVersion.SOAP_11, HTTPBinding.HTTP_BINDING,false) {
@Override
public Codec createEncoder(WSBinding binding) {
return new XMLHTTPBindingCodec(binding.getFeatures());
}
};
/**
* Constant that represents REST.
*/
private static final BindingID REST_HTTP = new Impl(SOAPVersion.SOAP_11, JAXWSProperties.REST_BINDING,true) {
@Override
public Codec createEncoder(WSBinding binding) {
return new XMLHTTPBindingCodec(binding.getFeatures());
}
};
private static abstract class Impl extends BindingID {
final SOAPVersion version;
private final String lexical;
private final boolean canGenerateWSDL;
public Impl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
this.version = version;
this.lexical = lexical;
this.canGenerateWSDL = canGenerateWSDL;
}
@Override
public SOAPVersion getSOAPVersion() {
return version;
}
@Override
public String toString() {
return lexical;
}
@Deprecated
@Override
public boolean canGenerateWSDL() {
return canGenerateWSDL;
}
}
/**
* Internal implementation for SOAP/HTTP.
*/
private static final class SOAPHTTPImpl extends Impl implements Cloneable {
/*final*/ Map<String,String> parameters = new HashMap<String,String>();
static final String MTOM_PARAM = "mtom";
public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL) {
super(version, lexical, canGenerateWSDL);
}
public SOAPHTTPImpl(SOAPVersion version, String lexical, boolean canGenerateWSDL,
boolean mtomEnabled) {
this(version, lexical, canGenerateWSDL);
String mtomStr = mtomEnabled ? "true" : "false";
parameters.put(MTOM_PARAM, mtomStr);
}
public @NotNull @Override Codec createEncoder(WSBinding binding) {
return new SOAPBindingCodec(binding.getFeatures());
}
private Boolean isMTOMEnabled() {
String mtom = parameters.get(MTOM_PARAM);
return mtom==null?null:Boolean.valueOf(mtom);
}
@Override
public WebServiceFeatureList createBuiltinFeatureList() {
WebServiceFeatureList r=super.createBuiltinFeatureList();
Boolean mtom = isMTOMEnabled();
if(mtom != null)
r.add(new MTOMFeature(mtom));
return r;
}
@Override
public String getParameter(String parameterName, String defaultValue) {
if (parameters.get(parameterName) == null)
return super.getParameter(parameterName, defaultValue);
return parameters.get(parameterName);
}
@Override
public SOAPHTTPImpl clone() throws CloneNotSupportedException {
return (SOAPHTTPImpl) super.clone();
}
}
}