| /* |
| * Copyright 2003-2004 The Apache Software Foundation. |
| // (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved |
| * |
| * 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.wsdl2ws; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| |
| import javax.wsdl.Port; |
| import javax.wsdl.Service; |
| import javax.xml.namespace.QName; |
| |
| import org.apache.axis.wsdl.symbolTable.BaseType; |
| import org.apache.axis.wsdl.symbolTable.BindingEntry; |
| import org.apache.axis.wsdl.symbolTable.DefinedType; |
| import org.apache.axis.wsdl.symbolTable.SymbolTable; |
| import org.apache.axis.wsdl.symbolTable.TypeEntry; |
| import org.apache.axis.wsdl.wsdl2ws.info.FaultInfo; |
| import org.apache.axis.wsdl.wsdl2ws.info.MethodInfo; |
| import org.apache.axis.wsdl.wsdl2ws.info.ParameterInfo; |
| import org.apache.axis.wsdl.wsdl2ws.info.ServiceInfo; |
| import org.apache.axis.wsdl.wsdl2ws.info.Type; |
| import org.apache.axis.wsdl.wsdl2ws.info.TypeMap; |
| import org.apache.axis.wsdl.wsdl2ws.info.WSDLInfo; |
| import org.apache.axis.wsdl.wsdl2ws.info.WebServiceContext; |
| import org.apache.axis.wsdl.wsdl2ws.info.WrapperInfo; |
| |
| /** |
| * This is the main class for the WSDL2Ws Tool. This class reuses the code in the |
| * Axis java implementations to parse the WSDL file. Here is what is done: |
| * |
| * 1) create a Symbol table by parsing WSDL file. |
| * 2) create TypeMap object by iterating through types in the Symbol Table. |
| * 3) create WrapperInfo object using command line arguments and SymbolTable information. |
| * 4) create ServiceInfo object parsing the Symbol table. |
| * 5) create WebServiceContext using above three classes and start execution |
| * |
| * @author hemapani@opensource.lk |
| * @author Samisa Abeysinghe (sabeysinghe@virtusa.com) |
| * @author hawkeye (hawkinsj@uk.ibm.com) |
| * @author nadir amra (amra@us.ibm.com) |
| */ |
| public class WSDL2Ws |
| { |
| public static boolean c_veryVerbose = false; |
| public static boolean c_verbose = false; |
| |
| // Command line arguments |
| private CLArgParser c_cmdLineArgs = null; |
| |
| // WSDL parser symbol table |
| private SymbolTable c_symbolTable; |
| |
| // WSDL info. |
| private WSDLInfo c_wsdlInfo; |
| |
| /** |
| * Prints out usage. |
| */ |
| public static void usage() |
| { |
| System.out.println( |
| "java WSDL2Ws -<options> <wsdlfile>\n" |
| + "-h, -help print this message\n" |
| + "-o<folder> target output folder - default is current folder.\n" |
| + "-l<c++|c> target language (c++|c) - default is c++.\n" |
| + "-s<server|client> target side (server|client) - default is server.\n" |
| + "-v, be verbose - will show exception stack trace when\n" |
| + " exceptions occur.\n" |
| + "-vv be very verbose - debug information will be shown.\n" |
| + "-t<timeout> uri resolution timeout in seconds - default\n" |
| + " is 0 (no timeout).\n" |
| + "-b<binding-name> binding name that will be used to generate stub.\n" |
| + "-w<wrapped|unwrapped> generate wrapper style or not - default is wrapped.\n" |
| + " In order for an operation to be eligible for\n" |
| + " wrapper-style, the following criteria must be met:\n" |
| + " -- There is at most one single part in input and\n" |
| + " output messages\n" |
| + " -- Each part definition must reference an element\n" |
| + " -- The input element must have the same name as\n" |
| + " the operation\n" |
| + " -- The input and output elements have no attributes\n" |
| ); |
| } |
| |
| /** |
| * Main entry point. |
| * |
| * @param args |
| * @throws Exception |
| */ |
| public static void main(String[] args) throws Exception |
| { |
| // Kick off code generation |
| WSDL2Ws gen = null; |
| try |
| { |
| gen = new WSDL2Ws(args); |
| gen.generateWrappers(); |
| } |
| catch (Exception e) |
| { |
| if (c_verbose) |
| e.printStackTrace(); |
| else if (e.getMessage() != null) |
| System.out.println("\nERROR: " + e.getMessage()); |
| |
| if (e.getMessage() != null) |
| System.out.println("\nCode generation failed. Please see errors above.\n"); |
| |
| return; |
| } |
| |
| // Indicate code generation complete and show where stored. |
| System.out.println("\nCode generation completed. Generated files in directory\n'" |
| + gen.getCmdLineArgs().getOutputDirectory() + "'."); |
| } |
| |
| /** |
| * Gathers the parameters passed in and parses the WSDL file, generating the symbol table. |
| * |
| * @param args |
| * @throws WrapperFault |
| */ |
| public WSDL2Ws(String[] args) throws WrapperFault |
| { |
| try |
| { |
| // ================================================== |
| // Process the parameters |
| // ================================================== |
| |
| // Get parameters and validate |
| c_cmdLineArgs = new CLArgParser(args); |
| if (!c_cmdLineArgs.areOptionsValid() |
| || c_cmdLineArgs.isSet("h") || c_cmdLineArgs.getArgumentCount() != 1) |
| { |
| usage(); |
| return; |
| } |
| |
| // Verbose mode? |
| c_veryVerbose = c_cmdLineArgs.beVeryVerbose(); |
| c_verbose = c_cmdLineArgs.beVerbose(); |
| |
| // language c or c++ - CUtils.setLanguage MUST be invoked at the very beginning! |
| CUtils.setLanguage(c_cmdLineArgs.getTargetLanguage()); |
| |
| // ================================================== |
| // Parse the WSDL file |
| // ================================================== |
| |
| c_wsdlInfo = new WSDLInfo(c_cmdLineArgs.getURIToWSDL()); |
| c_wsdlInfo.setVerbose(c_veryVerbose); |
| c_wsdlInfo.setTimeout(c_cmdLineArgs.getTimeout()); |
| c_wsdlInfo.setNoWrapperStyle(c_cmdLineArgs.isWrapperStyle() == false); |
| |
| c_symbolTable = c_wsdlInfo.parse(); |
| |
| // ================================================== |
| // Let us do some validation on the WSDL passed in. |
| // ================================================== |
| |
| // Maximum of one <service> tag is supported in WSDL for the time being. |
| if (c_wsdlInfo.getServices().size() > 1) |
| throw new WrapperFault("Multiple service definitions not supported."); |
| |
| // If a service definition is not defined then caller needs to specify a |
| // binding to use in order for us to generate the stubs. Not having service definition will |
| // only result in not knowing the Web service end point, so it is really not necessary. |
| if (c_wsdlInfo.getServices().size() == 0 && !c_cmdLineArgs.isSet("b")) |
| { |
| throw new WrapperFault( |
| "Service definition not found in WSDL document. " |
| + "A service definition must be specified in the WSDL document or " |
| + "a binding needs to be specified using the -b option."); |
| } |
| } |
| catch (WrapperFault e) |
| { |
| throw e; |
| } |
| catch (Exception e) |
| { |
| throw new WrapperFault(e); |
| } |
| } |
| |
| /** |
| * Kicks of the generation of the stub code. |
| * |
| * @throws WrapperFault |
| */ |
| public void generateWrappers() throws Exception |
| { |
| // ================================================== |
| // Get service, ports, binding, and port type |
| // ================================================== |
| |
| Port port = null; |
| BindingEntry bindingEntry = null; |
| |
| //TODO resolve this |
| // this code will generate one stub corresponding to a port. Other ports |
| // are ignored. Should really generate one service stub per port. |
| |
| // If binding is specified, ensure binding is supported and see if there is a port that uses |
| // it in order to get end point. If not specified, then use the service definition to |
| // obtain the information we need. |
| if (c_cmdLineArgs.isSet("b")) |
| { |
| // Validate binding |
| bindingEntry = c_wsdlInfo.getBindEntry(c_cmdLineArgs.getBinding()); |
| if (bindingEntry == null) |
| throw new WrapperFault("Specified binding '" + c_cmdLineArgs.getBinding() + "' not found."); |
| |
| if (!WSDLInfo.isSOAP11Binding(bindingEntry)) |
| throw new WrapperFault("Specified binding '" + c_cmdLineArgs.getBinding() + "' is not supported."); |
| |
| // Get port in order to obtain end point. Not having a port is OK. |
| if (c_wsdlInfo.getServices().size() != 0) |
| port = WSDLInfo.getPort((Service)c_wsdlInfo.getServices().get(0), bindingEntry); |
| } |
| else |
| { |
| // We first ask for SOAP 1.1 ports that have a binding style of document....if |
| // there is none, then we ask for SOAP 1.1 ports that have a binding style of rpc. |
| Service service = (Service)c_wsdlInfo.getServices().get(0); |
| ArrayList servicePorts = c_wsdlInfo.getPortsSOAP11Document(service); |
| if (servicePorts.isEmpty()) |
| servicePorts = c_wsdlInfo.getPortsSOAP11RPC(service); |
| if (servicePorts.isEmpty()) |
| throw new WrapperFault("A port with a supported binding was not found. Ensure SOAP 1.1 XML namespace is used. "); |
| |
| port = (Port)servicePorts.get(0); |
| bindingEntry = c_symbolTable.getBindingEntry(port.getBinding().getQName()); |
| } |
| |
| // ================================================== |
| // Build the context that is needed by the code generators. |
| // ================================================== |
| |
| // Wrapper info |
| WrapperInfo wrapperInfo = |
| new WrapperInfo(bindingEntry.getBindingStyle().getName(), |
| CUtils.getLanguage(), |
| c_cmdLineArgs.getOutputDirectory(), |
| c_cmdLineArgs.getTargetEngine(), |
| c_wsdlInfo.getTargetNameSpaceOfWSDL()); |
| |
| // Service info |
| boolean userRequestedWSDLWrappingStyle = c_cmdLineArgs.isSet("w") && c_cmdLineArgs.isWrapperStyle(); |
| String serviceName = WSDLInfo.getServiceName(bindingEntry); |
| ArrayList serviceMethods = c_wsdlInfo.processServiceMethods(bindingEntry, |
| c_cmdLineArgs.isWrapperStyle(), |
| userRequestedWSDLWrappingStyle); |
| ServiceInfo serviceInfo = new ServiceInfo(serviceName, serviceMethods, WSDLInfo.getTargetEndPointURI(port)); |
| |
| // Context |
| WebServiceContext wsContext = new WebServiceContext(wrapperInfo, serviceInfo, c_wsdlInfo.getTypeMap()); |
| |
| // Generator |
| WebServiceGenerator wsg = WebServiceGeneratorFactory.createWebServiceGenerator(wsContext); |
| |
| // ================================================== |
| // Determine which types to externalize. |
| // ================================================== |
| |
| // There must be a better way to do this |
| exposeReferenceTypes(wsContext); |
| exposeMessagePartsThatAreAnonymousTypes(wsContext); |
| // This call must be last one called of the exposexxx methods! |
| exposeNestedTypesThatAreAnonymousTypes(wsContext); |
| |
| // Dump the map if requested. |
| if (c_veryVerbose) |
| c_wsdlInfo.getTypeMap().dump(); |
| |
| // ================================================== |
| // Generate the artifacts |
| // ================================================== |
| |
| // Generate code |
| wsg.generate(); |
| } |
| |
| // The following 3 exposeXXX methods attempts to expose anonymous types so that |
| // the types are externalized to the user. |
| |
| /** |
| * This method goes through the types and for any types that are referenced works out whether |
| * they need to be exposed as a seperate class. |
| * If they do require to be a seperate class then the name of the type will be changed from |
| * ">nameoftype" to "nameoftype". This will then get picked up later on in the process and the |
| * type will be exposed as a seperate class. |
| * |
| * @param wsContext the webservice context. |
| */ |
| private void exposeReferenceTypes(WebServiceContext wsContext) |
| { |
| // get the main types |
| Collection types = c_symbolTable.getTypeIndex().values(); |
| Iterator typeIterator = types.iterator(); |
| while(typeIterator.hasNext()) |
| { |
| Object highLevelType = typeIterator.next(); |
| if(!(highLevelType instanceof BaseType)) |
| { |
| DefinedType type = (DefinedType)highLevelType; |
| |
| if(!type.getQName().getLocalPart().toString().startsWith(">")) |
| { |
| // It's not an "inner" type so look for the refs (this might not be valid |
| // logic and refs might be acceptable for these types too !) |
| HashSet nestedTypes = type.getNestedTypes(c_symbolTable, true); |
| Iterator nestTypeIter = nestedTypes.iterator(); |
| while(nestTypeIter.hasNext()) |
| { |
| Object nestedType =nestTypeIter.next(); |
| if(!(nestedType instanceof BaseType)) |
| { |
| TypeEntry defType = (TypeEntry)nestedType; |
| |
| TypeEntry referencedType =defType.getRefType(); |
| if (referencedType==null) |
| continue; |
| |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE1: Checking whether to expose ref-types for "+defType.getQName().getLocalPart()); |
| |
| // If ref type is anonymous and thus currently not exposed because |
| // it's an "inner" type, expose it and any nested types (latter is TODO). |
| |
| if(referencedType.getQName().getLocalPart().startsWith(">") |
| && referencedType.getQName().getLocalPart().lastIndexOf(">") == 0) |
| { |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE1: Exposing ref-type "+referencedType.getQName()); |
| |
| Type innerClassType = wsContext.getTypemap().getType(referencedType.getQName()); |
| |
| String newLocalPart = new QName(defType.getQName().getLocalPart()).toString(); |
| innerClassType.externalize(new QName(innerClassType.getName().getNamespaceURI(), newLocalPart)); |
| } |
| } |
| } |
| } |
| |
| } |
| } |
| } |
| |
| /** |
| * This method attempts to find anonymous types in the parameter list of |
| * web-service methods to determine if the type should be exposed. |
| * @param wsContext |
| */ |
| private void exposeMessagePartsThatAreAnonymousTypes(WebServiceContext wsContext) |
| { |
| // get the main types |
| Collection types = c_symbolTable.getTypeIndex().values(); |
| Iterator typeIterator = types.iterator(); |
| while(typeIterator.hasNext()) |
| { |
| Object highLevelType = typeIterator.next(); |
| if(!(highLevelType instanceof BaseType)) |
| { |
| DefinedType type = (DefinedType)highLevelType; |
| if(type.getQName().getLocalPart().toString().startsWith(">")) |
| { |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE2: Checking whether to expose anon type "+type.getQName().getLocalPart()); |
| |
| // this is an "inner" type that will not be exposed |
| // however, it needs to be if it is referenced in a message part. |
| // check all the messages |
| ArrayList methods = wsContext.getServiceInfo().getMethods(); |
| for(int i=0; i<methods.size(); i++) |
| { |
| MethodInfo method = (MethodInfo)methods.get(i); |
| |
| // Check for faults that need to be externalized |
| Collection faultTypes = method.getFaultType(); |
| Iterator faultIterator = faultTypes.iterator(); |
| while(faultIterator.hasNext()) |
| { |
| FaultInfo faultType = (FaultInfo)faultIterator.next(); |
| Collection parameterTypes = faultType.getParams(); |
| Iterator paramIterator = parameterTypes.iterator(); |
| while(paramIterator.hasNext()) |
| { |
| ParameterInfo parameterInfo =(ParameterInfo)paramIterator.next(); |
| Type parameterType = parameterInfo.getType(); |
| |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE2: Exposing fault type "+parameterType.getName()); |
| externalizeTypeAndUpdateTypeMap(wsContext, parameterType); |
| } |
| } |
| |
| // Check input parameters |
| Collection inputParameterTypes = method.getInputParameterTypes(); |
| Iterator paramIterator = inputParameterTypes.iterator(); |
| while(paramIterator.hasNext()) |
| { |
| ParameterInfo parameterInfo =(ParameterInfo)paramIterator.next(); |
| Type parameterType = parameterInfo.getType(); |
| if(parameterType.getName().equals(type.getQName())) |
| { |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE2: Matches input parm, exposing anon type "+parameterType.getName()); |
| externalizeTypeAndUpdateTypeMap(wsContext, parameterType); |
| } |
| } |
| |
| // Check output parameters |
| Collection outputParameterTypes = method.getOutputParameterTypes(); |
| paramIterator = outputParameterTypes.iterator(); |
| while(paramIterator.hasNext()) |
| { |
| ParameterInfo parameterInfo =(ParameterInfo)paramIterator.next(); |
| Type parameterType = parameterInfo.getType(); |
| if(parameterType.getName().equals(type.getQName())) |
| { |
| if(c_veryVerbose) |
| System.out.println( "EXPOSE2: Matches output parm, exposing anon type "+parameterType.getName()); |
| externalizeTypeAndUpdateTypeMap(wsContext, parameterType); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| * @param theOrigMap |
| * @param theType |
| * @param nameMapper |
| */ |
| private void exposeRelatedTypes(TypeMap theOrigMap, Type theType, Hashtable nameMapper) |
| { |
| QName oldName = theType.getName(); |
| Type classType = theOrigMap.getType(oldName); |
| if (classType != null && !classType.isExternalized()) |
| { |
| if(c_veryVerbose) |
| System.out.println("\nEXPOSE4: Externalizing type " + oldName); |
| |
| // Externalize the type - if anonymous we have to change to name |
| if (classType.isAnonymous()) |
| { |
| QName newName = new QName(oldName.getNamespaceURI(), |
| classType.getLanguageSpecificName()); |
| |
| classType.externalize(newName); |
| |
| // add old name to new name mapping to name mapper hash table |
| nameMapper.put(oldName, newName); |
| } |
| else |
| classType.externalize(true); |
| |
| // Now check to see related types of this type - recursively. |
| Iterator relatedTypesIt = theType.getRelatedTypes(); |
| Type relatedType; |
| while (relatedTypesIt.hasNext()) |
| { |
| relatedType = (Type) relatedTypesIt.next(); |
| if (!relatedType.isExternalized()) |
| exposeRelatedTypes(theOrigMap, relatedType, nameMapper); |
| } |
| } |
| } |
| |
| /** |
| * |
| * @param wsContext |
| */ |
| private void exposeNestedTypesThatAreAnonymousTypes(WebServiceContext wsContext) |
| { |
| // Go through the externalized types in the typemap and externalize the |
| // related types used by each externalized type. In order to complete the externalization, |
| // we need to remove the entry from the typemap and replace it with a new name. |
| // However, this will have to be done after we have iterated through the typemap since |
| // updating the typemap as we iterate through it will result in an exception. |
| // So we have a hash table to map old names to new names for those types that have been |
| // externalized. |
| Hashtable nameMapper = new Hashtable(); |
| Iterator typesIt = wsContext.getTypemap().getTypes().iterator(); |
| Type type; |
| while (typesIt.hasNext()) |
| { |
| type = (Type) typesIt.next(); |
| if (type.isExternalized()) |
| { |
| if(c_veryVerbose) |
| System.out.println("\nEXPOSE3: Checking related types for type " + type.getName()); |
| |
| Iterator relatedTypesIt = type.getRelatedTypes(); |
| Type relatedType; |
| while (relatedTypesIt.hasNext()) |
| { |
| relatedType = (Type) relatedTypesIt.next(); |
| if (!relatedType.isExternalized()) |
| exposeRelatedTypes(wsContext.getTypemap(), relatedType, nameMapper); |
| } |
| } |
| } |
| |
| // Now update the typemap, replacing old names with new names, using the hash table that |
| // maps old names to new names. |
| QName oldName; |
| QName newName; |
| |
| for (Enumeration e = nameMapper.keys(); e.hasMoreElements() ;) |
| { |
| oldName = (QName) e.nextElement(); |
| newName = (QName) nameMapper.get(oldName); |
| type = wsContext.getTypemap().getType(oldName); |
| if (type != null) |
| { |
| wsContext.getTypemap().removeType(oldName); |
| wsContext.getTypemap().addType(newName, type); |
| } |
| } |
| } |
| |
| /** |
| * |
| * @param wsContext |
| * @param parameterType |
| */ |
| private void externalizeTypeAndUpdateTypeMap(WebServiceContext wsContext, Type parameterType) |
| { |
| QName oldName = parameterType.getName(); |
| Type innerClassType = wsContext.getTypemap().getType(oldName); |
| if (innerClassType != null && !innerClassType.isExternalized()) |
| { |
| QName newTypeName = new QName(parameterType.getName().getNamespaceURI(), |
| parameterType.getLanguageSpecificName()); |
| |
| innerClassType.externalize(newTypeName); |
| |
| // Update the typemap with new info |
| wsContext.getTypemap().removeType(oldName); |
| wsContext.getTypemap().addType(newTypeName, innerClassType); |
| } |
| } |
| |
| /** |
| * Return command line arguments. |
| * |
| * @return c_cmdLineArgs |
| */ |
| private CLArgParser getCmdLineArgs() |
| { |
| return c_cmdLineArgs; |
| } |
| } |