blob: ae5bf29bb1b2998e497341285b3921eaabe0df3b [file] [log] [blame]
/*
* Copyright (c) 1997, 2014, 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.server;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;
import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
import com.sun.xml.internal.ws.api.BindingID;
import com.sun.xml.internal.ws.api.WSBinding;
import com.sun.xml.internal.ws.api.WSFeatureList;
import com.sun.xml.internal.ws.api.databinding.DatabindingConfig;
import com.sun.xml.internal.ws.api.databinding.DatabindingFactory;
import com.sun.xml.internal.ws.api.databinding.MetadataReader;
import com.sun.xml.internal.ws.api.databinding.WSDLGenInfo;
import com.sun.xml.internal.ws.api.model.SEIModel;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLModel;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLService;
import com.sun.xml.internal.ws.api.policy.PolicyResolver;
import com.sun.xml.internal.ws.api.policy.PolicyResolverFactory;
import com.sun.xml.internal.ws.api.server.AsyncProvider;
import com.sun.xml.internal.ws.api.server.Container;
import com.sun.xml.internal.ws.api.server.ContainerResolver;
import com.sun.xml.internal.ws.api.server.InstanceResolver;
import com.sun.xml.internal.ws.api.server.Invoker;
import com.sun.xml.internal.ws.api.server.SDDocument;
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.api.wsdl.parser.WSDLParserExtension;
import com.sun.xml.internal.ws.api.wsdl.parser.XMLEntityResolver;
import com.sun.xml.internal.ws.api.wsdl.parser.XMLEntityResolver.Parser;
import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGeneratorExtension;
import com.sun.xml.internal.ws.binding.BindingImpl;
import com.sun.xml.internal.ws.binding.SOAPBindingImpl;
import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
import com.sun.xml.internal.ws.model.AbstractSEIModelImpl;
import com.sun.xml.internal.ws.model.ReflectAnnotationReader;
import com.sun.xml.internal.ws.model.RuntimeModeler;
import com.sun.xml.internal.ws.model.SOAPSEIModel;
import com.sun.xml.internal.ws.policy.PolicyMap;
import com.sun.xml.internal.ws.policy.jaxws.PolicyUtil;
import com.sun.xml.internal.ws.resources.ServerMessages;
import com.sun.xml.internal.ws.server.provider.ProviderInvokerTube;
import com.sun.xml.internal.ws.server.sei.SEIInvokerTube;
import com.sun.xml.internal.ws.util.HandlerAnnotationInfo;
import com.sun.xml.internal.ws.util.HandlerAnnotationProcessor;
import com.sun.xml.internal.ws.util.ServiceConfigurationError;
import com.sun.xml.internal.ws.util.ServiceFinder;
import com.sun.xml.internal.ws.util.xml.XmlUtil;
import com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.soap.SOAPBinding;
import java.io.IOException;
import java.net.URL;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* Entry point to the JAX-WS RI server-side runtime.
*
* @author Kohsuke Kawaguchi
* @author Jitendra Kotamraju
*/
public class EndpointFactory {
private static final EndpointFactory instance = new EndpointFactory();
public static EndpointFactory getInstance() {
return instance;
}
/**
* Implements {@link WSEndpoint#create}.
*
* No need to take WebServiceContext implementation. When InvokerPipe is
* instantiated, it calls InstanceResolver to set up a WebServiceContext.
* We shall only take delegate to getUserPrincipal and isUserInRole from adapter.
*
* <p>
* Nobody else should be calling this method.
*/
public static <T> WSEndpoint<T> createEndpoint(
Class<T> implType, boolean processHandlerAnnotation, @Nullable Invoker invoker,
@Nullable QName serviceName, @Nullable QName portName,
@Nullable Container container, @Nullable WSBinding binding,
@Nullable SDDocumentSource primaryWsdl,
@Nullable Collection<? extends SDDocumentSource> metadata,
EntityResolver resolver, boolean isTransportSynchronous) {
return createEndpoint(implType, processHandlerAnnotation, invoker, serviceName,
portName, container, binding, primaryWsdl, metadata, resolver, isTransportSynchronous, true);
}
public static <T> WSEndpoint<T> createEndpoint(
Class<T> implType, boolean processHandlerAnnotation, @Nullable Invoker invoker,
@Nullable QName serviceName, @Nullable QName portName,
@Nullable Container container, @Nullable WSBinding binding,
@Nullable SDDocumentSource primaryWsdl,
@Nullable Collection<? extends SDDocumentSource> metadata,
EntityResolver resolver, boolean isTransportSynchronous, boolean isStandard) {
EndpointFactory factory = container != null ? container.getSPI(EndpointFactory.class) : null;
if (factory == null)
factory = EndpointFactory.getInstance();
return factory.create(
implType,processHandlerAnnotation, invoker,serviceName,portName,container,binding,primaryWsdl,metadata,resolver,isTransportSynchronous,isStandard);
}
/**
* Implements {@link WSEndpoint#create}.
*
* No need to take WebServiceContext implementation. When InvokerPipe is
* instantiated, it calls InstanceResolver to set up a WebServiceContext.
* We shall only take delegate to getUserPrincipal and isUserInRole from adapter.
*
* <p>
* Nobody else should be calling this method.
*/
public <T> WSEndpoint<T> create(
Class<T> implType, boolean processHandlerAnnotation, @Nullable Invoker invoker,
@Nullable QName serviceName, @Nullable QName portName,
@Nullable Container container, @Nullable WSBinding binding,
@Nullable SDDocumentSource primaryWsdl,
@Nullable Collection<? extends SDDocumentSource> metadata,
EntityResolver resolver, boolean isTransportSynchronous) {
return create(implType, processHandlerAnnotation, invoker, serviceName,
portName, container, binding, primaryWsdl, metadata, resolver, isTransportSynchronous,
true);
}
public <T> WSEndpoint<T> create(
Class<T> implType, boolean processHandlerAnnotation, @Nullable Invoker invoker,
@Nullable QName serviceName, @Nullable QName portName,
@Nullable Container container, @Nullable WSBinding binding,
@Nullable SDDocumentSource primaryWsdl,
@Nullable Collection<? extends SDDocumentSource> metadata,
EntityResolver resolver, boolean isTransportSynchronous, boolean isStandard) {
if(implType ==null)
throw new IllegalArgumentException();
MetadataReader metadataReader = getExternalMetadatReader(implType, binding);
if (isStandard) {
verifyImplementorClass(implType, metadataReader);
}
if (invoker == null) {
invoker = InstanceResolver.createDefault(implType).createInvoker();
}
// Performance analysis indicates that reading and parsing imported schemas is
// a major component of Endpoint creation time. Therefore, modify SDDocumentSource
// handling to delay iterating collection as long as possible.
Collection<SDDocumentSource> md = new CollectionCollection<SDDocumentSource>();
if(primaryWsdl!=null) {
if(metadata!=null) {
Iterator<? extends SDDocumentSource> it = metadata.iterator();
if (it.hasNext() && primaryWsdl.equals(it.next()))
md.addAll(metadata);
else {
md.add(primaryWsdl);
md.addAll(metadata);
}
} else
md.add(primaryWsdl);
} else if(metadata!=null)
md.addAll(metadata);
if(container==null)
container = ContainerResolver.getInstance().getContainer();
if(serviceName==null)
serviceName = getDefaultServiceName(implType, metadataReader);
if(portName==null)
portName = getDefaultPortName(serviceName,implType, metadataReader);
{// error check
String serviceNS = serviceName.getNamespaceURI();
String portNS = portName.getNamespaceURI();
if (!serviceNS.equals(portNS)) {
throw new ServerRtException("wrong.tns.for.port",portNS, serviceNS);
}
}
// setting a default binding
if (binding == null)
binding = BindingImpl.create(BindingID.parse(implType));
if ( isStandard && primaryWsdl != null) {
verifyPrimaryWSDL(primaryWsdl, serviceName);
}
QName portTypeName = null;
if (isStandard && implType.getAnnotation(WebServiceProvider.class)==null) {
portTypeName = RuntimeModeler.getPortTypeName(implType, metadataReader);
}
// Categorises the documents as WSDL, Schema etc
Collection<SDDocumentImpl> docList = categoriseMetadata(md.iterator(), serviceName, portTypeName);
// Finds the primary WSDL and makes sure that metadata doesn't have
// two concrete or abstract WSDLs
SDDocumentImpl primaryDoc = primaryWsdl != null ? SDDocumentImpl.create(primaryWsdl,serviceName,portTypeName) : findPrimary(docList);
EndpointAwareTube terminal;
WSDLPort wsdlPort = null;
AbstractSEIModelImpl seiModel = null;
// create WSDL model
if (primaryDoc != null) {
wsdlPort = getWSDLPort(primaryDoc, docList, serviceName, portName, container, resolver);
}
WebServiceFeatureList features=((BindingImpl)binding).getFeatures();
if (isStandard) {
features.parseAnnotations(implType);
}
PolicyMap policyMap = null;
// create terminal pipe that invokes the application
if (isUseProviderTube(implType, isStandard)) {
//TODO incase of Provider, provide a way to User for complete control of the message processing by giving
// ability to turn off the WSDL/Policy based features and its associated tubes.
//Even in case of Provider, merge all features configured via WSDL/Policy or deployment configuration
Iterable<WebServiceFeature> configFtrs;
if(wsdlPort != null) {
policyMap = wsdlPort.getOwner().getParent().getPolicyMap();
//Merge features from WSDL and other policy configuration
configFtrs = wsdlPort.getFeatures();
} else {
//No WSDL, so try to merge features from Policy configuration
policyMap = PolicyResolverFactory.create().resolve(
new PolicyResolver.ServerContext(null, container, implType, false));
configFtrs = PolicyUtil.getPortScopedFeatures(policyMap,serviceName,portName);
}
features.mergeFeatures(configFtrs, true);
terminal = createProviderInvokerTube(implType, binding, invoker, container);
} else {
// Create runtime model for non Provider endpoints
seiModel = createSEIModel(wsdlPort, implType, serviceName, portName, binding, primaryDoc);
if(binding instanceof SOAPBindingImpl){
//set portKnownHeaders on Binding, so that they can be used for MU processing
((SOAPBindingImpl)binding).setPortKnownHeaders(
((SOAPSEIModel)seiModel).getKnownHeaders());
}
// Generate WSDL for SEI endpoints(not for Provider endpoints)
if (primaryDoc == null) {
primaryDoc = generateWSDL(binding, seiModel, docList, container, implType);
// create WSDL model
wsdlPort = getWSDLPort(primaryDoc, docList, serviceName, portName, container, resolver);
seiModel.freeze(wsdlPort);
}
policyMap = wsdlPort.getOwner().getParent().getPolicyMap();
// New Features might have been added in WSDL through Policy.
//Merge features from WSDL and other policy configuration
// This sets only the wsdl features that are not already set(enabled/disabled)
features.mergeFeatures(wsdlPort.getFeatures(), true);
terminal = createSEIInvokerTube(seiModel,invoker,binding);
}
// Process @HandlerChain, if handler-chain is not set via Deployment Descriptor
if (processHandlerAnnotation) {
processHandlerAnnotation(binding, implType, serviceName, portName);
}
// Selects only required metadata for this endpoint from the passed-in metadata
if (primaryDoc != null) {
docList = findMetadataClosure(primaryDoc, docList, resolver);
}
ServiceDefinitionImpl serviceDefiniton = (primaryDoc != null) ? new ServiceDefinitionImpl(docList, primaryDoc) : null;
return create(serviceName, portName, binding, container, seiModel, wsdlPort, implType, serviceDefiniton,
terminal, isTransportSynchronous, policyMap);
}
protected <T> WSEndpoint<T> create(QName serviceName, QName portName, WSBinding binding, Container container, SEIModel seiModel, WSDLPort wsdlPort, Class<T> implType, ServiceDefinitionImpl serviceDefinition, EndpointAwareTube terminal, boolean isTransportSynchronous, PolicyMap policyMap) {
return new WSEndpointImpl<T>(serviceName, portName, binding, container, seiModel,
wsdlPort, implType, serviceDefinition, terminal, isTransportSynchronous, policyMap);
}
protected boolean isUseProviderTube(Class<?> implType, boolean isStandard) {
return !isStandard || implType.getAnnotation(WebServiceProvider.class)!=null;
}
protected EndpointAwareTube createSEIInvokerTube(AbstractSEIModelImpl seiModel, Invoker invoker, WSBinding binding) {
return new SEIInvokerTube(seiModel,invoker,binding);
}
protected <T> EndpointAwareTube createProviderInvokerTube(final Class<T> implType, final WSBinding binding,
final Invoker invoker, final Container container) {
return ProviderInvokerTube.create(implType, binding, invoker, container);
}
/**
* Goes through the original metadata documents and collects the required ones.
* This done traversing from primary WSDL and its imports until it builds a
* complete set of documents(transitive closure) for the endpoint.
*
* @param primaryDoc primary WSDL doc
* @param docList complete metadata
* @return new metadata that doesn't contain extraneous documents.
*/
private static Collection<SDDocumentImpl> findMetadataClosure(
final SDDocumentImpl primaryDoc, final Collection<SDDocumentImpl> docList, final EntityResolver resolver) {
return new AbstractCollection<SDDocumentImpl>() {
@Override
public Iterator<SDDocumentImpl> iterator() {
// create a map for old metadata
Map<String, SDDocumentImpl> oldMap = new HashMap<String, SDDocumentImpl>();
Iterator<SDDocumentImpl> oldDocs = docList.iterator();
// create a map for new metadata
Map<String, SDDocumentImpl> newMap = new HashMap<String, SDDocumentImpl>();
newMap.put(primaryDoc.getSystemId().toString(), primaryDoc);
List<String> remaining = new ArrayList<String>();
remaining.addAll(primaryDoc.getImports());
while(!remaining.isEmpty()) {
String url = remaining.remove(0);
SDDocumentImpl doc = oldMap.get(url);
if (doc == null) {
while (oldDocs.hasNext()) {
SDDocumentImpl old = oldDocs.next();
String id = old.getSystemId().toString();
oldMap.put(id, old);
if (id.equals(url)) {
doc = old;
break;
}
}
if (doc == null) {
// old metadata doesn't have this imported doc, may be external
if (resolver != null) {
try {
InputSource source = resolver.resolveEntity(null, url);
if (source != null) {
MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
XMLStreamReader reader = XmlUtil.newXMLInputFactory(true).createXMLStreamReader(source.getByteStream());
xsb.createFromXMLStreamReader(reader);
SDDocumentSource sdocSource = SDDocumentImpl.create(new URL(url), xsb);
doc = SDDocumentImpl.create(sdocSource, null, null);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
// Check if new metadata already contains this doc
if (doc != null && !newMap.containsKey(url)) {
newMap.put(url, doc);
remaining.addAll(doc.getImports());
}
}
return newMap.values().iterator();
}
@Override
public int size() {
int size = 0;
Iterator<SDDocumentImpl> it = iterator();
while (it.hasNext()) {
it.next();
size++;
}
return size;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return docList.isEmpty();
}
};
}
private static <T> void processHandlerAnnotation(WSBinding binding, Class<T> implType, QName serviceName, QName portName) {
HandlerAnnotationInfo chainInfo =
HandlerAnnotationProcessor.buildHandlerInfo(
implType, serviceName, portName, binding);
if (chainInfo != null) {
binding.setHandlerChain(chainInfo.getHandlers());
if (binding instanceof SOAPBinding) {
((SOAPBinding) binding).setRoles(chainInfo.getRoles());
}
}
}
/**
* Verifies if the endpoint implementor class has @WebService or @WebServiceProvider
* annotation
*
* @return
* true if it is a Provider or AsyncProvider endpoint
* false otherwise
* @throws java.lang.IllegalArgumentException
* If it doesn't have any one of @WebService or @WebServiceProvider
* If it has both @WebService and @WebServiceProvider annotations
*/
public static boolean verifyImplementorClass(Class<?> clz) {
return verifyImplementorClass(clz, null);
}
/**
* Verifies if the endpoint implementor class has @WebService or @WebServiceProvider
* annotation; passing MetadataReader instance allows to read annotations from
* xml descriptor instead of class's annotations
*
* @return
* true if it is a Provider or AsyncProvider endpoint
* false otherwise
* @throws java.lang.IllegalArgumentException
* If it doesn't have any one of @WebService or @WebServiceProvider
* If it has both @WebService and @WebServiceProvider annotations
*/
public static boolean verifyImplementorClass(Class<?> clz, MetadataReader metadataReader) {
if (metadataReader == null) {
metadataReader = new ReflectAnnotationReader();
}
WebServiceProvider wsProvider = metadataReader.getAnnotation(WebServiceProvider.class, clz);
WebService ws = metadataReader.getAnnotation(WebService.class, clz);
if (wsProvider == null && ws == null) {
throw new IllegalArgumentException(clz +" has neither @WebService nor @WebServiceProvider annotation");
}
if (wsProvider != null && ws != null) {
throw new IllegalArgumentException(clz +" has both @WebService and @WebServiceProvider annotations");
}
if (wsProvider != null) {
if (Provider.class.isAssignableFrom(clz) || AsyncProvider.class.isAssignableFrom(clz)) {
return true;
}
throw new IllegalArgumentException(clz +" doesn't implement Provider or AsyncProvider interface");
}
return false;
}
private static AbstractSEIModelImpl createSEIModel(WSDLPort wsdlPort,
Class<?> implType, @NotNull QName serviceName, @NotNull QName portName, WSBinding binding,
SDDocumentSource primaryWsdl) {
DatabindingFactory fac = DatabindingFactory.newInstance();
DatabindingConfig config = new DatabindingConfig();
config.setEndpointClass(implType);
config.getMappingInfo().setServiceName(serviceName);
config.setWsdlPort(wsdlPort);
config.setWSBinding(binding);
config.setClassLoader(implType.getClassLoader());
config.getMappingInfo().setPortName(portName);
if (primaryWsdl != null) config.setWsdlURL(primaryWsdl.getSystemId());
config.setMetadataReader(getExternalMetadatReader(implType, binding));
com.sun.xml.internal.ws.db.DatabindingImpl rt = (com.sun.xml.internal.ws.db.DatabindingImpl)fac.createRuntime(config);
return (AbstractSEIModelImpl) rt.getModel();
}
public static MetadataReader getExternalMetadatReader(Class<?> implType, WSBinding binding) {
com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature ef = binding.getFeature(
com.oracle.webservices.internal.api.databinding.ExternalMetadataFeature.class);
// TODO-Miran: would it be necessary to disable secure xml processing?
if (ef != null)
return ef.getMetadataReader(implType.getClassLoader(), false);
return null;
}
/**
*Set the mtom enable setting from wsdl model (mtom policy assertion) on to @link WSBinding} if DD has
* not already set it on BindingID. Also check conflicts.
*/
/*
private static void applyEffectiveMtomSetting(WSDLBoundPortType wsdlBinding, WSBinding binding){
if(wsdlBinding.isMTOMEnabled()){
BindingID bindingId = binding.getBindingId();
if(bindingId.isMTOMEnabled() == null){
binding.setMTOMEnabled(true);
}else if (bindingId.isMTOMEnabled() != null && bindingId.isMTOMEnabled() == Boolean.FALSE){
//TODO: i18N
throw new ServerRtException("Deployment failed! Mtom policy assertion in WSDL is enabled whereas the deplyment descriptor setting wants to disable it!");
}
}
}
*/
/**
* If service name is not already set via DD or programmatically, it uses
* annotations {@link WebServiceProvider}, {@link WebService} on implementorClass to get PortName.
*
* @return non-null service name
*/
public static @NotNull QName getDefaultServiceName(Class<?> implType) {
return getDefaultServiceName(implType, null);
}
public static @NotNull QName getDefaultServiceName(Class<?> implType, MetadataReader metadataReader) {
return getDefaultServiceName(implType, true, metadataReader);
}
public static @NotNull QName getDefaultServiceName(Class<?> implType, boolean isStandard) {
return getDefaultServiceName(implType, isStandard, null);
}
public static @NotNull QName getDefaultServiceName(Class<?> implType, boolean isStandard, MetadataReader metadataReader) {
if (metadataReader == null) {
metadataReader = new ReflectAnnotationReader();
}
QName serviceName;
WebServiceProvider wsProvider = metadataReader.getAnnotation(WebServiceProvider.class, implType);
if (wsProvider!=null) {
String tns = wsProvider.targetNamespace();
String local = wsProvider.serviceName();
serviceName = new QName(tns, local);
} else {
serviceName = RuntimeModeler.getServiceName(implType, metadataReader, isStandard);
}
assert serviceName != null;
return serviceName;
}
/**
* If portName is not already set via DD or programmatically, it uses
* annotations on implementorClass to get PortName.
*
* @return non-null port name
*/
public static @NotNull QName getDefaultPortName(QName serviceName, Class<?> implType) {
return getDefaultPortName(serviceName, implType, null);
}
public static @NotNull QName getDefaultPortName(QName serviceName, Class<?> implType, MetadataReader metadataReader) {
return getDefaultPortName(serviceName, implType, true, metadataReader);
}
public static @NotNull QName getDefaultPortName(QName serviceName, Class<?> implType, boolean isStandard) {
return getDefaultPortName(serviceName, implType, isStandard, null);
}
public static @NotNull QName getDefaultPortName(QName serviceName, Class<?> implType, boolean isStandard, MetadataReader metadataReader) {
if (metadataReader == null) {
metadataReader = new ReflectAnnotationReader();
}
QName portName;
WebServiceProvider wsProvider = metadataReader.getAnnotation(WebServiceProvider.class, implType);
if (wsProvider!=null) {
String tns = wsProvider.targetNamespace();
String local = wsProvider.portName();
portName = new QName(tns, local);
} else {
portName = RuntimeModeler.getPortName(implType, metadataReader, serviceName.getNamespaceURI(), isStandard);
}
assert portName != null;
return portName;
}
/**
* Returns the wsdl from @WebService, or @WebServiceProvider annotation using
* wsdlLocation element.
*
* @param implType
* endpoint implementation class
* make sure that you called {@link #verifyImplementorClass} on it.
* @return wsdl if there is wsdlLocation, else null
*/
public static @Nullable String getWsdlLocation(Class<?> implType) {
return getWsdlLocation(implType, new ReflectAnnotationReader());
}
/**
* Returns the wsdl from @WebService, or @WebServiceProvider annotation using
* wsdlLocation element.
*
* @param implType
* endpoint implementation class
* make sure that you called {@link #verifyImplementorClass} on it.
* @return wsdl if there is wsdlLocation, else null
*/
public static @Nullable String getWsdlLocation(Class<?> implType, MetadataReader metadataReader) {
if (metadataReader == null) {
metadataReader = new ReflectAnnotationReader();
}
WebService ws = metadataReader.getAnnotation(WebService.class, implType);
if (ws != null) {
return nullIfEmpty(ws.wsdlLocation());
} else {
WebServiceProvider wsProvider = implType.getAnnotation(WebServiceProvider.class);
assert wsProvider != null;
return nullIfEmpty(wsProvider.wsdlLocation());
}
}
private static String nullIfEmpty(String string) {
if (string.length() < 1) {
string = null;
}
return string;
}
/**
* Generates the WSDL and XML Schema for the endpoint if necessary
* It generates WSDL only for SOAP1.1, and for XSOAP1.2 bindings
*/
private static SDDocumentImpl generateWSDL(WSBinding binding, AbstractSEIModelImpl seiModel, Collection<SDDocumentImpl> docs,
Container container, Class implType) {
BindingID bindingId = binding.getBindingId();
if (!bindingId.canGenerateWSDL()) {
throw new ServerRtException("can.not.generate.wsdl", bindingId);
}
if (bindingId.toString().equals(SOAPBindingImpl.X_SOAP12HTTP_BINDING)) {
String msg = ServerMessages.GENERATE_NON_STANDARD_WSDL();
logger.warning(msg);
}
// Generate WSDL and schema documents using runtime model
WSDLGenResolver wsdlResolver = new WSDLGenResolver(docs,seiModel.getServiceQName(),seiModel.getPortTypeName());
WSDLGenInfo wsdlGenInfo = new WSDLGenInfo();
wsdlGenInfo.setWsdlResolver(wsdlResolver);
wsdlGenInfo.setContainer(container);
wsdlGenInfo.setExtensions(ServiceFinder.find(WSDLGeneratorExtension.class).toArray());
wsdlGenInfo.setInlineSchemas(false);
wsdlGenInfo.setSecureXmlProcessingDisabled(isSecureXmlProcessingDisabled(binding.getFeatures()));
seiModel.getDatabinding().generateWSDL(wsdlGenInfo);
// WSDLGenerator wsdlGen = new WSDLGenerator(seiModel, wsdlResolver, binding, container, implType, false,
// ServiceFinder.find(WSDLGeneratorExtension.class).toArray());
// wsdlGen.doGeneration();
return wsdlResolver.updateDocs();
}
private static boolean isSecureXmlProcessingDisabled(WSFeatureList featureList) {
// TODO-Miran: would it be necessary to disable secure xml processing?
return false;
}
/**
* Builds {@link SDDocumentImpl} from {@link SDDocumentSource}.
*/
private static Collection<SDDocumentImpl> categoriseMetadata(
final Iterator<SDDocumentSource> src, final QName serviceName, final QName portTypeName) {
return new AbstractCollection<SDDocumentImpl>() {
private final Collection<SDDocumentImpl> theConverted = new ArrayList<SDDocumentImpl>();
@Override
public boolean add(SDDocumentImpl arg0) {
return theConverted.add(arg0);
}
@Override
public Iterator<SDDocumentImpl> iterator() {
return new Iterator<SDDocumentImpl>() {
private Iterator<SDDocumentImpl> convIt = theConverted.iterator();
@Override
public boolean hasNext() {
if (convIt != null && convIt.hasNext())
return true;
return src.hasNext();
}
@Override
public SDDocumentImpl next() {
if (convIt != null && convIt.hasNext())
return convIt.next();
convIt = null;
if (!src.hasNext())
throw new NoSuchElementException();
SDDocumentImpl next = SDDocumentImpl.create(src.next(),serviceName,portTypeName);
theConverted.add(next);
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
if (!theConverted.isEmpty())
return false;
return !src.hasNext();
}
};
}
/**
* Verifies whether the given primaryWsdl contains the given serviceName.
* If the WSDL doesn't have the service, it throws an WebServiceException.
*/
private static void verifyPrimaryWSDL(@NotNull SDDocumentSource primaryWsdl, @NotNull QName serviceName) {
SDDocumentImpl primaryDoc = SDDocumentImpl.create(primaryWsdl,serviceName,null);
if (!(primaryDoc instanceof SDDocument.WSDL)) {
throw new WebServiceException(primaryWsdl.getSystemId()+
" is not a WSDL. But it is passed as a primary WSDL");
}
SDDocument.WSDL wsdlDoc = (SDDocument.WSDL)primaryDoc;
if (!wsdlDoc.hasService()) {
if(wsdlDoc.getAllServices().isEmpty())
throw new WebServiceException("Not a primary WSDL="+primaryWsdl.getSystemId()+
" since it doesn't have Service "+serviceName);
else
throw new WebServiceException("WSDL "+primaryDoc.getSystemId()
+" has the following services "+wsdlDoc.getAllServices()
+" but not "+serviceName+". Maybe you forgot to specify a serviceName and/or targetNamespace in @WebService/@WebServiceProvider?");
}
}
/**
* Finds the primary WSDL document from the list of metadata documents. If
* there are two metadata documents that qualify for primary, it throws an
* exception. If there are two metadata documents that qualify for porttype,
* it throws an exception.
*
* @return primay wsdl document, null if is not there in the docList
*
*/
private static @Nullable SDDocumentImpl findPrimary(@NotNull Collection<SDDocumentImpl> docList) {
SDDocumentImpl primaryDoc = null;
boolean foundConcrete = false;
boolean foundAbstract = false;
for(SDDocumentImpl doc : docList) {
if (doc instanceof SDDocument.WSDL) {
SDDocument.WSDL wsdlDoc = (SDDocument.WSDL)doc;
if (wsdlDoc.hasService()) {
primaryDoc = doc;
if (foundConcrete) {
throw new ServerRtException("duplicate.primary.wsdl", doc.getSystemId() );
}
foundConcrete = true;
}
if (wsdlDoc.hasPortType()) {
if (foundAbstract) {
throw new ServerRtException("duplicate.abstract.wsdl", doc.getSystemId());
}
foundAbstract = true;
}
}
}
return primaryDoc;
}
/**
* Parses the primary WSDL and returns the {@link WSDLPort} for the given service and port names
*
* @param primaryWsdl Primary WSDL
* @param metadata it may contain imported WSDL and schema documents
* @param serviceName service name in wsdl
* @param portName port name in WSDL
* @param container container in which this service is running
* @return non-null wsdl port object
*/
private static @NotNull WSDLPort getWSDLPort(SDDocumentSource primaryWsdl, Collection<? extends SDDocumentSource> metadata,
@NotNull QName serviceName, @NotNull QName portName, Container container,
EntityResolver resolver) {
URL wsdlUrl = primaryWsdl.getSystemId();
try {
// TODO: delegate to another entity resolver
WSDLModel wsdlDoc = RuntimeWSDLParser.parse(
new Parser(primaryWsdl), new EntityResolverImpl(metadata, resolver),
false, container, ServiceFinder.find(WSDLParserExtension.class).toArray());
if(wsdlDoc.getServices().size() == 0) {
throw new ServerRtException(ServerMessages.localizableRUNTIME_PARSER_WSDL_NOSERVICE_IN_WSDLMODEL(wsdlUrl));
}
WSDLService wsdlService = wsdlDoc.getService(serviceName);
if (wsdlService == null) {
throw new ServerRtException(ServerMessages.localizableRUNTIME_PARSER_WSDL_INCORRECTSERVICE(serviceName,wsdlUrl));
}
WSDLPort wsdlPort = wsdlService.get(portName);
if (wsdlPort == null) {
throw new ServerRtException(ServerMessages.localizableRUNTIME_PARSER_WSDL_INCORRECTSERVICEPORT(serviceName, portName, wsdlUrl));
}
return wsdlPort;
} catch (IOException e) {
throw new ServerRtException("runtime.parser.wsdl", wsdlUrl,e);
} catch (XMLStreamException e) {
throw new ServerRtException("runtime.saxparser.exception", e.getMessage(), e.getLocation(), e);
} catch (SAXException e) {
throw new ServerRtException("runtime.parser.wsdl", wsdlUrl,e);
} catch (ServiceConfigurationError e) {
throw new ServerRtException("runtime.parser.wsdl", wsdlUrl,e);
}
}
/**
* {@link XMLEntityResolver} that can resolve to {@link SDDocumentSource}s.
*/
private static final class EntityResolverImpl implements XMLEntityResolver {
private Iterator<? extends SDDocumentSource> origMetadata;
private Map<String,SDDocumentSource> metadata = new ConcurrentHashMap<String,SDDocumentSource>();
private EntityResolver resolver;
public EntityResolverImpl(Collection<? extends SDDocumentSource> metadata, EntityResolver resolver) {
this.origMetadata = metadata.iterator();
this.resolver = resolver;
}
public Parser resolveEntity (String publicId, String systemId) throws IOException, XMLStreamException {
if (systemId != null) {
SDDocumentSource doc = metadata.get(systemId);
if (doc != null)
return new Parser(doc);
synchronized(this) {
while(origMetadata.hasNext()) {
doc = origMetadata.next();
String extForm = doc.getSystemId().toExternalForm();
this.metadata.put(extForm,doc);
if (systemId.equals(extForm))
return new Parser(doc);
}
}
}
if (resolver != null) {
try {
InputSource source = resolver.resolveEntity(publicId, systemId);
if (source != null) {
Parser p = new Parser(null, XMLStreamReaderFactory.create(source, true));
return p;
}
} catch (SAXException e) {
throw new XMLStreamException(e);
}
}
return null;
}
}
private static final Logger logger = Logger.getLogger(
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.endpoint");
private static class CollectionCollection<T> extends AbstractCollection<T> {
private final Collection<Collection<? extends T>> cols = new ArrayList<Collection<? extends T>>();
@Override
public Iterator<T> iterator() {
final Iterator<Collection<? extends T>> colIt = cols.iterator();
return new Iterator<T>() {
private Iterator<? extends T> current = null;
@Override
public boolean hasNext() {
if (current == null || !current.hasNext()) {
do {
if (!colIt.hasNext())
return false;
current = colIt.next().iterator();
} while (!current.hasNext());
return true;
}
return true;
}
@Override
public T next() {
if (!hasNext())
throw new NoSuchElementException();
return current.next();
}
@Override
public void remove() {
if (current == null)
throw new IllegalStateException();
current.remove();
}
};
}
@Override
public int size() {
int size = 0;
for (Collection<? extends T> c : cols)
size += c.size();
return size;
}
@Override
public boolean add(T arg0) {
return cols.add(Collections.singleton(arg0));
}
@Override
public boolean addAll(Collection<? extends T> arg0) {
return cols.add(arg0);
}
@Override
public void clear() {
cols.clear();
}
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
}
}