/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.ws.util;
import org.apache.ws.resource.InvalidWsrfWsdlException;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.PrefixResolverDefault;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.PortType;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.xml.namespace.QName;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
/**
* Utility methods for working with a WSRF WSDL definition.
*
* @author Ian Springer (ian DOT springer AT hp DOT com)
*/
public abstract class WsrfWsdlUtils
{
/**
* DOCUMENT_ME
*
* @param mostDerivedPortType DOCUMENT_ME
* @param specPortType DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public static boolean implementsPortType( PortType mostDerivedPortType,
PortType specPortType )
{
List specOps = specPortType.getOperations();
if (specOps.size() == 0 ) return false; //todo need to determine if props are present for portTypes which have no methods.
boolean foundSome = false;
for ( int i = 0; i < specOps.size(); i++ )
{
Operation specOp = (Operation) specOps.get( i );
Operation aggregatedOp = mostDerivedPortType.getOperation( specOp.getName(),
null,
null );
if ( !WsdlUtils.equals( specOp, aggregatedOp ) )
{
if ( foundSome )
{
System.out.println( "WARNING: PortType " + mostDerivedPortType.getQName()
+ " contains some, but not all, operations from portType "
+ specPortType.getQName() );
}
return false;
}
if ( !isInheritedOperation( specOp, specPortType ) )
{
foundSome = true;
}
}
return true;
}
private static boolean isInheritedOperation( Operation specOp,
PortType specPortType )
{
return !specOp.getInput().getMessage().getQName().getNamespaceURI().equals( specPortType.getQName()
.getNamespaceURI() );
}
/**
* @param rpDocElemName
* @param def
* @param baseUrl
*
* @return the names of the resource properties associated with the specified portType
*/
public static QName[] getResourcePropertyNames( QName rpDocElemName,
Definition def, URL baseUrl ) throws InvalidWsrfWsdlException
{
if ( rpDocElemName == null )
{
return null;
}
Set propNames = new HashSet();
Element[] schemaElems = getSchemaElements( def );
XsdElement rpDocXsdElem = null;
for ( int i = 0; i < schemaElems.length; i++ )
{
rpDocXsdElem = getXsdElementElementRecursively( schemaElems[i], rpDocElemName, baseUrl );
if ( rpDocXsdElem != null )
{
break;
}
}
if ( rpDocXsdElem == null )
{
throw new InvalidWsrfWsdlException(
"Unable to locate the ResourceProperties document element with QName " + rpDocElemName );
}
QName type = getQualifiedAttribute( rpDocXsdElem, "type" );
Element rpDocTypeElem;
if ( type != null )
{
if ( ! type.getNamespaceURI().equals( rpDocXsdElem.getSchemaElement().getAttribute( "targetNamespace" ) ) )
{
throw new InvalidWsrfWsdlException(
"This tool currently does not support referencing a ResourceProperties complexType that is not in the current schema's targetNamespace." );
}
rpDocTypeElem = getComplexTypeByName( rpDocXsdElem.getSchemaElement(), type.getLocalPart() );
}
else
{
rpDocTypeElem =
(Element) rpDocXsdElem.getElement().getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD,
"complexType" )
.item( 0 );
}
if ( rpDocTypeElem == null )
{
throw new InvalidWsrfWsdlException( "Unable to determine type of the ResourceProperties document element with QName " + rpDocElemName + " - expected either a type atribute or a nested anonymous complexType element." );
}
Element sequenceElem = null;
NodeList sequenceNodes = rpDocTypeElem.getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD, "sequence" );
if ( sequenceNodes != null )
{
sequenceElem = (Element) sequenceNodes.item( 0 );
}
else
{
sequenceNodes = rpDocTypeElem.getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD, "all" );
if ( sequenceNodes != null )
{
sequenceElem = (Element) sequenceNodes.item( 0 );
}
}
if ( sequenceElem == null )
{
throw new RuntimeException(
"Resource property element definitions must be contained in an xsd:sequence or an xsd:all element." );
}
NodeList propElems = sequenceElem.getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD, "element" );
for ( int j = 0; j < propElems.getLength(); j++ )
{
Element propElem = (Element) propElems.item( j );
QName propName = getQualifiedAttribute( new XsdElement( propElem, rpDocXsdElem.getSchemaElement() ), "ref" );
if ( propName == null )
{
throw new InvalidWsrfWsdlException( "All element defs within the resource property document def must have a 'ref' attribute that points at a global element def." );
}
propNames.add( propName );
}
return (QName[]) propNames.toArray( new QName[0] );
}
private static XsdElement getXsdElementElementRecursively( Element xsdSchemaElem, QName xsdElemName, URL baseUrl )
throws InvalidWsrfWsdlException
{
XsdElement xsdElementElem = getXsdElementElement( xsdSchemaElem, xsdElemName );
if ( xsdElementElem == null )
{
String[] schemaLocations = findImportsAndIncludes( xsdSchemaElem, xsdElemName.getNamespaceURI() );
for ( int j = 0; j < schemaLocations.length; j++ )
{
String location = schemaLocations[j];
URL schemaUrl = null;
Document schemaDoc = null;
try
{
schemaUrl = new URL( baseUrl, location );
schemaDoc = JaxpUtils.loadDocument( schemaUrl.openStream() );
}
catch ( Exception e )
{
throw new InvalidWsrfWsdlException( "Failed to load schema at location " + location );
}
xsdElementElem =
getXsdElementElementRecursively( schemaDoc.getDocumentElement(), xsdElemName, schemaUrl );
if ( xsdElementElem != null )
{
break;
}
}
}
return xsdElementElem;
}
private static QName getQualifiedAttribute( XsdElement xsdElem, String attribName )
throws InvalidWsrfWsdlException
{
String value = xsdElem.getElement().getAttribute( attribName );
if ( "".equals( value ) )
{
return null;
}
PrefixResolver prefixResolver = new PrefixResolverDefault( xsdElem.getSchemaElement() );
QName propName = null;
try
{
propName = toQName( value, prefixResolver );
}
catch ( PrefixResolutionException pre )
{
throw new InvalidWsrfWsdlException( "Unable to resolve prefix '" + pre.getPrefix() + "' in xsd:element '" + attribName + "' attribute value." );
}
catch ( InvalidValueException ive )
{
throw new InvalidWsrfWsdlException( "Value for xsd:element 'ref' attribute must be qualified via a namespace prefix." );
}
return propName;
}
private static QName toQName( String value, PrefixResolver prefixResolver ) throws InvalidValueException,
PrefixResolutionException
{
StringTokenizer tokenizer = new StringTokenizer( value, ":" );
if ( tokenizer.countTokens() != 2 )
{
throw new InvalidValueException();
}
String prefix = tokenizer.nextToken();
String localName = tokenizer.nextToken();
// TODO: write our own prefix resolver to eliminate dep on Xalan
String namespace = prefixResolver.getNamespaceForPrefix( prefix );
if ( namespace == null )
{
throw new PrefixResolutionException( prefix );
}
QName qName = new QName( namespace, localName, prefix );
return qName;
}
private static String[] findImportsAndIncludes( Element schemaElem, String namespaceURI )
{
List elems = new ArrayList();
NodeList imports = schemaElem.getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD,
XmlConstants.XSD_ATTRIB_IMPORT );
for ( int i = 0; i < imports.getLength(); i++ )
{
Element importNode = (Element) imports.item( i );
Attr attributeNode = importNode.getAttributeNode( "namespace" );
if ( attributeNode != null )
{
if ( namespaceURI.equals( attributeNode.getValue() ) )
{
elems.add( importNode );
}
}
}
NodeList includes = schemaElem.getElementsByTagNameNS( XmlConstants.NSURI_SCHEMA_XSD,
XmlConstants.XSD_ATTRIB_INCLUDE );
for ( int i = 0; i < includes.getLength(); i++ )
{
elems.add( includes.item( i ) );
}
String[] locations = new String[elems.size()];
for ( int i = 0; i < elems.size(); i++ )
{
Element element = (Element) elems.get( i );
Attr attributeNode = element.getAttributeNode( "schemaLocation" );
if ( attributeNode != null )
{
locations[i] = attributeNode.getValue();
}
}
return locations;
}
private static XsdElement getXsdElementElement( Element schemaElem,
QName name )
{
NodeList children = schemaElem.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ )
{
Node child = children.item( i );
if ( child instanceof Element && child.getLocalName().equals( "element" ) )
{
Element elementElem = (Element) child;
if ( String.valueOf( elementElem.getAttribute( "name" ) ).equals( name.getLocalPart() ) )
{
return new XsdElement( elementElem, schemaElem );
}
}
}
return null;
}
private static Element getComplexTypeByName( Element schemaElem,
String name )
{
NodeList children = schemaElem.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ )
{
Node child = children.item( i );
if ( child instanceof Element && child.getLocalName().equals( "complexType" ) )
{
Element elementElem = (Element) child;
if ( String.valueOf( elementElem.getAttribute( "name" ) ).equals( name ) )
{
return elementElem;
}
}
}
return null;
}
public static QName getResourcePropertiesDocumentName( PortType portType )
{
Map extAttribs = portType.getExtensionAttributes();
QName rpDocDefQName = (QName) extAttribs.get(
org.apache.ws.resource.properties.v2004_11.ResourcePropertiesConstants.RESOURCE_PROPERTIES_PORTTYPE_ATTRIB );
if ( rpDocDefQName == null )
{
rpDocDefQName =
(QName) extAttribs.get(
org.apache.ws.resource.properties.v2004_06.ResourcePropertiesConstants.RESOURCE_PROPERTIES_PORTTYPE_ATTRIB );
}
return rpDocDefQName;
}
public static QName getMetadataDescriptorName( PortType portType )
{
Map extAttribs = portType.getExtensionAttributes();
return (QName) extAttribs.get( new QName(
"http://docs.oasis-open.org/wsrf/2004/10/wsrf-WSResourceMetadataDescriptor-1.0-draft-01.xsd",
"metadataDescriptor", "wsrmd" ) );
}
public static String getMetadataDescriptorLocation( PortType portType )
throws org.apache.ws.resource.InvalidWsrfWsdlException
{
Map extAttribs = portType.getExtensionAttributes();
final QName METADATA_DESCRIPTOR_PORTTYPE_ATTRIB =
new QName(
"http://docs.oasis-open.org/wsrf/2004/10/wsrf-WSResourceMetadataDescriptor-1.0-draft-01.xsd",
"metadataDescriptorLocation", "wsrmd" );
QName qValue = ( (QName) extAttribs.get( METADATA_DESCRIPTOR_PORTTYPE_ATTRIB ) );
if ( qValue == null )
{
return null;
}
String value = qValue.getLocalPart();
StringTokenizer tokenizer = new StringTokenizer( value );
if ( tokenizer.countTokens() != 2 )
{
throw new org.apache.ws.resource.InvalidWsrfWsdlException( METADATA_DESCRIPTOR_PORTTYPE_ATTRIB +
" attribute on portType must be of the format \"namespaceURI locationURL\"" );
}
tokenizer.nextToken();
return tokenizer.nextToken();
}
private static Element[] getSchemaElements( Definition def )
{
List schemaElems = new ArrayList();
List extElems = def.getTypes().getExtensibilityElements();
for ( int i = 0; i < extElems.size(); i++ )
{
ExtensibilityElement extElem = (ExtensibilityElement) extElems.get( i );
if ( extElem instanceof UnknownExtensibilityElement )
{
UnknownExtensibilityElement unknownExtElem = (UnknownExtensibilityElement) extElem;
Element elem = unknownExtElem.getElement();
if ( elem.getNamespaceURI().equals( XmlConstants.NSURI_SCHEMA_XSD )
&& elem.getLocalName().equals( XmlConstants.XSD_SCHEMA.getLocalPart() ) )
{
schemaElems.add( elem );
}
}
}
return (Element[]) schemaElems.toArray( new Element[0] );
}
private static class XsdElement
{
private Element m_elem;
private Element m_schemaElem;
public XsdElement( Element elem, Element schemaElem )
{
m_elem = elem;
m_schemaElem = schemaElem;
}
public Element getElement()
{
return m_elem;
}
public Element getSchemaElement()
{
return m_schemaElem;
}
}
private static class PrefixResolutionException extends Exception
{
private String m_prefix;
public PrefixResolutionException( String prefix )
{
super();
m_prefix = prefix;
}
public String getPrefix()
{
return m_prefix;
}
}
private static class InvalidValueException extends Exception
{
}
}