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.”的错误。

你可能感兴趣的:(webMethods,Socket,JVM,thread)