/* * 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.wsdl.codegen.extension; import org.apache.axis2.AxisFault; import org.apache.axis2.description.AxisMessage; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; import org.apache.axis2.wsdl.WSDLConstants; import org.apache.axis2.wsdl.WSDLUtil; import org.apache.axis2.wsdl.codegen.CodeGenConfiguration; import org.apache.axis2.wsdl.codegen.CodeGenerationException; import org.apache.axis2.wsdl.i18n.CodegenMessages; import org.apache.axis2.wsdl.util.ConfigPropertyFileLoader; import org.apache.axis2.wsdl.util.Constants; import org.apache.axis2.wsdl.util.MessagePartInformationHolder; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaAll; import org.apache.ws.commons.schema.XmlSchemaAny; import org.apache.ws.commons.schema.XmlSchemaAttribute; import org.apache.ws.commons.schema.XmlSchemaChoice; import org.apache.ws.commons.schema.XmlSchemaComplexContent; import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension; import org.apache.ws.commons.schema.XmlSchemaComplexType; import org.apache.ws.commons.schema.XmlSchemaContent; import org.apache.ws.commons.schema.XmlSchemaContentModel; import org.apache.ws.commons.schema.XmlSchemaElement; import org.apache.ws.commons.schema.XmlSchemaImport; import org.apache.ws.commons.schema.XmlSchemaInclude; import org.apache.ws.commons.schema.XmlSchemaObject; import org.apache.ws.commons.schema.XmlSchemaParticle; import org.apache.ws.commons.schema.XmlSchemaSequence; import org.apache.ws.commons.schema.XmlSchemaSequenceMember; import org.apache.ws.commons.schema.XmlSchemaSimpleType; import org.apache.ws.commons.schema.XmlSchemaType; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * This extension invokes the schema unwrapper depending on the users setting. it is desirable to * put this extension before other extensions since extnsions such as the databinding extension may * well depend on the schema being unwrapped previously. For a complete unwrap the following format * of the schema is expected < element > < complexType > < sequence > < element * /> < /sequence > < /complexType > < /element > * <p/> * When an unwrapped WSDL is encountered Axis2 generates a wrapper schema and that wrapper schema * has the above mentioned format. This unwrapping algorithm will work on a pure doc/lit WSDL if it * has the above mentioned format only */ public class SchemaUnwrapperExtension extends AbstractCodeGenerationExtension { private CodeGenConfiguration codeGenConfiguration; /** * @param configuration * @throws CodeGenerationException */ public void engage(CodeGenConfiguration configuration) throws CodeGenerationException { this.codeGenConfiguration = configuration; if (!configuration.isParametersWrapped() && !codeGenConfiguration.getOutputLanguage().equals("jax-ws")) { // A check to avoid nasty surprises - Since unwrapping is not // supported by all frameworks, we check the framework name to be // compatible if (!ConfigPropertyFileLoader.getUnwrapSupportedFrameworkNames(). contains(configuration.getDatabindingType())) { throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedforunwrapping")); } else if (!ConfigPropertyFileLoader.getUnwrapDirectFrameworkNames(). contains(configuration.getDatabindingType())) { //walk the schema and find the top level elements List services = configuration.getAxisServices(); AxisService axisService; for (Iterator servicesIter = services.iterator(); servicesIter.hasNext();) { axisService = (AxisService) servicesIter.next(); for (Iterator operations = axisService.getOperations(); operations.hasNext();) { AxisOperation op = (AxisOperation) operations.next(); if (WSDLUtil.isInputPresentForMEP(op.getMessageExchangePattern())) { walkSchema(op.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE), WSDLConstants.INPUT_PART_QNAME_SUFFIX); } // get the out put parameter details as well to unwrap the responses //TODO: support xmlbeans if (configuration.getDatabindingType().equals("adb")) { if (WSDLUtil.isOutputPresentForMEP(op.getMessageExchangePattern())) { walkSchema(op.getMessage(WSDLConstants.MESSAGE_LABEL_OUT_VALUE), WSDLConstants.OUTPUT_PART_QNAME_SUFFIX); } } } } } } } /** * walk the given schema element For a successful unwrapping the element should have the * following structure < element > < complexType > < sequence > < element * /> < /sequence > < /complexType > < /element > */ public void walkSchema(AxisMessage message, String qnameSuffix) throws CodeGenerationException { //nothing to unwrap if (message.getSchemaElement() == null) { return; } List partNameList = new LinkedList(); XmlSchemaElement schemaElement = message.getSchemaElement(); XmlSchemaType schemaType = schemaElement.getSchemaType(); QName schemaTypeQname = schemaElement.getSchemaTypeName(); if (schemaType == null) { if (schemaTypeQname != null) { // find the schema type from all the schemas // now we need to get the schema of the extension type from the parent schema. For that let's first retrieve // the parent schema AxisService axisService = message.getAxisOperation().getAxisService(); ArrayList schemasList = axisService.getSchema(); XmlSchema schema = null; for (Iterator iter = schemasList.iterator(); iter.hasNext();) { schema = (XmlSchema) iter.next(); schemaType = getSchemaType(schema, schemaTypeQname); if (schemaType != null) { break; } } } } if (schemaType instanceof XmlSchemaComplexType) { handleAllCasesOfComplexTypes(schemaType, message, partNameList, qnameSuffix); } else if ((schemaType instanceof XmlSchemaSimpleType) || ((schemaTypeQname != null) && (schemaTypeQname.equals(new QName("http://www.w3.org/2001/XMLSchema", "anyType")))) ) { QName opName = message.getAxisOperation().getName(); partNameList.add(WSDLUtil.getPartQName(opName.getLocalPart(), qnameSuffix, schemaElement.getQName().getLocalPart())); } else if (schemaType == null) { throw new CodeGenerationException("Can not determine the schema type for the " + schemaElement.getName()); } else { //we've no idea how to unwrap a non complexType!!!!!! throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedSchemaFormat", schemaType.getName(), "complexType")); } try { //set in the axis message that the unwrapping was success message.addParameter(getParameter( Constants.UNWRAPPED_KEY, Boolean.TRUE)); // attach the opName and the parts name list into the // axis message by using the holder MessagePartInformationHolder infoHolder = new MessagePartInformationHolder(); infoHolder.setOperationName(message.getAxisOperation().getName()); infoHolder.setPartsList(partNameList); //attach it to the parameters message.addParameter( getParameter(Constants.UNWRAPPED_DETAILS, infoHolder)); } catch (AxisFault axisFault) { throw new CodeGenerationException(axisFault); } } private void handleAllCasesOfComplexTypes(XmlSchemaType schemaType, AxisMessage message, List partNameList, String qnameSuffix) throws CodeGenerationException { // if a complex type name exits for a element then // we keep that complex type to support unwrapping if (schemaType instanceof XmlSchemaComplexType) { XmlSchemaComplexType cmplxType = (XmlSchemaComplexType) schemaType; if (cmplxType.getContentModel() == null) { if (cmplxType.getParticle() != null) { processXMLSchemaSequence(cmplxType.getParticle(), message, partNameList, qnameSuffix); } } else { // now lets handle case with extensions processComplexContentModel(cmplxType, message, partNameList, qnameSuffix); } // handle attributes here processAttributes(cmplxType, message, partNameList, qnameSuffix); } } private void processAttributes(XmlSchemaComplexType complexType, AxisMessage message, List partNameList, String qnameSuffix) { QName opName = message.getAxisOperation().getName(); XmlSchemaAttribute xmlSchemaAttribute; for (XmlSchemaObject item : complexType.getAttributes()) { if (item instanceof XmlSchemaAttribute) { xmlSchemaAttribute = (XmlSchemaAttribute) item; String partName = xmlSchemaAttribute.getName(); partNameList.add( WSDLUtil.getPartQName(opName.getLocalPart(), qnameSuffix, partName)); } } } private void processComplexContentModel(XmlSchemaComplexType cmplxType, AxisMessage message, List partNameList, String qnameSuffix) throws CodeGenerationException { XmlSchemaContentModel contentModel = cmplxType.getContentModel(); if (contentModel instanceof XmlSchemaComplexContent) { XmlSchemaComplexContent xmlSchemaComplexContent = (XmlSchemaComplexContent) contentModel; XmlSchemaContent content = xmlSchemaComplexContent.getContent(); if (content instanceof XmlSchemaComplexContentExtension) { XmlSchemaComplexContentExtension schemaExtension = (XmlSchemaComplexContentExtension) content; // process particles inside this extension, if any if (schemaExtension.getParticle() != null) { processXMLSchemaSequence(schemaExtension.getParticle(), message, partNameList, qnameSuffix); } // now we need to get the schema of the extension type from the parent schema. For that let's first retrieve // the parent schema AxisService axisService = message.getAxisOperation().getAxisService(); ArrayList schemasList = axisService.getSchema(); XmlSchema parentSchema = null; XmlSchema schema = null; XmlSchemaType extensionSchemaType = null; for (Iterator iter = schemasList.iterator(); iter.hasNext();) { schema = (XmlSchema) iter.next(); extensionSchemaType = getSchemaType(schema, schemaExtension.getBaseTypeName()); if (extensionSchemaType != null) { break; } } // ok now we got the parent schema. Now let's get the extension's schema type handleAllCasesOfComplexTypes(extensionSchemaType, message, partNameList, qnameSuffix); } } } private XmlSchemaType getSchemaType(XmlSchema schema, QName typeName) { XmlSchemaType xmlSchemaType = null; if (schema != null) { xmlSchemaType = schema.getTypeByName(typeName); if (xmlSchemaType == null) { // try to find in an import or an include for (XmlSchemaObject object : schema.getExternals()) { if (object instanceof XmlSchemaImport) { XmlSchema schema1 = ((XmlSchemaImport) object).getSchema(); xmlSchemaType = getSchemaType(schema1, typeName); } if (object instanceof XmlSchemaInclude) { XmlSchema schema1 = ((XmlSchemaInclude) object).getSchema(); xmlSchemaType = getSchemaType(schema1, typeName); } if (xmlSchemaType != null) { break; } } } } return xmlSchemaType; } private void processXMLSchemaSequence(XmlSchemaParticle schemaParticle, AxisMessage message, List partNameList, String qnameSuffix) throws CodeGenerationException { if (schemaParticle instanceof XmlSchemaSequence) { // get the name of the operation name and namespace, // part name and hang them somewhere ? The ideal place // would be the property bag in the codegen config! QName opName = message.getAxisOperation().getName(); XmlSchemaSequence sequence = (XmlSchemaSequence) schemaParticle; List<XmlSchemaSequenceMember> items = sequence.getItems(); // if this is an empty sequence, return if (items.isEmpty()) { return; } for (XmlSchemaSequenceMember item : items) { // get each and every element in the sequence and // traverse through them if (item instanceof XmlSchemaElement) { //add the element name to the part name list XmlSchemaElement xmlSchemaElement = (XmlSchemaElement) item; XmlSchemaType schemaType = xmlSchemaElement.getSchemaType(); String partName = null; if (xmlSchemaElement.getRef().getTargetQName() != null) { partName = xmlSchemaElement.getRef().getTargetQName().getLocalPart(); } else { partName = xmlSchemaElement.getName(); } // part names are not unique across messages. Hence // we need some way of making the part name a unique // one (due to the fact that the type mapper // is a global list of types). // The seemingly best way to do that is to // specify a namespace for the part QName reference which // is stored in the list. This part qname is // temporary and should not be used with it's // namespace URI (which happened to be the operation name) // with _input attached to it partNameList.add( WSDLUtil.getPartQName(opName.getLocalPart(), qnameSuffix, partName)); // if the particle contains anything other than // a XMLSchemaElement then we are not in a position // to unwrap it } else if (item instanceof XmlSchemaAny) { // if this is an instance of xs:any, then there is no part name for it. Using ANY_ELEMENT_FIELD_NAME // for it for now //we have to handle both maxoccurs 1 and maxoccurs > 1 situation XmlSchemaAny xmlSchemaAny = (XmlSchemaAny) item; partNameList.add( WSDLUtil.getPartQName(opName.getLocalPart(), qnameSuffix, Constants.ANY_ELEMENT_FIELD_NAME)); } else { throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedSchemaFormat", "unknown type", "Element")); } } //we do not know how to deal with other particles //such as xs:all or xs:choice. Usually occurs when //passed with the user built WSDL where the style //is document. } else if (schemaParticle instanceof XmlSchemaChoice) { throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedSchemaFormat", "choice", "sequence")); } else if (schemaParticle instanceof XmlSchemaAll) { throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedSchemaFormat", "all", "sequence")); } else { throw new CodeGenerationException( CodegenMessages.getMessage("extension.unsupportedSchemaFormat", "unknown", "sequence")); } } /** * Generate a parametes object * * @param key * @param value */ private Parameter getParameter(String key, Object value) { Parameter myParameter = new Parameter(); myParameter.setName(key); myParameter.setValue(value); return myParameter; } }