feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.transport;
|
||||
|
||||
import com.sun.xml.internal.ws.api.EndpointAddress;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.pipe.*;
|
||||
import com.sun.xml.internal.ws.api.pipe.helper.AbstractTubeImpl;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.developer.HttpConfigFeature;
|
||||
|
||||
import javax.xml.ws.BindingProvider;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
|
||||
/**
|
||||
* Proxy transport {@link Tube} and {@link Pipe} that lazily determines the
|
||||
* actual transport pipe by looking at {@link Packet#endpointAddress}.
|
||||
*
|
||||
* <p>
|
||||
* This pseudo transport is used when there's no statically known endpoint address,
|
||||
* and thus it's expected that the application will configure {@link BindingProvider}
|
||||
* at runtime before making invocation.
|
||||
*
|
||||
* <p>
|
||||
* Since a typical application makes multiple invocations with the same endpoint
|
||||
* address, this class implements a simple cache strategy to avoid re-creating
|
||||
* transport pipes excessively.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public final class DeferredTransportPipe extends AbstractTubeImpl {
|
||||
|
||||
private Tube transport;
|
||||
private EndpointAddress address;
|
||||
|
||||
// parameter to TransportPipeFactory
|
||||
private final ClassLoader classLoader;
|
||||
private final ClientTubeAssemblerContext context;
|
||||
|
||||
public DeferredTransportPipe(ClassLoader classLoader, ClientTubeAssemblerContext context) {
|
||||
this.classLoader = classLoader;
|
||||
this.context = context;
|
||||
if (context.getBinding().getFeature(HttpConfigFeature.class) == null) {
|
||||
context.getBinding().getFeatures().mergeFeatures(
|
||||
new WebServiceFeature[] { new HttpConfigFeature() }, false);
|
||||
}
|
||||
//See if we can create the transport pipe from the available information.
|
||||
try {
|
||||
this.transport = TransportTubeFactory.create(classLoader, context);
|
||||
this.address = context.getAddress();
|
||||
} catch(Exception e) {
|
||||
//No problem, transport will be initialized while processing the requests
|
||||
}
|
||||
}
|
||||
|
||||
public DeferredTransportPipe(DeferredTransportPipe that, TubeCloner cloner) {
|
||||
super(that,cloner);
|
||||
this.classLoader = that.classLoader;
|
||||
this.context = that.context;
|
||||
if(that.transport!=null) {
|
||||
this.transport = cloner.copy(that.transport);
|
||||
this.address = that.address;
|
||||
}
|
||||
}
|
||||
public NextAction processException(@NotNull Throwable t) {
|
||||
return transport.processException(t);
|
||||
}
|
||||
|
||||
public NextAction processRequest(@NotNull Packet request) {
|
||||
if(request.endpointAddress==address)
|
||||
// cache hit
|
||||
return transport.processRequest(request);
|
||||
|
||||
// cache miss
|
||||
|
||||
if(transport!=null) {
|
||||
// delete the current entry
|
||||
transport.preDestroy();
|
||||
transport = null;
|
||||
address = null;
|
||||
}
|
||||
|
||||
// otherwise find out what transport will process this.
|
||||
|
||||
ClientTubeAssemblerContext newContext = new ClientTubeAssemblerContext(
|
||||
request.endpointAddress,
|
||||
context.getWsdlModel(),
|
||||
context.getBindingProvider(),
|
||||
context.getBinding(),
|
||||
context.getContainer(),
|
||||
context.getCodec().copy(),
|
||||
context.getSEIModel(),
|
||||
context.getSEI()
|
||||
);
|
||||
|
||||
address = request.endpointAddress;
|
||||
transport = TransportTubeFactory.create(classLoader, newContext);
|
||||
// successful return from the above method indicates a successful pipe creation
|
||||
assert transport!=null;
|
||||
|
||||
return transport.processRequest(request);
|
||||
}
|
||||
|
||||
public NextAction processResponse(@NotNull Packet response) {
|
||||
if (transport != null)
|
||||
return transport.processResponse(response);
|
||||
return doReturnWith(response);
|
||||
}
|
||||
|
||||
public void preDestroy() {
|
||||
if(transport!=null) {
|
||||
transport.preDestroy();
|
||||
transport = null;
|
||||
address = null;
|
||||
}
|
||||
}
|
||||
|
||||
public DeferredTransportPipe copy(TubeCloner cloner) {
|
||||
return new DeferredTransportPipe(this,cloner);
|
||||
}
|
||||
}
|
||||
131
jdkSrc/jdk8/com/sun/xml/internal/ws/transport/Headers.java
Normal file
131
jdkSrc/jdk8/com/sun/xml/internal/ws/transport/Headers.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.transport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* HTTP request and response headers are represented by this class which implements
|
||||
* the interface {@link java.util.Map}<{@link String},
|
||||
* {@link List}<{@link String}>>.
|
||||
* The keys are case-insensitive Strings representing the header names and
|
||||
* the value associated with each key is a {@link List}<{@link String}> with one
|
||||
* element for each occurrence of the header name in the request or response.
|
||||
* <p>
|
||||
* For example, if the request has the the following headers:
|
||||
* <blockquote><pre>
|
||||
* HeaderName: value1
|
||||
* HeadernaMe: value2
|
||||
* </blockquote></pre>
|
||||
* Then get("hEaDeRnAmE") would give both "value1", and "value2" values in a list
|
||||
* <p>
|
||||
* All the normal {@link Map} methods are provided, but the following
|
||||
* additional convenience methods are most likely to be used:
|
||||
* <ul>
|
||||
* <li>{@link #getFirst(String)} returns a single valued header or the first
|
||||
* value of a multi-valued header.</li>
|
||||
* <li>{@link #add(String,String)} adds the given header value to the list
|
||||
* for the given key</li>
|
||||
* <li>{@link #set(String,String)} sets the given header field to the single
|
||||
* value given overwriting any existing values in the value list.
|
||||
* </ul><p>
|
||||
* All methods in this class accept <code>null</code> values for keys and values.
|
||||
* However, null keys will never will be present in HTTP request headers, and
|
||||
* will not be output/sent in response headers. Null values can be represented
|
||||
* as either a null entry for the key (i.e. the list is null) or where the key
|
||||
* has a list, but one (or more) of the list's values is null. Null values are
|
||||
* output as a header line containing the key but no associated value.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public class Headers extends TreeMap<String,List<String>> {
|
||||
|
||||
public Headers() {
|
||||
super(INSTANCE);
|
||||
}
|
||||
|
||||
private static final InsensitiveComparator INSTANCE = new InsensitiveComparator();
|
||||
|
||||
// case-insensitive string comparison of HTTP header names.
|
||||
private static final class InsensitiveComparator implements Comparator<String>, Serializable {
|
||||
public int compare(String o1, String o2) {
|
||||
if (o1 == null && o2 == null)
|
||||
return 0;
|
||||
if (o1 == null)
|
||||
return -1;
|
||||
if (o2 == null)
|
||||
return 1;
|
||||
return o1.compareToIgnoreCase(o2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given value to the list of headers for the given key. If the
|
||||
* mapping does not already exist, then it is created.
|
||||
*
|
||||
* @param key the header name
|
||||
* @param value the header value to add to the header
|
||||
*/
|
||||
public void add (String key, String value) {
|
||||
List<String> list = this.get(key);
|
||||
if (list == null) {
|
||||
list = new LinkedList<String>();
|
||||
put(key,list);
|
||||
}
|
||||
list.add (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first value from the List of String values for the given key
|
||||
* (if at least one exists).
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return the first string value associated with the key
|
||||
*/
|
||||
public String getFirst (String key) {
|
||||
List<String> l = get(key);
|
||||
return (l == null) ? null : l.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given value as the sole header value for the given key. If the
|
||||
* mapping does not already exist, then it is created.
|
||||
*
|
||||
* @param key the header name
|
||||
* @param value the header value to set.
|
||||
*/
|
||||
public void set (String key, String value) {
|
||||
LinkedList<String> l = new LinkedList<String>();
|
||||
l.add (value);
|
||||
put(key, l);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http;
|
||||
|
||||
import com.oracle.webservices.internal.api.databinding.DatabindingModeFeature;
|
||||
import com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.BindingID;
|
||||
import com.sun.xml.internal.ws.api.WSBinding;
|
||||
import com.sun.xml.internal.ws.api.databinding.MetadataReader;
|
||||
import com.sun.xml.internal.ws.api.server.Container;
|
||||
import com.sun.xml.internal.ws.api.server.SDDocumentSource;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
|
||||
import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
|
||||
|
||||
import com.sun.xml.internal.ws.handler.HandlerChainsModel;
|
||||
import com.sun.xml.internal.ws.resources.ServerMessages;
|
||||
import com.sun.xml.internal.ws.resources.WsservletMessages;
|
||||
import com.sun.xml.internal.ws.server.EndpointFactory;
|
||||
import com.sun.xml.internal.ws.server.ServerRtException;
|
||||
import com.sun.xml.internal.ws.streaming.Attributes;
|
||||
import com.sun.xml.internal.ws.streaming.TidyXMLStreamReader;
|
||||
import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
|
||||
import com.sun.xml.internal.ws.util.HandlerAnnotationInfo;
|
||||
import com.sun.xml.internal.ws.util.exception.LocatableWebServiceException;
|
||||
import com.sun.xml.internal.ws.util.xml.XmlUtil;
|
||||
import org.xml.sax.EntityResolver;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.XMLStreamConstants;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.http.HTTPBinding;
|
||||
import javax.xml.ws.soap.MTOMFeature;
|
||||
import javax.xml.ws.soap.SOAPBinding;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Parses {@code sun-jaxws.xml} into {@link WSEndpoint}.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* Since {@code sun-jaxws.xml} captures more information than what {@link WSEndpoint}
|
||||
* represents (in particular URL pattern and name), this class
|
||||
* takes a parameterization 'A' so that the user of this parser can choose to
|
||||
* create another type that wraps {@link WSEndpoint}.
|
||||
* <p/>
|
||||
* {@link HttpAdapter} and its derived type is used for this often,
|
||||
* but it can be anything.
|
||||
*
|
||||
* @author WS Development Team
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class DeploymentDescriptorParser<A> {
|
||||
|
||||
public static final String NS_RUNTIME = "http://java.sun.com/xml/ns/jax-ws/ri/runtime";
|
||||
public static final String JAXWS_WSDL_DD_DIR = "WEB-INF/wsdl";
|
||||
|
||||
public static final QName QNAME_ENDPOINTS = new QName(NS_RUNTIME, "endpoints");
|
||||
public static final QName QNAME_ENDPOINT = new QName(NS_RUNTIME, "endpoint");
|
||||
public static final QName QNAME_EXT_METADA = new QName(NS_RUNTIME, "external-metadata");
|
||||
|
||||
public static final String ATTR_FILE = "file";
|
||||
public static final String ATTR_RESOURCE = "resource";
|
||||
|
||||
public static final String ATTR_VERSION = "version";
|
||||
public static final String ATTR_NAME = "name";
|
||||
public static final String ATTR_IMPLEMENTATION = "implementation";
|
||||
public static final String ATTR_WSDL = "wsdl";
|
||||
public static final String ATTR_SERVICE = "service";
|
||||
public static final String ATTR_PORT = "port";
|
||||
public static final String ATTR_URL_PATTERN = "url-pattern";
|
||||
public static final String ATTR_ENABLE_MTOM = "enable-mtom";
|
||||
public static final String ATTR_MTOM_THRESHOLD_VALUE = "mtom-threshold-value";
|
||||
public static final String ATTR_BINDING = "binding";
|
||||
public static final String ATTR_DATABINDING = "databinding";
|
||||
|
||||
public static final List<String> ATTRVALUE_SUPPORTED_VERSIONS = Arrays.asList("2.0", "2.1");
|
||||
|
||||
private static final Logger logger = Logger.getLogger(com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.http");
|
||||
|
||||
private final Container container;
|
||||
private final ClassLoader classLoader;
|
||||
private final ResourceLoader loader;
|
||||
private final AdapterFactory<A> adapterFactory;
|
||||
|
||||
/**
|
||||
* Endpoint names that are declared.
|
||||
* Used to catch double definitions.
|
||||
*/
|
||||
private final Set<String> names = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* WSDL/schema documents collected from /WEB-INF/wsdl. Keyed by the system ID.
|
||||
*/
|
||||
private final Map<String, SDDocumentSource> docs = new HashMap<String, SDDocumentSource>();
|
||||
|
||||
/**
|
||||
* @param cl Used to load service implementations.
|
||||
* @param loader Used to locate resources, in particular WSDL.
|
||||
* @param container Optional {@link Container} that {@link WSEndpoint}s receive.
|
||||
* @param adapterFactory Creates {@link HttpAdapter} (or its derived class.)
|
||||
*/
|
||||
public DeploymentDescriptorParser(ClassLoader cl, ResourceLoader loader, Container container,
|
||||
AdapterFactory<A> adapterFactory) throws MalformedURLException {
|
||||
classLoader = cl;
|
||||
this.loader = loader;
|
||||
this.container = container;
|
||||
this.adapterFactory = adapterFactory;
|
||||
|
||||
collectDocs("/WEB-INF/wsdl/");
|
||||
logger.log(Level.FINE, "war metadata={0}", docs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code sun-jaxws.xml} file and configures
|
||||
* a set of {@link HttpAdapter}s.
|
||||
*/
|
||||
public @NotNull List<A> parse(String systemId, InputStream is) {
|
||||
XMLStreamReader reader = null;
|
||||
try {
|
||||
reader = new TidyXMLStreamReader(
|
||||
XMLStreamReaderFactory.create(systemId, is, true), is);
|
||||
XMLStreamReaderUtil.nextElementContent(reader);
|
||||
return parseAdapters(reader);
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new ServerRtException("runtime.parser.xmlReader", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@code sun-jaxws.xml} file and configures
|
||||
* a set of {@link HttpAdapter}s.
|
||||
*/
|
||||
public @NotNull List<A> parse(File f) throws IOException {
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
try {
|
||||
return parse(f.getPath(), in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the WSDL & schema documents recursively.
|
||||
*/
|
||||
private void collectDocs(String dirPath) throws MalformedURLException {
|
||||
Set<String> paths = loader.getResourcePaths(dirPath);
|
||||
if (paths != null) {
|
||||
for (String path : paths) {
|
||||
if (path.endsWith("/")) {
|
||||
if (path.endsWith("/CVS/") || path.endsWith("/.svn/")) {
|
||||
continue;
|
||||
}
|
||||
collectDocs(path);
|
||||
} else {
|
||||
URL res = loader.getResource(path);
|
||||
docs.put(res.toString(), SDDocumentSource.create(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<A> parseAdapters(XMLStreamReader reader) {
|
||||
if (!reader.getName().equals(QNAME_ENDPOINTS)) {
|
||||
failWithFullName("runtime.parser.invalidElement", reader);
|
||||
}
|
||||
|
||||
List<A> adapters = new ArrayList<A>();
|
||||
|
||||
Attributes attrs = XMLStreamReaderUtil.getAttributes(reader);
|
||||
String version = getMandatoryNonEmptyAttribute(reader, attrs, ATTR_VERSION);
|
||||
if (!ATTRVALUE_SUPPORTED_VERSIONS.contains(version)) {
|
||||
failWithLocalName("runtime.parser.invalidVersionNumber", reader, version);
|
||||
}
|
||||
|
||||
while (XMLStreamReaderUtil.nextElementContent(reader) != XMLStreamConstants.END_ELEMENT) {
|
||||
|
||||
if (reader.getName().equals(QNAME_ENDPOINT)) {
|
||||
attrs = XMLStreamReaderUtil.getAttributes(reader);
|
||||
|
||||
String name = getMandatoryNonEmptyAttribute(reader, attrs, ATTR_NAME);
|
||||
if (!names.add(name)) {
|
||||
logger.warning(
|
||||
WsservletMessages.SERVLET_WARNING_DUPLICATE_ENDPOINT_NAME(/*name*/));
|
||||
}
|
||||
|
||||
String implementationName =
|
||||
getMandatoryNonEmptyAttribute(reader, attrs, ATTR_IMPLEMENTATION);
|
||||
Class<?> implementorClass = getImplementorClass(implementationName, reader);
|
||||
|
||||
MetadataReader metadataReader = null;
|
||||
ExternalMetadataFeature externalMetadataFeature = null;
|
||||
|
||||
// parse subelements to instantiate externalMetadataReader, if necessary ...
|
||||
XMLStreamReaderUtil.nextElementContent(reader);
|
||||
if (reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
|
||||
externalMetadataFeature = configureExternalMetadataReader(reader);
|
||||
if (externalMetadataFeature != null) {
|
||||
metadataReader = externalMetadataFeature.getMetadataReader(implementorClass.getClassLoader(), false);
|
||||
}
|
||||
}
|
||||
|
||||
QName serviceName = getQNameAttribute(attrs, ATTR_SERVICE);
|
||||
if (serviceName == null) {
|
||||
serviceName = EndpointFactory.getDefaultServiceName(implementorClass, metadataReader);
|
||||
}
|
||||
|
||||
QName portName = getQNameAttribute(attrs, ATTR_PORT);
|
||||
if (portName == null) {
|
||||
portName = EndpointFactory.getDefaultPortName(serviceName, implementorClass, metadataReader);
|
||||
}
|
||||
|
||||
//get enable-mtom attribute value
|
||||
String enable_mtom = getAttribute(attrs, ATTR_ENABLE_MTOM);
|
||||
String mtomThreshold = getAttribute(attrs, ATTR_MTOM_THRESHOLD_VALUE);
|
||||
String dbMode = getAttribute(attrs, ATTR_DATABINDING);
|
||||
String bindingId = getAttribute(attrs, ATTR_BINDING);
|
||||
if (bindingId != null) {
|
||||
// Convert short-form tokens to API's binding ids
|
||||
bindingId = getBindingIdForToken(bindingId);
|
||||
}
|
||||
WSBinding binding = createBinding(bindingId, implementorClass, enable_mtom, mtomThreshold, dbMode);
|
||||
if (externalMetadataFeature != null) {
|
||||
binding.getFeatures().mergeFeatures(new WebServiceFeature[]{externalMetadataFeature},
|
||||
true);
|
||||
}
|
||||
|
||||
String urlPattern = getMandatoryNonEmptyAttribute(reader, attrs, ATTR_URL_PATTERN);
|
||||
|
||||
// TODO use 'docs' as the metadata. If wsdl is non-null it's the primary.
|
||||
boolean handlersSetInDD = setHandlersAndRoles(binding, reader, serviceName, portName);
|
||||
|
||||
EndpointFactory.verifyImplementorClass(implementorClass, metadataReader);
|
||||
SDDocumentSource primaryWSDL = getPrimaryWSDL(reader, attrs, implementorClass, metadataReader);
|
||||
|
||||
WSEndpoint<?> endpoint = WSEndpoint.create(
|
||||
implementorClass, !handlersSetInDD,
|
||||
null,
|
||||
serviceName, portName, container, binding,
|
||||
primaryWSDL, docs.values(), createEntityResolver(), false
|
||||
);
|
||||
adapters.add(adapterFactory.createAdapter(name, urlPattern, endpoint));
|
||||
} else {
|
||||
failWithLocalName("runtime.parser.invalidElement", reader);
|
||||
}
|
||||
}
|
||||
return adapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ddBindingId binding id explicitlyspecified in the DeploymentDescriptor or parameter
|
||||
* @param implClass Endpoint Implementation class
|
||||
* @param mtomEnabled represents mtom-enabled attribute in DD
|
||||
* @param mtomThreshold threshold value specified in DD
|
||||
* @return is returned with only MTOMFeature set resolving the various precendece rules
|
||||
*/
|
||||
private static WSBinding createBinding(String ddBindingId, Class implClass,
|
||||
String mtomEnabled, String mtomThreshold, String dataBindingMode) {
|
||||
// Features specified through DD
|
||||
WebServiceFeatureList features;
|
||||
|
||||
MTOMFeature mtomfeature = null;
|
||||
if (mtomEnabled != null) {
|
||||
if (mtomThreshold != null) {
|
||||
mtomfeature = new MTOMFeature(Boolean.valueOf(mtomEnabled),
|
||||
Integer.valueOf(mtomThreshold));
|
||||
} else {
|
||||
mtomfeature = new MTOMFeature(Boolean.valueOf(mtomEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
BindingID bindingID;
|
||||
if (ddBindingId != null) {
|
||||
bindingID = BindingID.parse(ddBindingId);
|
||||
features = bindingID.createBuiltinFeatureList();
|
||||
|
||||
if (checkMtomConflict(features.get(MTOMFeature.class), mtomfeature)) {
|
||||
throw new ServerRtException(ServerMessages.DD_MTOM_CONFLICT(ddBindingId, mtomEnabled));
|
||||
}
|
||||
} else {
|
||||
bindingID = BindingID.parse(implClass);
|
||||
// Since bindingID is coming from implclass,
|
||||
// mtom through Feature annotation or DD takes precendece
|
||||
|
||||
features = new WebServiceFeatureList();
|
||||
if (mtomfeature != null) { // this wins over MTOM setting in bindingID
|
||||
features.add(mtomfeature);
|
||||
}
|
||||
features.addAll(bindingID.createBuiltinFeatureList());
|
||||
}
|
||||
|
||||
if (dataBindingMode != null) {
|
||||
features.add(new DatabindingModeFeature(dataBindingMode));
|
||||
}
|
||||
|
||||
return bindingID.createBinding(features.toArray());
|
||||
}
|
||||
|
||||
private static boolean checkMtomConflict(MTOMFeature lhs, MTOMFeature rhs) {
|
||||
if (lhs == null || rhs == null) {
|
||||
return false;
|
||||
}
|
||||
return lhs.isEnabled() ^ rhs.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSR-109 defines short-form tokens for standard binding Ids. These are
|
||||
* used only in DD. So stand alone deployment descirptor should also honor
|
||||
* these tokens. This method converts the tokens to API's standard
|
||||
* binding ids
|
||||
*
|
||||
* @param lexical binding attribute value from DD. Always not null
|
||||
* @return returns corresponding API's binding ID or the same lexical
|
||||
*/
|
||||
public static @NotNull String getBindingIdForToken(@NotNull String lexical) {
|
||||
if (lexical.equals("##SOAP11_HTTP")) {
|
||||
return SOAPBinding.SOAP11HTTP_BINDING;
|
||||
} else if (lexical.equals("##SOAP11_HTTP_MTOM")) {
|
||||
return SOAPBinding.SOAP11HTTP_MTOM_BINDING;
|
||||
} else if (lexical.equals("##SOAP12_HTTP")) {
|
||||
return SOAPBinding.SOAP12HTTP_BINDING;
|
||||
} else if (lexical.equals("##SOAP12_HTTP_MTOM")) {
|
||||
return SOAPBinding.SOAP12HTTP_MTOM_BINDING;
|
||||
} else if (lexical.equals("##XML_HTTP")) {
|
||||
return HTTPBinding.HTTP_BINDING;
|
||||
}
|
||||
return lexical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "Adapter".
|
||||
* <p/>
|
||||
* Normally 'A' would be {@link HttpAdapter} or some derived class.
|
||||
* But the parser doesn't require that to be of any particular type.
|
||||
*/
|
||||
public static interface AdapterFactory<A> {
|
||||
A createAdapter(String name, String urlPattern, WSEndpoint<?> endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the deployment descriptor or {@link @WebServiceProvider} annotation
|
||||
* to see if it points to any WSDL. If so, returns the {@link SDDocumentSource}.
|
||||
*
|
||||
* @return The pointed WSDL, if any. Otherwise null.
|
||||
*/
|
||||
private SDDocumentSource getPrimaryWSDL(XMLStreamReader xsr, Attributes attrs, Class<?> implementorClass, MetadataReader metadataReader) {
|
||||
|
||||
String wsdlFile = getAttribute(attrs, ATTR_WSDL);
|
||||
if (wsdlFile == null) {
|
||||
wsdlFile = EndpointFactory.getWsdlLocation(implementorClass, metadataReader);
|
||||
}
|
||||
|
||||
if (wsdlFile != null) {
|
||||
if (!wsdlFile.startsWith(JAXWS_WSDL_DD_DIR)) {
|
||||
logger.log(Level.WARNING, "Ignoring wrong wsdl={0}. It should start with {1}. Going to generate and publish a new WSDL.", new Object[]{wsdlFile, JAXWS_WSDL_DD_DIR});
|
||||
return null;
|
||||
}
|
||||
|
||||
URL wsdl;
|
||||
try {
|
||||
wsdl = loader.getResource('/' + wsdlFile);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new LocatableWebServiceException(
|
||||
ServerMessages.RUNTIME_PARSER_WSDL_NOT_FOUND(wsdlFile), e, xsr);
|
||||
}
|
||||
if (wsdl == null) {
|
||||
throw new LocatableWebServiceException(
|
||||
ServerMessages.RUNTIME_PARSER_WSDL_NOT_FOUND(wsdlFile), xsr);
|
||||
}
|
||||
SDDocumentSource docInfo = docs.get(wsdl.toExternalForm());
|
||||
assert docInfo != null;
|
||||
return docInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link EntityResolver} that consults {@code /WEB-INF/jax-ws-catalog.xml}.
|
||||
*/
|
||||
private EntityResolver createEntityResolver() {
|
||||
try {
|
||||
return XmlUtil.createEntityResolver(loader.getCatalogFile());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new WebServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getAttribute(Attributes attrs, String name) {
|
||||
String value = attrs.getValue(name);
|
||||
if (value != null) {
|
||||
value = value.trim();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected QName getQNameAttribute(Attributes attrs, String name) {
|
||||
String value = getAttribute(attrs, name);
|
||||
if (value == null || value.equals("")) {
|
||||
return null;
|
||||
} else {
|
||||
return QName.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getNonEmptyAttribute(XMLStreamReader reader, Attributes attrs, String name) {
|
||||
String value = getAttribute(attrs, name);
|
||||
if (value != null && value.equals("")) {
|
||||
failWithLocalName(
|
||||
"runtime.parser.invalidAttributeValue",
|
||||
reader,
|
||||
name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected String getMandatoryAttribute(XMLStreamReader reader, Attributes attrs, String name) {
|
||||
String value = getAttribute(attrs, name);
|
||||
if (value == null) {
|
||||
failWithLocalName("runtime.parser.missing.attribute", reader, name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected String getMandatoryNonEmptyAttribute(XMLStreamReader reader, Attributes attributes,
|
||||
String name) {
|
||||
String value = getAttribute(attributes, name);
|
||||
if (value == null) {
|
||||
failWithLocalName("runtime.parser.missing.attribute", reader, name);
|
||||
} else if (value.equals("")) {
|
||||
failWithLocalName(
|
||||
"runtime.parser.invalidAttributeValue",
|
||||
reader,
|
||||
name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the handler and role information and sets it
|
||||
* on the {@link WSBinding}.
|
||||
*
|
||||
* @return true if <handler-chains> element present in DD
|
||||
* false otherwise.
|
||||
*/
|
||||
protected boolean setHandlersAndRoles(WSBinding binding, XMLStreamReader reader, QName serviceName, QName portName) {
|
||||
|
||||
if (reader.getEventType() == XMLStreamConstants.END_ELEMENT ||
|
||||
!reader.getName().equals(HandlerChainsModel.QNAME_HANDLER_CHAINS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HandlerAnnotationInfo handlerInfo = HandlerChainsModel.parseHandlerFile(
|
||||
reader, classLoader, serviceName, portName, binding);
|
||||
|
||||
binding.setHandlerChain(handlerInfo.getHandlers());
|
||||
if (binding instanceof SOAPBinding) {
|
||||
((SOAPBinding) binding).setRoles(handlerInfo.getRoles());
|
||||
}
|
||||
|
||||
// move past </handler-chains>
|
||||
XMLStreamReaderUtil.nextContent(reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ExternalMetadataFeature configureExternalMetadataReader(XMLStreamReader reader) {
|
||||
|
||||
ExternalMetadataFeature.Builder featureBuilder = null;
|
||||
while (QNAME_EXT_METADA.equals(reader.getName())) {
|
||||
|
||||
if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
|
||||
Attributes attrs = XMLStreamReaderUtil.getAttributes(reader);
|
||||
String file = getAttribute(attrs, ATTR_FILE);
|
||||
if (file != null) {
|
||||
if (featureBuilder == null) {
|
||||
featureBuilder = ExternalMetadataFeature.builder();
|
||||
}
|
||||
featureBuilder.addFiles(new File(file));
|
||||
}
|
||||
|
||||
String res = getAttribute(attrs, ATTR_RESOURCE);
|
||||
if (res != null) {
|
||||
if (featureBuilder == null) {
|
||||
featureBuilder = ExternalMetadataFeature.builder();
|
||||
}
|
||||
featureBuilder.addResources(res);
|
||||
}
|
||||
}
|
||||
|
||||
XMLStreamReaderUtil.nextElementContent(reader);
|
||||
}
|
||||
|
||||
return buildFeature(featureBuilder);
|
||||
}
|
||||
|
||||
private ExternalMetadataFeature buildFeature(ExternalMetadataFeature.Builder builder) {
|
||||
return builder != null ? builder.build() : null;
|
||||
}
|
||||
|
||||
protected static void fail(String key, XMLStreamReader reader) {
|
||||
logger.log(Level.SEVERE, "{0}{1}", new Object[]{key, reader.getLocation().getLineNumber()});
|
||||
throw new ServerRtException(
|
||||
key,
|
||||
Integer.toString(reader.getLocation().getLineNumber()));
|
||||
}
|
||||
|
||||
protected static void failWithFullName(String key, XMLStreamReader reader) {
|
||||
throw new ServerRtException(
|
||||
key,
|
||||
reader.getLocation().getLineNumber(),
|
||||
reader.getName());
|
||||
}
|
||||
|
||||
protected static void failWithLocalName(String key, XMLStreamReader reader) {
|
||||
throw new ServerRtException(
|
||||
key,
|
||||
reader.getLocation().getLineNumber(),
|
||||
reader.getLocalName());
|
||||
}
|
||||
|
||||
protected static void failWithLocalName(String key, XMLStreamReader reader, String arg) {
|
||||
throw new ServerRtException(
|
||||
key,
|
||||
reader.getLocation().getLineNumber(),
|
||||
reader.getLocalName(),
|
||||
arg);
|
||||
}
|
||||
|
||||
protected Class loadClass(String name) {
|
||||
try {
|
||||
return Class.forName(name, true, classLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
throw new ServerRtException(
|
||||
"runtime.parser.classNotFound",
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads the class of the given name.
|
||||
*
|
||||
* @param xsr Used to report the source location information if there's any error.
|
||||
*/
|
||||
private Class getImplementorClass(String name, XMLStreamReader xsr) {
|
||||
try {
|
||||
return Class.forName(name, true, classLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
throw new LocatableWebServiceException(
|
||||
ServerMessages.RUNTIME_PARSER_CLASS_NOT_FOUND(name), e, xsr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.xml.ws.Binding;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.http.HTTPBinding;
|
||||
|
||||
import com.oracle.webservices.internal.api.message.PropertySet;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.xml.internal.ws.api.Component;
|
||||
import com.sun.xml.internal.ws.api.EndpointAddress;
|
||||
import com.sun.xml.internal.ws.api.SOAPVersion;
|
||||
import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
|
||||
import com.sun.xml.internal.ws.api.addressing.NonAnonymousResponseProcessor;
|
||||
import com.sun.xml.internal.ws.api.ha.HaInfo;
|
||||
import com.sun.xml.internal.ws.api.message.ExceptionHasMessage;
|
||||
import com.sun.xml.internal.ws.api.message.Message;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.pipe.Codec;
|
||||
import com.sun.xml.internal.ws.api.pipe.ContentType;
|
||||
import com.sun.xml.internal.ws.api.server.AbstractServerAsyncTransport;
|
||||
import com.sun.xml.internal.ws.api.server.Adapter;
|
||||
import com.sun.xml.internal.ws.api.server.BoundEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.DocumentAddressResolver;
|
||||
import com.sun.xml.internal.ws.api.server.Module;
|
||||
import com.sun.xml.internal.ws.api.server.PortAddressResolver;
|
||||
import com.sun.xml.internal.ws.api.server.SDDocument;
|
||||
import com.sun.xml.internal.ws.api.server.ServiceDefinition;
|
||||
import com.sun.xml.internal.ws.api.server.TransportBackChannel;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
|
||||
import com.sun.xml.internal.ws.fault.SOAPFaultBuilder;
|
||||
import com.sun.xml.internal.ws.resources.WsservletMessages;
|
||||
import com.sun.xml.internal.ws.server.UnsupportedMediaException;
|
||||
import com.sun.xml.internal.ws.util.ByteArrayBuffer;
|
||||
import com.sun.xml.internal.ws.util.Pool;
|
||||
|
||||
|
||||
/**
|
||||
* {@link com.sun.xml.internal.ws.api.server.Adapter} that receives messages in HTTP.
|
||||
*
|
||||
* <p>
|
||||
* This object also assigns unique query string (such as "xsd=1") to
|
||||
* each {@link com.sun.xml.internal.ws.api.server.SDDocument} so that they can be served by HTTP GET requests.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public class HttpAdapter extends Adapter<HttpAdapter.HttpToolkit> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(HttpAdapter.class.getName());
|
||||
|
||||
/**
|
||||
* {@link com.sun.xml.internal.ws.api.server.SDDocument}s keyed by the query string like "?abc".
|
||||
* Used for serving documents via HTTP GET.
|
||||
*
|
||||
* Empty if the endpoint doesn't have {@link com.sun.xml.internal.ws.api.server.ServiceDefinition}.
|
||||
* Read-only.
|
||||
*/
|
||||
protected Map<String,SDDocument> wsdls;
|
||||
|
||||
/**
|
||||
* Reverse map of {@link #wsdls}. Read-only.
|
||||
*/
|
||||
private Map<SDDocument,String> revWsdls;
|
||||
|
||||
/**
|
||||
* A reference to the service definition from which the map of wsdls/revWsdls
|
||||
* was created. This allows us to establish if the service definition documents
|
||||
* have changed in the meantime.
|
||||
*/
|
||||
private ServiceDefinition serviceDefinition = null;
|
||||
|
||||
public final HttpAdapterList<? extends HttpAdapter> owner;
|
||||
|
||||
/**
|
||||
* Servlet URL pattern with which this {@link com.sun.xml.internal.ws.transport.http.HttpAdapter} is associated.
|
||||
*/
|
||||
public final String urlPattern;
|
||||
|
||||
protected boolean stickyCookie;
|
||||
|
||||
protected boolean disableJreplicaCookie = false;
|
||||
|
||||
/**
|
||||
* Creates a lone {@link com.sun.xml.internal.ws.transport.http.HttpAdapter} that does not know of any other
|
||||
* {@link com.sun.xml.internal.ws.transport.http.HttpAdapter}s.
|
||||
*
|
||||
* This is convenient for creating an {@link com.sun.xml.internal.ws.transport.http.HttpAdapter} for an environment
|
||||
* where they don't know each other (such as JavaSE deployment.)
|
||||
*
|
||||
* @param endpoint web service endpoint
|
||||
* @return singe adapter to process HTTP messages
|
||||
*/
|
||||
public static HttpAdapter createAlone(WSEndpoint endpoint) {
|
||||
return new DummyList().createAdapter("","",endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* remove as soon as we can update the test util.
|
||||
* @param endpoint web service endpoint
|
||||
* @param owner list of related adapters
|
||||
*/
|
||||
protected HttpAdapter(WSEndpoint endpoint,
|
||||
HttpAdapterList<? extends HttpAdapter> owner) {
|
||||
this(endpoint,owner,null);
|
||||
}
|
||||
|
||||
protected HttpAdapter(WSEndpoint endpoint,
|
||||
HttpAdapterList<? extends HttpAdapter> owner,
|
||||
String urlPattern) {
|
||||
super(endpoint);
|
||||
this.owner = owner;
|
||||
this.urlPattern = urlPattern;
|
||||
|
||||
initWSDLMap(endpoint.getServiceDefinition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last known service definition of the endpoint.
|
||||
*
|
||||
* @return The service definition of the endpoint
|
||||
*/
|
||||
public ServiceDefinition getServiceDefinition() {
|
||||
return this.serviceDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in WSDL map.
|
||||
*
|
||||
* @param sdef service definition
|
||||
*/
|
||||
public final void initWSDLMap(ServiceDefinition sdef) {
|
||||
this.serviceDefinition = sdef;
|
||||
if(sdef==null) {
|
||||
wsdls = Collections.emptyMap();
|
||||
revWsdls = Collections.emptyMap();
|
||||
} else {
|
||||
wsdls = new HashMap<String, SDDocument>(); // wsdl=1 --> Doc
|
||||
// Sort WSDL, Schema documents based on SystemId so that the same
|
||||
// document gets wsdl=x mapping
|
||||
Map<String, SDDocument> systemIds = new TreeMap<String, SDDocument>();
|
||||
for (SDDocument sdd : sdef) {
|
||||
if (sdd == sdef.getPrimary()) { // No sorting for Primary WSDL
|
||||
wsdls.put("wsdl", sdd);
|
||||
wsdls.put("WSDL", sdd);
|
||||
} else {
|
||||
systemIds.put(sdd.getURL().toString(), sdd);
|
||||
}
|
||||
}
|
||||
|
||||
int wsdlnum = 1;
|
||||
int xsdnum = 1;
|
||||
for (Entry<String, SDDocument> e : systemIds.entrySet()) {
|
||||
SDDocument sdd = e.getValue();
|
||||
if (sdd.isWSDL()) {
|
||||
wsdls.put("wsdl="+(wsdlnum++),sdd);
|
||||
}
|
||||
if (sdd.isSchema()) {
|
||||
wsdls.put("xsd="+(xsdnum++),sdd);
|
||||
}
|
||||
}
|
||||
|
||||
revWsdls = new HashMap<SDDocument,String>(); // Doc --> wsdl=1
|
||||
for (Entry<String,SDDocument> e : wsdls.entrySet()) {
|
||||
if (!e.getKey().equals("WSDL")) { // map Doc --> wsdl, not WSDL
|
||||
revWsdls.put(e.getValue(),e.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "/abc/def/ghi" portion if
|
||||
* the URL pattern is "/abc/def/ghi/*".
|
||||
*/
|
||||
public String getValidPath() {
|
||||
if (urlPattern.endsWith("/*")) {
|
||||
return urlPattern.substring(0, urlPattern.length() - 2);
|
||||
} else {
|
||||
return urlPattern;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpToolkit createToolkit() {
|
||||
return new HttpToolkit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives the incoming HTTP connection and dispatches
|
||||
* it to JAX-WS. This method returns when JAX-WS completes
|
||||
* processing the request and the whole reply is written
|
||||
* to {@link WSHTTPConnection}.
|
||||
*
|
||||
* <p>
|
||||
* This method is invoked by the lower-level HTTP stack,
|
||||
* and "connection" here is an HTTP connection.
|
||||
*
|
||||
* <p>
|
||||
* To populate a request {@link com.sun.xml.internal.ws.api.message.Packet} with more info,
|
||||
* define {@link com.oracle.webservices.internal.api.message.PropertySet.Property properties} on
|
||||
* {@link WSHTTPConnection}.
|
||||
*
|
||||
* @param connection to receive/send HTTP messages for web service endpoints
|
||||
* @throws java.io.IOException when I/O errors happen
|
||||
*/
|
||||
public void handle(@NotNull WSHTTPConnection connection) throws IOException {
|
||||
if (handleGet(connection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the Toolkit is recycled by the same pool instance from which it was taken
|
||||
final Pool<HttpToolkit> currentPool = getPool();
|
||||
// normal request handling
|
||||
final HttpToolkit tk = currentPool.take();
|
||||
try {
|
||||
tk.handle(connection);
|
||||
} finally {
|
||||
currentPool.recycle(tk);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handleGet(@NotNull WSHTTPConnection connection) throws IOException {
|
||||
if (connection.getRequestMethod().equals("GET")) {
|
||||
// metadata query. let the interceptor run
|
||||
for (Component c : endpoint.getComponents()) {
|
||||
HttpMetadataPublisher spi = c.getSPI(HttpMetadataPublisher.class);
|
||||
if (spi != null && spi.handleMetadataRequest(this, connection)) {
|
||||
return true;
|
||||
} // handled
|
||||
}
|
||||
|
||||
if (isMetadataQuery(connection.getQueryString())) {
|
||||
// Sends published WSDL and schema documents as the default action.
|
||||
publishWSDL(connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
Binding binding = getEndpoint().getBinding();
|
||||
if (!(binding instanceof HTTPBinding)) {
|
||||
// Writes HTML page with all the endpoint descriptions
|
||||
writeWebServicesHtmlPage(connection);
|
||||
return true;
|
||||
}
|
||||
} else if (connection.getRequestMethod().equals("HEAD")) {
|
||||
connection.getInput().close();
|
||||
Binding binding = getEndpoint().getBinding();
|
||||
if (isMetadataQuery(connection.getQueryString())) {
|
||||
SDDocument doc = wsdls.get(connection.getQueryString());
|
||||
connection.setStatus(doc != null
|
||||
? HttpURLConnection.HTTP_OK
|
||||
: HttpURLConnection.HTTP_NOT_FOUND);
|
||||
connection.getOutput().close();
|
||||
connection.close();
|
||||
return true;
|
||||
} else if (!(binding instanceof HTTPBinding)) {
|
||||
connection.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
|
||||
connection.getOutput().close();
|
||||
connection.close();
|
||||
return true;
|
||||
}
|
||||
// Let the endpoint handle for HTTPBinding
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
/*
|
||||
*
|
||||
* @param con
|
||||
* @param codec
|
||||
* @return
|
||||
* @throws IOException
|
||||
* ExceptionHasMessage exception that contains particular fault message
|
||||
* UnsupportedMediaException to indicate to send 415 error code
|
||||
*/
|
||||
private Packet decodePacket(@NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException {
|
||||
String ct = con.getRequestHeader("Content-Type");
|
||||
InputStream in = con.getInput();
|
||||
Packet packet = new Packet();
|
||||
packet.soapAction = fixQuotesAroundSoapAction(con.getRequestHeader("SOAPAction"));
|
||||
packet.wasTransportSecure = con.isSecure();
|
||||
packet.acceptableMimeTypes = con.getRequestHeader("Accept");
|
||||
packet.addSatellite(con);
|
||||
addSatellites(packet);
|
||||
packet.isAdapterDeliversNonAnonymousResponse = true;
|
||||
packet.component = this;
|
||||
packet.transportBackChannel = new Oneway(con);
|
||||
packet.webServiceContextDelegate = con.getWebServiceContextDelegate();
|
||||
packet.setState(Packet.State.ServerRequest);
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
buf.write(in);
|
||||
in.close();
|
||||
dump(buf, "HTTP request", con.getRequestHeaders());
|
||||
in = buf.newInputStream();
|
||||
}
|
||||
codec.decode(in, ct, packet);
|
||||
return packet;
|
||||
}
|
||||
|
||||
protected void addSatellites(Packet packet) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Some stacks may send non WS-I BP 1.2 conforming SoapAction.
|
||||
* Make sure SOAPAction is quoted as {@link com.sun.xml.internal.ws.api.message.Packet#soapAction} expects quoted soapAction value.
|
||||
*
|
||||
* @param soapAction SoapAction HTTP Header
|
||||
* @return quoted SOAPAction value
|
||||
*/
|
||||
static public String fixQuotesAroundSoapAction(String soapAction) {
|
||||
if(soapAction != null && (!soapAction.startsWith("\"") || !soapAction.endsWith("\"")) ) {
|
||||
if (LOGGER.isLoggable(Level.INFO)) {
|
||||
LOGGER.log(Level.INFO, "Received WS-I BP non-conformant Unquoted SoapAction HTTP header: {0}", soapAction);
|
||||
}
|
||||
String fixedSoapAction = soapAction;
|
||||
if(!soapAction.startsWith("\"")) {
|
||||
fixedSoapAction = "\"" + fixedSoapAction;
|
||||
}
|
||||
if(!soapAction.endsWith("\"")) {
|
||||
fixedSoapAction = fixedSoapAction + "\"";
|
||||
}
|
||||
return fixedSoapAction;
|
||||
}
|
||||
return soapAction;
|
||||
}
|
||||
|
||||
protected NonAnonymousResponseProcessor getNonAnonymousResponseProcessor() {
|
||||
return NonAnonymousResponseProcessor.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is added for the case of the sub-class wants to override the method to
|
||||
* print details. E.g. convert soapfault as HTML msg for 403 error connstatus.
|
||||
* @param os
|
||||
*/
|
||||
protected void writeClientError(int connStatus, @NotNull OutputStream os, @NotNull Packet packet) throws IOException {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
private boolean isClientErrorStatus(int connStatus)
|
||||
{
|
||||
return (connStatus == HttpURLConnection.HTTP_FORBIDDEN); // add more for future.
|
||||
}
|
||||
|
||||
private boolean isNonAnonymousUri(EndpointAddress addr){
|
||||
return (addr != null) && !addr.toString().equals(AddressingVersion.W3C.anonymousUri) &&
|
||||
!addr.toString().equals(AddressingVersion.MEMBER.anonymousUri);
|
||||
}
|
||||
|
||||
private void encodePacket(@NotNull Packet packet, @NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException {
|
||||
if (isNonAnonymousUri(packet.endpointAddress) && packet.getMessage() != null) {
|
||||
try {
|
||||
// Message is targeted to non-anonymous response endpoint.
|
||||
// After call to non-anonymous processor, typically, packet.getMessage() will be null
|
||||
// however, processors could use this pattern to modify the response sent on the back-channel,
|
||||
// e.g. send custom HTTP headers with the HTTP 202
|
||||
packet = getNonAnonymousResponseProcessor().process(packet);
|
||||
} catch (RuntimeException re) {
|
||||
// if processing by NonAnonymousResponseProcessor fails, new SOAPFaultMessage is created to be sent
|
||||
// to back-channel client
|
||||
SOAPVersion soapVersion = packet.getBinding().getSOAPVersion();
|
||||
Message faultMsg = SOAPFaultBuilder.createSOAPFaultMessage(soapVersion, null, re);
|
||||
packet = packet.createServerResponse(faultMsg, packet.endpoint.getPort(), null, packet.endpoint.getBinding());
|
||||
}
|
||||
}
|
||||
|
||||
if (con.isClosed()) {
|
||||
return; // Connection is already closed
|
||||
}
|
||||
Message responseMessage = packet.getMessage();
|
||||
addStickyCookie(con);
|
||||
addReplicaCookie(con, packet);
|
||||
if (responseMessage == null) {
|
||||
if (!con.isClosed()) {
|
||||
// set the response code if not already set
|
||||
// for example, 415 may have been set earlier for Unsupported Content-Type
|
||||
if (con.getStatus() == 0) {
|
||||
con.setStatus(WSHTTPConnection.ONEWAY);
|
||||
}
|
||||
OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con);
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
codec.encode(packet, buf);
|
||||
dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
|
||||
buf.writeTo(os);
|
||||
} else {
|
||||
codec.encode(packet, os);
|
||||
}
|
||||
// close the response channel now
|
||||
try {
|
||||
os.close(); // no payload
|
||||
} catch (IOException e) {
|
||||
throw new WebServiceException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (con.getStatus() == 0) {
|
||||
// if the appliation didn't set the status code,
|
||||
// set the default one.
|
||||
con.setStatus(responseMessage.isFault()
|
||||
? HttpURLConnection.HTTP_INTERNAL_ERROR
|
||||
: HttpURLConnection.HTTP_OK);
|
||||
}
|
||||
|
||||
if (isClientErrorStatus(con.getStatus())) {
|
||||
OutputStream os = con.getOutput();
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
writeClientError(con.getStatus(), buf, packet);
|
||||
dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
|
||||
buf.writeTo(os);
|
||||
} else {
|
||||
writeClientError(con.getStatus(), os, packet);
|
||||
}
|
||||
os.close();
|
||||
return;
|
||||
}
|
||||
|
||||
ContentType contentType = codec.getStaticContentType(packet);
|
||||
if (contentType != null) {
|
||||
con.setContentTypeResponseHeader(contentType.getContentType());
|
||||
OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con);
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
codec.encode(packet, buf);
|
||||
dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
|
||||
buf.writeTo(os);
|
||||
} else {
|
||||
codec.encode(packet, os);
|
||||
}
|
||||
os.close();
|
||||
} else {
|
||||
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
contentType = codec.encode(packet, buf);
|
||||
con.setContentTypeResponseHeader(contentType.getContentType());
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
|
||||
}
|
||||
OutputStream os = con.getOutput();
|
||||
buf.writeTo(os);
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* GlassFish Load-balancer plugin always add a header proxy-jroute on
|
||||
* request being send from load-balancer plugin to server
|
||||
*
|
||||
* JROUTE cookie need to be stamped in two cases
|
||||
* 1 : At the time of session creation. In this case, request will not have
|
||||
* any JROUTE cookie.
|
||||
* 2 : At the time of fail-over. In this case, value of proxy-jroute
|
||||
* header(will point to current instance) and JROUTE cookie(will point to
|
||||
* previous failed instance) will be different. This logic can be used
|
||||
* to determine fail-over scenario.
|
||||
*/
|
||||
private void addStickyCookie(WSHTTPConnection con) {
|
||||
if (stickyCookie) {
|
||||
String proxyJroute = con.getRequestHeader("proxy-jroute");
|
||||
if (proxyJroute == null) {
|
||||
// Load-balancer plugin is not front-ending this instance
|
||||
return;
|
||||
}
|
||||
|
||||
String jrouteId = con.getCookie("JROUTE");
|
||||
if (jrouteId == null || !jrouteId.equals(proxyJroute)) {
|
||||
// Initial request or failover
|
||||
con.setCookie("JROUTE", proxyJroute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addReplicaCookie(WSHTTPConnection con, Packet packet) {
|
||||
if (stickyCookie) {
|
||||
HaInfo haInfo = null;
|
||||
if (packet.supports(Packet.HA_INFO)) {
|
||||
haInfo = (HaInfo)packet.get(Packet.HA_INFO);
|
||||
}
|
||||
if (haInfo != null) {
|
||||
con.setCookie("METRO_KEY", haInfo.getKey());
|
||||
if (!disableJreplicaCookie) {
|
||||
con.setCookie("JREPLICA", haInfo.getReplicaInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invokeAsync(final WSHTTPConnection con) throws IOException {
|
||||
invokeAsync(con, NO_OP_COMPLETION_CALLBACK);
|
||||
}
|
||||
|
||||
public void invokeAsync(final WSHTTPConnection con, final CompletionCallback callback) throws IOException {
|
||||
|
||||
if (handleGet(con)) {
|
||||
callback.onCompletion();
|
||||
return;
|
||||
}
|
||||
final Pool<HttpToolkit> currentPool = getPool();
|
||||
final HttpToolkit tk = currentPool.take();
|
||||
final Packet request;
|
||||
|
||||
try {
|
||||
|
||||
request = decodePacket(con, tk.codec);
|
||||
} catch (ExceptionHasMessage e) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
Packet response = new Packet();
|
||||
response.setMessage(e.getFaultMessage());
|
||||
encodePacket(response, con, tk.codec);
|
||||
currentPool.recycle(tk);
|
||||
con.close();
|
||||
callback.onCompletion();
|
||||
return;
|
||||
} catch (UnsupportedMediaException e) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
Packet response = new Packet();
|
||||
con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA);
|
||||
encodePacket(response, con, tk.codec);
|
||||
currentPool.recycle(tk);
|
||||
con.close();
|
||||
callback.onCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
endpoint.process(request, new WSEndpoint.CompletionCallback() {
|
||||
@Override
|
||||
public void onCompletion(@NotNull Packet response) {
|
||||
try {
|
||||
try {
|
||||
encodePacket(response, con, tk.codec);
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.log(Level.SEVERE, ioe.getMessage(), ioe);
|
||||
}
|
||||
currentPool.recycle(tk);
|
||||
} finally {
|
||||
con.close();
|
||||
callback.onCompletion();
|
||||
|
||||
}
|
||||
}
|
||||
},null);
|
||||
|
||||
}
|
||||
|
||||
public static final CompletionCallback NO_OP_COMPLETION_CALLBACK = new CompletionCallback() {
|
||||
|
||||
@Override
|
||||
public void onCompletion() {
|
||||
//NO-OP
|
||||
}
|
||||
};
|
||||
|
||||
public interface CompletionCallback{
|
||||
void onCompletion();
|
||||
}
|
||||
|
||||
final class AsyncTransport extends AbstractServerAsyncTransport<WSHTTPConnection> {
|
||||
|
||||
public AsyncTransport() {
|
||||
super(endpoint);
|
||||
}
|
||||
|
||||
public void handleAsync(WSHTTPConnection con) throws IOException {
|
||||
super.handle(con);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encodePacket(WSHTTPConnection con, @NotNull Packet packet, @NotNull Codec codec) throws IOException {
|
||||
HttpAdapter.this.encodePacket(packet, con, codec);
|
||||
}
|
||||
|
||||
protected @Override @Nullable String getAcceptableMimeTypes(WSHTTPConnection con) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected @Override @Nullable TransportBackChannel getTransportBackChannel(WSHTTPConnection con) {
|
||||
return new Oneway(con);
|
||||
}
|
||||
|
||||
protected @Override @NotNull
|
||||
PropertySet getPropertySet(WSHTTPConnection con) {
|
||||
return con;
|
||||
}
|
||||
|
||||
protected @Override @NotNull WebServiceContextDelegate getWebServiceContextDelegate(WSHTTPConnection con) {
|
||||
return con.getWebServiceContextDelegate();
|
||||
}
|
||||
}
|
||||
|
||||
static final class Oneway implements TransportBackChannel {
|
||||
WSHTTPConnection con;
|
||||
boolean closed;
|
||||
|
||||
Oneway(WSHTTPConnection con) {
|
||||
this.con = con;
|
||||
}
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
// close the response channel now
|
||||
if (con.getStatus() == 0) {
|
||||
// if the appliation didn't set the status code,
|
||||
// set the default one.
|
||||
con.setStatus(WSHTTPConnection.ONEWAY);
|
||||
}
|
||||
|
||||
OutputStream output = null;
|
||||
try {
|
||||
output = con.getOutput();
|
||||
} catch (IOException e) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
try {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
|
||||
} catch (Exception e) {
|
||||
throw new WebServiceException(e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close(); // no payload
|
||||
} catch (IOException e) {
|
||||
throw new WebServiceException(e);
|
||||
}
|
||||
}
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class HttpToolkit extends Adapter.Toolkit {
|
||||
public void handle(WSHTTPConnection con) throws IOException {
|
||||
try {
|
||||
boolean invoke = false;
|
||||
Packet packet;
|
||||
try {
|
||||
packet = decodePacket(con, codec);
|
||||
invoke = true;
|
||||
} catch(Exception e) {
|
||||
packet = new Packet();
|
||||
if (e instanceof ExceptionHasMessage) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
packet.setMessage(((ExceptionHasMessage)e).getFaultMessage());
|
||||
} else if (e instanceof UnsupportedMediaException) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA);
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
if (invoke) {
|
||||
try {
|
||||
packet = head.process(packet, con.getWebServiceContextDelegate(),
|
||||
packet.transportBackChannel);
|
||||
} catch(Throwable e) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
if (!con.isClosed()) {
|
||||
writeInternalServerError(con);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
encodePacket(packet, con, codec);
|
||||
} finally {
|
||||
if (!con.isClosed()) {
|
||||
if (LOGGER.isLoggable(Level.FINE)) {
|
||||
LOGGER.log(Level.FINE, "Closing HTTP Connection with status: {0}", con.getStatus());
|
||||
}
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given query string is for metadata request.
|
||||
*
|
||||
* @param query
|
||||
* String like "xsd=1" or "perhaps=some&unrelated=query".
|
||||
* Can be null.
|
||||
* @return true for metadata requests
|
||||
* false for web service requests
|
||||
*/
|
||||
private boolean isMetadataQuery(String query) {
|
||||
// we intentionally return true even if documents don't exist,
|
||||
// so that they get 404.
|
||||
return query != null && (query.equals("WSDL") || query.startsWith("wsdl") || query.startsWith("xsd="));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends out the WSDL (and other referenced documents)
|
||||
* in response to the GET requests to URLs like "?wsdl" or "?xsd=2".
|
||||
*
|
||||
* @param con
|
||||
* The connection to which the data will be sent.
|
||||
*
|
||||
* @throws java.io.IOException when I/O errors happen
|
||||
*/
|
||||
public void publishWSDL(@NotNull WSHTTPConnection con) throws IOException {
|
||||
con.getInput().close();
|
||||
|
||||
SDDocument doc = wsdls.get(con.getQueryString());
|
||||
if (doc == null) {
|
||||
writeNotFoundErrorPage(con,"Invalid Request");
|
||||
return;
|
||||
}
|
||||
|
||||
con.setStatus(HttpURLConnection.HTTP_OK);
|
||||
con.setContentTypeResponseHeader("text/xml;charset=utf-8");
|
||||
|
||||
OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con);
|
||||
|
||||
PortAddressResolver portAddressResolver = getPortAddressResolver(con.getBaseAddress());
|
||||
DocumentAddressResolver resolver = getDocumentAddressResolver(portAddressResolver);
|
||||
|
||||
doc.writeTo(portAddressResolver, resolver, os);
|
||||
os.close();
|
||||
}
|
||||
|
||||
public PortAddressResolver getPortAddressResolver(String baseAddress) {
|
||||
return owner.createPortAddressResolver(baseAddress, endpoint.getImplementationClass());
|
||||
}
|
||||
|
||||
public DocumentAddressResolver getDocumentAddressResolver(
|
||||
PortAddressResolver portAddressResolver) {
|
||||
final String address = portAddressResolver.getAddressFor(endpoint.getServiceName(), endpoint.getPortName().getLocalPart());
|
||||
assert address != null;
|
||||
return new DocumentAddressResolver() {
|
||||
@Override
|
||||
public String getRelativeAddressFor(@NotNull SDDocument current, @NotNull SDDocument referenced) {
|
||||
// the map on endpoint should account for all SDDocument
|
||||
assert revWsdls.containsKey(referenced);
|
||||
return address+'?'+ revWsdls.get(referenced);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP/1.0 connections require Content-Length. So just buffer to find out
|
||||
* the length.
|
||||
*/
|
||||
private final static class Http10OutputStream extends ByteArrayBuffer {
|
||||
private final WSHTTPConnection con;
|
||||
|
||||
Http10OutputStream(WSHTTPConnection con) {
|
||||
this.con = con;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
con.setContentLengthResponseHeader(size());
|
||||
OutputStream os = con.getOutput();
|
||||
writeTo(os);
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNotFoundErrorPage(WSHTTPConnection con, String message) throws IOException {
|
||||
con.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
|
||||
con.setContentTypeResponseHeader("text/html; charset=utf-8");
|
||||
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8"));
|
||||
out.println("<html>");
|
||||
out.println("<head><title>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_TITLE());
|
||||
out.println("</title></head>");
|
||||
out.println("<body>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_NOT_FOUND(message));
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
out.close();
|
||||
}
|
||||
|
||||
private void writeInternalServerError(WSHTTPConnection con) throws IOException {
|
||||
con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
|
||||
con.getOutput().close(); // Sets the status code
|
||||
}
|
||||
|
||||
private static final class DummyList extends HttpAdapterList<HttpAdapter> {
|
||||
@Override
|
||||
protected HttpAdapter createHttpAdapter(String name, String urlPattern, WSEndpoint<?> endpoint) {
|
||||
return new HttpAdapter(endpoint,this,urlPattern);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dump(ByteArrayBuffer buf, String caption, Map<String, List<String>> headers) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintWriter pw = new PrintWriter(baos, true);
|
||||
pw.println("---["+caption +"]---");
|
||||
if (headers != null) {
|
||||
for (Entry<String, List<String>> header : headers.entrySet()) {
|
||||
if (header.getValue().isEmpty()) {
|
||||
// I don't think this is legal, but let's just dump it,
|
||||
// as the point of the dump is to uncover problems.
|
||||
pw.println(header.getValue());
|
||||
} else {
|
||||
for (String value : header.getValue()) {
|
||||
pw.println(header.getKey() + ": " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buf.size() > dump_threshold) {
|
||||
byte[] b = buf.getRawData();
|
||||
baos.write(b, 0, dump_threshold);
|
||||
pw.println();
|
||||
pw.println(WsservletMessages.MESSAGE_TOO_LONG(HttpAdapter.class.getName() + ".dumpTreshold"));
|
||||
} else {
|
||||
buf.writeTo(baos);
|
||||
}
|
||||
pw.println("--------------------");
|
||||
|
||||
String msg = baos.toString();
|
||||
if (dump) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
if (LOGGER.isLoggable(Level.FINER)) {
|
||||
LOGGER.log(Level.FINER, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the listing of all services.
|
||||
*/
|
||||
private void writeWebServicesHtmlPage(WSHTTPConnection con) throws IOException {
|
||||
if (!publishStatusPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: resurrect the ability to localize according to the current request.
|
||||
|
||||
con.getInput().close();
|
||||
|
||||
// standard browsable page
|
||||
con.setStatus(WSHTTPConnection.OK);
|
||||
con.setContentTypeResponseHeader("text/html; charset=utf-8");
|
||||
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8"));
|
||||
out.println("<html>");
|
||||
out.println("<head><title>");
|
||||
// out.println("Web Services");
|
||||
out.println(WsservletMessages.SERVLET_HTML_TITLE());
|
||||
out.println("</title></head>");
|
||||
out.println("<body>");
|
||||
// out.println("<h1>Web Services</h1>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_TITLE_2());
|
||||
|
||||
// what endpoints do we have in this system?
|
||||
Module module = getEndpoint().getContainer().getSPI(Module.class);
|
||||
List<BoundEndpoint> endpoints = Collections.emptyList();
|
||||
if(module!=null) {
|
||||
endpoints = module.getBoundEndpoints();
|
||||
}
|
||||
|
||||
if (endpoints.isEmpty()) {
|
||||
// out.println("<p>No JAX-WS context information available.</p>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_NO_INFO_AVAILABLE());
|
||||
} else {
|
||||
out.println("<table width='100%' border='1'>");
|
||||
out.println("<tr>");
|
||||
out.println("<td>");
|
||||
// out.println("Endpoint");
|
||||
out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_PORT_NAME());
|
||||
out.println("</td>");
|
||||
|
||||
out.println("<td>");
|
||||
// out.println("Information");
|
||||
out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_INFORMATION());
|
||||
out.println("</td>");
|
||||
out.println("</tr>");
|
||||
|
||||
for (BoundEndpoint a : endpoints) {
|
||||
String endpointAddress = a.getAddress(con.getBaseAddress()).toString();
|
||||
out.println("<tr>");
|
||||
|
||||
out.println("<td>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_ENDPOINT_TABLE(
|
||||
a.getEndpoint().getServiceName(),
|
||||
a.getEndpoint().getPortName()
|
||||
));
|
||||
out.println("</td>");
|
||||
|
||||
out.println("<td>");
|
||||
out.println(WsservletMessages.SERVLET_HTML_INFORMATION_TABLE(
|
||||
endpointAddress,
|
||||
a.getEndpoint().getImplementationClass().getName()
|
||||
));
|
||||
out.println("</td>");
|
||||
|
||||
out.println("</tr>");
|
||||
}
|
||||
out.println("</table>");
|
||||
}
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps what goes across HTTP transport.
|
||||
*/
|
||||
public static volatile boolean dump = false;
|
||||
|
||||
public static volatile int dump_threshold = 4096;
|
||||
|
||||
public static volatile boolean publishStatusPage = true;
|
||||
|
||||
public static synchronized void setPublishStatus(boolean publish) {
|
||||
publishStatusPage = publish;
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
dump = Boolean.getBoolean(HttpAdapter.class.getName() + ".dump");
|
||||
} catch (SecurityException se) {
|
||||
if (LOGGER.isLoggable(Level.CONFIG)) {
|
||||
LOGGER.log(Level.CONFIG, "Cannot read ''{0}'' property, using defaults.",
|
||||
new Object[] {HttpAdapter.class.getName() + ".dump"});
|
||||
}
|
||||
}
|
||||
try {
|
||||
dump_threshold = Integer.getInteger(HttpAdapter.class.getName() + ".dumpTreshold", 4096);
|
||||
} catch (SecurityException se) {
|
||||
if (LOGGER.isLoggable(Level.CONFIG)) {
|
||||
LOGGER.log(Level.CONFIG, "Cannot read ''{0}'' property, using defaults.",
|
||||
new Object[] {HttpAdapter.class.getName() + ".dumpTreshold"});
|
||||
}
|
||||
}
|
||||
try {
|
||||
setPublishStatus(Boolean.getBoolean(HttpAdapter.class.getName() + ".publishStatusPage"));
|
||||
} catch (SecurityException se) {
|
||||
if (LOGGER.isLoggable(Level.CONFIG)) {
|
||||
LOGGER.log(Level.CONFIG, "Cannot read ''{0}'' property, using defaults.",
|
||||
new Object[] {HttpAdapter.class.getName() + ".publishStatusPage"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDump(boolean dumpMessages) {
|
||||
HttpAdapter.dump = dumpMessages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.transport.http;
|
||||
|
||||
import com.sun.xml.internal.ws.transport.http.DeploymentDescriptorParser.AdapterFactory;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.PortAddressResolver;
|
||||
import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* List of {@link HttpAdapter}s created together.
|
||||
*
|
||||
* <p>
|
||||
* Some cases WAR file may contain multiple endpoints for ports in a WSDL.
|
||||
* If the runtime knows these ports, their port addresses can be patched.
|
||||
* This class keeps a list of {@link HttpAdapter}s and use that information to patch
|
||||
* multiple port addresses.
|
||||
*
|
||||
* <p>
|
||||
* Concrete implementations of this class need to override {@link #createHttpAdapter}
|
||||
* method to create implementations of {@link HttpAdapter}.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public abstract class HttpAdapterList<T extends HttpAdapter> extends AbstractList<T> implements AdapterFactory<T> {
|
||||
private final List<T> adapters = new ArrayList<T>();
|
||||
private final Map<PortInfo, String> addressMap = new HashMap<PortInfo, String>();
|
||||
|
||||
// TODO: documented because it's used by AS
|
||||
@Override
|
||||
public T createAdapter(String name, String urlPattern, WSEndpoint<?> endpoint) {
|
||||
T t = createHttpAdapter(name, urlPattern, endpoint);
|
||||
adapters.add(t);
|
||||
WSDLPort port = endpoint.getPort();
|
||||
if (port != null) {
|
||||
PortInfo portInfo = new PortInfo(port.getOwner().getName(),port.getName().getLocalPart(), endpoint.getImplementationClass());
|
||||
addressMap.put(portInfo, getValidPath(urlPattern));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementations need to override this one to create a concrete class
|
||||
* of HttpAdapter
|
||||
*/
|
||||
protected abstract T createHttpAdapter(String name, String urlPattern, WSEndpoint<?> endpoint);
|
||||
|
||||
/**
|
||||
* @return urlPattern without "/*"
|
||||
*/
|
||||
private String getValidPath(@NotNull String urlPattern) {
|
||||
if (urlPattern.endsWith("/*")) {
|
||||
return urlPattern.substring(0, urlPattern.length() - 2);
|
||||
} else {
|
||||
return urlPattern;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PortAddressResolver that maps portname to its address
|
||||
*
|
||||
* @param endpointImpl application endpoint Class that eventually serves the request.
|
||||
*/
|
||||
public PortAddressResolver createPortAddressResolver(final String baseAddress, final Class<?> endpointImpl) {
|
||||
return new PortAddressResolver() {
|
||||
@Override
|
||||
public String getAddressFor(@NotNull QName serviceName, @NotNull String portName) {
|
||||
String urlPattern = addressMap.get(new PortInfo(serviceName,portName, endpointImpl));
|
||||
if (urlPattern == null) {
|
||||
//if a WSDL defines more ports, urlpattern is null (portName does not match endpointImpl)
|
||||
//so fallback to the default behaviour where only serviceName/portName is checked
|
||||
for (Entry<PortInfo, String> e : addressMap.entrySet()) {
|
||||
if (serviceName.equals(e.getKey().serviceName) && portName.equals(e.getKey().portName)) {
|
||||
urlPattern = e.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (urlPattern == null) ? null : baseAddress+urlPattern;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public T get(int index) {
|
||||
return adapters.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return adapters.size();
|
||||
}
|
||||
|
||||
private static class PortInfo {
|
||||
private final QName serviceName;
|
||||
private final String portName;
|
||||
private final Class<?> implClass;
|
||||
|
||||
PortInfo(@NotNull QName serviceName, @NotNull String portName, Class<?> implClass) {
|
||||
this.serviceName = serviceName;
|
||||
this.portName = portName;
|
||||
this.implClass = implClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object portInfo) {
|
||||
if (portInfo instanceof PortInfo) {
|
||||
PortInfo that = (PortInfo)portInfo;
|
||||
if (this.implClass == null) {
|
||||
return this.serviceName.equals(that.serviceName) && this.portName.equals(that.portName) && that.implClass == null;
|
||||
}
|
||||
return this.serviceName.equals(that.serviceName) && this.portName.equals(that.portName) && this.implClass.equals(that.implClass);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int retVal = serviceName.hashCode()+portName.hashCode();
|
||||
return implClass != null ? retVal + implClass.hashCode() : retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.transport.http;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.Component;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Intercepts GET HTTP requests to process the requests.
|
||||
*
|
||||
* <p>
|
||||
* {@link HttpAdapter} looks for this SPI in {@link WSEndpoint#getComponents()}
|
||||
* to allow components to expose additional information through HTTP.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see Component#getSPI(Class)
|
||||
* @since 2.1.2
|
||||
*/
|
||||
public abstract class HttpMetadataPublisher {
|
||||
/**
|
||||
* When {@link HttpAdapter} receives a GET request with a query string
|
||||
* (which is a convention for metadata requests, such as '?wsdl' or '?xsd=...'),
|
||||
* then this method is invoked to allow components to intercept the request.
|
||||
*
|
||||
* @param adapter
|
||||
* Adapter that accepted the connection.
|
||||
* @param connection
|
||||
* Represents the current connection.
|
||||
* @return
|
||||
* true if the request is processed. If false is returned the default processing kicks in.
|
||||
*/
|
||||
public abstract boolean handleMetadataRequest(@NotNull HttpAdapter adapter, @NotNull WSHTTPConnection connection) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.transport.http;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used to locate resources for {@link DeploymentDescriptorParser}.
|
||||
*
|
||||
* <p>
|
||||
* This allows {@link DeploymentDescriptorParser} to be used outside a servlet container,
|
||||
* but it still needs to work with a layout similar to the web application.
|
||||
* If this can be abstracted away better, that would be nice.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public interface ResourceLoader {
|
||||
/**
|
||||
* Returns the actual location of the resource from the 'path'
|
||||
* that represents a virtual locaion of a file inside a web application.
|
||||
*
|
||||
* @param path
|
||||
* Desiganates an absolute path within an web application, such as:
|
||||
* '/WEB-INF/web.xml' or some such.
|
||||
*
|
||||
* @return
|
||||
* the actual location, if found, or null if not found.
|
||||
*/
|
||||
URL getResource(String path) throws MalformedURLException;
|
||||
|
||||
/**
|
||||
* Gets the catalog XML file that should be consulted when
|
||||
* loading resources from this {@link ResourceLoader}.
|
||||
*/
|
||||
URL getCatalogFile() throws MalformedURLException;
|
||||
|
||||
/**
|
||||
* Returns the list of files in the given directory.
|
||||
*
|
||||
* @return
|
||||
* null if the path is invalid. empty if the path didn't contain
|
||||
* any entry in it.
|
||||
*
|
||||
* @see javax.servlet.ServletContext#getResourcePaths(String)
|
||||
*/
|
||||
Set<String> getResourcePaths(String path);
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* 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.transport.http;
|
||||
|
||||
import com.oracle.webservices.internal.api.message.BasePropertySet;
|
||||
import com.oracle.webservices.internal.api.message.PropertySet;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
|
||||
|
||||
import javax.xml.ws.WebServiceContext;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The view of an HTTP exchange from the point of view of JAX-WS.
|
||||
*
|
||||
* <p>
|
||||
* Different HTTP server layer uses different implementations of this class
|
||||
* so that JAX-WS can be shielded from individuality of such layers.
|
||||
* This is an interface implemented as an abstract class, so that
|
||||
* future versions of the JAX-WS RI can add new methods.
|
||||
*
|
||||
* <p>
|
||||
* This class extends {@link PropertySet} so that a transport can
|
||||
* expose its properties to the application and pipes. (This object
|
||||
* will be added to {@link Packet#addSatellite(PropertySet)}.)
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public abstract class WSHTTPConnection extends BasePropertySet {
|
||||
|
||||
public static final int OK=200;
|
||||
public static final int ONEWAY=202;
|
||||
public static final int UNSUPPORTED_MEDIA=415;
|
||||
public static final int MALFORMED_XML=400;
|
||||
public static final int INTERNAL_ERR=500;
|
||||
|
||||
/**
|
||||
* Overwrites all the HTTP response headers written thus far.
|
||||
*
|
||||
* <p>
|
||||
* The implementation should copy the contents of the {@link Map},
|
||||
* rather than retaining a reference. The {@link Map} passed as a
|
||||
* parameter may change after this method is invoked.
|
||||
*
|
||||
* <p>
|
||||
* This method may be called repeatedly, although in normal use
|
||||
* case that's rare (so the implementation is encourage to take
|
||||
* advantage of this usage pattern to improve performance, if possible.)
|
||||
*
|
||||
* <p>
|
||||
* Initially, no header is set.
|
||||
*
|
||||
* <p>
|
||||
* This parameter is usually exposed to {@link WebServiceContext}
|
||||
* as {@link Packet#OUTBOUND_TRANSPORT_HEADERS}, and thus it
|
||||
* should ignore <tt>Content-Type</tt> and <tt>Content-Length</tt> headers.
|
||||
*
|
||||
* @param headers
|
||||
* See {@link HttpURLConnection#getHeaderFields()} for the format.
|
||||
* This parameter may not be null, but since the user application
|
||||
* code may invoke this method, a graceful error checking with
|
||||
* an helpful error message should be provided if it's actually null.
|
||||
* @see #setContentTypeResponseHeader(String)
|
||||
*/
|
||||
public abstract void setResponseHeaders(@NotNull Map<String,List<String>> headers);
|
||||
|
||||
public void setResponseHeader(String key, String value) {
|
||||
setResponseHeader(key, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
public abstract void setResponseHeader(String key, List<String> value);
|
||||
|
||||
/**
|
||||
* Sets the <tt>"Content-Type"</tt> header.
|
||||
*
|
||||
* <p>
|
||||
* If the Content-Type header has already been set, this method will overwrite
|
||||
* the previously set value. If not, this method adds it.
|
||||
*
|
||||
* <p>
|
||||
* Note that this method and {@link #setResponseHeaders(java.util.Map)}
|
||||
* may be invoked in any arbitrary order.
|
||||
*
|
||||
* @param value
|
||||
* strings like <tt>"application/xml; charset=UTF-8"</tt> or
|
||||
* <tt>"image/jpeg"</tt>.
|
||||
*/
|
||||
public abstract void setContentTypeResponseHeader(@NotNull String value);
|
||||
|
||||
/**
|
||||
* Sets the HTTP response code like {@link #OK}.
|
||||
*
|
||||
* <p>
|
||||
* While JAX-WS processes a {@link WSHTTPConnection}, it
|
||||
* will at least call this method once to set a valid HTTP response code.
|
||||
* Note that this method may be invoked multiple times (from user code),
|
||||
* so do not consider the value to be final until {@link #getOutput()}
|
||||
* is invoked.
|
||||
*/
|
||||
|
||||
public abstract void setStatus(int status);
|
||||
|
||||
/**
|
||||
* Gets the last value set by {@link #setStatus(int)}.
|
||||
*
|
||||
* @return
|
||||
* if {@link #setStatus(int)} has not been invoked yet,
|
||||
* return 0.
|
||||
*/
|
||||
// I know this is ugly method!
|
||||
public abstract int getStatus();
|
||||
|
||||
/**
|
||||
* Transport's underlying input stream.
|
||||
*
|
||||
* <p>
|
||||
* This method will be invoked at most once by the JAX-WS RI to
|
||||
* read the request body. If there's no request body, this method
|
||||
* should return an empty {@link InputStream}.
|
||||
*
|
||||
* @return
|
||||
* the stream from which the request body will be read.
|
||||
*/
|
||||
public abstract @NotNull InputStream getInput() throws IOException;
|
||||
|
||||
/**
|
||||
* Transport's underlying output stream
|
||||
*
|
||||
* <p>
|
||||
* This method will be invoked exactly once by the JAX-WS RI
|
||||
* to start writing the response body (unless the processing aborts abnormally.)
|
||||
* Even if there's no response body to write, this method will
|
||||
* still be invoked only to be closed immediately.
|
||||
*
|
||||
* <p>
|
||||
* Once this method is called, the status code and response
|
||||
* headers will never change (IOW {@link #setStatus(int)},
|
||||
* {@link #setResponseHeaders}, and {@link #setContentTypeResponseHeader(String)}
|
||||
* will never be invoked.
|
||||
*/
|
||||
public abstract @NotNull OutputStream getOutput() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the {@link WebServiceContextDelegate} for this connection.
|
||||
*/
|
||||
public abstract @NotNull WebServiceContextDelegate getWebServiceContextDelegate();
|
||||
|
||||
/**
|
||||
* HTTP request method, such as "GET" or "POST".
|
||||
*/
|
||||
public abstract @NotNull String getRequestMethod();
|
||||
|
||||
/**
|
||||
* HTTP request headers.
|
||||
*
|
||||
* @deprecated
|
||||
* This is a potentially expensive operation.
|
||||
* Programs that want to access HTTP headers should consider using
|
||||
* other methods such as {@link #getRequestHeader(String)}.
|
||||
*
|
||||
* @return
|
||||
* can be empty but never null.
|
||||
*/
|
||||
public abstract @NotNull Map<String,List<String>> getRequestHeaders();
|
||||
|
||||
/**
|
||||
* HTTP request header names.
|
||||
*
|
||||
* @deprecated
|
||||
* This is a potentially expensive operation.
|
||||
* Programs that want to access HTTP headers should consider using
|
||||
* other methods such as {@link #getRequestHeader(String)}.
|
||||
*
|
||||
* @return
|
||||
* can be empty but never null.
|
||||
*/
|
||||
public abstract @NotNull Set<String> getRequestHeaderNames();
|
||||
|
||||
/**
|
||||
* @return
|
||||
* HTTP response headers.
|
||||
*/
|
||||
public abstract Map<String,List<String>> getResponseHeaders();
|
||||
|
||||
/**
|
||||
* Gets an HTTP request header.
|
||||
*
|
||||
* <p>
|
||||
* if multiple headers are present, this method returns one of them.
|
||||
* (The implementation is free to choose which one it returns.)
|
||||
*
|
||||
* @return
|
||||
* null if no header exists.
|
||||
*/
|
||||
public abstract @Nullable String getRequestHeader(@NotNull String headerName);
|
||||
|
||||
/**
|
||||
* Gets an HTTP request header.
|
||||
*
|
||||
* @return
|
||||
* null if no header exists.
|
||||
*/
|
||||
public abstract @Nullable List<String> getRequestHeaderValues(@NotNull String headerName);
|
||||
|
||||
/**
|
||||
* HTTP Query string, such as "foo=bar", or null if none exists.
|
||||
*/
|
||||
public abstract @Nullable String getQueryString();
|
||||
|
||||
/**
|
||||
* Extra portion of the request URI after the end of the expected address of the service
|
||||
* but before the query string
|
||||
*/
|
||||
public abstract @Nullable String getPathInfo();
|
||||
|
||||
/**
|
||||
* Requested path. A string like "/foo/bar/baz"
|
||||
*/
|
||||
public abstract @NotNull String getRequestURI();
|
||||
|
||||
/**
|
||||
* Requested scheme, e.g. "http" or "https"
|
||||
*/
|
||||
public abstract @NotNull String getRequestScheme();
|
||||
|
||||
/**
|
||||
* Server name
|
||||
*/
|
||||
public abstract @NotNull String getServerName();
|
||||
|
||||
/**
|
||||
* Server port
|
||||
*/
|
||||
public abstract int getServerPort();
|
||||
|
||||
/**
|
||||
* Portion of the request URI that groups related service addresses. The value, if non-empty, will
|
||||
* always begin with '/', but will never end with '/'. Environments that do not support
|
||||
* context paths must return an empty string.
|
||||
*/
|
||||
public @NotNull String getContextPath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Environment specific context , if available
|
||||
*/
|
||||
public Object getContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the absolute URL up to the context path.
|
||||
* @return
|
||||
* String like "http://myhost/myapp"
|
||||
* @since 2.1.2
|
||||
*/
|
||||
public @NotNull String getBaseAddress() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether connection is HTTPS or not
|
||||
*
|
||||
* @return if the received request is on HTTPS, return true
|
||||
* else false
|
||||
*/
|
||||
public abstract boolean isSecure();
|
||||
|
||||
/**
|
||||
* User principal associated with the request
|
||||
*
|
||||
* @return user principal
|
||||
*/
|
||||
public Principal getUserPrincipal() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether user associated with the request holds the given role
|
||||
*
|
||||
* @param role Role to check
|
||||
* @return if the caller holds the role
|
||||
*/
|
||||
public boolean isUserInRole(String role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets request metadata attribute
|
||||
* @param key Request metadata key
|
||||
* @return Value of metadata attribute or null, if no value present
|
||||
*/
|
||||
public Object getRequestAttribute(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private volatile boolean closed;
|
||||
|
||||
/**
|
||||
* Close the connection
|
||||
*/
|
||||
public void close() {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retuns whether connection is closed or not.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses are expected to override
|
||||
*
|
||||
* @return a {@link String} containing the protocol name and version number
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return "HTTP/1.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses are expected to override
|
||||
*
|
||||
* @since JAX-WS RI 2.2.2
|
||||
* @return value of given cookie
|
||||
*/
|
||||
public String getCookie(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses are expected to override
|
||||
*
|
||||
*
|
||||
* @since JAX-WS RI 2.2.2
|
||||
*/
|
||||
public void setCookie(String name, String value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses are expected to override
|
||||
*/
|
||||
public void setContentLengthResponseHeader(int value) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* 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.transport.http.client;
|
||||
|
||||
import com.sun.xml.internal.ws.api.EndpointAddress;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.client.BindingProviderProperties;
|
||||
import static com.sun.xml.internal.ws.client.BindingProviderProperties.*;
|
||||
import com.sun.xml.internal.ws.client.ClientTransportException;
|
||||
import com.sun.xml.internal.ws.resources.ClientMessages;
|
||||
import com.sun.xml.internal.ws.transport.Headers;
|
||||
import com.sun.xml.internal.ws.developer.JAXWSProperties;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.handler.MessageContext;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author WS Development Team
|
||||
*/
|
||||
public class HttpClientTransport {
|
||||
|
||||
private static final byte[] THROW_AWAY_BUFFER = new byte[8192];
|
||||
|
||||
// Need to use JAXB first to register DatatypeConverter
|
||||
static {
|
||||
try {
|
||||
JAXBContext.newInstance().createUnmarshaller();
|
||||
} catch(JAXBException je) {
|
||||
// Nothing much can be done. Intentionally left empty
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ int statusCode;
|
||||
/*package*/ String statusMessage;
|
||||
/*package*/ int contentLength;
|
||||
private final Map<String, List<String>> reqHeaders;
|
||||
private Map<String, List<String>> respHeaders = null;
|
||||
|
||||
private OutputStream outputStream;
|
||||
private boolean https;
|
||||
private HttpURLConnection httpConnection = null;
|
||||
private final EndpointAddress endpoint;
|
||||
private final Packet context;
|
||||
private final Integer chunkSize;
|
||||
|
||||
|
||||
public HttpClientTransport(@NotNull Packet packet, @NotNull Map<String,List<String>> reqHeaders) {
|
||||
endpoint = packet.endpointAddress;
|
||||
context = packet;
|
||||
this.reqHeaders = reqHeaders;
|
||||
chunkSize = (Integer)context.invocationProperties.get(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the stream for HTTP request
|
||||
*/
|
||||
OutputStream getOutput() {
|
||||
try {
|
||||
createHttpConnection();
|
||||
// for "GET" request no need to get outputStream
|
||||
if (requiresOutputStream()) {
|
||||
outputStream = httpConnection.getOutputStream();
|
||||
if (chunkSize != null) {
|
||||
outputStream = new WSChunkedOuputStream(outputStream, chunkSize);
|
||||
}
|
||||
List<String> contentEncoding = reqHeaders.get("Content-Encoding");
|
||||
// TODO need to find out correct encoding based on q value - RFC 2616
|
||||
if (contentEncoding != null && contentEncoding.get(0).contains("gzip")) {
|
||||
outputStream = new GZIPOutputStream(outputStream);
|
||||
}
|
||||
}
|
||||
httpConnection.connect();
|
||||
} catch (Exception ex) {
|
||||
throw new ClientTransportException(
|
||||
ClientMessages.localizableHTTP_CLIENT_FAILED(ex),ex);
|
||||
}
|
||||
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
void closeOutput() throws IOException {
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
outputStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the response from HTTP connection and prepare the input stream for response
|
||||
*/
|
||||
@Nullable InputStream getInput() {
|
||||
// response processing
|
||||
|
||||
InputStream in;
|
||||
try {
|
||||
in = readResponse();
|
||||
if (in != null) {
|
||||
String contentEncoding = httpConnection.getContentEncoding();
|
||||
if (contentEncoding != null && contentEncoding.contains("gzip")) {
|
||||
in = new GZIPInputStream(in);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(statusCode, statusMessage), e);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
if (respHeaders != null) {
|
||||
return respHeaders;
|
||||
}
|
||||
respHeaders = new Headers();
|
||||
respHeaders.putAll(httpConnection.getHeaderFields());
|
||||
return respHeaders;
|
||||
}
|
||||
|
||||
protected @Nullable InputStream readResponse() {
|
||||
InputStream is;
|
||||
try {
|
||||
is = httpConnection.getInputStream();
|
||||
} catch(IOException ioe) {
|
||||
is = httpConnection.getErrorStream();
|
||||
}
|
||||
if (is == null) {
|
||||
return is;
|
||||
}
|
||||
// Since StreamMessage doesn't read </s:Body></s:Envelope>, there
|
||||
// are some bytes left in the InputStream. This confuses JDK and may
|
||||
// not reuse underlying sockets. Hopefully JDK fixes it in its code !
|
||||
final InputStream temp = is;
|
||||
return new FilterInputStream(temp) {
|
||||
// Workaround for "SJSXP XMLStreamReader.next() closes stream".
|
||||
// So it doesn't read from the closed stream
|
||||
boolean closed;
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
while(temp.read(THROW_AWAY_BUFFER) != -1);
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void readResponseCodeAndMessage() {
|
||||
try {
|
||||
statusCode = httpConnection.getResponseCode();
|
||||
statusMessage = httpConnection.getResponseMessage();
|
||||
contentLength = httpConnection.getContentLength();
|
||||
} catch(IOException ioe) {
|
||||
throw new WebServiceException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpURLConnection openConnection(Packet packet) {
|
||||
// default do nothing
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean checkHTTPS(HttpURLConnection connection) {
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
|
||||
// TODO The above property needs to be removed in future version as the semantics of this property are not preoperly defined.
|
||||
// One should use JAXWSProperties.HOSTNAME_VERIFIER to control the behavior
|
||||
|
||||
// does the client want client hostname verification by the service
|
||||
String verificationProperty =
|
||||
(String) context.invocationProperties.get(HOSTNAME_VERIFICATION_PROPERTY);
|
||||
if (verificationProperty != null) {
|
||||
if (verificationProperty.equalsIgnoreCase("true")) {
|
||||
((HttpsURLConnection) connection).setHostnameVerifier(new HttpClientVerifier());
|
||||
}
|
||||
}
|
||||
|
||||
// Set application's HostNameVerifier for this connection
|
||||
HostnameVerifier verifier =
|
||||
(HostnameVerifier) context.invocationProperties.get(JAXWSProperties.HOSTNAME_VERIFIER);
|
||||
if (verifier != null) {
|
||||
((HttpsURLConnection) connection).setHostnameVerifier(verifier);
|
||||
}
|
||||
|
||||
// Set application's SocketFactory for this connection
|
||||
SSLSocketFactory sslSocketFactory =
|
||||
(SSLSocketFactory) context.invocationProperties.get(JAXWSProperties.SSL_SOCKET_FACTORY);
|
||||
if (sslSocketFactory != null) {
|
||||
((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createHttpConnection() throws IOException {
|
||||
httpConnection = openConnection(context);
|
||||
|
||||
if (httpConnection == null)
|
||||
httpConnection = (HttpURLConnection) endpoint.openConnection();
|
||||
|
||||
String scheme = endpoint.getURI().getScheme();
|
||||
if (scheme.equals("https")) {
|
||||
https = true;
|
||||
}
|
||||
if (checkHTTPS(httpConnection))
|
||||
https = true;
|
||||
|
||||
// allow interaction with the web page - user may have to supply
|
||||
// username, password id web page is accessed from web browser
|
||||
httpConnection.setAllowUserInteraction(true);
|
||||
|
||||
// enable input, output streams
|
||||
httpConnection.setDoOutput(true);
|
||||
httpConnection.setDoInput(true);
|
||||
|
||||
String requestMethod = (String) context.invocationProperties.get(MessageContext.HTTP_REQUEST_METHOD);
|
||||
String method = (requestMethod != null) ? requestMethod : "POST";
|
||||
httpConnection.setRequestMethod(method);
|
||||
|
||||
//this code or something similiar needs t be moved elsewhere for error checking
|
||||
/*if (context.invocationProperties.get(BindingProviderProperties.BINDING_ID_PROPERTY).equals(HTTPBinding.HTTP_BINDING)){
|
||||
method = (requestMethod != null)?requestMethod:method;
|
||||
} else if
|
||||
(context.invocationProperties.get(BindingProviderProperties.BINDING_ID_PROPERTY).equals(SOAPBinding.SOAP12HTTP_BINDING) &&
|
||||
"GET".equalsIgnoreCase(requestMethod)) {
|
||||
}
|
||||
*/
|
||||
|
||||
Integer reqTimeout = (Integer)context.invocationProperties.get(BindingProviderProperties.REQUEST_TIMEOUT);
|
||||
if (reqTimeout != null) {
|
||||
httpConnection.setReadTimeout(reqTimeout);
|
||||
}
|
||||
|
||||
Integer connectTimeout = (Integer)context.invocationProperties.get(JAXWSProperties.CONNECT_TIMEOUT);
|
||||
if (connectTimeout != null) {
|
||||
httpConnection.setConnectTimeout(connectTimeout);
|
||||
}
|
||||
|
||||
Integer chunkSize = (Integer)context.invocationProperties.get(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE);
|
||||
if (chunkSize != null) {
|
||||
httpConnection.setChunkedStreamingMode(chunkSize);
|
||||
}
|
||||
|
||||
// set the properties on HttpURLConnection
|
||||
for (Map.Entry<String, List<String>> entry : reqHeaders.entrySet()) {
|
||||
if ("Content-Length".equals(entry.getKey())) continue;
|
||||
for(String value : entry.getValue()) {
|
||||
httpConnection.addRequestProperty(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSecure() {
|
||||
return https;
|
||||
}
|
||||
|
||||
protected void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
private boolean requiresOutputStream() {
|
||||
return !(httpConnection.getRequestMethod().equalsIgnoreCase("GET") ||
|
||||
httpConnection.getRequestMethod().equalsIgnoreCase("HEAD") ||
|
||||
httpConnection.getRequestMethod().equalsIgnoreCase("DELETE"));
|
||||
}
|
||||
|
||||
@Nullable String getContentType() {
|
||||
return httpConnection.getContentType();
|
||||
}
|
||||
|
||||
public int getContentLength() {
|
||||
return httpConnection.getContentLength();
|
||||
}
|
||||
|
||||
// overide default SSL HttpClientVerifier to always return true
|
||||
// effectively overiding Hostname client verification when using SSL
|
||||
private static class HttpClientVerifier implements HostnameVerifier {
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalhostHttpClientVerifier implements HostnameVerifier {
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return "localhost".equalsIgnoreCase(s) || "127.0.0.1".equals(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HttpURLConnection.getOuputStream() returns sun.net.www.http.ChunkedOuputStream in chunked
|
||||
* streaming mode. If you call ChunkedOuputStream.write(byte[20MB], int, int), then the whole data
|
||||
* is kept in memory. This wraps the ChunkedOuputStream so that it writes only small
|
||||
* chunks.
|
||||
*/
|
||||
private static final class WSChunkedOuputStream extends FilterOutputStream {
|
||||
final int chunkSize;
|
||||
|
||||
WSChunkedOuputStream(OutputStream actual, int chunkSize) {
|
||||
super(actual);
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
while(len > 0) {
|
||||
int sent = (len > chunkSize) ? chunkSize : len;
|
||||
out.write(b, off, sent); // don't use super.write() as it writes byte-by-byte
|
||||
len -= sent;
|
||||
off += sent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.transport.http.client;
|
||||
|
||||
import com.oracle.webservices.internal.api.message.BasePropertySet;
|
||||
import com.oracle.webservices.internal.api.message.PropertySet;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.client.ResponseContext;
|
||||
|
||||
import javax.xml.ws.handler.MessageContext;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Properties exposed from {@link HttpTransportPipe} for {@link ResponseContext}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
final class HttpResponseProperties extends BasePropertySet {
|
||||
|
||||
private final HttpClientTransport deferedCon;
|
||||
|
||||
public HttpResponseProperties(@NotNull HttpClientTransport con) {
|
||||
this.deferedCon = con;
|
||||
}
|
||||
|
||||
@Property(MessageContext.HTTP_RESPONSE_HEADERS)
|
||||
public Map<String, List<String>> getResponseHeaders() {
|
||||
return deferedCon.getHeaders();
|
||||
}
|
||||
|
||||
@Property(MessageContext.HTTP_RESPONSE_CODE)
|
||||
public int getResponseCode() {
|
||||
return deferedCon.statusCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMap getPropertyMap() {
|
||||
return model;
|
||||
}
|
||||
|
||||
private static final PropertyMap model;
|
||||
|
||||
static {
|
||||
model = parse(HttpResponseProperties.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http.client;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.SOAPVersion;
|
||||
import com.sun.xml.internal.ws.api.WSBinding;
|
||||
import com.sun.xml.internal.ws.api.ha.StickyFeature;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.pipe.*;
|
||||
import com.sun.xml.internal.ws.api.pipe.helper.AbstractTubeImpl;
|
||||
import com.sun.xml.internal.ws.client.ClientTransportException;
|
||||
import com.sun.xml.internal.ws.developer.HttpConfigFeature;
|
||||
import com.sun.xml.internal.ws.resources.ClientMessages;
|
||||
import com.sun.xml.internal.ws.resources.WsservletMessages;
|
||||
import com.sun.xml.internal.ws.transport.Headers;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.util.ByteArrayBuffer;
|
||||
import com.sun.xml.internal.ws.util.RuntimeVersion;
|
||||
import com.sun.xml.internal.ws.util.StreamUtils;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import javax.xml.ws.BindingProvider;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.WebServiceFeature;
|
||||
import javax.xml.ws.handler.MessageContext;
|
||||
import javax.xml.ws.soap.SOAPBinding;
|
||||
import java.io.*;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link Tube} that sends a request to a remote HTTP server.
|
||||
*
|
||||
* TODO: need to create separate HTTP transport pipes for binding. SOAP1.1, SOAP1.2,
|
||||
* TODO: XML/HTTP differ in handling status codes.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public class HttpTransportPipe extends AbstractTubeImpl {
|
||||
|
||||
private static final List<String> USER_AGENT = Collections.singletonList(RuntimeVersion.VERSION.toString());
|
||||
private static final Logger LOGGER = Logger.getLogger(HttpTransportPipe.class.getName());
|
||||
|
||||
/**
|
||||
* Dumps what goes across HTTP transport.
|
||||
*/
|
||||
public static boolean dump;
|
||||
|
||||
private final Codec codec;
|
||||
private final WSBinding binding;
|
||||
private final CookieHandler cookieJar; // shared object among the tubes
|
||||
private final boolean sticky;
|
||||
|
||||
static {
|
||||
boolean b;
|
||||
try {
|
||||
b = Boolean.getBoolean(HttpTransportPipe.class.getName()+".dump");
|
||||
} catch( Throwable t ) {
|
||||
b = false;
|
||||
}
|
||||
dump = b;
|
||||
}
|
||||
|
||||
public HttpTransportPipe(Codec codec, WSBinding binding) {
|
||||
this.codec = codec;
|
||||
this.binding = binding;
|
||||
this.sticky = isSticky(binding);
|
||||
HttpConfigFeature configFeature = binding.getFeature(HttpConfigFeature.class);
|
||||
if (configFeature == null) {
|
||||
configFeature = new HttpConfigFeature();
|
||||
}
|
||||
this.cookieJar = configFeature.getCookieHandler();
|
||||
}
|
||||
|
||||
private static boolean isSticky(WSBinding binding) {
|
||||
boolean tSticky = false;
|
||||
WebServiceFeature[] features = binding.getFeatures().toArray();
|
||||
for(WebServiceFeature f : features) {
|
||||
if (f instanceof StickyFeature) {
|
||||
tSticky = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tSticky;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy constructor for {@link Tube#copy(TubeCloner)}.
|
||||
*/
|
||||
private HttpTransportPipe(HttpTransportPipe that, TubeCloner cloner) {
|
||||
this(that.codec.copy(), that.binding);
|
||||
cloner.add(that,this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NextAction processException(@NotNull Throwable t) {
|
||||
return doThrow(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NextAction processRequest(@NotNull Packet request) {
|
||||
return doReturnWith(process(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NextAction processResponse(@NotNull Packet response) {
|
||||
return doReturnWith(response);
|
||||
}
|
||||
|
||||
protected HttpClientTransport getTransport(Packet request, Map<String, List<String>> reqHeaders) {
|
||||
return new HttpClientTransport(request, reqHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet process(Packet request) {
|
||||
HttpClientTransport con;
|
||||
try {
|
||||
// get transport headers from message
|
||||
Map<String, List<String>> reqHeaders = new Headers();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
|
||||
boolean addUserAgent = true;
|
||||
if (userHeaders != null) {
|
||||
// userHeaders may not be modifiable like SingletonMap, just copy them
|
||||
reqHeaders.putAll(userHeaders);
|
||||
// application wants to use its own User-Agent header
|
||||
if (userHeaders.get("User-Agent") != null) {
|
||||
addUserAgent = false;
|
||||
}
|
||||
}
|
||||
if (addUserAgent) {
|
||||
reqHeaders.put("User-Agent", USER_AGENT);
|
||||
}
|
||||
|
||||
addBasicAuth(request, reqHeaders);
|
||||
addCookies(request, reqHeaders);
|
||||
|
||||
con = getTransport(request, reqHeaders);
|
||||
request.addSatellite(new HttpResponseProperties(con));
|
||||
|
||||
ContentType ct = codec.getStaticContentType(request);
|
||||
if (ct == null) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
|
||||
ct = codec.encode(request, buf);
|
||||
// data size is available, set it as Content-Length
|
||||
reqHeaders.put("Content-Length", Collections.singletonList(Integer.toString(buf.size())));
|
||||
reqHeaders.put("Content-Type", Collections.singletonList(ct.getContentType()));
|
||||
if (ct.getAcceptHeader() != null) {
|
||||
reqHeaders.put("Accept", Collections.singletonList(ct.getAcceptHeader()));
|
||||
}
|
||||
if (binding instanceof SOAPBinding) {
|
||||
writeSOAPAction(reqHeaders, ct.getSOAPActionHeader());
|
||||
}
|
||||
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
dump(buf, "HTTP request", reqHeaders);
|
||||
}
|
||||
|
||||
buf.writeTo(con.getOutput());
|
||||
} else {
|
||||
// Set static Content-Type
|
||||
reqHeaders.put("Content-Type", Collections.singletonList(ct.getContentType()));
|
||||
if (ct.getAcceptHeader() != null) {
|
||||
reqHeaders.put("Accept", Collections.singletonList(ct.getAcceptHeader()));
|
||||
}
|
||||
if (binding instanceof SOAPBinding) {
|
||||
writeSOAPAction(reqHeaders, ct.getSOAPActionHeader());
|
||||
}
|
||||
|
||||
if(dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
codec.encode(request, buf);
|
||||
dump(buf, "HTTP request - "+request.endpointAddress, reqHeaders);
|
||||
OutputStream out = con.getOutput();
|
||||
if (out != null) {
|
||||
buf.writeTo(out);
|
||||
}
|
||||
} else {
|
||||
OutputStream os = con.getOutput();
|
||||
if (os != null) {
|
||||
codec.encode(request, os);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con.closeOutput();
|
||||
|
||||
return createResponsePacket(request, con);
|
||||
} catch(WebServiceException wex) {
|
||||
throw wex;
|
||||
} catch(Exception ex) {
|
||||
throw new WebServiceException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Packet createResponsePacket(Packet request, HttpClientTransport con) throws IOException {
|
||||
con.readResponseCodeAndMessage(); // throws IOE
|
||||
recordCookies(request, con);
|
||||
|
||||
InputStream responseStream = con.getInput();
|
||||
if (dump || LOGGER.isLoggable(Level.FINER)) {
|
||||
ByteArrayBuffer buf = new ByteArrayBuffer();
|
||||
if (responseStream != null) {
|
||||
buf.write(responseStream);
|
||||
responseStream.close();
|
||||
}
|
||||
dump(buf,"HTTP response - "+request.endpointAddress+" - "+con.statusCode, con.getHeaders());
|
||||
responseStream = buf.newInputStream();
|
||||
}
|
||||
|
||||
// Check if stream contains any data
|
||||
int cl = con.contentLength;
|
||||
InputStream tempIn = null;
|
||||
if (cl == -1) { // No Content-Length header
|
||||
tempIn = StreamUtils.hasSomeData(responseStream);
|
||||
if (tempIn != null) {
|
||||
responseStream = tempIn;
|
||||
}
|
||||
}
|
||||
if (cl == 0 || (cl == -1 && tempIn == null)) {
|
||||
if(responseStream != null) {
|
||||
responseStream.close(); // No data, so close the stream
|
||||
responseStream = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Allows only certain http status codes for a binding. For all
|
||||
// other status codes, throws exception
|
||||
checkStatusCode(responseStream, con); // throws ClientTransportException
|
||||
|
||||
Packet reply = request.createClientResponse(null);
|
||||
reply.wasTransportSecure = con.isSecure();
|
||||
if (responseStream != null) {
|
||||
String contentType = con.getContentType();
|
||||
if (contentType != null && contentType.contains("text/html") && binding instanceof SOAPBinding) {
|
||||
throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(con.statusCode, con.statusMessage));
|
||||
}
|
||||
codec.decode(responseStream, contentType, reply);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows the following HTTP status codes.
|
||||
* SOAP 1.1/HTTP - 200, 202, 500
|
||||
* SOAP 1.2/HTTP - 200, 202, 400, 500
|
||||
* XML/HTTP - all
|
||||
*
|
||||
* For all other status codes, it throws an exception
|
||||
*/
|
||||
private void checkStatusCode(InputStream in, HttpClientTransport con) throws IOException {
|
||||
int statusCode = con.statusCode;
|
||||
String statusMessage = con.statusMessage;
|
||||
// SOAP1.1 and SOAP1.2 differ here
|
||||
if (binding instanceof SOAPBinding) {
|
||||
if (binding.getSOAPVersion() == SOAPVersion.SOAP_12) {
|
||||
//In SOAP 1.2, Fault messages can be sent with 4xx and 5xx error codes
|
||||
if (statusCode == HttpURLConnection.HTTP_OK || statusCode == HttpURLConnection.HTTP_ACCEPTED || isErrorCode(statusCode)) {
|
||||
// acceptable status codes for SOAP 1.2
|
||||
if (isErrorCode(statusCode) && in == null) {
|
||||
// No envelope for the error, so throw an exception with http error details
|
||||
throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(statusCode, statusMessage));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// SOAP 1.1
|
||||
if (statusCode == HttpURLConnection.HTTP_OK || statusCode == HttpURLConnection.HTTP_ACCEPTED || statusCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
|
||||
// acceptable status codes for SOAP 1.1
|
||||
if (statusCode == HttpURLConnection.HTTP_INTERNAL_ERROR && in == null) {
|
||||
// No envelope for the error, so throw an exception with http error details
|
||||
throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(statusCode, statusMessage));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(statusCode, statusMessage));
|
||||
}
|
||||
// Every status code is OK for XML/HTTP
|
||||
}
|
||||
|
||||
private boolean isErrorCode(int code) {
|
||||
//if(code/100 == 5/*Server-side error*/ || code/100 == 4 /*client error*/ ) {
|
||||
return code == 500 || code == 400;
|
||||
}
|
||||
|
||||
private void addCookies(Packet context, Map<String, List<String>> reqHeaders) throws IOException {
|
||||
Boolean shouldMaintainSessionProperty =
|
||||
(Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY);
|
||||
if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) {
|
||||
return; // explicitly turned off
|
||||
}
|
||||
if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) {
|
||||
Map<String, List<String>> rememberedCookies = cookieJar.get(context.endpointAddress.getURI(), reqHeaders);
|
||||
processCookieHeaders(reqHeaders, rememberedCookies, "Cookie");
|
||||
processCookieHeaders(reqHeaders, rememberedCookies, "Cookie2");
|
||||
}
|
||||
}
|
||||
|
||||
private void processCookieHeaders(Map<String, List<String>> requestHeaders, Map<String, List<String>> rememberedCookies, String cookieHeader) {
|
||||
List<String> jarCookies = rememberedCookies.get(cookieHeader);
|
||||
if (jarCookies != null && !jarCookies.isEmpty()) {
|
||||
List<String> resultCookies = mergeUserCookies(jarCookies, requestHeaders.get(cookieHeader));
|
||||
requestHeaders.put(cookieHeader, resultCookies);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> mergeUserCookies(List<String> rememberedCookies, List<String> userCookies) {
|
||||
|
||||
// nothing to merge
|
||||
if (userCookies == null || userCookies.isEmpty()) {
|
||||
return rememberedCookies;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
cookieListToMap(rememberedCookies, map);
|
||||
cookieListToMap(userCookies, map);
|
||||
|
||||
return new ArrayList<String>(map.values());
|
||||
}
|
||||
|
||||
private void cookieListToMap(List<String> cookieList, Map<String, String> targetMap) {
|
||||
for(String cookie : cookieList) {
|
||||
int index = cookie.indexOf("=");
|
||||
String cookieName = cookie.substring(0, index);
|
||||
targetMap.put(cookieName, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordCookies(Packet context, HttpClientTransport con) throws IOException {
|
||||
Boolean shouldMaintainSessionProperty =
|
||||
(Boolean) context.invocationProperties.get(BindingProvider.SESSION_MAINTAIN_PROPERTY);
|
||||
if (shouldMaintainSessionProperty != null && !shouldMaintainSessionProperty) {
|
||||
return; // explicitly turned off
|
||||
}
|
||||
if (sticky || (shouldMaintainSessionProperty != null && shouldMaintainSessionProperty)) {
|
||||
cookieJar.put(context.endpointAddress.getURI(), con.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
private void addBasicAuth(Packet context, Map<String, List<String>> reqHeaders) {
|
||||
String user = (String) context.invocationProperties.get(BindingProvider.USERNAME_PROPERTY);
|
||||
if (user != null) {
|
||||
String pw = (String) context.invocationProperties.get(BindingProvider.PASSWORD_PROPERTY);
|
||||
if (pw != null) {
|
||||
StringBuilder buf = new StringBuilder(user);
|
||||
buf.append(":");
|
||||
buf.append(pw);
|
||||
String creds = DatatypeConverter.printBase64Binary(buf.toString().getBytes());
|
||||
reqHeaders.put("Authorization", Collections.singletonList("Basic "+creds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* write SOAPAction header if the soapAction parameter is non-null or BindingProvider properties set.
|
||||
* BindingProvider properties take precedence.
|
||||
*/
|
||||
private void writeSOAPAction(Map<String, List<String>> reqHeaders, String soapAction) {
|
||||
//dont write SOAPAction HTTP header for SOAP 1.2 messages.
|
||||
if(SOAPVersion.SOAP_12.equals(binding.getSOAPVersion())) {
|
||||
return;
|
||||
}
|
||||
if (soapAction != null) {
|
||||
reqHeaders.put("SOAPAction", Collections.singletonList(soapAction));
|
||||
} else {
|
||||
reqHeaders.put("SOAPAction", Collections.singletonList("\"\""));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestroy() {
|
||||
// nothing to do. Intentionally left empty.
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpTransportPipe copy(TubeCloner cloner) {
|
||||
return new HttpTransportPipe(this,cloner);
|
||||
}
|
||||
|
||||
|
||||
private void dump(ByteArrayBuffer buf, String caption, Map<String, List<String>> headers) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintWriter pw = new PrintWriter(baos, true);
|
||||
pw.println("---["+caption +"]---");
|
||||
for (Entry<String,List<String>> header : headers.entrySet()) {
|
||||
if(header.getValue().isEmpty()) {
|
||||
// I don't think this is legal, but let's just dump it,
|
||||
// as the point of the dump is to uncover problems.
|
||||
pw.println(header.getValue());
|
||||
} else {
|
||||
for (String value : header.getValue()) {
|
||||
pw.println(header.getKey()+": "+value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buf.size() > HttpAdapter.dump_threshold) {
|
||||
byte[] b = buf.getRawData();
|
||||
baos.write(b, 0, HttpAdapter.dump_threshold);
|
||||
pw.println();
|
||||
pw.println(WsservletMessages.MESSAGE_TOO_LONG(HttpAdapter.class.getName() + ".dumpTreshold"));
|
||||
} else {
|
||||
buf.writeTo(baos);
|
||||
}
|
||||
pw.println("--------------------");
|
||||
|
||||
String msg = baos.toString();
|
||||
if (dump) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
if (LOGGER.isLoggable(Level.FINER)) {
|
||||
LOGGER.log(Level.FINER, msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.xml.internal.stream.buffer.XMLStreamBufferResult;
|
||||
import com.sun.xml.internal.ws.api.Component;
|
||||
import com.sun.xml.internal.ws.api.WSBinding;
|
||||
import com.sun.xml.internal.ws.api.BindingID;
|
||||
import com.sun.xml.internal.ws.api.databinding.MetadataReader;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.binding.BindingImpl;
|
||||
import com.sun.xml.internal.ws.api.server.*;
|
||||
import com.sun.xml.internal.ws.server.EndpointFactory;
|
||||
import com.sun.xml.internal.ws.server.ServerRtException;
|
||||
import com.sun.xml.internal.ws.util.xml.XmlUtil;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapterList;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.istack.internal.NotNull;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.ws.*;
|
||||
import javax.xml.ws.spi.http.HttpContext;
|
||||
import javax.xml.ws.wsaddressing.W3CEndpointReference;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.xml.sax.EntityResolver;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
||||
/**
|
||||
* Implements {@link Endpoint}.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* This class accumulates the information necessary to create
|
||||
* {@link WSEndpoint}, and then when {@link #publish} method
|
||||
* is called it will be created.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* This object also allows accumulated information to be retrieved.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public class EndpointImpl extends Endpoint {
|
||||
|
||||
private static final WebServicePermission ENDPOINT_PUBLISH_PERMISSION =
|
||||
new WebServicePermission("publishEndpoint");
|
||||
|
||||
/**
|
||||
* Once the service is published, this field will
|
||||
* be set to the {@link HttpEndpoint} instance.
|
||||
* <p/>
|
||||
* But don't declare the type as {@link HttpEndpoint}
|
||||
* to avoid static type dependency that cause the class loading to
|
||||
* fail if the LW HTTP server doesn't exist.
|
||||
*/
|
||||
private Object actualEndpoint;
|
||||
|
||||
// information accumulated for creating WSEndpoint
|
||||
private final WSBinding binding;
|
||||
private @Nullable final Object implementor;
|
||||
private List<Source> metadata;
|
||||
private Executor executor;
|
||||
private Map<String, Object> properties = Collections.emptyMap(); // always non-null
|
||||
private boolean stopped;
|
||||
private @Nullable EndpointContext endpointContext;
|
||||
private @NotNull final Class<?> implClass;
|
||||
private final Invoker invoker;
|
||||
private Container container;
|
||||
|
||||
|
||||
public EndpointImpl(@NotNull BindingID bindingId, @NotNull Object impl,
|
||||
WebServiceFeature ... features) {
|
||||
this(bindingId, impl, impl.getClass(),
|
||||
InstanceResolver.createSingleton(impl).createInvoker(), features);
|
||||
}
|
||||
|
||||
public EndpointImpl(@NotNull BindingID bindingId, @NotNull Class implClass,
|
||||
javax.xml.ws.spi.Invoker invoker,
|
||||
WebServiceFeature ... features) {
|
||||
this(bindingId, null, implClass, new InvokerImpl(invoker), features);
|
||||
}
|
||||
|
||||
private EndpointImpl(@NotNull BindingID bindingId, Object impl, @NotNull Class implClass,
|
||||
Invoker invoker, WebServiceFeature ... features) {
|
||||
binding = BindingImpl.create(bindingId, features);
|
||||
this.implClass = implClass;
|
||||
this.invoker = invoker;
|
||||
this.implementor = impl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl},
|
||||
* and immediately publishes it with the given context.
|
||||
*
|
||||
* @param wse created endpoint
|
||||
* @param serverContext supported http context
|
||||
* @deprecated This is a backdoor method. Don't use it unless you know what you are doing.
|
||||
*/
|
||||
public EndpointImpl(WSEndpoint wse, Object serverContext) {
|
||||
this(wse, serverContext, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl},
|
||||
* and immediately publishes it with the given context.
|
||||
*
|
||||
* @param wse created endpoint
|
||||
* @param serverContext supported http context
|
||||
* @param ctxt endpoint context
|
||||
* @deprecated This is a backdoor method. Don't use it unless you know what you are doing.
|
||||
*/
|
||||
public EndpointImpl(WSEndpoint wse, Object serverContext, EndpointContext ctxt) {
|
||||
endpointContext = ctxt;
|
||||
actualEndpoint = new HttpEndpoint(null, getAdapter(wse, ""));
|
||||
((HttpEndpoint) actualEndpoint).publish(serverContext);
|
||||
binding = wse.getBinding();
|
||||
implementor = null; // this violates the semantics, but hey, this is a backdoor.
|
||||
implClass = null;
|
||||
invoker = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl},
|
||||
* and immediately publishes it with the given context.
|
||||
*
|
||||
* @param wse created endpoint
|
||||
* @param address endpoint address
|
||||
* @deprecated This is a backdoor method. Don't use it unless you know what you are doing.
|
||||
*/
|
||||
public EndpointImpl(WSEndpoint wse, String address) {
|
||||
this(wse, address, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl},
|
||||
* and immediately publishes it with the given context.
|
||||
*
|
||||
* @param wse created endpoint
|
||||
* @param address endpoint address
|
||||
* @param ctxt endpoint context
|
||||
* @deprecated This is a backdoor method. Don't use it unless you know what you are doing.
|
||||
*/
|
||||
public EndpointImpl(WSEndpoint wse, String address, EndpointContext ctxt) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(address);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException("Cannot create URL for this address " + address);
|
||||
}
|
||||
if (!url.getProtocol().equals("http")) {
|
||||
throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported");
|
||||
}
|
||||
if (!url.getPath().startsWith("/")) {
|
||||
throw new IllegalArgumentException("Incorrect WebService address=" + address +
|
||||
". The address's path should start with /");
|
||||
}
|
||||
endpointContext = ctxt;
|
||||
actualEndpoint = new HttpEndpoint(null, getAdapter(wse, url.getPath()));
|
||||
((HttpEndpoint) actualEndpoint).publish(address);
|
||||
binding = wse.getBinding();
|
||||
implementor = null; // this violates the semantics, but hey, this is a backdoor.
|
||||
implClass = null;
|
||||
invoker = null;
|
||||
}
|
||||
|
||||
public Binding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
|
||||
public Object getImplementor() {
|
||||
return implementor;
|
||||
}
|
||||
|
||||
public void publish(String address) {
|
||||
canPublish();
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(address);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException("Cannot create URL for this address " + address);
|
||||
}
|
||||
if (!url.getProtocol().equals("http")) {
|
||||
throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported");
|
||||
}
|
||||
if (!url.getPath().startsWith("/")) {
|
||||
throw new IllegalArgumentException("Incorrect WebService address=" + address +
|
||||
". The address's path should start with /");
|
||||
}
|
||||
createEndpoint(url.getPath());
|
||||
((HttpEndpoint) actualEndpoint).publish(address);
|
||||
}
|
||||
|
||||
public void publish(Object serverContext) {
|
||||
canPublish();
|
||||
if (!com.sun.net.httpserver.HttpContext.class.isAssignableFrom(serverContext.getClass())) {
|
||||
throw new IllegalArgumentException(serverContext.getClass() + " is not a supported context.");
|
||||
}
|
||||
createEndpoint(((com.sun.net.httpserver.HttpContext)serverContext).getPath());
|
||||
((HttpEndpoint) actualEndpoint).publish(serverContext);
|
||||
}
|
||||
|
||||
public void publish(HttpContext serverContext) {
|
||||
canPublish();
|
||||
createEndpoint(serverContext.getPath());
|
||||
((HttpEndpoint) actualEndpoint).publish(serverContext);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (isPublished()) {
|
||||
((HttpEndpoint) actualEndpoint).stop();
|
||||
actualEndpoint = null;
|
||||
stopped = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPublished() {
|
||||
return actualEndpoint != null;
|
||||
}
|
||||
|
||||
public List<Source> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(java.util.List<Source> metadata) {
|
||||
if (isPublished()) {
|
||||
throw new IllegalStateException("Cannot set Metadata. Endpoint is already published");
|
||||
}
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public Executor getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public Map<String, Object> getProperties() {
|
||||
return new HashMap<String, Object>(properties);
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, Object> map) {
|
||||
this.properties = new HashMap<String, Object>(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the permission of "publishEndpoint" before accessing HTTP classes.
|
||||
* Also it checks if there is an available HTTP server implementation.
|
||||
*/
|
||||
private void createEndpoint(String urlPattern) {
|
||||
// Checks permission for "publishEndpoint"
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(ENDPOINT_PUBLISH_PERMISSION);
|
||||
}
|
||||
|
||||
// See if HttpServer implementation is available
|
||||
try {
|
||||
Class.forName("com.sun.net.httpserver.HttpServer");
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("Couldn't load light weight http server", e);
|
||||
}
|
||||
container = getContainer();
|
||||
MetadataReader metadataReader = EndpointFactory.getExternalMetadatReader(implClass, binding);
|
||||
WSEndpoint wse = WSEndpoint.create(
|
||||
implClass, true,
|
||||
invoker,
|
||||
getProperty(QName.class, Endpoint.WSDL_SERVICE),
|
||||
getProperty(QName.class, Endpoint.WSDL_PORT),
|
||||
container,
|
||||
binding,
|
||||
getPrimaryWsdl(metadataReader),
|
||||
buildDocList(),
|
||||
(EntityResolver) null,
|
||||
false
|
||||
);
|
||||
// Don't load HttpEndpoint class before as it may load HttpServer classes
|
||||
actualEndpoint = new HttpEndpoint(executor, getAdapter(wse, urlPattern));
|
||||
}
|
||||
|
||||
private <T> T getProperty(Class<T> type, String key) {
|
||||
Object o = properties.get(key);
|
||||
if (o == null) return null;
|
||||
if (type.isInstance(o))
|
||||
return type.cast(o);
|
||||
else
|
||||
throw new IllegalArgumentException("Property " + key + " has to be of type " + type); // i18n
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert metadata sources using identity transform. So that we can
|
||||
* reuse the Source object multiple times.
|
||||
*/
|
||||
private List<SDDocumentSource> buildDocList() {
|
||||
List<SDDocumentSource> r = new ArrayList<SDDocumentSource>();
|
||||
|
||||
if (metadata != null) {
|
||||
for (Source source : metadata) {
|
||||
try {
|
||||
XMLStreamBufferResult xsbr = XmlUtil.identityTransform(source, new XMLStreamBufferResult());
|
||||
String systemId = source.getSystemId();
|
||||
|
||||
r.add(SDDocumentSource.create(new URL(systemId), xsbr.getXMLStreamBuffer()));
|
||||
} catch (TransformerException te) {
|
||||
throw new ServerRtException("server.rt.err", te);
|
||||
} catch (IOException te) {
|
||||
throw new ServerRtException("server.rt.err", te);
|
||||
} catch (SAXException e) {
|
||||
throw new ServerRtException("server.rt.err", e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new ServerRtException("server.rt.err", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets wsdl from @WebService or @WebServiceProvider
|
||||
*/
|
||||
private @Nullable SDDocumentSource getPrimaryWsdl(MetadataReader metadataReader) {
|
||||
// Takes care of @WebService, @WebServiceProvider's wsdlLocation
|
||||
EndpointFactory.verifyImplementorClass(implClass, metadataReader);
|
||||
String wsdlLocation = EndpointFactory.getWsdlLocation(implClass, metadataReader);
|
||||
if (wsdlLocation != null) {
|
||||
ClassLoader cl = implClass.getClassLoader();
|
||||
URL url = cl.getResource(wsdlLocation);
|
||||
if (url != null) {
|
||||
return SDDocumentSource.create(url);
|
||||
}
|
||||
throw new ServerRtException("cannot.load.wsdl", wsdlLocation);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void canPublish() {
|
||||
if (isPublished()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot publish this endpoint. Endpoint has been already published.");
|
||||
}
|
||||
if (stopped) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot publish this endpoint. Endpoint has been already stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
public EndpointReference getEndpointReference(Element...referenceParameters) {
|
||||
return getEndpointReference(W3CEndpointReference.class, referenceParameters);
|
||||
}
|
||||
|
||||
public <T extends EndpointReference> T getEndpointReference(Class<T> clazz, Element...referenceParameters) {
|
||||
if (!isPublished()) {
|
||||
throw new WebServiceException("Endpoint is not published yet");
|
||||
}
|
||||
return ((HttpEndpoint)actualEndpoint).getEndpointReference(clazz,referenceParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEndpointContext(EndpointContext ctxt) {
|
||||
this.endpointContext = ctxt;
|
||||
}
|
||||
|
||||
private HttpAdapter getAdapter(WSEndpoint endpoint, String urlPattern) {
|
||||
HttpAdapterList adapterList = null;
|
||||
if (endpointContext != null) {
|
||||
if (endpointContext instanceof Component) {
|
||||
adapterList = ((Component) endpointContext).getSPI(HttpAdapterList.class);
|
||||
}
|
||||
|
||||
if (adapterList == null) {
|
||||
for(Endpoint e : endpointContext.getEndpoints()) {
|
||||
if (e.isPublished() && e != this) {
|
||||
adapterList = ((HttpEndpoint)(((EndpointImpl)e).actualEndpoint)).getAdapterOwner();
|
||||
assert adapterList != null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (adapterList == null) {
|
||||
adapterList = new ServerAdapterList();
|
||||
}
|
||||
return adapterList.createAdapter("", urlPattern, endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoints within a EndpointContext get the same container.
|
||||
*/
|
||||
private Container getContainer() {
|
||||
if (endpointContext != null) {
|
||||
if (endpointContext instanceof Component) {
|
||||
Container c = ((Component) endpointContext).getSPI(Container.class);
|
||||
if (c != null)
|
||||
return c;
|
||||
}
|
||||
|
||||
for(Endpoint e : endpointContext.getEndpoints()) {
|
||||
if (e.isPublished() && e != this) {
|
||||
return ((EndpointImpl)e).container;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ServerContainer();
|
||||
}
|
||||
|
||||
private static class InvokerImpl extends Invoker {
|
||||
private javax.xml.ws.spi.Invoker spiInvoker;
|
||||
|
||||
InvokerImpl(javax.xml.ws.spi.Invoker spiInvoker) {
|
||||
this.spiInvoker = spiInvoker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NotNull WSWebServiceContext wsc, @NotNull WSEndpoint endpoint) {
|
||||
try {
|
||||
spiInvoker.inject(wsc);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new WebServiceException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new WebServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object invoke(@NotNull Packet p, @NotNull Method m, @NotNull Object... args) throws InvocationTargetException, IllegalAccessException {
|
||||
return spiInvoker.invoke(m, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.transport.http.server;
|
||||
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapterList;
|
||||
import com.sun.xml.internal.ws.server.ServerRtException;
|
||||
import com.sun.xml.internal.ws.resources.ServerMessages;
|
||||
|
||||
import javax.xml.ws.EndpointReference;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Hides {@link HttpContext} so that {@link EndpointImpl}
|
||||
* may load even without {@link HttpContext}.
|
||||
*
|
||||
* TODO: But what's the point? If Light-weight HTTP server isn't present,
|
||||
* all the publish operations will fail way. Why is it better to defer
|
||||
* the failure, as opposed to cause the failure as earyl as possible? -KK
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public final class HttpEndpoint extends com.sun.xml.internal.ws.api.server.HttpEndpoint {
|
||||
private String address;
|
||||
private HttpContext httpContext;
|
||||
private final HttpAdapter adapter;
|
||||
private final Executor executor;
|
||||
|
||||
public HttpEndpoint(Executor executor, HttpAdapter adapter) {
|
||||
this.executor = executor;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public void publish(String address) {
|
||||
this.address = address;
|
||||
httpContext = ServerMgr.getInstance().createContext(address);
|
||||
publish(httpContext);
|
||||
}
|
||||
|
||||
public void publish(Object serverContext) {
|
||||
if (serverContext instanceof javax.xml.ws.spi.http.HttpContext) {
|
||||
setHandler((javax.xml.ws.spi.http.HttpContext)serverContext);
|
||||
return;
|
||||
}
|
||||
if (serverContext instanceof HttpContext) {
|
||||
this.httpContext = (HttpContext)serverContext;
|
||||
setHandler(httpContext);
|
||||
return;
|
||||
}
|
||||
throw new ServerRtException(ServerMessages.NOT_KNOW_HTTP_CONTEXT_TYPE(
|
||||
serverContext.getClass(), HttpContext.class,
|
||||
javax.xml.ws.spi.http.HttpContext.class));
|
||||
}
|
||||
|
||||
HttpAdapterList getAdapterOwner() {
|
||||
return adapter.owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be called only after publish
|
||||
* @return address of the Endpoint
|
||||
*/
|
||||
private String getEPRAddress() {
|
||||
if (address == null)
|
||||
return httpContext.getServer().getAddress().toString();
|
||||
try {
|
||||
URL u = new URL(address);
|
||||
if (u.getPort() == 0) {
|
||||
return new URL(u.getProtocol(),u.getHost(),
|
||||
httpContext.getServer().getAddress().getPort(),u.getFile()).toString();
|
||||
}
|
||||
} catch (MalformedURLException murl) {}
|
||||
return address;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (httpContext != null) {
|
||||
if (address == null) {
|
||||
// Application created its own HttpContext
|
||||
// httpContext.setHandler(null);
|
||||
httpContext.getServer().removeContext(httpContext);
|
||||
} else {
|
||||
// Remove HttpContext created by JAXWS runtime
|
||||
ServerMgr.getInstance().removeContext(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke WebService Life cycle method
|
||||
adapter.getEndpoint().dispose();
|
||||
}
|
||||
|
||||
private void setHandler(HttpContext context) {
|
||||
context.setHandler(new WSHttpHandler(adapter, executor));
|
||||
}
|
||||
|
||||
private void setHandler(javax.xml.ws.spi.http.HttpContext context) {
|
||||
context.setHandler(new PortableHttpHandler(adapter, executor));
|
||||
}
|
||||
|
||||
public <T extends EndpointReference> T getEndpointReference(Class<T> clazz, Element...referenceParameters) {
|
||||
String eprAddress = getEPRAddress();
|
||||
return clazz.cast(adapter.getEndpoint().getEndpointReference(clazz, eprAddress,eprAddress+"?wsdl", referenceParameters));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
|
||||
import com.sun.xml.internal.ws.api.server.PortAddressResolver;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.transport.http.WSHTTPConnection;
|
||||
import com.sun.xml.internal.ws.developer.JAXWSProperties;
|
||||
import com.sun.xml.internal.ws.resources.WsservletMessages;
|
||||
|
||||
import javax.xml.ws.handler.MessageContext;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import javax.xml.ws.spi.http.HttpExchange;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link WSHTTPConnection} used with Java SE endpoints. It provides connection
|
||||
* implementation using {@link HttpExchange} object.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
final class PortableConnectionImpl extends WSHTTPConnection implements WebServiceContextDelegate {
|
||||
|
||||
private final HttpExchange httpExchange;
|
||||
private int status;
|
||||
private final HttpAdapter adapter;
|
||||
private boolean outputWritten;
|
||||
|
||||
public PortableConnectionImpl(@NotNull HttpAdapter adapter, @NotNull HttpExchange httpExchange) {
|
||||
this.adapter = adapter;
|
||||
this.httpExchange = httpExchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(value = {MessageContext.HTTP_REQUEST_HEADERS, Packet.INBOUND_TRANSPORT_HEADERS})
|
||||
public @NotNull Map<String,List<String>> getRequestHeaders() {
|
||||
return httpExchange.getRequestHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestHeader(String headerName) {
|
||||
return httpExchange.getRequestHeader(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResponseHeaders(Map<String,List<String>> headers) {
|
||||
Map<String, List<String>> r = httpExchange.getResponseHeaders();
|
||||
r.clear();
|
||||
for(Map.Entry <String, List<String>> entry : headers.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
List<String> values = entry.getValue();
|
||||
// ignore headers that interfere with our correct operations
|
||||
if (!name.equalsIgnoreCase("Content-Length") && !name.equalsIgnoreCase("Content-Type")) {
|
||||
r.put(name,new ArrayList<String>(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResponseHeader(String key, List<String> value) {
|
||||
httpExchange.getResponseHeaders().put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequestHeaderNames() {
|
||||
return httpExchange.getRequestHeaders().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequestHeaderValues(String headerName) {
|
||||
return httpExchange.getRequestHeaders().get(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property({MessageContext.HTTP_RESPONSE_HEADERS,Packet.OUTBOUND_TRANSPORT_HEADERS})
|
||||
public Map<String,List<String>> getResponseHeaders() {
|
||||
return httpExchange.getResponseHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentTypeResponseHeader(@NotNull String value) {
|
||||
httpExchange.addResponseHeader("Content-Type", value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.HTTP_RESPONSE_CODE)
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public @Override @NotNull InputStream getInput() throws IOException {
|
||||
return httpExchange.getRequestBody();
|
||||
}
|
||||
|
||||
public @Override @NotNull OutputStream getOutput() throws IOException {
|
||||
assert !outputWritten;
|
||||
outputWritten = true;
|
||||
|
||||
httpExchange.setStatus(getStatus());
|
||||
return httpExchange.getResponseBody();
|
||||
}
|
||||
|
||||
public @Override @NotNull WebServiceContextDelegate getWebServiceContextDelegate() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal(Packet request) {
|
||||
return httpExchange.getUserPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(Packet request, String role) {
|
||||
return httpExchange.isUserInRole(role);
|
||||
}
|
||||
|
||||
public @Override @NotNull String getEPRAddress(Packet request, WSEndpoint endpoint) {
|
||||
PortAddressResolver resolver = adapter.owner.createPortAddressResolver(getBaseAddress(), endpoint.getImplementationClass());
|
||||
String address = resolver.getAddressFor(endpoint.getServiceName(), endpoint.getPortName().getLocalPart());
|
||||
if(address==null) {
|
||||
throw new WebServiceException(WsservletMessages.SERVLET_NO_ADDRESS_AVAILABLE(endpoint.getPortName()));
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
@Property(MessageContext.SERVLET_CONTEXT)
|
||||
public Object getServletContext() {
|
||||
return httpExchange.getAttribute(MessageContext.SERVLET_CONTEXT);
|
||||
}
|
||||
|
||||
@Property(MessageContext.SERVLET_RESPONSE)
|
||||
public Object getServletResponse() {
|
||||
return httpExchange.getAttribute(MessageContext.SERVLET_RESPONSE);
|
||||
}
|
||||
|
||||
@Property(MessageContext.SERVLET_REQUEST)
|
||||
public Object getServletRequest() {
|
||||
return httpExchange.getAttribute(MessageContext.SERVLET_REQUEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWSDLAddress(@NotNull Packet request, @NotNull WSEndpoint endpoint) {
|
||||
String eprAddress = getEPRAddress(request,endpoint);
|
||||
if(adapter.getEndpoint().getPort() != null) {
|
||||
return eprAddress+"?wsdl";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return httpExchange.getScheme().equals("https");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.HTTP_REQUEST_METHOD)
|
||||
public @NotNull String getRequestMethod() {
|
||||
return httpExchange.getRequestMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.QUERY_STRING)
|
||||
public String getQueryString() {
|
||||
return httpExchange.getQueryString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.PATH_INFO)
|
||||
public String getPathInfo() {
|
||||
return httpExchange.getPathInfo();
|
||||
}
|
||||
|
||||
@Property(JAXWSProperties.HTTP_EXCHANGE)
|
||||
public HttpExchange getExchange() {
|
||||
return httpExchange;
|
||||
}
|
||||
|
||||
@Override @NotNull
|
||||
public String getBaseAddress() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(httpExchange.getScheme());
|
||||
sb.append("://");
|
||||
sb.append(httpExchange.getLocalAddress().getHostName());
|
||||
sb.append(":");
|
||||
sb.append(httpExchange.getLocalAddress().getPort());
|
||||
sb.append(httpExchange.getContextPath());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return httpExchange.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLengthResponseHeader(int value) {
|
||||
httpExchange.addResponseHeader("Content-Length", ""+value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return httpExchange.getRequestURI().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestScheme() {
|
||||
return httpExchange.getScheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return httpExchange.getLocalAddress().getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getServerPort() {
|
||||
return httpExchange.getLocalAddress().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyMap getPropertyMap() {
|
||||
return model;
|
||||
}
|
||||
|
||||
private static final PropertyMap model;
|
||||
|
||||
static {
|
||||
model = parse(PortableConnectionImpl.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.xml.internal.ws.resources.HttpserverMessages;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.transport.http.WSHTTPConnection;
|
||||
|
||||
import javax.xml.ws.spi.http.HttpHandler;
|
||||
import javax.xml.ws.spi.http.HttpExchange;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} implementation that serves the actual request.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
final class PortableHttpHandler extends HttpHandler {
|
||||
|
||||
private static final String GET_METHOD = "GET";
|
||||
private static final String POST_METHOD = "POST";
|
||||
private static final String HEAD_METHOD = "HEAD";
|
||||
private static final String PUT_METHOD = "PUT";
|
||||
private static final String DELETE_METHOD = "DELETE";
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(
|
||||
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.http");
|
||||
|
||||
private final HttpAdapter adapter;
|
||||
private final Executor executor;
|
||||
|
||||
public PortableHttpHandler(@NotNull HttpAdapter adapter, @Nullable Executor executor) {
|
||||
assert adapter!=null;
|
||||
this.adapter = adapter;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by HttpServer when there is a matching request for the context
|
||||
*/
|
||||
@Override
|
||||
public void handle(HttpExchange msg) {
|
||||
try {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.log(Level.FINE, "Received HTTP request:{0}", msg.getRequestURI());
|
||||
}
|
||||
if (executor != null) {
|
||||
// Use application's Executor to handle request. Application may
|
||||
// have set an executor using Endpoint.setExecutor().
|
||||
executor.execute(new HttpHandlerRunnable(msg));
|
||||
} else {
|
||||
handleExchange(msg);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Dont't propagate the exception otherwise it kills the httpserver
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleExchange(HttpExchange msg) throws IOException {
|
||||
WSHTTPConnection con = new PortableConnectionImpl(adapter,msg);
|
||||
try {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.log(Level.FINE, "Received HTTP request:{0}", msg.getRequestURI());
|
||||
}
|
||||
String method = msg.getRequestMethod();
|
||||
if(method.equals(GET_METHOD) || method.equals(POST_METHOD) || method.equals(HEAD_METHOD)
|
||||
|| method.equals(PUT_METHOD) || method.equals(DELETE_METHOD)) {
|
||||
adapter.handle(con);
|
||||
} else {
|
||||
logger.warning(HttpserverMessages.UNEXPECTED_HTTP_METHOD(method));
|
||||
}
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the processing of request in a Runnable so that it can be
|
||||
* executed in Executor.
|
||||
*/
|
||||
class HttpHandlerRunnable implements Runnable {
|
||||
final HttpExchange msg;
|
||||
|
||||
HttpHandlerRunnable(HttpExchange msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("CallToThreadDumpStack")
|
||||
public void run() {
|
||||
try {
|
||||
handleExchange(msg);
|
||||
} catch (Throwable e) {
|
||||
// Does application's executor handle this exception ?
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.server.BoundEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.Module;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.WebModule;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link HttpAdapter} for Endpoint API.
|
||||
*
|
||||
* <p>
|
||||
* This is a thin wrapper around {@link HttpAdapter}
|
||||
* with some description specified in the deployment (in particular those
|
||||
* information are related to how a request is routed to a {@link ServerAdapter}.
|
||||
*
|
||||
* <p>
|
||||
* This class implements {@link BoundEndpoint} and represent the
|
||||
* server-{@link WSEndpoint} association for Endpoint API's transport
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
public final class ServerAdapter extends HttpAdapter implements BoundEndpoint {
|
||||
final String name;
|
||||
|
||||
protected ServerAdapter(String name, String urlPattern, WSEndpoint endpoint, ServerAdapterList owner) {
|
||||
super(endpoint, owner, urlPattern);
|
||||
this.name = name;
|
||||
// registers itself with the container
|
||||
Module module = endpoint.getContainer().getSPI(Module.class);
|
||||
if(module==null)
|
||||
LOGGER.log(Level.WARNING, "Container {0} doesn''t support {1}",
|
||||
new Object[]{endpoint.getContainer(), Module.class});
|
||||
else {
|
||||
module.getBoundEndpoints().add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the endpoint as given in the <tt>sun-jaxws.xml</tt>
|
||||
* deployment descriptor.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull URI getAddress() {
|
||||
WebModule webModule = endpoint.getContainer().getSPI(WebModule.class);
|
||||
if(webModule==null)
|
||||
// this is really a bug in the container implementation
|
||||
throw new WebServiceException("Container "+endpoint.getContainer()+" doesn't support "+WebModule.class);
|
||||
|
||||
return getAddress(webModule.getContextPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull URI getAddress(String baseAddress) {
|
||||
String adrs = baseAddress+getValidPath();
|
||||
try {
|
||||
return new URI(adrs);
|
||||
} catch (URISyntaxException e) {
|
||||
// this is really a bug in the container implementation
|
||||
throw new WebServiceException("Unable to compute address for "+endpoint,e);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
endpoint.dispose();
|
||||
}
|
||||
|
||||
public String getUrlPattern() {
|
||||
return urlPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()+"[name="+name+']';
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ServerAdapter.class.getName());
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 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.transport.http.server;
|
||||
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapterList;
|
||||
|
||||
public class ServerAdapterList extends HttpAdapterList<ServerAdapter> {
|
||||
@Override
|
||||
protected ServerAdapter createHttpAdapter(String name, String urlPattern, WSEndpoint<?> endpoint) {
|
||||
return new ServerAdapter(name, urlPattern, endpoint, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpsExchange;
|
||||
import com.sun.xml.internal.ws.api.message.Packet;
|
||||
import com.sun.xml.internal.ws.api.server.WSEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
|
||||
import com.sun.xml.internal.ws.api.server.PortAddressResolver;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.transport.http.WSHTTPConnection;
|
||||
import com.sun.xml.internal.ws.developer.JAXWSProperties;
|
||||
import com.sun.xml.internal.ws.resources.WsservletMessages;
|
||||
import com.sun.xml.internal.ws.util.ReadAllStream;
|
||||
|
||||
import javax.xml.ws.handler.MessageContext;
|
||||
import javax.xml.ws.WebServiceException;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* {@link WSHTTPConnection} used with Java SE endpoints. It provides connection
|
||||
* implementation using {@link HttpExchange} object.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
final class ServerConnectionImpl extends WSHTTPConnection implements WebServiceContextDelegate {
|
||||
|
||||
private final HttpExchange httpExchange;
|
||||
private int status;
|
||||
private final HttpAdapter adapter;
|
||||
private LWHSInputStream in;
|
||||
private OutputStream out;
|
||||
|
||||
|
||||
public ServerConnectionImpl(@NotNull HttpAdapter adapter, @NotNull HttpExchange httpExchange) {
|
||||
this.adapter = adapter;
|
||||
this.httpExchange = httpExchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(value = {MessageContext.HTTP_REQUEST_HEADERS, Packet.INBOUND_TRANSPORT_HEADERS})
|
||||
public @NotNull Map<String,List<String>> getRequestHeaders() {
|
||||
return httpExchange.getRequestHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestHeader(String headerName) {
|
||||
return httpExchange.getRequestHeaders().getFirst(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResponseHeaders(Map<String,List<String>> headers) {
|
||||
Headers r = httpExchange.getResponseHeaders();
|
||||
r.clear();
|
||||
for(Map.Entry <String, List<String>> entry : headers.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
List<String> values = entry.getValue();
|
||||
// ignore headers that interfere with our correct operations
|
||||
if (!"Content-Length".equalsIgnoreCase(name) && !"Content-Type".equalsIgnoreCase(name)) {
|
||||
r.put(name,new ArrayList<String>(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResponseHeader(String key, List<String> value) {
|
||||
httpExchange.getResponseHeaders().put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequestHeaderNames() {
|
||||
return httpExchange.getRequestHeaders().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequestHeaderValues(String headerName) {
|
||||
return httpExchange.getRequestHeaders().get(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property({MessageContext.HTTP_RESPONSE_HEADERS,Packet.OUTBOUND_TRANSPORT_HEADERS})
|
||||
public Map<String,List<String>> getResponseHeaders() {
|
||||
return httpExchange.getResponseHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentTypeResponseHeader(@NotNull String value) {
|
||||
httpExchange.getResponseHeaders().set("Content-Type",value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.HTTP_RESPONSE_CODE)
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public @NotNull InputStream getInput() {
|
||||
if (in == null) {
|
||||
in = new LWHSInputStream(httpExchange.getRequestBody());
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
// Light weight http server's InputStream.close() throws exception if
|
||||
// all the bytes are not read. Work around until it is fixed.
|
||||
private static class LWHSInputStream extends FilterInputStream {
|
||||
// Workaround for "SJSXP XMLStreamReader.next() closes stream".
|
||||
boolean closed;
|
||||
boolean readAll;
|
||||
|
||||
LWHSInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
void readAll() throws IOException {
|
||||
if (!closed && !readAll) {
|
||||
ReadAllStream all = new ReadAllStream();
|
||||
all.readAll(in, 4000000);
|
||||
in.close();
|
||||
in = all;
|
||||
readAll = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!closed) {
|
||||
readAll();
|
||||
super.close();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public @NotNull OutputStream getOutput() throws IOException {
|
||||
if (out == null) {
|
||||
String lenHeader = httpExchange.getResponseHeaders().getFirst("Content-Length");
|
||||
int length = (lenHeader != null) ? Integer.parseInt(lenHeader) : 0;
|
||||
httpExchange.sendResponseHeaders(getStatus(), length);
|
||||
|
||||
// Light weight http server's OutputStream.close() throws exception if
|
||||
// all the bytes are not read on the client side(StreamMessage on the client
|
||||
// side doesn't read all bytes.
|
||||
out = new FilterOutputStream(httpExchange.getResponseBody()) {
|
||||
boolean closed;
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
// lwhs closes input stream, when you close the output stream
|
||||
// This causes problems for streaming in one-way cases
|
||||
in.readAll();
|
||||
try {
|
||||
super.close();
|
||||
} catch(IOException ioe) {
|
||||
// Ignoring purposefully.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, FilterOutpuStream writes byte by byte
|
||||
@Override
|
||||
public void write(byte[] buf, int start, int len) throws IOException {
|
||||
out.write(buf, start, len);
|
||||
}
|
||||
};
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public @NotNull WebServiceContextDelegate getWebServiceContextDelegate() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Principal getUserPrincipal(Packet request) {
|
||||
return httpExchange.getPrincipal();
|
||||
}
|
||||
|
||||
public boolean isUserInRole(Packet request, String role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public @NotNull String getEPRAddress(Packet request, WSEndpoint endpoint) {
|
||||
//return WSHttpHandler.getRequestAddress(httpExchange);
|
||||
|
||||
PortAddressResolver resolver = adapter.owner.createPortAddressResolver(getBaseAddress(), endpoint.getImplementationClass());
|
||||
String address = resolver.getAddressFor(endpoint.getServiceName(), endpoint.getPortName().getLocalPart());
|
||||
if(address==null)
|
||||
throw new WebServiceException(WsservletMessages.SERVLET_NO_ADDRESS_AVAILABLE(endpoint.getPortName()));
|
||||
return address;
|
||||
|
||||
}
|
||||
|
||||
public String getWSDLAddress(@NotNull Packet request, @NotNull WSEndpoint endpoint) {
|
||||
String eprAddress = getEPRAddress(request,endpoint);
|
||||
if(adapter.getEndpoint().getPort() != null)
|
||||
return eprAddress+"?wsdl";
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return (httpExchange instanceof HttpsExchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.HTTP_REQUEST_METHOD)
|
||||
public @NotNull String getRequestMethod() {
|
||||
return httpExchange.getRequestMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.QUERY_STRING)
|
||||
public String getQueryString() {
|
||||
URI requestUri = httpExchange.getRequestURI();
|
||||
String query = requestUri.getQuery();
|
||||
if (query != null)
|
||||
return query;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Property(MessageContext.PATH_INFO)
|
||||
public String getPathInfo() {
|
||||
URI requestUri = httpExchange.getRequestURI();
|
||||
String reqPath = requestUri.getPath();
|
||||
String ctxtPath = httpExchange.getHttpContext().getPath();
|
||||
if (reqPath.length() > ctxtPath.length()) {
|
||||
return reqPath.substring(ctxtPath.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Property(JAXWSProperties.HTTP_EXCHANGE)
|
||||
public HttpExchange getExchange() {
|
||||
return httpExchange;
|
||||
}
|
||||
|
||||
@Override @NotNull
|
||||
public String getBaseAddress() {
|
||||
/*
|
||||
* Computes the Endpoint's address from the request. Use "Host" header
|
||||
* so that it has correct address(IP address or someother hostname)
|
||||
* through which the application reached the endpoint.
|
||||
*
|
||||
*/
|
||||
StringBuilder strBuf = new StringBuilder();
|
||||
strBuf.append((httpExchange instanceof HttpsExchange) ? "https" : "http");
|
||||
strBuf.append("://");
|
||||
|
||||
String hostHeader = httpExchange.getRequestHeaders().getFirst("Host");
|
||||
if (hostHeader != null) {
|
||||
strBuf.append(hostHeader); // Uses Host header
|
||||
} else {
|
||||
strBuf.append(httpExchange.getLocalAddress().getHostName());
|
||||
strBuf.append(":");
|
||||
strBuf.append(httpExchange.getLocalAddress().getPort());
|
||||
}
|
||||
//Do not include URL pattern here
|
||||
//strBuf.append(httpExchange.getRequestURI().getPath());
|
||||
|
||||
return strBuf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return httpExchange.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentLengthResponseHeader(int value) {
|
||||
httpExchange.getResponseHeaders().set("Content-Length", ""+value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return httpExchange.getRequestURI().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestScheme() {
|
||||
return (httpExchange instanceof HttpsExchange) ? "https" : "http";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return httpExchange.getLocalAddress().getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getServerPort() {
|
||||
return httpExchange.getLocalAddress().getPort();
|
||||
}
|
||||
|
||||
protected PropertyMap getPropertyMap() {
|
||||
return model;
|
||||
}
|
||||
|
||||
private static final PropertyMap model;
|
||||
|
||||
static {
|
||||
model = parse(ServerConnectionImpl.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.xml.internal.ws.api.server.BoundEndpoint;
|
||||
import com.sun.xml.internal.ws.api.server.Container;
|
||||
import com.sun.xml.internal.ws.api.server.Module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
class ServerContainer extends Container {
|
||||
|
||||
private final Module module = new Module() {
|
||||
private final List<BoundEndpoint> endpoints = new ArrayList<BoundEndpoint>();
|
||||
|
||||
public @NotNull List<BoundEndpoint> getBoundEndpoints() {
|
||||
return endpoints;
|
||||
}
|
||||
};
|
||||
|
||||
public <T> T getSPI(Class<T> spiType) {
|
||||
T t = super.getSPI(spiType);
|
||||
if (t != null)
|
||||
return t;
|
||||
if (spiType == Module.class) {
|
||||
return spiType.cast(module);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2016, 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.transport.http.server;
|
||||
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.xml.internal.ws.server.ServerRtException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Manages all the WebService HTTP servers created by JAXWS runtime.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
*/
|
||||
final class ServerMgr {
|
||||
|
||||
private static final ServerMgr serverMgr = new ServerMgr();
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(
|
||||
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.http");
|
||||
private final Map<InetSocketAddress,ServerState> servers = new HashMap<InetSocketAddress,ServerState>();
|
||||
|
||||
private ServerMgr() {}
|
||||
|
||||
/**
|
||||
* Gets the singleton instance.
|
||||
* @return manager instance
|
||||
*/
|
||||
static ServerMgr getInstance() {
|
||||
return serverMgr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a HttpContext at the given address. If there is already a server
|
||||
* it uses that server to create a context. Otherwise, it creates a new
|
||||
* HTTP server. This sever is added to servers Map.
|
||||
*/
|
||||
/*package*/ HttpContext createContext(String address) {
|
||||
try {
|
||||
HttpServer server;
|
||||
ServerState state;
|
||||
URL url = new URL(address);
|
||||
int port = url.getPort();
|
||||
if (port == -1) {
|
||||
port = url.getDefaultPort();
|
||||
}
|
||||
InetSocketAddress inetAddress = new InetSocketAddress(url.getHost(),
|
||||
port);
|
||||
synchronized(servers) {
|
||||
state = servers.get(inetAddress);
|
||||
if (state == null) {
|
||||
final int finalPortNum = port;
|
||||
for (ServerState s: servers.values()) {
|
||||
if (s.getServer()
|
||||
.getAddress()
|
||||
.getPort() == finalPortNum) {
|
||||
state = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inetAddress.getAddress().isAnyLocalAddress() ||
|
||||
state == null) {
|
||||
logger.fine("Creating new HTTP Server at "+inetAddress);
|
||||
// Creates server with default socket backlog
|
||||
server = HttpServer.create(inetAddress, 0);
|
||||
server.setExecutor(Executors.newCachedThreadPool());
|
||||
String path = url.toURI().getPath();
|
||||
logger.fine("Creating HTTP Context at = "+path);
|
||||
HttpContext context = server.createContext(path);
|
||||
server.start();
|
||||
|
||||
// we have to get actual inetAddress from server, which can differ from the original in some cases.
|
||||
// e.g. A port number of zero will let the system pick up an ephemeral port in a bind operation,
|
||||
// or IP: 0.0.0.0 - which is used to monitor network traffic from any valid IP address
|
||||
inetAddress = server.getAddress();
|
||||
|
||||
logger.fine("HTTP server started = "+inetAddress);
|
||||
state = new ServerState(server, path);
|
||||
servers.put(inetAddress, state);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
server = state.getServer();
|
||||
|
||||
if (state.getPaths().contains(url.getPath())) {
|
||||
String err = "Context with URL path "+url.getPath()+ " already exists on the server "+server.getAddress();
|
||||
logger.fine(err);
|
||||
throw new IllegalArgumentException(err);
|
||||
}
|
||||
|
||||
logger.fine("Creating HTTP Context at = "+url.getPath());
|
||||
HttpContext context = server.createContext(url.getPath());
|
||||
state.oneMoreContext(url.getPath());
|
||||
return context;
|
||||
} catch(Exception e) {
|
||||
throw new ServerRtException("server.rt.err",e );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a context. If the server doesn't have anymore contexts, it
|
||||
* would stop the server and server is removed from servers Map.
|
||||
*/
|
||||
/*package*/ void removeContext(HttpContext context) {
|
||||
InetSocketAddress inetAddress = context.getServer().getAddress();
|
||||
synchronized(servers) {
|
||||
ServerState state = servers.get(inetAddress);
|
||||
int instances = state.noOfContexts();
|
||||
if (instances < 2) {
|
||||
((ExecutorService)state.getServer().getExecutor()).shutdown();
|
||||
state.getServer().stop(0);
|
||||
servers.remove(inetAddress);
|
||||
} else {
|
||||
state.getServer().removeContext(context);
|
||||
state.oneLessContext(context.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ServerState {
|
||||
private final HttpServer server;
|
||||
private int instances;
|
||||
private Set<String> paths = new HashSet<String>();
|
||||
|
||||
ServerState(HttpServer server, String path) {
|
||||
this.server = server;
|
||||
this.instances = 1;
|
||||
paths.add(path);
|
||||
}
|
||||
|
||||
public HttpServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void oneMoreContext(String path) {
|
||||
++instances;
|
||||
paths.add(path);
|
||||
}
|
||||
|
||||
public void oneLessContext(String path) {
|
||||
--instances;
|
||||
paths.remove(path);
|
||||
}
|
||||
|
||||
public int noOfContexts() {
|
||||
return instances;
|
||||
}
|
||||
|
||||
public Set<String> getPaths() {
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2013, 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.transport.http.server;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.xml.internal.ws.resources.HttpserverMessages;
|
||||
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
|
||||
import com.sun.xml.internal.ws.transport.http.WSHTTPConnection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} implementation that serves the actual request.
|
||||
*
|
||||
* @author Jitendra Kotamraju
|
||||
* @author Kohsuke Kawaguhi
|
||||
*/
|
||||
final class WSHttpHandler implements HttpHandler {
|
||||
|
||||
private static final String GET_METHOD = "GET";
|
||||
private static final String POST_METHOD = "POST";
|
||||
private static final String HEAD_METHOD = "HEAD";
|
||||
private static final String PUT_METHOD = "PUT";
|
||||
private static final String DELETE_METHOD = "DELETE";
|
||||
|
||||
private static final Logger LOGGER =
|
||||
Logger.getLogger(
|
||||
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.http");
|
||||
private static final boolean fineTraceEnabled = LOGGER.isLoggable(Level.FINE);
|
||||
|
||||
private final HttpAdapter adapter;
|
||||
private final Executor executor;
|
||||
|
||||
public WSHttpHandler(@NotNull HttpAdapter adapter, @Nullable Executor executor) {
|
||||
assert adapter!=null;
|
||||
this.adapter = adapter;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by HttpServer when there is a matching request for the context
|
||||
*/
|
||||
public void handle(HttpExchange msg) {
|
||||
try {
|
||||
if (fineTraceEnabled) {
|
||||
LOGGER.log(Level.FINE, "Received HTTP request:{0}", msg.getRequestURI());
|
||||
}
|
||||
if (executor != null) {
|
||||
// Use application's Executor to handle request. Application may
|
||||
// have set an executor using Endpoint.setExecutor().
|
||||
executor.execute(new HttpHandlerRunnable(msg));
|
||||
} else {
|
||||
handleExchange(msg);
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
// Dont't propagate the exception otherwise it kills the httpserver
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExchange(HttpExchange msg) throws IOException {
|
||||
WSHTTPConnection con = new ServerConnectionImpl(adapter,msg);
|
||||
try {
|
||||
if (fineTraceEnabled) {
|
||||
LOGGER.log(Level.FINE, "Received HTTP request:{0}", msg.getRequestURI());
|
||||
}
|
||||
String method = msg.getRequestMethod();
|
||||
if(method.equals(GET_METHOD) || method.equals(POST_METHOD) || method.equals(HEAD_METHOD)
|
||||
|| method.equals(PUT_METHOD) || method.equals(DELETE_METHOD)) {
|
||||
adapter.handle(con);
|
||||
} else {
|
||||
if (LOGGER.isLoggable(Level.WARNING)) {
|
||||
LOGGER.warning(HttpserverMessages.UNEXPECTED_HTTP_METHOD(method));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
msg.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the processing of request in a Runnable so that it can be
|
||||
* executed in Executor.
|
||||
*/
|
||||
class HttpHandlerRunnable implements Runnable {
|
||||
final HttpExchange msg;
|
||||
|
||||
HttpHandlerRunnable(HttpExchange msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
handleExchange(msg);
|
||||
} catch (Throwable e) {
|
||||
// Does application's executor handle this exception ?
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user