/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.axis2.description.java2wsdl; import org.apache.axis2.AxisFault; import org.apache.axis2.deployment.util.Utils; import org.apache.axis2.description.AxisMessage; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.WSDL2Constants; import org.apache.axis2.wsdl.WSDLConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaComplexType; import org.apache.ws.commons.schema.XmlSchemaElement; import org.apache.ws.commons.schema.XmlSchemaSequence; import org.apache.ws.commons.schema.utils.NamespaceMap; import org.w3c.dom.Document; import javax.xml.namespace.QName; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; public class DocLitBareSchemaGenerator extends DefaultSchemaGenerator { private static final Log log = LogFactory.getLog(DocLitBareSchemaGenerator.class); private HashMap<String,Method> processedParameters = new LinkedHashMap<String,Method>(); public DocLitBareSchemaGenerator(ClassLoader loader, String className, String schematargetNamespace, String schematargetNamespacePrefix, AxisService service) throws Exception { super(loader, className, schematargetNamespace, schematargetNamespacePrefix, service); } @Override protected Method[] processMethods(Method[] declaredMethods) throws Exception { ArrayList<Method> list = new ArrayList<Method>(); //short the elements in the array Arrays.sort(declaredMethods, new MathodComparator()); // since we do not support overload HashMap<String, Method> uniqueMethods = new LinkedHashMap<String, Method>(); XmlSchemaComplexType methodSchemaType; XmlSchemaSequence sequence; for (Method jMethod : declaredMethods) { if (jMethod.isBridge() || jMethod.getDeclaringClass().getName().equals(Object.class.getName())) { continue; } String methodName = jMethod.getName(); // no need to think abt this method , since that is system // config method if (excludeMethods.contains(methodName)) { continue; } if (uniqueMethods.get(methodName) != null) { log.warn("We don't support method overloading. Ignoring [" + methodName + "]"); continue; } if (!Modifier.isPublic(jMethod.getModifiers())) { // no need to generate Schema for non public methods continue; } boolean addToService = false; AxisOperation axisOperation = service.getOperation(new QName(methodName)); if (axisOperation == null) { axisOperation = Utils.getAxisOperationForJmethod(jMethod); if (WSDL2Constants.MEP_URI_ROBUST_IN_ONLY.equals( axisOperation.getMessageExchangePattern())) { AxisMessage outMessage = axisOperation.getMessage( WSDLConstants.MESSAGE_LABEL_OUT_VALUE); if (outMessage != null) { outMessage.setName(methodName + RESULT); } } addToService = true; } // Maintain a list of methods we actually work with list.add(jMethod); processException(jMethod, axisOperation); uniqueMethods.put(methodName, jMethod); //create the schema type for the method wrapper uniqueMethods.put(methodName, jMethod); Class<?>[] paras = jMethod.getParameterTypes(); Type[] genericParameterTypes = jMethod.getGenericParameterTypes(); String parameterNames[] = methodTable.getParameterNames(methodName); AxisMessage inMessage = axisOperation.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE); if (inMessage != null) { inMessage.setName(methodName + "RequestMessage"); } Annotation[][] parameterAnnotation = jMethod.getParameterAnnotations(); if (paras.length > 1) { sequence = new XmlSchemaSequence(); methodSchemaType = createSchemaTypeForMethodPart(methodName); methodSchemaType.setParticle(sequence); inMessage.setElementQName(typeTable.getQNamefortheType(methodName)); service.addMessageElementQNameToOperationMapping(methodSchemaType.getQName(), axisOperation); inMessage.setPartName(methodName); for (int j = 0; j < paras.length; j++) { Class<?> methodParameter = paras[j]; String parameterName = getParameterName(parameterAnnotation, j, parameterNames); if (generateRequestSchema(methodParameter, parameterName, jMethod, sequence, genericParameterTypes[j])) { break; } } } else if (paras.length == 1) { if (paras[0].isArray()) { sequence = new XmlSchemaSequence(); methodSchemaType = createSchemaTypeForMethodPart(methodName); methodSchemaType.setParticle(sequence); Class<?> methodParameter = paras[0]; inMessage.setElementQName(typeTable.getQNamefortheType(methodName)); service.addMessageElementQNameToOperationMapping(methodSchemaType.getQName(), axisOperation); inMessage.setPartName(methodName); String parameterName = getParameterName(parameterAnnotation, 0, parameterNames); if (generateRequestSchema(methodParameter, parameterName, jMethod, sequence, genericParameterTypes[0])) { break; } } else { String parameterName = getParameterName(parameterAnnotation, 0, parameterNames); Class<?> methodParameter = paras[0]; Method processMethod = processedParameters.get(parameterName); if (processMethod != null) { throw new AxisFault("Inavalid Java class," + " there are two methods [" + processMethod.getName() + " and " + jMethod.getName() + " ]which have the same parameter names"); } else { processedParameters.put(parameterName, jMethod); if (methodParameter != null && Map.class.isAssignableFrom(methodParameter)) { generateBareSchemaTypeForMap(parameterName, genericParameterTypes[0], null); } else if (methodParameter != null && Collection.class .isAssignableFrom(methodParameter)) { sequence = new XmlSchemaSequence(); methodSchemaType = createSchemaTypeForMethodPart(methodName); methodSchemaType.setParticle(sequence); generateBareSchemaTypeForCollection(sequence, genericParameterTypes[0], parameterName, methodName); parameterName = methodName; } else if (methodParameter != null && Document.class.isAssignableFrom(methodParameter)) { generateBareSchemaTypeForDocument(null, parameterName); } else { generateSchemaForType(null, methodParameter, parameterName); } inMessage.setElementQName(typeTable.getQNamefortheType(parameterName)); inMessage.setPartName(parameterName); inMessage.setWrapped(false); service.addMessageElementQNameToOperationMapping(typeTable.getQNamefortheType(parameterName), axisOperation); } } } // for its return type Class<?> returnType = jMethod.getReturnType(); Type genericReturnType = jMethod.getGenericReturnType(); String methodTypeName = jMethod.getName() + RESULT; String returnName = "return"; if (!"void".equals(jMethod.getReturnType().getName())) { AxisMessage outMessage = axisOperation.getMessage( WSDLConstants.MESSAGE_LABEL_OUT_VALUE); if (returnType.isArray()) { methodSchemaType = createSchemaTypeForMethodPart(methodTypeName); sequence = new XmlSchemaSequence(); methodSchemaType.setParticle(sequence); if (nonRpcMethods.contains(methodName)) { generateSchemaForType(sequence, null, returnName); } else { generateSchemaForType(sequence, returnType, returnName); } } else { if (returnType != null && Document.class.isAssignableFrom(returnType)) { generateBareSchemaTypeForDocument(null, methodTypeName); } else if (returnType != null && Map.class.isAssignableFrom(returnType)) { generateBareSchemaTypeForMap(methodTypeName, genericReturnType, null); } else if (returnType != null && Collection.class.isAssignableFrom(returnType)) { sequence = new XmlSchemaSequence(); methodSchemaType = createSchemaTypeForMethodPart(methodTypeName); methodSchemaType.setParticle(sequence); generateBareSchemaTypeForCollection(sequence, genericReturnType, returnName, methodName); } else { generateSchemaForType(null, returnType, methodTypeName); } outMessage.setWrapped(false); } outMessage.setElementQName(typeTable.getQNamefortheType(methodTypeName)); outMessage.setName(methodName + "ResponseMessage"); outMessage.setPartName(methodTypeName); service.addMessageElementQNameToOperationMapping( typeTable.getQNamefortheType(methodTypeName), axisOperation); } if (addToService) { service.addOperation(axisOperation); } } return list.toArray(new Method[list.size()]); } private boolean generateRequestSchema(Class<?> methodParameter, String parameterName, Method jMethod, XmlSchemaSequence sequence, Type genericParameterType) throws Exception { if (nonRpcMethods.contains(jMethod.getName())) { generateSchemaForType(sequence, null, jMethod.getName()); return true; } else if (methodParameter != null && Map.class.isAssignableFrom(methodParameter)){ generateBareSchemaTypeForMap(parameterName, genericParameterType, sequence); } else if (methodParameter != null && Collection.class.isAssignableFrom(methodParameter)) { generateBareSchemaTypeForCollection(sequence, genericParameterType, parameterName, jMethod.getName()); } else if (methodParameter != null && Document.class.isAssignableFrom(methodParameter)) { generateBareSchemaTypeForDocument(sequence, parameterName); } else { generateSchemaForType(sequence, methodParameter, parameterName); } return false; } private QName generateSchemaForType(XmlSchemaSequence sequence, Class<?> type, String partName) throws Exception { boolean isArrayType = false; if (type != null) { isArrayType = type.isArray(); } if (isArrayType) { type = type.getComponentType(); } if (AxisFault.class.getName().equals(type)) { return null; } String classTypeName; if (type == null) { classTypeName = "java.lang.Object"; } else { classTypeName = type.getName(); } if (isArrayType && "byte".equals(classTypeName)) { classTypeName = "base64Binary"; isArrayType = false; } if (isDataHandler(type)) { classTypeName = "base64Binary"; } QName schemaTypeName = typeTable.getSimpleSchemaTypeName(classTypeName); if (schemaTypeName == null && type != null) { schemaTypeName = generateSchema(type); addContentToMethodSchemaType(sequence, schemaTypeName, partName, isArrayType); String schemaNamespace = resolveSchemaNamespace(getQualifiedName(type.getPackage())); addImport(getXmlSchema(schemaNamespace), schemaTypeName); if(sequence==null){ generateSchemaForSingleElement(schemaTypeName, partName, isArrayType); } } else { if (sequence == null) { generateSchemaForSingleElement(schemaTypeName, partName, isArrayType); } else { addContentToMethodSchemaType(sequence, schemaTypeName, partName, isArrayType); } } addImport(getXmlSchema(schemaTargetNameSpace), schemaTypeName); return schemaTypeName; } protected void generateSchemaForSingleElement(QName schemaTypeName, String paraName, boolean isArray) throws Exception { XmlSchemaElement elt1 = new XmlSchemaElement(getXmlSchema(schemaTargetNameSpace), false); elt1.setName(paraName); elt1.setSchemaTypeName(schemaTypeName); elt1.setNillable(true); QName elementName = new QName(schemaTargetNameSpace, paraName, schema_namespace_prefix); XmlSchema xmlSchema = getXmlSchema(schemaTargetNameSpace); xmlSchema.getElements().put(elementName, elt1); xmlSchema.getItems().add(elt1); typeTable.addComplexSchema(paraName, elementName); } /** * Generate schema construct for given type * * @param localPartName */ private XmlSchemaComplexType createSchemaTypeForMethodPart(String localPartName) { XmlSchema xmlSchema = getXmlSchema(schemaTargetNameSpace); QName elementName = new QName(this.schemaTargetNameSpace, localPartName, this.schema_namespace_prefix); XmlSchemaComplexType complexType = getComplexTypeForElement(xmlSchema, elementName); if (complexType == null) { complexType = new XmlSchemaComplexType(xmlSchema, false); XmlSchemaElement globalElement = new XmlSchemaElement(xmlSchema, false); globalElement.setSchemaType(complexType); globalElement.setName(localPartName); xmlSchema.getItems().add(globalElement); xmlSchema.getElements().put(elementName, globalElement); } typeTable.addComplexSchema(localPartName, elementName); return complexType; } // TODO: explain why we need to override the method if the implementation is identical! @Override protected XmlSchema getXmlSchema(String targetNamespace) { XmlSchema xmlSchema; if ((xmlSchema = schemaMap.get(targetNamespace)) == null) { String targetNamespacePrefix; if (targetNamespace.equals(schemaTargetNameSpace) && schema_namespace_prefix != null) { targetNamespacePrefix = schema_namespace_prefix; } else { targetNamespacePrefix = generatePrefix(); } xmlSchema = new XmlSchema(targetNamespace, xmlSchemaCollection); xmlSchema.setAttributeFormDefault(getAttrFormDefaultSetting()); xmlSchema.setElementFormDefault(getElementFormDefaultSetting()); targetNamespacePrefixMap.put(targetNamespace, targetNamespacePrefix); schemaMap.put(targetNamespace, xmlSchema); NamespaceMap prefixmap = new NamespaceMap(); prefixmap.put(DEFAULT_SCHEMA_NAMESPACE_PREFIX, URI_2001_SCHEMA_XSD); prefixmap.put(targetNamespacePrefix, targetNamespace); xmlSchema.setNamespaceContext(prefixmap); } return xmlSchema; } /** * Generate bare schema type for map. * * @param paraName the para name * @param genericParameterType the generic parameter type * @param sequence the sequence * @throws Exception the exception */ private void generateBareSchemaTypeForMap(String paraName, Type genericParameterType, XmlSchemaSequence sequence) throws Exception { QName schemaTypeName = generateSchemaTypeForMap(sequence, genericParameterType, paraName, false); if (sequence != null) { return; } XmlSchema xmlSchema = getXmlSchema(schemaTargetNameSpace); XmlSchemaElement elt1 = new XmlSchemaElement(xmlSchema, false); elt1.setSchemaTypeName(schemaTypeName); elt1.setName(paraName); elt1.setNillable(true); QName elementName = new QName(schemaTargetNameSpace, paraName, schema_namespace_prefix); xmlSchema.getElements().put(elementName, elt1); xmlSchema.getItems().add(elt1); typeTable.addComplexSchema(paraName, elementName); } /** * Generate bare schema type for collection. * * @param sequence the sequence * @param genericType the generic type * @param partName the part name * @param methodName the method name * @throws Exception the exception */ private void generateBareSchemaTypeForCollection( XmlSchemaSequence sequence, Type genericType, String partName, String methodName) throws Exception { QName schemaTypeName = generateSchemaForCollection(sequence, genericType, partName); } /** * Generate bare schema type for document. * * @param sequence * the sequence * @param parameterName * the parameter name */ private void generateBareSchemaTypeForDocument(XmlSchemaSequence sequence, String parameterName) { QName schemaTypeName = generateSchemaTypeForDocument(sequence, parameterName); if (sequence != null) { return; } XmlSchema xmlSchema = getXmlSchema(schemaTargetNameSpace); XmlSchemaElement elt1 = new XmlSchemaElement(xmlSchema, false); elt1.setSchemaTypeName(schemaTypeName); elt1.setName(parameterName); elt1.setNillable(true); QName elementName = new QName(schemaTargetNameSpace, parameterName, schema_namespace_prefix); xmlSchema.getElements().put(elementName, elt1); xmlSchema.getItems().add(elt1); typeTable.addComplexSchema(parameterName, elementName); } }