/* * Copyright 2001-2002,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.axis.wsdl.fromJava; import org.apache.axis.AxisFault; import org.apache.axis.AxisProperties; import org.apache.axis.Constants; import org.apache.axis.InternalException; import org.apache.axis.MessageContext; import org.apache.axis.handlers.soap.SOAPService; import org.apache.axis.components.logger.LogFactory; import org.apache.axis.constants.Style; import org.apache.axis.description.ServiceDesc; import org.apache.axis.encoding.Serializer; import org.apache.axis.encoding.SerializerFactory; import org.apache.axis.encoding.SimpleType; import org.apache.axis.encoding.TypeMapping; import org.apache.axis.encoding.ser.BeanSerializerFactory; import org.apache.axis.encoding.ser.EnumSerializerFactory; import org.apache.axis.soap.SOAPConstants; import org.apache.axis.utils.JavaUtils; import org.apache.axis.utils.Messages; import org.apache.axis.utils.XMLUtils; import org.apache.axis.utils.StringUtils; import org.apache.axis.wsdl.symbolTable.BaseTypeMapping; import org.apache.axis.wsdl.symbolTable.SymbolTable; import org.apache.axis.wsdl.symbolTable.TypeEntry; import org.apache.commons.logging.Log; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.wsdl.Definition; import javax.wsdl.WSDLException; import javax.xml.namespace.QName; import javax.xml.parsers.ParserConfigurationException; import javax.xml.rpc.holders.Holder; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * <p>Description: </p> This class is used to recursively serializes a Java Class into * an XML Schema representation. * <p/> * It has utility methods to create a schema node, assosiate namespaces to the various types * * @author unascribed */ public class Types { /** Field log */ protected static Log log = LogFactory.getLog(Types.class.getName()); /** Field def */ Definition def; /** Field namespaces */ Namespaces namespaces = null; /** Field tm */ TypeMapping tm; /** Field defaultTM */ TypeMapping defaultTM; /** Field targetNamespace */ String targetNamespace; /** Field wsdlTypesElem */ Element wsdlTypesElem = null; /** Field schemaTypes */ HashMap schemaTypes = null; /** Field schemaElementNames */ HashMap schemaElementNames = null; /** Field schemaUniqueElementNames */ HashMap schemaUniqueElementNames = null; /** Field wrapperMap */ HashMap wrapperMap = new HashMap(); /** Field stopClasses */ List stopClasses = null; /** Field beanCompatErrs */ List beanCompatErrs = new ArrayList(); /** Field serviceDesc */ private ServiceDesc serviceDesc = null; /** Keep track of the element QNames we've written to avoid dups */ private Set writtenElementQNames = new HashSet(); /** Which types have we already written? */ Class [] mappedTypes = null; /** The java to wsdl emitter */ Emitter emitter = null; public static boolean isArray(Class clazz) { return clazz.isArray() || java.util.Collection.class.isAssignableFrom(clazz); } private static Class getComponentType(Class clazz) { if (clazz.isArray()) { return clazz.getComponentType(); } else if (java.util.Collection.class.isAssignableFrom(clazz)) { return Object.class; } else { return null; } } /** * This class serailizes a <code>Class</code> to XML Schema. The constructor * provides the context for the streamed node within the WSDL document * * @param def WSDL Definition Element to declare namespaces * @param tm TypeMappingRegistry to handle known types * @param defaultTM default TM * @param namespaces user defined or autogenerated namespace and prefix maps * @param targetNamespace targetNamespace of the document * @param stopClasses * @param serviceDesc */ public Types(Definition def, TypeMapping tm, TypeMapping defaultTM, Namespaces namespaces, String targetNamespace, List stopClasses, ServiceDesc serviceDesc) { this.def = def; this.serviceDesc = serviceDesc; createDocumentFragment(); this.tm = tm; this.defaultTM = defaultTM; mappedTypes = tm.getAllClasses(); this.namespaces = namespaces; this.targetNamespace = targetNamespace; this.stopClasses = stopClasses; schemaElementNames = new HashMap(); schemaUniqueElementNames = new HashMap(); schemaTypes = new HashMap(); } /** * This class serailizes a <code>Class</code> to XML Schema. The constructor * provides the context for the streamed node within the WSDL document * * @param def WSDL Definition Element to declare namespaces * @param tm TypeMappingRegistry to handle known types * @param defaultTM default TM * @param namespaces user defined or autogenerated namespace and prefix maps * @param targetNamespace targetNamespace of the document * @param stopClasses * @param serviceDesc * @param emitter Java2Wsdl emitter */ public Types(Definition def, TypeMapping tm, TypeMapping defaultTM, Namespaces namespaces, String targetNamespace, List stopClasses, ServiceDesc serviceDesc, Emitter emitter) { this(def, tm, defaultTM, namespaces, targetNamespace, stopClasses, serviceDesc); this.emitter = emitter; } /** * Return the namespaces object for the current context * * @return */ public Namespaces getNamespaces() { return namespaces; } /** * Loads the types from the input schema file. * * @param inputSchema file or URL * @throws IOException * @throws WSDLException * @throws SAXException * @throws ParserConfigurationException */ public void loadInputSchema(String inputSchema) throws IOException, WSDLException, SAXException, ParserConfigurationException { // Read the input wsdl file into a Document Document doc = XMLUtils.newDocument(inputSchema); // Ensure that the root element is xsd:schema Element root = doc.getDocumentElement(); if (root.getLocalName().equals("schema") && Constants.isSchemaXSD(root.getNamespaceURI())) { Node schema = docHolder.importNode(root, true); if (null == wsdlTypesElem) { writeWsdlTypesElement(); } wsdlTypesElem.appendChild(schema); // Create a symbol table and populate it with the input types BaseTypeMapping btm = new BaseTypeMapping() { public String getBaseName(QName qNameIn) { QName qName = new QName(qNameIn.getNamespaceURI(), qNameIn.getLocalPart()); Class cls = defaultTM.getClassForQName(qName); if (cls == null) { return null; } else { return JavaUtils.getTextClassName(cls.getName()); } } }; SymbolTable symbolTable = new SymbolTable(btm, true, false, false); symbolTable.populateTypes(new URL(inputSchema), doc); processSymTabEntries(symbolTable); } else { // If not, we'll just bail out... perhaps we should log a warning // or throw an exception? ; } } /** * Walk the type/element entries in the symbol table and * add each one to the list of processed types. This prevents * the types from being duplicated. * * @param symbolTable */ private void processSymTabEntries(SymbolTable symbolTable) { Iterator iterator = symbolTable.getElementIndex().entrySet().iterator(); while (iterator.hasNext()) { Map.Entry me = (Map.Entry) iterator.next(); QName name = (QName) me.getKey(); TypeEntry te = (TypeEntry) me.getValue(); String prefix = XMLUtils.getPrefix(name.getNamespaceURI(), te.getNode()); if (!((null == prefix) || "".equals(prefix))) { namespaces.putPrefix(name.getNamespaceURI(), prefix); def.addNamespace(prefix, name.getNamespaceURI()); } addToElementsList(name); } iterator = symbolTable.getTypeIndex().entrySet().iterator(); while (iterator.hasNext()) { Map.Entry me = (Map.Entry) iterator.next(); QName name = (QName) me.getKey(); TypeEntry te = (TypeEntry) me.getValue(); String prefix = XMLUtils.getPrefix(name.getNamespaceURI(), te.getNode()); if (!((null == prefix) || "".equals(prefix))) { namespaces.putPrefix(name.getNamespaceURI(), prefix); def.addNamespace(prefix, name.getNamespaceURI()); } addToTypesList(name); } } /** * Load the types from the input wsdl file. * * @param inputWSDL file or URL * @throws IOException * @throws WSDLException * @throws SAXException * @throws ParserConfigurationException */ public void loadInputTypes(String inputWSDL) throws IOException, WSDLException, SAXException, ParserConfigurationException { // Read the input wsdl file into a Document Document doc = XMLUtils.newDocument(inputWSDL); // Search for the 'types' element NodeList elements = doc.getChildNodes(); if ((elements.getLength() > 0) && elements.item(0).getLocalName().equals("definitions")) { elements = elements.item(0).getChildNodes(); for (int i = 0; (i < elements.getLength()) && (wsdlTypesElem == null); i++) { Node node = elements.item(i); if ((node.getLocalName() != null) && node.getLocalName().equals("types")) { wsdlTypesElem = (Element) node; } } } // If types element not found, there is no need to continue. if (wsdlTypesElem == null) { return; } // Import the types element into the Types docHolder document wsdlTypesElem = (Element) docHolder.importNode(wsdlTypesElem, true); docHolder.appendChild(wsdlTypesElem); // Create a symbol table and populate it with the input wsdl document BaseTypeMapping btm = new BaseTypeMapping() { public String getBaseName(QName qNameIn) { QName qName = new QName(qNameIn.getNamespaceURI(), qNameIn.getLocalPart()); Class cls = tm.getClassForQName(qName); if (cls == null) { return null; } else { return JavaUtils.getTextClassName(cls.getName()); } } }; SymbolTable symbolTable = new SymbolTable(btm, true, false, false); symbolTable.populate(null, doc); processSymTabEntries(symbolTable); } /** * Write out a type referenced by a part type attribute. * * @param type <code>Class</code> to generate the XML Schema info for * @param qname <code>QName</code> of the type. If null, qname is * defaulted from the class. * @return the QName of the generated Schema type, null if void, * if the Class type cannot be converted to a schema type * then xsd:anytype is returned. * @throws AxisFault */ public QName writeTypeForPart(Class type, QName qname) throws AxisFault { // patch by costin to fix an NPE; commented out till we find out what the problem is // if you get NullPointerExceptions in this class, turn it on and submit some // replicable test data to the Axis team via bugzilla /* * if( type==null ) { * return null; * } */ if (type.getName().equals("void")) { return null; } if (Holder.class.isAssignableFrom(type)) { type = JavaUtils.getHolderValueType(type); } // Get the qname if ((qname == null) || (Constants.isSOAP_ENC(qname.getNamespaceURI()) && "Array".equals(qname.getLocalPart()))) { qname = getTypeQName(type); if (qname == null) { throw new AxisFault("Class:" + type.getName()); } } if (!makeTypeElement(type, qname, null)) { qname = Constants.XSD_ANYTYPE; } return qname; } /** * Write out a type (and its subtypes) referenced by a part type attribute. * * @param type <code>Class</code> to generate the XML Schema info for * @param qname <code>QName</code> of the type. If null, qname is * defaulted from the class. * @return the QName of the generated Schema type, null if void, * if the Class type cannot be converted to a schema type * then xsd:anytype is returned. */ public QName writeTypeAndSubTypeForPart(Class type, QName qname) throws AxisFault { // Write out type in parameter QName qNameRet = writeTypeForPart(type, qname); // If mappedTypesexists // Will write subTypes of the type in parameters if (mappedTypes != null) { for (int i = 0; i < mappedTypes.length; i++) { Class tempMappedType = mappedTypes[i]; QName name; // If tempMappedType is subtype of the "type" parameter // and type is not Object (Object superclass of all Java class...) // write the subtype if (tempMappedType != null && type != Object.class && tempMappedType != type && type.isAssignableFrom(tempMappedType)) { name = tm.getTypeQName(tempMappedType); if (!isAnonymousType(name)) { writeTypeForPart(tempMappedType, name); } // Only do each one once. This is OK to do because each // Types instance is for generating a single WSDL. mappedTypes[i] = null; } } } //if (mappedTyped != null) { return qNameRet; } /** * Write out an element referenced by a part element attribute. * * @param type <code>Class</code> to generate the XML Schema info for * @param qname <code>QName</code> of the element. If null, qname is * defaulted from the class. * @return the QName of the generated Schema type, null if no element * @throws AxisFault */ public QName writeElementForPart(Class type, QName qname) throws AxisFault { // patch by costin to fix an NPE; commented out till we find out what the problem is // if you get NullPointerExceptions in this class, turn it on and submit some // replicable test data to the Axis team via bugzilla /* * if( type==null ) { * return null; * } */ if (type.getName().equals("void")) { return null; } if (Holder.class.isAssignableFrom(type)) { type = JavaUtils.getHolderValueType(type); } // Get the qname if ((qname == null) || (Constants.isSOAP_ENC(qname.getNamespaceURI()) && "Array".equals(qname.getLocalPart()))) { qname = getTypeQName(type); if (qname == null) { throw new AxisFault("Class:" + type.getName()); } } // Return null it a simple type (not an element) String nsURI = qname.getNamespaceURI(); if (Constants.isSchemaXSD(nsURI) || (Constants.isSOAP_ENC(nsURI) && !"Array".equals(qname.getLocalPart()))) { return null; } // Make sure a types section is present if (wsdlTypesElem == null) { writeWsdlTypesElement(); } // Write Element, if problems occur return null. if (writeTypeAsElement(type, qname) == null) { qname = null; } return qname; } /** * Write the element definition for a WRAPPED operation. This will * write out any necessary namespace/schema declarations, an an element * definition with an internal (anonymous) complexType. The name of the * element will be *foo*Request or *foo*Response depending on whether the * request boolean is true. If the operation contains parameters, then * we also generate a >sequence< node underneath the complexType, * and return it for later use by writeWrappedParameter() below. * * @param qname the desired element QName * @param request true if we're writing the request wrapper, false if * writing the response. * @param hasParams true if there are parameters, and thus a sequence * node is needed * @return a DOM Element for the sequence, inside which we'll write the * parameters as elements, or null if there are no parameters * @throws AxisFault */ public Element writeWrapperElement( QName qname, boolean request, boolean hasParams) throws AxisFault { // Make sure a types section is present if (wsdlTypesElem == null) { writeWsdlTypesElement(); } // Write the namespace definition for the wrapper writeTypeNamespace(qname.getNamespaceURI()); // Create an <element> for the wrapper Element wrapperElement = docHolder.createElement("element"); writeSchemaElementDecl(qname, wrapperElement); wrapperElement.setAttribute("name", qname.getLocalPart()); // Create an anonymous <complexType> for the wrapper Element complexType = docHolder.createElement("complexType"); wrapperElement.appendChild(complexType); // If we have parameters in the operation, create a <sequence> // under the complexType and return it. if (hasParams) { Element sequence = docHolder.createElement("sequence"); complexType.appendChild(sequence); return sequence; } return null; } /** * Write a parameter (a sub-element) into a sequence generated by * writeWrapperElement() above. * * @param sequence the <sequence> in which we're writing * @param name is the name of an element to add to the wrapper element. * @param type is the QName of the type of the element. * @param javaType * @throws AxisFault */ public void writeWrappedParameter( Element sequence, String name, QName type, Class javaType) throws AxisFault { if (javaType == void.class) { return; } // JAX-RPC 1.1 says that byte[] should always be a Base64Binary // This (rather strange) hack will ensure that we don't map it // in to an maxoccurs=unbounded array. if (javaType.isArray() && !javaType.equals(byte[].class)) { type = writeTypeForPart(javaType.getComponentType(), null); } else { type = writeTypeForPart(javaType, type); } if (type == null) { // TODO: throw an Exception!! return; } Element childElem; if (isAnonymousType(type)) { childElem = createElementWithAnonymousType(name, javaType, false, docHolder); } else { // Create the child <element> and add it to the wrapper <sequence> childElem = docHolder.createElement("element"); childElem.setAttribute("name", name); String prefix = namespaces.getCreatePrefix(type.getNamespaceURI()); String prefixedName = prefix + ":" + type.getLocalPart(); childElem.setAttribute("type", prefixedName); // JAX-RPC 1.1 says that byte[] should always be a Base64Binary // This (rather strange) hack will ensure that we don't map it // in to an maxoccurs=unbounded array. if (javaType.isArray() && !javaType.equals(byte[].class)) { childElem.setAttribute("maxOccurs", "unbounded"); } } sequence.appendChild(childElem); } /** * Method isAnonymousType * * @param type * @return */ private boolean isAnonymousType(QName type) { return type.getLocalPart().indexOf(SymbolTable.ANON_TOKEN) != -1; } /** * Create a schema element for the given type * * @param type the class type * @param qName * @return the QName of the generated Element or problems occur * @throws AxisFault */ private QName writeTypeAsElement(Class type, QName qName) throws AxisFault { if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) { qName = getTypeQName(type); } writeTypeNamespace(type, qName); String elementType = writeType(type, qName); if (elementType != null) { // Element element = createElementDecl(qName.getLocalPart(), type, qName, isNullable(type), false); // if (element != null) // writeSchemaElement(typeQName,element); return qName; } return null; } /** * write out the namespace declaration and return the type QName for the * given <code>Class</code> * * @param type input Class * @param qName qname of the Class * @return QName for the schema type representing the class */ private QName writeTypeNamespace(Class type, QName qName) { if (qName == null) { qName = getTypeQName(type); } writeTypeNamespace(qName.getNamespaceURI()); return qName; } /** * write out the namespace declaration. * * @param namespaceURI qname of the type */ private void writeTypeNamespace(String namespaceURI) { if ((namespaceURI != null) && !namespaceURI.equals("")) { String pref = def.getPrefix(namespaceURI); if (pref == null) { def.addNamespace(namespaces.getCreatePrefix(namespaceURI), namespaceURI); } } } /** * Return the QName of the specified javaType * * @param javaType input javaType Class * @return QName */ public QName getTypeQName(Class javaType) { QName qName = null; // Use the typeMapping information to lookup the qName. qName = tm.getTypeQName(javaType); // If the javaType is an array and the qName is // SOAP_ARRAY, construct the QName using the // QName of the component type if (isArray(javaType) && Constants.equals(Constants.SOAP_ARRAY, qName)) { Class componentType = getComponentType(javaType); // For WS-I BP compliance, we can't use "ArrayOf" as a type prefix // instead use "MyArrayOf" (gag) String arrayTypePrefix = "ArrayOf"; boolean isWSICompliant = JavaUtils.isTrue( AxisProperties.getProperty(Constants.WSIBP11_COMPAT_PROPERTY)); if (isWSICompliant) { arrayTypePrefix = "MyArrayOf"; } // If component namespace uri == targetNamespace // Construct ArrayOf<componentLocalPart> // Else // Construct ArrayOf_<componentPrefix>_<componentLocalPart> QName cqName = getTypeQName(componentType); if (targetNamespace.equals(cqName.getNamespaceURI())) { qName = new QName(targetNamespace, arrayTypePrefix + cqName.getLocalPart()); } else { String pre = namespaces.getCreatePrefix(cqName.getNamespaceURI()); qName = new QName(targetNamespace, arrayTypePrefix + "_" + pre + "_" + cqName.getLocalPart()); } return qName; } // If a qName was not found construct one using the // class name information. if (qName == null) { String pkg = getPackageNameFromFullName(javaType.getName()); String lcl = getLocalNameFromFullName(javaType.getName()); String ns = namespaces.getCreate(pkg); namespaces.getCreatePrefix(ns); String localPart = lcl.replace('$', '_'); qName = new QName(ns, localPart); } return qName; } /** * Return a string suitable for representing a given QName in the context * of this WSDL document. If the namespace of the QName is not yet * registered, we will register it up in the Definitions. * * @param qname a QName (typically a type) * @return a String containing a standard "ns:localPart" rep of the QName */ public String getQNameString(QName qname) { String prefix = namespaces.getCreatePrefix(qname.getNamespaceURI()); return prefix + ":" + qname.getLocalPart(); } /** * Utility method to get the package name from a fully qualified java class name * * @param full input class name * @return package name */ public static String getPackageNameFromFullName(String full) { if (full.lastIndexOf('.') < 0) { return ""; } else { return full.substring(0, full.lastIndexOf('.')); } } /** * Utility method to get the local class name from a fully qualified java class name * * @param full input class name * @return package name */ public static String getLocalNameFromFullName(String full) { String end = ""; if (full.startsWith("[L")) { end = "[]"; full = full.substring(3, full.length() - 1); } if (full.lastIndexOf('.') < 0) { return full + end; } else { return full.substring(full.lastIndexOf('.') + 1) + end; } } /** * Method writeSchemaTypeDecl * * @param qname * @param element * @throws AxisFault */ public void writeSchemaTypeDecl(QName qname, Element element) throws AxisFault { writeSchemaElement(qname.getNamespaceURI(), element); } /** * Method writeSchemaElementDecl * * @param qname * @param element * @throws AxisFault */ public void writeSchemaElementDecl(QName qname, Element element) throws AxisFault { if (writtenElementQNames.contains(qname)) { throw new AxisFault( Constants.FAULT_SERVER_GENERAL, Messages.getMessage( "duplicateSchemaElement", qname.toString()), null, null); } writeSchemaElement(qname.getNamespaceURI(), element); writtenElementQNames.add(qname); } /** * @deprecated * Please use writeSchemaElement(String namespaceURI, Element element) * * @param qName qName to get the namespace of the schema node * @param element the Element to append to the Schema node * @throws AxisFault */ public void writeSchemaElement(QName qName, Element element) throws AxisFault { writeSchemaElement(qName.getNamespaceURI(), element); } /** * Write out the given Element into the appropriate schema node. * If need be create the schema node as well * * @param namespaceURI namespace this node should get dropped into * @param element the Element to append to the Schema node * @throws AxisFault */ public void writeSchemaElement(String namespaceURI, Element element) throws AxisFault { if (wsdlTypesElem == null) { try { writeWsdlTypesElement(); } catch (Exception e) { log.error(e); return; } } if ((namespaceURI == null) || namespaceURI.equals("")) { throw new AxisFault( Constants.FAULT_SERVER_GENERAL, Messages.getMessage("noNamespace00", namespaceURI), null, null); } Element schemaElem = null; NodeList nl = wsdlTypesElem.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { NamedNodeMap attrs = nl.item(i).getAttributes(); if (attrs != null) { for (int n = 0; n < attrs.getLength(); n++) { Attr a = (Attr) attrs.item(n); if (a.getName().equals("targetNamespace") && a.getValue().equals(namespaceURI)) { schemaElem = (Element) nl.item(i); } } } } if (schemaElem == null) { schemaElem = docHolder.createElement("schema"); wsdlTypesElem.appendChild(schemaElem); schemaElem.setAttribute("xmlns", Constants.URI_DEFAULT_SCHEMA_XSD); schemaElem.setAttribute("targetNamespace", namespaceURI); // Add SOAP-ENC namespace import if necessary if (serviceDesc.getStyle() == Style.RPC) { Element importElem = docHolder.createElement("import"); schemaElem.appendChild(importElem); importElem.setAttribute("namespace", Constants.URI_DEFAULT_SOAP_ENC); } SOAPService service = null; if(MessageContext.getCurrentContext() != null) { service = MessageContext.getCurrentContext().getService(); } if(service != null && isPresent((String) service.getOption("schemaQualified"), namespaceURI)){ schemaElem.setAttribute("elementFormDefault", "qualified"); } else if(service != null && isPresent((String) service.getOption("schemaUnqualified"), namespaceURI)){ // DO nothing..default is unqualified. } else if ((serviceDesc.getStyle() == Style.DOCUMENT) || (serviceDesc.getStyle() == Style.WRAPPED)) { schemaElem.setAttribute("elementFormDefault", "qualified"); } writeTypeNamespace(namespaceURI); } schemaElem.appendChild(element); } /** * check if the namespace is present in the list. * @param list * @param namespace * @return */ private boolean isPresent(String list, String namespace) { if(list == null || list.length()==0) return false; String[] array = StringUtils.split(list,','); for(int i=0;i<array.length;i++){ if(array[i].equals(namespace)) return true; } return false; } /** * Get the Types element for the WSDL document. If not present, create one */ private void writeWsdlTypesElement() { if (wsdlTypesElem == null) { // Create a <wsdl:types> element corresponding to the wsdl namespaces. wsdlTypesElem = docHolder.createElementNS(Constants.NS_URI_WSDL11, "types"); wsdlTypesElem.setPrefix(Constants.NS_PREFIX_WSDL); } } /** * Write a schema representation for the given <code>Class</code>. Recurse * through all the public fields as well as fields represented by java * bean compliant accessor methods. * <p/> * Then return the qualified string representation of the generated type * * @param type Class for which to generate schema * @return a prefixed string for the schema type * @throws AxisFault */ public String writeType(Class type) throws AxisFault { return writeType(type, null); } /** * Write a schema representation for the given <code>Class</code>. Recurse * through all the public fields as well as fields represented by java * bean compliant accessor methods. * <p/> * Then return the qualified string representation of the generated type * * @param type Class for which to generate schema * @param qName of the type to write * @return a prefixed string for the schema type or null if problems occur * @throws AxisFault */ public String writeType(Class type, QName qName) throws AxisFault { // Get a corresponding QName if one is not provided if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) { qName = getTypeQName(type); } if (!makeTypeElement(type, qName, null)) { return null; } return getQNameString(qName); } /** * Method createArrayElement * * @param componentTypeName * @return */ public Element createArrayElement(String componentTypeName) { SOAPConstants constants; MessageContext mc = MessageContext.getCurrentContext(); if(mc==null||mc.getSOAPConstants()==null){ constants = SOAPConstants.SOAP11_CONSTANTS; } else { constants = mc.getSOAPConstants(); } String prefix = namespaces.getCreatePrefix(constants.getEncodingURI()); // ComplexType representation of array Element complexType = docHolder.createElement("complexType"); Element complexContent = docHolder.createElement("complexContent"); complexType.appendChild(complexContent); Element restriction = docHolder.createElement("restriction"); complexContent.appendChild(restriction); restriction.setAttribute("base", prefix + ":Array"); Element attribute = docHolder.createElement("attribute"); restriction.appendChild(attribute); attribute.setAttribute("ref", prefix + ":arrayType"); prefix = namespaces.getCreatePrefix(Constants.NS_URI_WSDL11); attribute.setAttribute(prefix + ":arrayType", componentTypeName); return complexType; } /** * Create an array which is a wrapper type for "item" elements * of a component type. This is basically the unencoded parallel to * a SOAP-encoded array. * * @param componentType * @param itemName the QName of the inner element (right now we only use the localPart) * @return */ public Element createLiteralArrayElement(String componentType, QName itemName) { String itemLocalName = "item"; if (itemName != null) { itemLocalName = itemName.getLocalPart(); } Element complexType = docHolder.createElement("complexType"); Element sequence = docHolder.createElement("sequence"); complexType.appendChild(sequence); Element elem = docHolder.createElement("element"); elem.setAttribute("name", itemLocalName); elem.setAttribute("type", componentType); elem.setAttribute("minOccurs", "0"); elem.setAttribute("maxOccurs", "unbounded"); sequence.appendChild(elem); return complexType; } /** * Returns true if indicated type matches the JAX-RPC enumeration class. * Note: supports JSR 101 version 0.6 Public Draft * * @param cls * @return */ public static boolean isEnumClass(Class cls) { try { java.lang.reflect.Method m = cls.getMethod("getValue", null); java.lang.reflect.Method m2 = cls.getMethod("toString", null); if ((m != null) && (m2 != null)) { java.lang.reflect.Method m3 = cls.getDeclaredMethod("fromString", new Class[]{ java.lang.String.class}); java.lang.reflect.Method m4 = cls.getDeclaredMethod("fromValue", new Class[]{ m.getReturnType()}); if ((m3 != null) && Modifier.isStatic(m3.getModifiers()) && Modifier.isPublic(m3.getModifiers()) && (m4 != null) && Modifier.isStatic(m4.getModifiers()) && Modifier.isPublic(m4.getModifiers())) { // Return false if there is a setValue member method try { if (cls.getMethod("setValue", new Class[]{ m.getReturnType()}) == null) { return true; } return false; } catch (java.lang.NoSuchMethodException e) { return true; } } } } catch (java.lang.NoSuchMethodException e) { } return false; } /** * Write Enumeration Complex Type * (Only supports enumeration classes of string types) * * @param qName QName of type. * @param cls class of type * @return * @throws NoSuchMethodException * @throws IllegalAccessException * @throws AxisFault */ public Element writeEnumType(QName qName, Class cls) throws NoSuchMethodException, IllegalAccessException, AxisFault { if (!isEnumClass(cls)) { return null; } // Get the base type of the enum class java.lang.reflect.Method m = cls.getMethod("getValue", null); Class base = m.getReturnType(); // Create simpleType, restriction elements Element simpleType = docHolder.createElement("simpleType"); simpleType.setAttribute("name", qName.getLocalPart()); Element restriction = docHolder.createElement("restriction"); simpleType.appendChild(restriction); String baseType = writeType(base, null); restriction.setAttribute("base", baseType); // Create an enumeration using the field values Field[] fields = cls.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; int mod = field.getModifiers(); // Inspect each public static final field of the same type // as the base if (Modifier.isPublic(mod) && Modifier.isStatic(mod) && Modifier.isFinal(mod) && (field.getType() == base)) { // Create an enumeration using the value specified Element enumeration = docHolder.createElement("enumeration"); enumeration.setAttribute("value", field.get(null).toString()); restriction.appendChild(enumeration); } } return simpleType; } /** * Create a top-level element declaration in our generated schema * * @param qname * @param javaType * @param typeQName * @param nillable nillable attribute of the element * @param itemQName * @throws AxisFault */ public void writeElementDecl(QName qname, Class javaType, QName typeQName, boolean nillable, QName itemQName) throws AxisFault { if (writtenElementQNames.contains(qname)) { return; } String name = qname.getLocalPart(); Element element = docHolder.createElement("element"); // Generate an element name that matches the type. element.setAttribute("name", name); if (nillable) { element.setAttribute("nillable", "true"); } /* * These are not legal on top-level elements! * (feel free to delete this block after say Oct 2005) if (omittable) { element.setAttribute("minOccurs", "0"); element.setAttribute("maxOccurs", "1"); } if (javaType.isArray()) { element.setAttribute("maxOccurs", "unbounded"); } */ if (javaType.isArray()) { // TODO : Should check to see if this array type is specifically mapped String componentType = writeType(javaType.getComponentType()); Element complexType = createLiteralArrayElement(componentType, itemQName); element.appendChild(complexType); } else { // Write the type for this element, handling anonymous or named // types appropriately. makeTypeElement(javaType, typeQName, element); } writeSchemaElementDecl(qname, element); } /** * Create Element with a given name and type * * @param elementName the name of the created element * @param elementType schema type representation of the element * @param nullable nullable attribute of the element * @param omittable * @param docHolder * @return the created Element */ public Element createElement(String elementName, String elementType, boolean nullable, boolean omittable, Document docHolder) { Element element = docHolder.createElement("element"); element.setAttribute("name", elementName); if (nullable) { element.setAttribute("nillable", "true"); } if (omittable) { element.setAttribute("minOccurs", "0"); element.setAttribute("maxOccurs", "1"); } if (elementType != null) { element.setAttribute("type", elementType); } return element; } /** * Create Attribute Element with a given name and type * * @param elementName the name of the created element * @param javaType * @param xmlType * @param nullable nullable attribute of the element * @param docHolder * @return the created Element * @throws AxisFault */ public Element createAttributeElement( String elementName, Class javaType, QName xmlType, boolean nullable, Document docHolder) throws AxisFault { Element element = docHolder.createElement("attribute"); element.setAttribute("name", elementName); if (nullable) { element.setAttribute("nillable", "true"); } makeTypeElement(javaType, xmlType, element); return element; } /** * Is the given class one of the simple types? In other words, * do we have a mapping for this type which is in the xsd or * soap-enc namespaces? * * @param type input Class * @return true if the type is a simple type */ boolean isSimpleType(Class type) { QName qname = tm.getTypeQName(type); if (qname == null) { return false; // No mapping } String nsURI = qname.getNamespaceURI(); return (Constants.isSchemaXSD(nsURI) || Constants.isSOAP_ENC(nsURI)); } /** * Is the given class acceptable as an attribute * * @param type input Class * @return true if the type is a simple, enum type or extends SimpleType */ public boolean isAcceptableAsAttribute(Class type) { return isSimpleType(type) || isEnumClass(type) || implementsSimpleType(type); } /** * Does the class implement SimpleType * * @param type input Class * @return true if the type implements SimpleType */ boolean implementsSimpleType(Class type) { Class[] impls = type.getInterfaces(); for (int i = 0; i < impls.length; i++) { if (impls[i] == SimpleType.class) { return true; } } return false; } /** * Generates a unique element name for a given namespace of the form * el0, el1 .... * * @param qName the namespace for the generated element * @return elementname */ // *** NOT USED? *** // // private String generateUniqueElementName(QName qName) { // Integer count = (Integer)schemaUniqueElementNames.get(qName.getNamespaceURI()); // if (count == null) // count = new Integer(0); // else // count = new Integer(count.intValue() + 1); // schemaUniqueElementNames.put(qName.getNamespaceURI(), count); // return "el" + count.intValue(); // } /** * Add the type to an ArrayList and return true if the Schema node * needs to be generated * If the type already exists, just return false to indicate that the type is already * generated in a previous iteration * * @param qName of the type. * @return if the type is added returns true, * else if the type is already present returns false */ private boolean addToTypesList(QName qName) { boolean added = false; String namespaceURI = qName.getNamespaceURI(); ArrayList types = (ArrayList) schemaTypes.get(namespaceURI); // Quick return if schema type (will never add these ourselves) if (Constants.isSchemaXSD(namespaceURI) || (Constants.isSOAP_ENC(namespaceURI) && !"Array".equals(qName.getLocalPart()))) { // Make sure we do have the namespace declared, though... writeTypeNamespace(namespaceURI); return false; } if (types == null) { types = new ArrayList(); types.add(qName.getLocalPart()); writeTypeNamespace(namespaceURI); schemaTypes.put(namespaceURI, types); added = true; } else { if (!types.contains(qName.getLocalPart())) { types.add(qName.getLocalPart()); added = true; } } // If addded, look at the namespace uri to see if the schema element should be // generated. if (added) { String prefix = namespaces.getCreatePrefix(namespaceURI); if (prefix.equals(Constants.NS_PREFIX_SOAP_ENV) || prefix.equals(Constants.NS_PREFIX_SOAP_ENC) || prefix.equals(Constants.NS_PREFIX_SCHEMA_XSD) || prefix.equals(Constants.NS_PREFIX_WSDL) || prefix.equals(Constants.NS_PREFIX_WSDL_SOAP)) { return false; } else { return true; } } return false; } /** * Add the element to an ArrayList and return true if the Schema element * needs to be generated * If the element already exists, just return false to indicate that the type is already * generated in a previous iteration * * @param qName the name space of the element * @return if the type is added returns true, else if the type is already present returns false */ private boolean addToElementsList(QName qName) { if (qName == null) { return false; } boolean added = false; ArrayList elements = (ArrayList) schemaElementNames.get(qName.getNamespaceURI()); if (elements == null) { elements = new ArrayList(); elements.add(qName.getLocalPart()); schemaElementNames.put(qName.getNamespaceURI(), elements); added = true; } else { if (!elements.contains(qName.getLocalPart())) { elements.add(qName.getLocalPart()); added = true; } } return added; } /** * Determines if the field is nullable. All non-primitives are nillable. * * @param type input Class * @return true if nullable */ public static boolean isNullable(Class type) { if (type.isPrimitive()) { return false; } else { return true; } } /** * todo ravi: Get rid of Doccument fragment and import node stuuf, * once I have a handle on the wsdl4j mechanism to get at types. * <p/> * Switch over notes: remove docHolder, docFragment in favor of wsdl4j Types * <p/> * DocumentFragment docFragment; * <p/> * DocumentFragment docFragment; * <p/> * DocumentFragment docFragment; * <p/> * DocumentFragment docFragment; */ // DocumentFragment docFragment; Document docHolder; /** * Method createDocumentFragment */ private void createDocumentFragment() { try { this.docHolder = XMLUtils.newDocument(); } catch (ParserConfigurationException e) { // This should not occur throw new InternalException(e); } } /** * Method updateNamespaces */ public void updateNamespaces() { Namespaces namespaces = getNamespaces(); Iterator nspIterator = namespaces.getNamespaces(); while (nspIterator.hasNext()) { String nsp = (String) nspIterator.next(); String pref = def.getPrefix(nsp); if (pref == null) { def.addNamespace(namespaces.getCreatePrefix(nsp), nsp); } } } /** * Inserts the type fragment into the given wsdl document and ensures * that definitions from each embedded schema are allowed to reference * schema components from the other sibling schemas. * @param doc */ public void insertTypesFragment(Document doc) { updateNamespaces(); if (wsdlTypesElem == null) return; // Make sure that definitions from each embedded schema are allowed // to reference schema components from the other sibling schemas. Element schemaElem = null; String tns = null; NodeList nl = wsdlTypesElem.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { NamedNodeMap attrs = nl.item(i).getAttributes(); if (attrs == null) continue; // Should never happen. for (int n = 0; n < attrs.getLength(); n++) { Attr a = (Attr) attrs.item(n); if (a.getName().equals("targetNamespace")) { tns = a.getValue(); schemaElem = (Element) nl.item(i); break; } } // Ignore what appears to be a not namespace-qualified // schema definition. if (tns != null && !"".equals(tns.trim())) { // By now we know that an import element might be necessary // for some sibling schemas. However, in the absence of // a symbol table proper, the best we can do is add one // for each sibling schema. Iterator it = schemaTypes.keySet().iterator(); String otherTns; Element importElem; while (it.hasNext()) { if (!tns.equals(otherTns = (String) it.next())) { importElem = docHolder.createElement("import"); importElem.setAttribute("namespace", otherTns); schemaElem.insertBefore(importElem, schemaElem.getFirstChild()); } } } schemaElem = null; tns = null; } // Import the wsdlTypesElement into the doc. org.w3c.dom.Node node = doc.importNode(wsdlTypesElem, true); // Insert the imported element at the beginning of the document doc.getDocumentElement(). insertBefore(node, doc.getDocumentElement().getFirstChild()); } /** * Return the list of classes that we should not emit WSDL for. * * @return */ public List getStopClasses() { return stopClasses; } /** * Create a DOM Element in this context * * @param elementName * @return */ public Element createElement(String elementName) { return docHolder.createElement(elementName); } /** * isBeanCompatible * * @param javaType Class * @param issueErrors if true, issue messages if not compatible * Returns true if it appears that this class is a bean and * can be mapped to a complexType * @return */ protected boolean isBeanCompatible(Class javaType, boolean issueErrors) { // Must be a non-primitive and non array if (javaType.isArray() || javaType.isPrimitive()) { if (issueErrors && !beanCompatErrs.contains(javaType)) { log.warn(Messages.getMessage("beanCompatType00", javaType.getName())); beanCompatErrs.add(javaType); } return false; } // Anything in the java or javax package that // does not have a defined mapping is excluded. if (javaType.getName().startsWith("java.") || javaType.getName().startsWith("javax.")) { if (issueErrors && !beanCompatErrs.contains(javaType)) { log.warn(Messages.getMessage("beanCompatPkg00", javaType.getName())); beanCompatErrs.add(javaType); } return false; } // Return true if appears to be an enum class if (JavaUtils.isEnumClass(javaType)) { return true; } // Must have a default public constructor if not // Throwable if (!java.lang.Throwable.class.isAssignableFrom(javaType)) { try { javaType.getConstructor(new Class[]{ }); } catch (java.lang.NoSuchMethodException e) { if (issueErrors && !beanCompatErrs.contains(javaType)) { log.warn(Messages.getMessage("beanCompatConstructor00", javaType.getName())); beanCompatErrs.add(javaType); } return false; } } // Make sure superclass is compatible Class superClass = javaType.getSuperclass(); if ((superClass != null) && (superClass != java.lang.Object.class) && (superClass != java.lang.Exception.class) && (superClass != java.lang.Throwable.class) && (superClass != java.rmi.RemoteException.class) && (superClass != org.apache.axis.AxisFault.class) && ((stopClasses == null) || !(stopClasses.contains(superClass.getName())))) { if (!isBeanCompatible(superClass, false)) { if (issueErrors && !beanCompatErrs.contains(javaType)) { log.warn(Messages.getMessage("beanCompatExtends00", javaType.getName(), superClass.getName(), javaType.getName())); beanCompatErrs.add(javaType); } return false; } } return true; } /** * Write an <element> with an anonymous internal ComplexType * * @param elementName * @param fieldType * @param omittable * @param ownerDocument * @return * @throws AxisFault */ public Element createElementWithAnonymousType(String elementName, Class fieldType, boolean omittable, Document ownerDocument) throws AxisFault { Element element = docHolder.createElement("element"); element.setAttribute("name", elementName); if (isNullable(fieldType)) { element.setAttribute("nillable", "true"); } if (omittable) { element.setAttribute("minOccurs", "0"); element.setAttribute("maxOccurs", "1"); } makeTypeElement(fieldType, null, element); return element; } private HashMap<String, Boolean> qnameReturnValue = new HashMap<String, Boolean>(); /** * Create a schema type element (either simpleType or complexType) for * the particular type/qName combination. If the type is named, we * handle inserting the new type into the appropriate <schema> * in the WSDL types section. If the type is anonymous, we append the * definition underneath the Element which was passed as the container * (typically a field of a higher-level type or a parameter in a wrapped * operation). * * @param type Java type to write * @param qName the desired type QName * @param containingElement a schema element ("element" or "attribute") * which should either receive a type="" attribute decoration * (for named types) or a child element defining an anonymous * type * @return true if the type was already present or was added, false if there was a problem * @throws AxisFault */ private boolean makeTypeElement(Class type, QName qName, Element containingElement) throws AxisFault { // Get a corresponding QName if one is not provided if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) { qName = getTypeQName(type); } boolean anonymous = isAnonymousType(qName); // Can't have an anonymous type outside of a containing element if (anonymous && (containingElement == null)) { throw new AxisFault( Messages.getMessage( "noContainerForAnonymousType", qName.toString())); } // If we've already got this type (because it's a native type or // because we've already written it), just add the type="" attribute // (if appropriate) and return. // liferay - when added to type list, remember returned value! boolean addedToTypeList = addToTypesList(qName); if (!addedToTypeList && !anonymous) { if (containingElement != null) { containingElement.setAttribute("type", getQNameString(qName)); } // liferay - get result from the map Boolean storedResult = qnameReturnValue.get(getQNameString(qName)); if (storedResult == null) { return true; } return storedResult.booleanValue(); } // look up the serializer in the TypeMappingRegistry SerializerFactory factory; factory = (SerializerFactory) tm.getSerializer(type, qName); // If no factory is found, use the BeanSerializerFactory // if applicable, otherwise issue errors and treat as an anyType if (factory == null) { if (isEnumClass(type)) { factory = new EnumSerializerFactory(type, qName); } else if (isBeanCompatible(type, true)) { factory = new BeanSerializerFactory(type, qName); } else { // liferay if (addedToTypeList) { qnameReturnValue.put(getQNameString(qName), Boolean.FALSE); } return false; } } // factory is not null Serializer ser = (Serializer) factory.getSerializerAs(Constants.AXIS_SAX); // if we can't get a serializer, that is bad. if (ser == null) { throw new AxisFault(Messages.getMessage("NoSerializer00", type.getName())); } Element typeEl; try { typeEl = ser.writeSchema(type, this); } catch (Exception e) { throw AxisFault.makeFault(e); } // If this is an anonymous type, just make the type element a child // of containingElement. If not, set the "type" attribute of // containingElement to the right QName, and make sure the type is // correctly written into the appropriate <schema> element. if (anonymous) { if (typeEl == null) { containingElement.setAttribute("type", getQNameString(getTypeQName(type))); } else { containingElement.appendChild(typeEl); } } else { if (typeEl != null) { typeEl.setAttribute("name", qName.getLocalPart()); // Write the type in the appropriate <schema> writeSchemaTypeDecl(qName, typeEl); } if (containingElement != null) { containingElement.setAttribute("type", getQNameString(qName)); } } // store the mapping of type qname and its correspoding java type if (emitter != null) { emitter.getQName2ClassMap().put(qName, type); } // liferay if (addedToTypeList) { qnameReturnValue.put(getQNameString(qName), Boolean.TRUE); } return true; } /** * return the service description * @return */ public ServiceDesc getServiceDesc() { return serviceDesc; } } /* @generated */