feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -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);
}
}

View 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}&lt;{@link String},
* {@link List}&lt;{@link String}&gt;&gt;.
* The keys are case-insensitive Strings representing the header names and
* the value associated with each key is a {@link List}&lt;{@link String}&gt; 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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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&amp;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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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) {
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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());
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}
}