读书人

webMethods IS源代码分析起动类

发布时间: 2012-10-29 10:03:53 作者: rapoo

webMethods IS源代码分析——启动类
webMethods IS中负责服务器启动的类有四个:执行入口类com.wm.app.b2b.server.Main、服务器初始化类com.wm.app.b2b.server.Server、以及两个辅助类com.wm.util.UniqueApp、com.wm.util.AppRegistry。

让我们先来看看Main的main以及注释:

public static void main(String argv[])    {        int port = 0;        String serviceName = null;        //处理在启动脚本中传入的几个参数        for(int i = 0; i < argv.length; i++)        {            if(argv[i].equals("-port") && i < argv.length - 1)                port = Integer.valueOf(argv[1 + i++]).intValue();            if(argv[i].equals("-home") && i < argv.length - 1)                Config.setProperty("watt.server.homeDir", argv[1 + i++]);            if(argv[i].equals("-service") && i < argv.length - 1)                serviceName = argv[1 + i++];        }                if(port != 0)            Config.setProperty("watt.server.port", Integer.toString(port));        UniqueApp ua = null;        int uaport = 4321;        //获得一个叫uaport的端口参数,默认是4321    String uapstr = System.getProperty("watt.server.uaport");        if(uapstr != null)            try            {                uaport = Integer.parseInt(uapstr);            }            catch(NumberFormatException _ex) { }        //用这个uaport创建一个app        ua = new UniqueApp(uaport);        try        {            ua.start();            //调用Server这个类的start方法初始化IS            Server.start(argv);            ua.quit();            if(Server.restart())            {                try                {                    if(serviceName != null)                    {                        String homepath = System.getProperty("watt.server.homeDir", ".");                        String cmd = homepath + File.separator + "bin" + File.separator + "RestartService.exe " + serviceName;                        Runtime.getRuntime().exec(cmd);                    }                }                catch(Exception e)                {                    e.printStackTrace();                }                System.exit(42);            } else            {                System.exit(0);            }        }        catch(com.wm.util.UniqueApp.DeniedException _ex)        {            System.out.println("Server cannot execute. A server with this configuration is already running.");            System.exit(0);        }    }

看到这里大家可能会产生疑问,这个UniqueApp的实例的ua到底有什么用呢,为什么要在Server初始化之前先start它,初始化之后又quit呢?要解答这些问题让我们先看看这个UniqueApp究竟做了什么事:

UniqueApp类的start方法、send方法和runRegistry方法:
public void start()        throws DeniedException    {        if(port_ > lastPort_)            debugPrint("UNQ  ERROR: port_ (" + port_ + ") > lastPort_ (" + lastPort_ + ")");        for(; port_ <= lastPort_; port_++)        {            debugPrint("UNQ  attempting connection on port " + port_);            try            {                debugPrint("UNQ  " + iden_ + " sending initial message");                String response = send("init");                debugPrint("UNQ  " + iden_ + " got response " + response);                if(response == null)                {                    debugPrint("UNQ  connected on port " + port_ + ", but no response");                    continue;                }                if(response.equals("DENIED"))                    throw new DeniedException();                if(response.equals("OK"))                {                    runHeartbeat();                    break;                }                debugPrint("UNQ  connected on port " + port_ + ", but incomprehensible response");                continue;            }            catch(IOException ioe)            {                if(DEBUG)                    ioe.printStackTrace(System.out);                runRegistry();            }            break;        }    }public String send(String type)        throws IOException    {        Socket socket = new Socket("localhost", port_);        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);        String msg = type + ": " + iden_;        out.println(msg);        out.flush();        return in.readLine();    }public void runRegistry()    {        debugPrint("UNQ  " + iden_ + " starting registry on port " + port_);        registry_ = new AppRegistry(iden_, port_);    }

这里可以很清楚的看到,UniqueApp是一个socket的client端,它的作用就是用一些特定的原语连接localhost上的uaport端口,如果这个端口没有响应或者响应的不对,它就“new AppRegistry(iden_, port_)”。
AppRegistry是一个实现了Runnable接口的类,它又做了什么呢?请看:

AppRegistry类的构造方法、run方法、openSocket方法以及解析原语的processLineReceived方法:

public AppRegistry(String iden, int port)    {        info_ = new HashMap();        out_ = null;        socket_ = null;        info_.put(iden, new Long(0x7fffffffffffffffL));        port_ = port;        Thread t = new Thread(this);        t.setDaemon(true);        t.start();    }public void run()    {        debugPrint("REG  running");        openSocket();        if(socket_ == null)            debugPrint("REG  no socket");        else            while(socket_ != null)                 processConnection();        debugPrint("REG  stopped");    }protected void openSocket()    {        try        {            debugPrint("REG  running socket server on port " + port_);            socket_ = new ServerSocket(port_);            socket_.setSoTimeout(3000);        }        catch(IOException ioe)        {            if(DEBUG)                ioe.printStackTrace(System.out);            debugPrint("REG  failed to open socket");            socket_ = null;        }    }protected void processLineReceived(String line)    {        debugPrint("REG  processLineReceived(" + line + ")");        int pos = line != null ? line.indexOf(": ") : -1;        if(pos != -1)        {            String type = line.substring(0, pos);            String identifier = line.substring(pos + 2);            if(type.equals("init"))                handleInit(identifier);            else            if(type.equals("update"))                update(identifier);            else            if(type.equals("quit"))                quit(identifier);            else                debugPrint("REG  unknown type: '" + type + "' in line: '" + line + "'");        }    }

由此看出AppRegistry是一个单线程的Socket Server。虽然到了这里我们都知道了UniqueApp和AppRegistry的作用,但是疑惑仍然没有解答,为什么要在服务器启动之前先连接或者启动一个Socket Server呢?

要得到答案,让我们来看看Server初化始化的时候究竟做了些什么:

Server类的start方法:

public static void start(String args[])    {        gServer = new Server(args);        gServer.start();        try        {            gServer.join();        }        catch(Exception _ex) { }    }

这里要说明的是Server类是Thread类的子类,它的run方法如下:
public void run()    {        try        {            long start = System.currentTimeMillis();            com.wm.util.Config.setProperty("watt.server", "true");            gRestart = false;            gInShutdown = false;            gListeners = new Values();            gResources = new Resources(System.getProperty("watt.server.homeDir", "."), true);            gConfFile = gResources.getConfigFile("server.cnf");            gResources.getLogJobsInDir();            gResources.getLogJobsOutDir();            gResources.getDatastoreDir();            loadConfiguration();            Scheduler.init();            String tmp = com.wm.util.Config.getProperty("false", "watt.server.control.runMemorySensor");            if(Boolean.valueOf(tmp).booleanValue())                MemorySensor.init(ServerController.getInstance());            ThreadPoolSensor.init(ServerController.getInstance());            checkProperties();            com.wm.util.Config.processCmdLine(args);            setupLogging();            JournalLog.init(args);            JournalLogger.init(JournalLog.newProducer(), JournalLog.getHandler());            JournalLogger.logCritical(1, 25, Build.getVersion(), Build.getBuild());            if(!LicenseManager.init())            {                JournalLogger.logCritical(1, 14);                return;            }            RepositoryManager.init();            JDBCConnectionManager.init();            AuditLogManager.init();            setupIPRules();            UserManager.init();            ACLManager.init();            ThreadManager.init();            StateManager.init();            ListenerAdmin.init();            ServiceManager.init();            InvokeManager.init();            Statistics.init();            NetURLConnection.init();            ContentManager.init();            CacheManager.init();            EventManager.init();            boolean dispatcherCorrectlyInit = false;            try            {                DispatchFacade.init();                dispatcherCorrectlyInit = true;            }            catch(Exception e)            {                JournalLogger.logCritical(31, 25, e);            }            WebContainer.init();            ISMonEvtMgr.create();            PackageManager.init();            DependencyManager dm = NSDependencyManager.current();            if(dm == null || !dm.isEnabled())                JournalLogger.logDebugPlus(3, 15, 25);            else                JournalLogger.logDebugPlus(3, 14, 25);            try            {                if(dispatcherCorrectlyInit)                    DispatchFacade.start();            }            catch(Exception ce)            {                JournalLogger.logError(35, 25, ce);            }            PortManager.init();            MimeTypes.init();            HTTPDispatch.init();            ProxyHTTPDispatch.init();            LBHTTPDispatch.init();            CacheManager.startSweeper();            Document.setHostServices(new ServerXmlHostServices());            try            {                ClusterManager.init();            }            catch(Exception e)            {                JournalLogger.logDebug(103, 33, e.getMessage());            }            try            {                String jobDir = com.wm.util.Config.getProperty("logs/jobsout", "watt.tx.jobdir");                TContext.init(jobDir);            }            catch(ServiceException e)            {                JournalLogger.logDebugPlus(3, 9998, 36, e);            }            if(Config.isSSLPresent())                try                {                    Class c = Class.forName("com.wm.app.b2b.server.ServerTrustDeciderManager");                    TrustDeciderManager tdm = (TrustDeciderManager)c.newInstance();                    TrustManager.setManager(tdm);                }                catch(Throwable _ex) { }            if(!ListenerAdmin.isReady() && !gCanListen)            {                JournalLogger.logCritical(4, 14);                return;            }            Scheduler.scheduleTask("Key Update", new KeyUpdate(), 0L, 0x5265c00L);            String sc = com.wm.util.Config.getProperty("true", "watt.server.saveConfigFiles");            if((new Boolean(sc)).booleanValue())                saveConfigFiles();            if(Configurator.isBrokerConfigured())                SyncManager.init();            gRunning = true;            long startTimeSeconds = (System.currentTimeMillis() - start) / 1000L;            JournalLogger.logCritical(2, 14, Long.toString(startTimeSeconds));            synchronized(this)            {                try                {                    wait();                }                catch(Exception e)                {                    e.printStackTrace();                }            }        }        catch(Exception e)        {            e.printStackTrace();        }        JournalLog.unInit();    }

上面的代码不用多解释,大家可以看到IS启动做了哪些事情,IS分为哪些模块,以及这些模块的加载顺序。
IS启动的操作很多,启动时间也比较长,而且有“PortManager.init()”、“saveConfigFiles()”这样需要独占运行的端口占用和文件操作,如果是在同一个JVM上我们可以用Synchronized,如果是不到的JVM呢?答案揭晓了,UniqueApp的作用是使用端口占用的方式在IS启动阶段实现互斥,即对于同一个启动文件夹下的配置一台机器只能有一个IS在启动,否则会报出“Server cannot execute. A server with this configuration is already running.”的错误。 1 楼 galaxystar 2006-11-08 不错,虽然没用过webmethods的产品!

读书人网 >Web前端

热点推荐