001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.api;
038:
039: import com.sun.istack.NotNull;
040: import com.sun.xml.ws.api.message.Message;
041: import com.sun.xml.ws.api.pipe.Codec;
042: import com.sun.xml.ws.api.pipe.Tube;
043: import com.sun.xml.ws.binding.BindingImpl;
044: import com.sun.xml.ws.binding.SOAPBindingImpl;
045: import com.sun.xml.ws.binding.WebServiceFeatureList;
046: import com.sun.xml.ws.encoding.SOAPBindingCodec;
047: import com.sun.xml.ws.encoding.XMLHTTPBindingCodec;
048: import com.sun.xml.ws.util.ServiceFinder;
049:
050: import javax.xml.ws.BindingType;
051: import javax.xml.ws.WebServiceException;
052: import javax.xml.ws.WebServiceFeature;
053: import javax.xml.ws.handler.Handler;
054: import javax.xml.ws.http.HTTPBinding;
055: import javax.xml.ws.soap.MTOMFeature;
056: import javax.xml.ws.soap.SOAPBinding;
057: import java.io.UnsupportedEncodingException;
058: import java.net.URL;
059: import java.net.URLDecoder;
060: import java.util.HashMap;
061: import java.util.Map;
062:
063: /**
064: * Parsed binding ID string.
065: *
066: * <p>
067: * {@link BindingID} is an immutable object that represents a binding ID,
068: * much like how {@link URL} is a representation of an URL.
069: * Like {@link URL}, this class offers a bunch of methods that let you
070: * query various traits/properties of a binding ID.
071: *
072: * <p>
073: * {@link BindingID} is extensible; one can plug in a parser from
074: * {@link String} to {@link BindingID} to interpret binding IDs that
075: * the JAX-WS RI does no a-priori knowledge of.
076: * Technologies such as Tango uses this to make the JAX-WS RI understand
077: * binding IDs defined in their world.
078: *
079: * Such technologies are free to extend this class and expose more characterstics.
080: *
081: * <p>
082: * Even though this class defines a few well known constants, {@link BindingID}
083: * instances do not necessarily have singleton semantics. Use {@link #equals(Object)}
084: * for the comparison.
085: *
086: * <h3>{@link BindingID} and {@link WSBinding}</h3>
087: * <p>
088: * {@link WSBinding} is mutable and represents a particular "use" of a {@link BindingID}.
089: * As such, it has state like a list of {@link Handler}s, which are inherently local
090: * to a particular usage. For example, if you have two proxies, you need two instances.
091: *
092: * {@link BindingID}, OTOH, is immutable and thus the single instance
093: * that represents "SOAP1.2/HTTP" can be shared and reused by all proxies in the same VM.
094: *
095: * @author Kohsuke Kawaguchi
096: */
097: public abstract class BindingID {
098:
099: /**
100: * Creates an instance of {@link WSBinding} (which is conceptually an "use"
101: * of {@link BindingID}) from a {@link BindingID}.
102: *
103: * @return
104: * Always a new instance.
105: */
106: public final @NotNull
107: WSBinding createBinding() {
108: return BindingImpl.create(this );
109: }
110:
111: public final @NotNull
112: WSBinding createBinding(WebServiceFeature... features) {
113: return BindingImpl.create(this , features);
114: }
115:
116: public final @NotNull
117: WSBinding createBinding(WSFeatureList features) {
118: return createBinding(features.toArray());
119: }
120:
121: /**
122: * Gets the SOAP version of this binding.
123: *
124: * TODO: clarify what to do with XML/HTTP binding
125: *
126: * @return
127: * If the binding is using SOAP, this method returns
128: * a {@link SOAPVersion} constant.
129: *
130: * If the binding is not based on SOAP, this method
131: * returns null. See {@link Message} for how a non-SOAP
132: * binding shall be handled by {@link Tube}s.
133: */
134: public abstract SOAPVersion getSOAPVersion();
135:
136: /**
137: * Creates a new {@link Codec} for this binding.
138: *
139: * @param binding
140: * Ocassionally some aspects of binding can be overridden by
141: * {@link WSBinding} at runtime by users, so some {@link Codec}s
142: * need to have access to {@link WSBinding} that it's working for.
143: */
144: public abstract @NotNull
145: Codec createEncoder(@NotNull
146: WSBinding binding);
147:
148: /**
149: * Gets the binding ID, which uniquely identifies the binding.
150: *
151: * <p>
152: * The relevant specs define the binding IDs and what they mean.
153: * The ID is used in many places to identify the kind of binding
154: * (such as SOAP1.1, SOAP1.2, REST, ...)
155: *
156: * @return
157: * Always non-null same value.
158: */
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: public boolean equals(Object obj) {
221: if (!(obj instanceof BindingID))
222: return false;
223: return toString().equals(obj.toString());
224: }
225:
226: public int hashCode() {
227: return toString().hashCode();
228: }
229:
230: /**
231: * Parses a binding ID string into a {@link BindingID} object.
232: *
233: * <p>
234: * This method first checks for a few known values and then delegate
235: * the parsing to {@link BindingIDFactory}.
236: *
237: * <p>
238: * If parsing succeeds this method returns a value. Otherwise
239: * throws {@link WebServiceException}.
240: *
241: * @throws WebServiceException
242: * If the binding ID is not understood.
243: */
244: public static @NotNull
245: BindingID parse(String lexical) {
246: if (lexical.equals(XML_HTTP.toString()))
247: return XML_HTTP;
248: if (belongsTo(lexical, SOAP11_HTTP.toString()))
249: return customize(lexical, SOAP11_HTTP);
250: if (belongsTo(lexical, SOAP12_HTTP.toString()))
251: return customize(lexical, SOAP12_HTTP);
252: if (belongsTo(lexical, SOAPBindingImpl.X_SOAP12HTTP_BINDING))
253: return customize(lexical, X_SOAP12_HTTP);
254:
255: // OK, it's none of the values JAX-WS understands.
256: for (BindingIDFactory f : ServiceFinder
257: .find(BindingIDFactory.class)) {
258: BindingID r = f.parse(lexical);
259: if (r != null)
260: return r;
261: }
262:
263: // nobody understood this value
264: throw new WebServiceException("Wrong binding ID: " + lexical);
265: }
266:
267: private static boolean belongsTo(String lexical, String id) {
268: return lexical.equals(id) || lexical.startsWith(id + '?');
269: }
270:
271: /**
272: * Parses parameter portion and returns appropriately populated {@link SOAPHTTPImpl}
273: */
274: private static SOAPHTTPImpl customize(String lexical,
275: SOAPHTTPImpl base) {
276: if (lexical.equals(base.toString()))
277: return base;
278:
279: // otherwise we must have query parameter
280: // we assume the spec won't define any tricky parameters that require
281: // complicated handling (such as %HH or non-ASCII char), so this parser
282: // is quite simple-minded.
283: SOAPHTTPImpl r = new SOAPHTTPImpl(base.getSOAPVersion(),
284: 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
291: .indexOf('?') + 1), "UTF-8");
292: for (String token : query.split("&")) {
293: int idx = token.indexOf('=');
294: if (idx < 0)
295: throw new WebServiceException(
296: "Malformed binding ID (no '=' in " + token
297: + ")");
298: r.parameters.put(token.substring(0, idx), token
299: .substring(idx + 1));
300: }
301: } catch (UnsupportedEncodingException e) {
302: throw new AssertionError(e); // UTF-8 is supported everywhere
303: }
304:
305: return r;
306: }
307:
308: /**
309: * Figures out the binding from {@link BindingType} annotation.
310: *
311: * @return
312: * default to {@link BindingID#SOAP11_HTTP}, if no such annotation is present.
313: * @see #parse(String)
314: */
315: public static @NotNull
316: BindingID parse(Class<?> implClass) {
317: BindingType bindingType = implClass
318: .getAnnotation(BindingType.class);
319: if (bindingType != null) {
320: String bindingId = bindingType.value();
321: if (bindingId.length() > 0) {
322: return BindingID.parse(bindingId);
323: }
324: }
325: return SOAP11_HTTP;
326: }
327:
328: /**
329: * Constant that represents implementation specific SOAP1.2/HTTP which is
330: * used to generate non-standard WSDLs
331: */
332: public static final SOAPHTTPImpl X_SOAP12_HTTP = new SOAPHTTPImpl(
333: SOAPVersion.SOAP_12, SOAPBindingImpl.X_SOAP12HTTP_BINDING,
334: true);
335:
336: /**
337: * Constant that represents SOAP1.2/HTTP.
338: */
339: public static final SOAPHTTPImpl SOAP12_HTTP = new SOAPHTTPImpl(
340: SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_BINDING, false);
341: /**
342: * Constant that represents SOAP1.1/HTTP.
343: */
344: public static final SOAPHTTPImpl SOAP11_HTTP = new SOAPHTTPImpl(
345: SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_BINDING, true);
346:
347: /**
348: * Constant that represents SOAP1.2/HTTP.
349: */
350: public static final SOAPHTTPImpl SOAP12_HTTP_MTOM = new SOAPHTTPImpl(
351: SOAPVersion.SOAP_12, SOAPBinding.SOAP12HTTP_MTOM_BINDING,
352: false, true);
353: /**
354: * Constant that represents SOAP1.1/HTTP.
355: */
356: public static final SOAPHTTPImpl SOAP11_HTTP_MTOM = new SOAPHTTPImpl(
357: SOAPVersion.SOAP_11, SOAPBinding.SOAP11HTTP_MTOM_BINDING,
358: true, true);
359:
360: /**
361: * Constant that represents REST.
362: */
363: public static final BindingID XML_HTTP = new Impl(
364: SOAPVersion.SOAP_11, HTTPBinding.HTTP_BINDING, false) {
365: public Codec createEncoder(WSBinding binding) {
366: return new XMLHTTPBindingCodec(binding);
367: }
368: };
369:
370: private static abstract class Impl extends BindingID {
371: final SOAPVersion version;
372: private final String lexical;
373: private final boolean canGenerateWSDL;
374:
375: public Impl(SOAPVersion version, String lexical,
376: boolean canGenerateWSDL) {
377: this .version = version;
378: this .lexical = lexical;
379: this .canGenerateWSDL = canGenerateWSDL;
380: }
381:
382: public SOAPVersion getSOAPVersion() {
383: return version;
384: }
385:
386: public String toString() {
387: return lexical;
388: }
389:
390: @Deprecated
391: public boolean canGenerateWSDL() {
392: return canGenerateWSDL;
393: }
394: }
395:
396: /**
397: * Internal implementation for SOAP/HTTP.
398: */
399: private static final class SOAPHTTPImpl extends Impl implements
400: Cloneable {
401: /*final*/Map<String, String> parameters = new HashMap<String, String>();
402:
403: static final String MTOM_PARAM = "mtom";
404: Boolean mtomSetting = null;
405:
406: public SOAPHTTPImpl(SOAPVersion version, String lexical,
407: boolean canGenerateWSDL) {
408: super (version, lexical, canGenerateWSDL);
409: }
410:
411: public SOAPHTTPImpl(SOAPVersion version, String lexical,
412: boolean canGenerateWSDL, boolean mtomEnabled) {
413: this (version, lexical, canGenerateWSDL);
414: String mtomStr = mtomEnabled ? "true" : "false";
415: parameters.put(MTOM_PARAM, mtomStr);
416: mtomSetting = mtomEnabled;
417: }
418:
419: public @NotNull
420: Codec createEncoder(WSBinding binding) {
421: return new SOAPBindingCodec(binding);
422: }
423:
424: private Boolean isMTOMEnabled() {
425: String mtom = parameters.get(MTOM_PARAM);
426: return mtom == null ? null : Boolean.valueOf(mtom);
427: }
428:
429: public WebServiceFeatureList createBuiltinFeatureList() {
430: WebServiceFeatureList r = super .createBuiltinFeatureList();
431: Boolean mtom = isMTOMEnabled();
432: if (mtom != null)
433: r.add(new MTOMFeature(mtom));
434: return r;
435: }
436:
437: public String getParameter(String parameterName,
438: String defaultValue) {
439: if (parameters.get(parameterName) == null)
440: return super.getParameter(parameterName, defaultValue);
441: return parameters.get(parameterName);
442: }
443: }
444: }
|