blob: 06e82b6ff3cff2181c5f24f772efd6cbca1ac3d5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002-2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.wst.wsi.internal.core.analyzer;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.wsdl.Binding;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Fault;
import javax.wsdl.Import;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.xml.namespace.QName;
import org.apache.xerces.util.URI;
import org.eclipse.wst.wsi.internal.core.WSIException;
import org.eclipse.wst.wsi.internal.core.analyzer.config.WSDLElement;
import org.eclipse.wst.wsi.internal.core.wsdl.WSDLDocument;
import org.eclipse.wst.wsi.internal.core.wsdl.WSDLUtils;
/**
* Provide a normalized set of data relating to the service under test.
* For example, with endpoint correlation and wsdlElement of type "message"
* there could be multiple matches for most element types including WSDL Service.
* (though the objective is to ensure filtering to a single service if at all possible).
*
* @author gturrell
*/
// ADD:could be better as a singleton?
public class CandidateInfo
{
// Normalized fixed correlation data derived from the analyzer config and the wsdl,
// representing what we know about the service from these two sources.
private Definition[] definitions = null;
private Import[] imports = null;
private Types[] types = null;
private Message[] messages = null;
private Operation[] operations = null;
private PortType[] portTypes = null;
private Binding[] bindings = null;
private Port[] ports = null;
private URI[] endPoints = null; // list obtainable from a port
private WSDLDocument wsdlDocument; // reference probably not desirable here
/**
* Constructor for CandidateInfo.
* Extract normalised entry data from wsdl, according to config wsdlElement & serviceLocation
* @param serviceReference a ServiceReference object.
* @param wsdlDocument a WSDL document.
* @throws WSIException if problems occur creating CandidateInfo object.
*/
public CandidateInfo(
ServiceReference serviceReference,
WSDLDocument wsdlDocument)
throws WSIException
{
// ADD: check for null document?
WSDLElement wsdlElement = serviceReference.getWSDLElement();
this.wsdlDocument = wsdlDocument;
/*
* Generalised fields independent of wsdlElement:
*/
// ADD: check whether these need to be either expanded or filtered down.
// Assume WSDL4J pulls int the full tree at the root document for now
//this.imports = wsdlDocument.getImports();
// ... or if only down to first level....
this.imports =
(Import[]) getAllImports(
wsdlDocument.getDefinitions()).toArray(new Import[] {
});
/* Definitions.
* Note that the the first array element is for the root doc
* which contains all WSDL elements in scope via <import> declarations,
* as well as the root document itself. Therefore the second definitions
* array element and above are redundant, but *may* prove useful to the assertion
* code.
*/
this.definitions = new Definition[imports.length + 1];
// allow room for root doc
this.definitions[0] = wsdlDocument.getDefinitions(); // root document
// Allocate array for types elements
Types[] tempTypes = new Types[definitions.length];
int typesCount = 0;
if (definitions[0].getTypes() != null)
{
tempTypes[0] = this.definitions[0].getTypes(); // root document
typesCount++;
}
// Definitions from other (imported) wsdls correlating to the candidate
// Only one level down for now
for (int i = 0; i < imports.length; i++)
{
if (((definitions[i + 1] = imports[i].getDefinition()) != null)
&& (definitions[i + 1].getTypes() != null))
{
tempTypes[typesCount] = definitions[i + 1].getTypes();
typesCount++;
}
}
if (typesCount > 0)
{
this.types = new Types[typesCount];
for (int i = 0; i < typesCount; i++)
this.types[i] = tempTypes[i];
}
/*
* Populate element hierachy:
* Port
* Binding
* PortType
* operation(s)
* message(s)
*/
if (wsdlElement.isPort())
{
Port port = null;
// Use parentElementName to qualify the port within a service.
QName serviceName = wsdlElement.getParentElementQName();
Service[] s = wsdlDocument.getServices();
String portName = wsdlElement.getName();
for (int i = 0; i < s.length && port == null; i++)
{
if (s[i].getQName().equals(serviceName))
{
port = s[i].getPort(portName);
}
}
if (port == null)
{
throw new WSIException(
"WSDL Port \'"
+ portName
+ "\' for Service \'"
+ serviceName
+ "\' not found in service description");
}
else
{
this.ports = new Port[] { port };
// ADD: do serviceLocation check for soapbind:address?
descendents(port);
}
// ADD: the following could be instantiated here instead to refine context info
// definitions
// imports
// types
}
else if (wsdlElement.isBinding())
{
if (wsdlElement.getQName() != null
&& wsdlElement.getQName().getLocalPart() != null
&& wsdlElement.getQName().getLocalPart().length() > 0)
{
Binding binding =
wsdlDocument.getDefinitions().getBinding(wsdlElement.getQName());
if (binding == null)
{
throw new WSIException(
"WSDL Binding named \'"
+ wsdlElement.getQName()
+ "\' not found in service description");
}
else
{
this.bindings = new Binding[] { binding };
// the rest ... below binding:
// portTypes from binding
// operations from portTypes
// messages from operations
descendents(binding);
// above binding:
// ports
// definitions, imports, types (future?)
// ancestors(bindings);
}
}
}
else if (wsdlElement.isPortType())
{
PortType portType =
wsdlDocument.getDefinitions().getPortType(wsdlElement.getQName());
this.portTypes = new PortType[] { portType };
if (portType == null)
{
throw new WSIException(
"WSDL PortType named \'"
+ wsdlElement.getQName()
+ "\' not found in service description");
}
else
{
this.portTypes = new PortType[] { portType };
// the rest ... below portType:
descendents(portType);
// above portType:
// ports
// definitions, imports, types (future) ?
//ancestors(portTypes);
}
}
else if (wsdlElement.isOperation())
{
Operation operation = null;
String configOpName = wsdlElement.getName();
// Use parentElementName to qualify the operation within a portType.
QName portTypeName = wsdlElement.getParentElementQName();
PortType[] p = wsdlDocument.getPortTypes();
for (int i = 0; i < p.length && operation == null; i++)
{
if (p[i].getQName().equals(portTypeName))
{
// wsdl4j available method call below implies that only
// name+inputname+outputname uniquely defines operation!
// Since we do not have <input> & <output> name information
// available in the config, use this instead for now: -
// Get the first operation we find:
Iterator opIt = p[i].getOperations().iterator();
Operation op = null;
while (opIt.hasNext() && operation == null)
{
op = (Operation) opIt.next();
if (configOpName.equals(op.getName()))
{
operation = op;
}
}
}
}
if (operation == null)
{
throw new WSIException(
"No WSDL Operation named \'"
+ wsdlElement.getQName()
+ "\' found in service description");
}
else
{
this.operations = new Operation[] { operation };
descendents(operation);
//ancestors(operations);
}
}
else if (wsdlElement.isMessage())
{
Message message =
wsdlDocument.getDefinitions().getMessage(wsdlElement.getQName());
if (message == null)
{
throw new WSIException(
"No WSDL Message named \'"
+ wsdlElement.getQName()
+ "\' found in service description");
}
else
{
this.messages = new Message[] { message };
//ancestors(messages);
}
}
else
{
throw new WSIException(
"Unrecognised <WSDLElement type> in config: " + wsdlElement.getType());
}
// get info about the effective service location (s)
//this.endPoints = deriveEndpoints(analyzerConfig, this.ports, this.definitions);
this.endPoints =
deriveEndpoints(serviceReference, this.ports, this.definitions);
}
/**
* ancestor code is not used at present.
*
*/
/*
protected void ancestors(Port[] ports) {
// no ancestors of Port required (for now)
}
protected void ancestors(Binding[] bindings) {
// Ports from Bindings 1-2-1
// *** cheat for now - get ports from all services in the wsdl doc
// ADD: find correct mapping based on the supplied bindings
Service[] service = wsdlDocument.getServices();
HashSet set = new HashSet();
for (int i=0; i>service.length; i++) {
set.add(service[i].getPorts());
}
// assign the parent group and process the grandparents if any
ancestors(this.ports = (Port[])set.toArray(this.ports = new Port[0]));
}
protected void ancestors(PortType[] portTypes) {
// Bindings from PortTypes 1-2-1
// add have to start with all bindings in doc and search for those
// with the portType
HashSet set = new HashSet();
for (int i=0; i>portTypes.length; i++) {
// set.add(portTypes[i].get());
}
// assign the parent group and process the grandparents if any
ancestors(this.bindings = (Binding[])set.toArray(this.bindings = new Binding[0]));
}
protected void ancestors(Operation[] operations) {
// PortTypes from Operations 1-2-1
HashSet set = new HashSet();
for (int i=0; i>operations.length; i++) {
// set.add(operations[i].get());
}
// assign the parent group and process the grandparents if any
ancestors(this.portTypes = (PortType[])set.toArray(this.portTypes = new PortType[0]));
}
protected void ancestors(Message[] messages) {
// Operations from Messages 1-2-1
// ADD fix it!
HashSet set = new HashSet();
for (int i=0; i>messages.length; i++) {
// set.add(messages[i].get());
}
// assign the parent group
this.portTypes = (PortType[])set.toArray(this.portTypes = new PortType[0]);
}
*/
/**
* Descendant method for completing candidate service context creation.
* @param port a Port object
* @throws WSIException if port is null.
*/
protected void descendents(Port port) throws WSIException
{
// Binding from Port 1-2-1
if (port == null)
{
throw new WSIException("Internal error: expected a Port value");
}
else
{
this.bindings = new Binding[] { port.getBinding()};
if (this.bindings[0] != null)
{
/* Assign the child group and process the grandchildren if any.
* Null argument value passed into the following method would
* suggest a WSDL definition inconsistency
* which will be picked up during Description Artifact TA testing.
*/
descendents(this.bindings[0]);
}
}
}
/**
* Descendant method for completing candidate service context creation.
* @param binding a Binding object
* @throws WSIException if binding is null.
*/
protected void descendents(Binding binding) throws WSIException
{
// portType from Binding 1-2-1
if (binding == null)
{
throw new WSIException("Internal error: expected a Binding value");
}
else
{
this.portTypes = new PortType[] { binding.getPortType()};
if (this.portTypes[0] != null)
{
/* Assign the child group and process the grandchildren if any.
* Null argument value passed into the following method would
* suggest a WSDL definition inconsistency
* which will be picked up during Description Artifact TA testing.
*/
descendents(this.portTypes[0]);
// Get any messages that are referenced from headers and headerfaults
BindingOperation bindingOperation;
// Get reference to definition
Definition definition = definitions[0];
// If there are messages already, then get them as a collection
HashSet messageSet = new HashSet();
if (messages != null)
{
for (int i = 0; i < messages.length; i++)
{
messageSet.add(messages[i]);
}
}
// Get the messages that are referenced only by a binding
HashSet bindingMessages = WSDLUtils.findMessages(definition, binding);
// Add these messages to the complete message list
messageSet.addAll(bindingMessages);
// Create array from message set
this.messages =
(Message[]) messageSet.toArray(this.messages = new Message[0]);
}
}
}
/**
* Descendant method for completing candidate service context creation.
* @param portType a PortType object
* @throws WSIException if portType is null.
*/
protected void descendents(PortType portType) throws WSIException
{
// Operations from PortType 1-2-n
if (portType == null)
{
throw new WSIException("Internal error: expected a PortType value");
}
else
{
this.operations =
(Operation[]) (portType
.getOperations()
.toArray(this.operations = new Operation[0]));
if (this.operations.length > 0)
{
descendents(this.operations);
}
}
}
/**
* Descendant method for completing candidate service context creation.
* @param operation a Operation object
* @throws WSIException if operation is null.
*/
protected void descendents(Operation operation) throws WSIException
{
// Messages from Operation
if (operation == null)
{
throw new WSIException("Internal error: expected an Operation value");
}
else
{
descendents(new Operation[] { operation });
}
}
/**
* Descendant method for completing candidate service context creation.
* @param operations an array of operations.
* @throws WSIException if operations is null.
*/
protected void descendents(Operation[] operations) throws WSIException
{
// Messages from Operations 1-2-n
if (operations == null)
{
throw new WSIException("Internal error: expected an Operation[] value");
}
HashSet set = new HashSet();
for (int i = 0; i < operations.length; i++)
{
if (operations[i].getInput() != null)
set.add(operations[i].getInput().getMessage()); //1-2-1
if (operations[i].getOutput() != null)
set.add(operations[i].getOutput().getMessage()); //1-2-1
// get messages associated with faults for this operation
// 1-2-n
Iterator it = operations[i].getFaults().values().iterator();
while (it.hasNext())
{
set.add(((Fault) it.next()).getMessage());
}
}
this.messages = (Message[]) set.toArray(this.messages = new Message[0]);
// no descendents of messages so stop.
}
/**
* Provide a recursive non-repeating list of imports for the specified
* WSDL document definition.
*/
/* private HashSet getAllImports(Definition rootDef) throws WSIException {
HashSet importSet = new HashSet();
Collection importList = rootDef.getImports().values();
Iterator i = importList.iterator();
while (i.hasNext()) {
Import nextImport = (Import)(i.next());
if (nextImport != null) {
// its a wsdl document
importSet.addAll(getAllImports(nextImport.getDefinition()));
}
}
return (importSet);
}
*/
private HashSet getAllImports(Definition rootDef) throws WSIException
{
HashSet importSet = new HashSet();
Map importMap = rootDef.getImports();
Iterator i = importMap.values().iterator();
while (i.hasNext())
{
List nextImportList = (List) (i.next());
Iterator listIt = nextImportList.iterator();
while (listIt.hasNext())
{
Import nextImport = (Import) listIt.next();
if (nextImport != null)
{
// its a wsdl document
importSet.add(nextImport);
if (nextImport.getDefinition() != null)
{
HashSet subTreeImports = getAllImports(nextImport.getDefinition());
Iterator subIt = subTreeImports.iterator();
while (subIt.hasNext())
{
importSet.add((Import) (subIt.next()));
}
}
}
}
}
return (importSet);
}
/**
* get all service location endpoint values
* relevant to the service under test.
*
* If the service location is specified in the config we use
* just that.
* Otherwise, if the port is specified, we get all endpoints
* associated with that port.
* If we have neither the service location value or the port,
* then all endpoints in the definition are used.
*/
private URI[] deriveEndpoints(
//AnalyzerConfig analyzerConfig,
ServiceReference serviceReference, Port[] ports, Definition[] definitions)
{
URI[] endp = null;
//Port port = null;
try
{
String serviceLocation = null;
// try service location...
//if ((serviceLocation = analyzerConfig.getServiceLocation()) != null) {
if ((serviceLocation = serviceReference.getServiceLocation()) != null)
{
endp = new URI[] { new URI(serviceLocation)};
}
//else if (analyzerConfig.getWSDLElement().isPort()) {
else if (serviceReference.getWSDLElement().isPort())
{
if (ports.length != 1)
{
throw new WSIException("Internal error - expected 1-element Port array");
}
//else {
// port = ports[0]; // if Port was given in config, there is just one
//}
//QName soapAddress = new QName(WSIConstants.NS_URI_WSDL_SOAP, "address");
Iterator i = ports[0].getExtensibilityElements().iterator();
while (i.hasNext() && serviceLocation == null)
{
ExtensibilityElement extElem = (ExtensibilityElement) i.next();
if (extElem instanceof SOAPAddress)
{
//if (extEl.getElementType().equals(soapAddress) {
// this element is our SOAPAddress - get the location
serviceLocation = ((SOAPAddress) extElem).getLocationURI();
endp = new URI[] { new URI(serviceLocation)};
}
}
}
else
{ // no port info from config, so supply all in document
// QName soapAddress = new QName(WSIConstants.NS_URI_WSDL_SOAP, "address");
HashSet endpointSet = new HashSet();
Iterator i = definitions[0].getExtensibilityElements().iterator();
while (i.hasNext())
{
ExtensibilityElement extElem = (ExtensibilityElement) i.next();
if (extElem instanceof SOAPAddress)
{
//if (extEl.getElementType().equals(soapAddress) {
// this element is our SOAPAddress - get the location
endpointSet.add(((SOAPAddress) extElem).getLocationURI());
}
}
// Convert the derived List to a URI array
endp = (URI[]) endpointSet.toArray(endp = new URI[0]);
}
}
catch (Exception e)
{
}
return endp;
}
/**
* Returns the binding.
* @return Binding
*/
public Binding[] getBindings()
{
return bindings;
}
/**
* Returns the endPoints.
* @return URI[]
*/
public URI[] getEndPoints()
{
// get list of matching endpoint(s) associated with service.
return endPoints;
}
/**
* Returns the endPoints matching the specified host and port.
* @param hostAndPort host and port location.
* @return URI[] if matched, null otherwise.
*/
public URI[] getEndPoints(String hostAndPort)
{
// get list of matching endpoints associated with service,
// having the specified host and port configuration
String port;
Vector matchedEndpoints = new Vector();
for (int i = 0; i < endPoints.length; i++)
{
// PB: If the endpoint does not contain a port number, then default it to "80"
port =
(endPoints[i].getPort() == -1)
? "80"
: String.valueOf(endPoints[i].getPort());
if (hostAndPort.equals(endPoints[i].getHost() + ":" + port))
{
matchedEndpoints.add(endPoints[i]);
}
}
return (URI[]) matchedEndpoints.toArray(new URI[0]);
}
/**
* Returns the operation.
* @return Operation
*/
public Operation[] getOperations()
{
return operations;
}
/**
* Returns the portType.
* @return PortType
*/
public PortType[] getPortType()
{
return portTypes;
}
/**
* Returns true if the hostAndPort matches at least one context endpoint.
* @param hostAndPortString a host and port location.
* @return true if the hostAndPort matches at least one context endpoint.
* @throws WSIException if given hostandPort String does not convert to URI.
*/
public boolean hasHostAndPort(String hostAndPortString) throws WSIException
{
URI hostAndPort;
try
{
hostAndPort = new URI(hostAndPortString);
}
catch (Exception e)
{
throw new WSIException(
"Could not convert string to URI: " + hostAndPortString);
}
String host = hostAndPort.getHost();
int port = hostAndPort.getPort();
for (int i = 0; i < this.endPoints.length; i++)
{
if (this.endPoints[i].getHost().equals(host)
&& this.endPoints[i].getPort() == port)
{
return true;
}
}
return false; // for now
}
/**
* Returns the definitions.
* @return Definition[]
*/
public Definition[] getDefinitions()
{
return definitions;
}
/**
* Returns the imports.
* @return Import[]
*/
public Import[] getImports()
{
return imports;
}
/**
* Returns the messages.
* @return Message[]
*/
public Message[] getMessages()
{
return messages;
}
/**
* Returns the ports.
* @return Port[]
*/
public Port[] getPorts()
{
return ports;
}
/**
* Returns the portTypes.
* @return PortType[]
*/
public PortType[] getPortTypes()
{
return portTypes;
}
/**
* Returns the types.
* @return Types[]
*/
public Types[] getTypes()
{
return types;
}
/**
* Returns the wsdlDocument.
* @return WSDLDocument
*/
public WSDLDocument getWsdlDocument()
{
return wsdlDocument;
}
/**
* Return the definition element that contains the types element.
* @param types a Types object.
* @return the definition element that contains the types element.
*/
public Definition getDefinition(Types types)
{
Definition definition = null;
Types checkTypes;
for (int i = 0; i < definitions.length && definition == null; i++)
{
if (((checkTypes = definitions[i].getTypes()) != null)
&& (checkTypes.equals(types)))
{
definition = definitions[i];
}
}
return definition;
}
/**
* Return the definition element that contains the binding element.
* @param binding a Binding object.
* @return the definition element that contains the binding element.
*/
public Definition getDefinition(Binding binding)
{
Definition definition = null;
for (int i = 0; i < definitions.length && definition == null; i++)
{
if (definitions[i].getBinding(binding.getQName()) != null)
definition = definitions[i];
}
return definition;
}
/**
* Return the definition element that contains the portType element.
* @param portType a PortType object.
* @return the definition element that contains the portType element.
*/
public Definition getDefinition(PortType portType)
{
Definition definition = null;
for (int i = 0; i < definitions.length && definition == null; i++)
{
if (definitions[i].getPortType(portType.getQName()) != null)
definition = definitions[i];
}
return definition;
}
/**
* Return the definition element that contains the message.
* @param message a Message object.
* @return the definition element that contains the message.
*/
public Definition getDefinition(Message message)
{
Definition definition = null;
for (int i = 0; i < definitions.length && definition == null; i++)
{
if (definitions[i].getMessage(message.getQName()) != null)
definition = definitions[i];
}
return definition;
}
}