/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/server/resin/ResinArgs.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.server.resin;

import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.VersionFactory;
import com.caucho.log.EnvironmentStream;
import com.caucho.log.RotateStream;
import com.caucho.server.util.JniCauchoSystem;
import com.caucho.util.L10N;
import com.caucho.vfs.Path;
import com.caucho.vfs.QJniServerSocket;
import com.caucho.vfs.QServerSocket;
import com.caucho.vfs.Vfs;
import com.caucho.vfs.WriteStream;

/**
 * The parsed Resin command-line arguments
 */
class ResinArgs
{
  private static final Logger log = Logger.getLogger(ResinArgs.class.getName());
  private static final L10N L = new L10N(ResinArgs.class);

  private String _serverId = "default";

  private Path _resinHome;
  private Path _rootDirectory;
  private Path _dataDirectory;

  private String _resinConf;
  
  private Socket _pingSocket;
  
  private String _joinCluster;
  private String _serverAddress;
  private int _serverPort;
  
  private boolean _isRestart;

  private ArrayList<BoundPort> _boundPortList
    = new ArrayList<BoundPort>();

  private String _stage = null;
  private boolean _isDumpHeapOnExit;
  
  public ResinArgs(String []args)
    throws Exception
  {
    preConfigureInit();
    
    parseCommandLine(args);
  }

  /**
   * Must be called after the Resin.create()
   */
  private void preConfigureInit()
  {
    String resinHome = System.getProperty("resin.home");

    if (resinHome != null)
      _resinHome = Vfs.lookup(resinHome);
    else
      _resinHome = Vfs.getPwd();

    _rootDirectory = _resinHome;

    // server.root backwards compat
    String resinRoot = System.getProperty("server.root");

    if (resinRoot != null)
      _rootDirectory = Vfs.lookup(resinRoot);

    // resin.root backwards compat
    resinRoot = System.getProperty("resin.root");

    if (resinRoot != null)
      _rootDirectory = Vfs.lookup(resinRoot);
  }
  
  public String getServerId()
  {
    return _serverId;
  }

  public Path getResinHome()
  {
    return _resinHome;
  }
  
  /**
   * Gets the root directory.
   */
  public Path getRootDirectory()
  {
    return _rootDirectory;
  }
  
  /**
   * Gets the root directory.
   */
  public Path getDataDirectory()
  {
    return _dataDirectory;
  }
  
  public Socket getPingSocket()
  {
    return _pingSocket;
  }

  /**
   * The configuration file used to start the server.
   */
  public String getResinConf()
  {
    return _resinConf;
  }

  public Path getResinConfPath()
  {
    Path pwd = Vfs.lookup();

    Path resinConf = null;
    
    String resinConfFile = getResinConf();
    
    if (resinConfFile != null) {
      if (log.isLoggable(Level.FINER))
        log.finer(this + " looking for conf in " +  pwd.lookup(resinConfFile));

      resinConf = pwd.lookup(resinConfFile);
    }
    else if (pwd.lookup("conf/resin.xml").canRead())
      resinConfFile = "conf/resin.xml";
    else { // backward compat
      resinConfFile = "conf/resin.conf";
    }
    
    Path rootDirectory = getRootDirectory();

    if (resinConf == null || ! resinConf.exists()) {
      if (log.isLoggable(Level.FINER))
        log.finer(this + " looking for conf in "
                  +  rootDirectory.lookup(resinConfFile));

      resinConf = _rootDirectory.lookup(resinConfFile);
    }

    if (! resinConf.exists() && ! _resinHome.equals(_rootDirectory)) {
      if (log.isLoggable(Level.FINER))
        log.finer(this + " looking for conf in "
                  +  _resinHome.lookup(resinConfFile));

      resinConf = _resinHome.lookup(resinConfFile);
    }

    // for error messages, show path relative to rootDirectory
    if (! resinConf.exists())
      resinConf = rootDirectory.lookup(resinConfFile);

    return resinConf;
  }
  
  /**
   * Returns the bound port list.
   */
  public ArrayList<BoundPort> getBoundPortList()
  {
    return _boundPortList;
  }
  
  /**
   * Returns the stage to start with.
   */
  public String getStage()
  {
    return _stage;
  }
  
  public String getJoinCluster()
  {
    return _joinCluster;
  }
  
  public String getServerAddress()
  {
    return _serverAddress;
  }
  
  public int getServerPort()
  {
    return _serverPort;
  }
  
  public boolean isDumpHeapOnExit()
  {
    return _isDumpHeapOnExit;
  }

  private void parseCommandLine(String []argv)
    throws Exception
  {
    int len = argv.length;
    int i = 0;

    while (i < len) {
      // RandomUtil.addRandom(argv[i]);

      if (i + 1 < len
          && (argv[i].equals("-stdout")
              || argv[i].equals("--stdout"))) {
        Path path = Vfs.lookup(argv[i + 1]);

        RotateStream stream = RotateStream.create(path);
        stream.init();
        WriteStream out = stream.getStream();
        out.setDisableClose(true);

        EnvironmentStream.setStdout(out);

        i += 2;
      }
      else if (i + 1 < len
               && (argv[i].equals("-stderr")
                   || argv[i].equals("--stderr"))) {
        Path path = Vfs.lookup(argv[i + 1]);

        RotateStream stream = RotateStream.create(path);
        stream.init();
        WriteStream out = stream.getStream();
        out.setDisableClose(true);

        EnvironmentStream.setStderr(out);

        i += 2;
      }
      else if (i + 1 < len
               && (argv[i].equals("-conf")
                   || argv[i].equals("--conf"))) {
        _resinConf = argv[i + 1];
        i += 2;
      }
      else if (argv[i].equals("-log-directory")
               || argv[i].equals("--log-directory")) {
        i += 2;
      }
      else if (argv[i].equals("-config-server")
               || argv[i].equals("--config-server")) {
        i += 2;
      }
      else if (argv[i].equals("--dump-heap-on-exit")) {
        _isDumpHeapOnExit = true;

        i += 1;
      }
      else if (i + 1 < len
          && (argv[i].equals("-server")
              || argv[i].equals("--server"))) {
        _serverId = argv[i + 1];
   
        if (_serverId.equals(""))
          _serverId = "default";
   
        i += 2;
      }
      else if (argv[i].equals("-resin-home")
               || argv[i].equals("--resin-home")) {
        _resinHome = Vfs.lookup(argv[i + 1]);

        i += 2;
      }
      else if (argv[i].equals("-root-directory")
          || argv[i].equals("--root-directory")
          || argv[i].equals("-resin-root")
          || argv[i].equals("--resin-root")
          || argv[i].equals("-server-root")
          || argv[i].equals("--server-root")) {
        _rootDirectory = _resinHome.lookup(argv[i + 1]);

        i += 2;
      }
      else if (argv[i].equals("-data-directory")
            || argv[i].equals("--data-directory")) {
        _dataDirectory = Vfs.lookup(argv[i + 1]);

        i += 2;
      }
      else if (argv[i].equals("-service")) {
        JniCauchoSystem.create().initJniBackground();
        // windows service
        i += 1;
      }
      else if (i + 1 < len
          && (argv[i].equals("-join-cluster")
              || argv[i].equals("--join-cluster"))) {
        _joinCluster = argv[i + 1];
   
        i += 2;
      }
      else if (i + 1 < len
          && (argv[i].equals("-server-address")
              || argv[i].equals("--server-address"))) {
        _serverAddress = argv[i + 1];
   
        i += 2;
      }
      else if (i + 1 < len
          && (argv[i].equals("-server-port")
              || argv[i].equals("--server-port"))) {
        _serverPort = Integer.parseInt(argv[i + 1]);
   
        i += 2;
      }
      else if (argv[i].equals("-version")
               || argv[i].equals("--version")) {
        System.out.println(VersionFactory.getFullVersion());
        System.exit(66);
      }
      else if (argv[i].equals("-watchdog-port")
               || argv[i].equals("--watchdog-port")) {
        // watchdog
        i += 2;
      }
      else if (argv[i].equals("-socketwait")
               || argv[i].equals("--socketwait")
               || argv[i].equals("-pingwait")
               || argv[i].equals("--pingwait")) {
        int socketport = Integer.parseInt(argv[i + 1]);

        Socket socket = null;
        for (int k = 0; k < 15 && socket == null; k++) {
          try {
            socket = new Socket("127.0.0.1", socketport);
          } catch (Throwable e) {
            System.out.println(new Date());
            e.printStackTrace();
          }

          if (socket == null)
            Thread.sleep(1000);
        }

        if (socket == null) {
          System.err.println("Can't connect to parent process through socket " + socketport);
          System.err.println("Resin needs to connect to its parent.");
          System.exit(1);
        }

        /*
        if (argv[i].equals("-socketwait") || argv[i].equals("--socketwait"))
          _waitIn = socket.getInputStream();
        */

        _pingSocket = socket;

        //socket.setSoTimeout(60000);

        i += 2;
      }
      else if ("-port".equals(argv[i]) || "--port".equals(argv[i])) {
        int fd = Integer.parseInt(argv[i + 1]);
        String addr = argv[i + 2];
        if ("null".equals(addr))
          addr = null;
        int port = Integer.parseInt(argv[i + 3]);

        _boundPortList.add(new BoundPort(QJniServerSocket.openJNI(fd, port),
                                         addr,
                                         port));

        i += 4;
      }
      else if ("start".equals(argv[i])
               || "restart".equals(argv[i])) {
        JniCauchoSystem.create().initJniBackground();
        i++;
      }
      else if (argv[i].equals("-verbose")
               || argv[i].equals("--verbose")) {
        i += 1;
      }
      else if (argv[i].equals("gui")) {
        i += 1;
      }
      else if (argv[i].equals("console")) {
        i += 1;
      }
      else if (argv[i].equals("watchdog")) {
        i += 1;
      }
      else if (argv[i].equals("start-with-foreground")) {
        i += 1;
      }
      else if (argv[i].equals("-fine")
               || argv[i].equals("--fine")) {
        i += 1;
      }
      else if (argv[i].equals("-finer")
               || argv[i].equals("--finer")) {
        i += 1;
      }
      else if (argv[i].startsWith("-D")
               || argv[i].startsWith("-J")
               || argv[i].startsWith("-X")) {
        i += 1;
      }
      else if ("-stage".equals(argv[i])
               || "--stage".equals(argv[i])) {
        _stage = argv[i + 1];
        i += 2;
      }
      else if ("-preview".equals(argv[i])
               || "--preview".equals(argv[i])) {
        _stage = "preview";
        i += 1;
      }
      else if ("-debug-port".equals(argv[i])
               || "--debug-port".equals(argv[i])) {
        i += 2;
      }
      else if ("-jmx-port".equals(argv[i])
               || "--jmx-port".equals(argv[i])) {
        i += 2;
      }
      else {
        System.out.println(L.l("unknown argument '{0}'", argv[i]));
        System.out.println();
        usage();
        System.exit(66);
      }
    }
  }

  private static void usage()
  {
    System.err.println(L.l("usage: bin/resin.sh [-options] [start | stop | restart]"));
    System.err.println(L.l(""));
    System.err.println(L.l("where options include:"));
    System.err.println(L.l("   -conf <file>          : select a configuration file"));
    System.err.println(L.l("   -data-directory <dir> : select a resin-data directory"));
    System.err.println(L.l("   -log-directory <dir>  : select a logging directory"));
    System.err.println(L.l("   -resin-home <dir>     : select a resin home directory"));
    System.err.println(L.l("   -root-directory <dir> : select a root directory"));
    System.err.println(L.l("   -server <id>          : select a <server> to run"));
    System.err.println(L.l("   -watchdog-port <port> : override the watchdog-port"));
    System.err.println(L.l("   -verbose              : print verbose starting information"));
    System.err.println(L.l("   -preview              : run as a preview server"));
  }

  static class DynamicServer {
    private final String _cluster;
    private final String _address;
    private final int _port;

    DynamicServer(String cluster, String address, int port)
    {
      _cluster = cluster;
      _address = address;
      _port = port;
    }

    String getCluster()
    {
      return _cluster;
    }

    String getAddress()
    {
      return _address;
    }

    int getPort()
    {
      return _port;
    }
  }
  
  static class BoundPort {
    private QServerSocket _ss;
    private String _address;
    private int _port;

    BoundPort(QServerSocket ss, String address, int port)
    {
      if (ss == null)
        throw new NullPointerException();

      _ss = ss;
      _address = address;
      _port = port;
    }

    public QServerSocket getServerSocket()
    {
      return _ss;
    }

    public int getPort()
    {
      return _port;
    }

    public String getAddress()
    {
      return _address;
    }
  }
}
