Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/*============================================================================
**
**    ERG TRANSIT SYSTEMS      Licensed software
**    (C) 2008                 All rights reserved
**
**============================================================================
**
**  Project/Product : wsdl-tools
**  Filename        : WsdlExtract.cs
**  Author(s)       : David Purdie
**
**  Description     : Extract WSDLs from Managed Web Service DLL
**
**
**  Information     :
**   Compiler       : CSHARP 1 and 2
**   Target         : Build Tools
**
**==========================================================================*/

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Xml;

using Microsoft.CSharp;


namespace JATS.WsdlExtractor
{

    /*
    **  Class to contain user command line options
    */
    public class CmdLineParams
    {
        public string AssemblyName;
        public string OutDir = "";
        public string Uri = "http://www.erggroup.com/";
        public int Verbose = 0;
        public ArrayList EnvPath = new ArrayList();

        /*
        **  Constructor
        */
        public CmdLineParams()
        {
            /*
            **  Add default EnvVars to the EnvPath
            **  Cover all bases
            */
            EnvPath.Add ("CS_LIB");
            EnvPath.Add ("LIB");
            EnvPath.Add ("PATH");
        }
    }


    public class App
    {
        static CmdLineParams par = new CmdLineParams();
        static CmdLineParams ParseCommandLine(string[] args)
        {
            /*
            **  Extract command line arguments - very simple
            **  Format:
            **      First must be assembly
            **      Remainder are program options of the form
            **              /x:value        (value starts at value_index)
            **         or   /x
            */
            const int value_index = 3;
            
            par.AssemblyName = args[0];
            for (int i = 1; i < args.Length; i++)
            {
                string arg = args[i];
                if (arg[0] != '/')
                {
                    throw new Exception("Incorrect command line parameter " + (i + 1) + " :" + arg);
                }

                switch (arg[1])
                {
                    case 'r':
                    case 'R':
                        par.Uri = arg.Substring(value_index);
                        break;

                    case 'p':
                    case 'P':
                        par.EnvPath.Insert( 0,arg.Substring(value_index));
                        break;
                        

                    case 'o':
                    case 'O':
                        par.OutDir = arg.Substring(value_index);
                        break;

                    case 'v':
                    case 'V':
                        par.Verbose += 1;
                        break;

                    default:
                        throw new Exception("Incorrect command line parameter " + (i + 1) + " :" + arg);
                }
            }
            return par;
        }

        /*
        **  Main entry point
        */
        static int Main(string[] args)
        {
            Console.WriteLine("Web Service WSDL Extractor");

            /*
            **  Need at least one argument
            */
            if (args.Length <= 1)
            {
                Console.WriteLine(@"Usage: WsdlExtract assembly [/r:URI] [/o:Basedir] [/p:EnvVar] [/v]");
                Console.WriteLine(@"");
                Console.WriteLine(@"assembly   - Path to Web Services DLL");
                Console.WriteLine(@"/o:Output  - Set Base Directory");
                Console.WriteLine(@"/p:EnvVar  - A path to search for required DLLs");
                Console.WriteLine(@"/r:URI     - Set web service XML namespace Uri");
                Console.WriteLine(@"/v         - Verbose. Multiple allowed");
                return -1;
            }
            par = ParseCommandLine(args);

            try
            {
                /*
                **  Locate and load assembly DLL
                **  The assembly will load - even if it can't be used
                */
                Assembly assembly = Assembly.LoadFrom(par.AssemblyName);


                /*
                **  Install an event handler to handle resolver events
                **  May be used to assist in determining DLLs that need to
                **  be loaded
                */
                AppDomain currentDomain = AppDomain.CurrentDomain;
                currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);


                if (par.Verbose > 0 )
                {
                    /*
                    **  Display a list of all assemblies references by the one just
                    **  loaded. May be useful when determining problems
                    */
                    Console.WriteLine("Referenced assemblies:");
                    foreach (AssemblyName an in assembly.GetReferencedAssemblies())
                    {
                        Console.WriteLine("    " + an.ToString());
                    }
                }

                /*
                **  Find every type in the assembly that has a [WebMethod]
                **  Extract a WSDL if any of its methods are marked
                */
                foreach (Module module in assembly.GetModules())
                {
                    foreach (Type type in module.GetTypes())
                    {
                        bool hasWebMethod = false;
                        foreach (MethodInfo method in type.GetMethods())
                        {
                            if (method.GetCustomAttributes(typeof(WebMethodAttribute), false).Length > 0)
                            {
                                hasWebMethod = true;
                                break;
                            }
                        }

                        if (hasWebMethod)
                        {
                            ExtractWsdl(type, par.Uri, par.OutDir + type.Name + ".wsdl");
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return -1;
            }
            return 0;
        }


        /// <summary>
        /// Given a web service type
        ///     Extract the WSDL
        /// </summary>
        /// <param name="type">The web service type</param>
        /// <param name="uri">The URL for the service that will be set in the constructor</param>
        /// <param name="path">The path to the .cs file that will be generated</param>

        private static void ExtractWsdl(Type type, string uri, string path)
        {
            /*
            **  Load up the type and create a reflection
            */
            ServiceDescriptionReflector reflector = new ServiceDescriptionReflector();
            reflector.Reflect(type, uri);

            if (reflector.ServiceDescriptions.Count > 1)
            {
                throw new Exception(string.Format("Don't know how to deal with multiple service descriptions in {0}",
                        type));
            }

            // Write out the basic WSDL file
            reflector.ServiceDescriptions[0].Write(path);
            Console.WriteLine("    Extracted WSDL: {1} for {0}", type.FullName, path);

        }

        /*
        **  Event Handler: ResolveEvents
        **  Called when the Assembly Loader has tried all the normal avenues
        **  Atempt to locate the required DLL within the toolset library
        **  path.
        **
        **  A bit ugly
        **      - Take the name of required assembly
        **      - Tac a .DLL to the end
        **      - Attempt to locate it in the configured EnvVar
        **      - If found, then load it
        **          - Check that its the correct version
        */
        static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
        {
            if (par.Verbose > 0 )
            {
                Console.WriteLine("Need to Resolve: " + args.Name);
            }
            string Name =  args.Name.Split(',')[0] + ".DLL";

            foreach (string Var in par.EnvPath )
            {
                if (par.Verbose > 0 )
                    Console.WriteLine("Use EnvVar: " + Var);
                string Path = Environment.GetEnvironmentVariable(Var);

                /*
                **  Ignore EnvVars that don't exist
                */
                if ( Path == null )
                    continue;

                /*
                **  The EnvVar is a ; sep list of dirs
                **  Walk the list looking for a suitable DLL
                */
                foreach (string dir in Path.Split(';') )
                {
                    if (par.Verbose > 0 )
                        Console.WriteLine("   Path:" + dir);

                    if (File.Exists( dir + "\\" + Name ) )
                    {
                        if (par.Verbose > 0 )
                            Console.WriteLine("" + Name + " Found in: " + dir);
                        Assembly assembly = Assembly.LoadFrom(dir + "\\" + Name);
                        if ( assembly != null )
                        {
                            if (par.Verbose > 0 )
                                Console.WriteLine("" + Name + " Called: " + assembly.GetName());
                            if(String.Compare(assembly.GetName().FullName, args.Name) == 0)
                            {
                                return assembly;
                            }
                            
                            if (par.Verbose > 0 )
                            {
                                Console.WriteLine("Assembly Name Mismatch");
                                Console.WriteLine("   " + assembly.GetName().FullName);
                                Console.WriteLine("   " + args.Name);
                            }
                        }
                    }
                }
            }

            /*
            **  Not found
            **  Let the loader report the error
            */
            return null;
        }
    }
}