blob: ecc5fbd3c81d04318f70770c592c3494a638eb59 [file] [log] [blame]
/*
* 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.");
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;
}
}