gt:            cause != null && cause.getCause() != null;
context: /*
   * Copyright (c) 1998-2012 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.io.IOException;
  import java.io.InputStream;
  import java.net.BindException;
  import java.net.InetAddress;
  import java.net.NetworkInterface;
  import java.net.Socket;
  import java.net.SocketException;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import com.caucho.VersionFactory;
  import com.caucho.bam.broker.Broker;
  import com.caucho.cloud.bam.BamSystem;
  import com.caucho.cloud.license.LicenseClient;
  import com.caucho.cloud.loadbalance.LoadBalanceService;
  import com.caucho.cloud.network.ClusterServer;
  import com.caucho.cloud.network.NetworkClusterSystem;
  import com.caucho.cloud.network.NetworkListenSystem;
  import com.caucho.cloud.topology.CloudServer;
  import com.caucho.cloud.topology.CloudSystem;
  import com.caucho.config.Config;
  import com.caucho.config.ConfigException;
  import com.caucho.config.core.ResinProperties;
  import com.caucho.config.inject.WebBeansAddLoaderListener;
  import com.caucho.config.program.ConfigProgram;
  import com.caucho.ejb.manager.EjbEnvironmentListener;
  import com.caucho.env.deploy.DeployControllerService;
  import com.caucho.env.git.GitSystem;
  import com.caucho.env.jpa.ListenerPersistenceEnvironment;
  import com.caucho.env.log.LogSystem;
  import com.caucho.env.repository.AbstractRepository;
  import com.caucho.env.repository.LocalRepositoryService;
  import com.caucho.env.repository.RepositorySpi;
  import com.caucho.env.repository.RepositorySystem;
  import com.caucho.env.service.ResinSystem;
  import com.caucho.env.service.RootDirectorySystem;
  import com.caucho.env.shutdown.ExitCode;
  import com.caucho.env.shutdown.ShutdownSystem;
  import com.caucho.env.vfs.RepositoryScheme;
  import com.caucho.java.WorkDir;
  import com.caucho.license.LicenseCheck;
  import com.caucho.license.LicenseStore;
  import com.caucho.lifecycle.Lifecycle;
  import com.caucho.lifecycle.LifecycleState;
  import com.caucho.loader.Environment;
  import com.caucho.loader.EnvironmentClassLoader;
  import com.caucho.loader.EnvironmentLocal;
  import com.caucho.server.admin.Management;
  import com.caucho.server.admin.StatSystem;
  import com.caucho.server.cluster.ServerConfig;
  import com.caucho.server.cluster.ServletContainerConfig;
  import com.caucho.server.cluster.ServletService;
  import com.caucho.server.cluster.ServletSystem;
  import com.caucho.server.resin.BootConfig.BootType;
  import com.caucho.server.resin.ResinArgs.BoundPort;
  import com.caucho.util.CompileException;
  import com.caucho.util.CurrentTime;
  import com.caucho.util.HostUtil;
  import com.caucho.util.L10N;
  import com.caucho.util.QDate;
  import com.caucho.vfs.MemoryPath;
  import com.caucho.vfs.Path;
  
  /**
   * The Resin class represents the top-level container for Resin.
   * It exactly matches the &lt;resin> tag in the resin.xml
   */
  public class Resin
  {
    private static Logger _log;
    private static L10N _L;
  
    public static final int EXIT_OK = 0;
  
    private static final EnvironmentLocal<Resin> _resinLocal
      = new EnvironmentLocal<Resin>();
  
    private final ResinArgs _args;
    
    private final ResinSystem _resinSystem;
    
    private final String _serverId;
    private final String _displayServerId;
  
    private Path _resinHome;
    private Path _resinConf;
    
    private Path _confDirectory;
  
    private Path _rootDirectory;
    private Path _resinDataDirectory;
    private Path _serverDataDirectory;
    
    private Path _logDirectory;
    
    private String _homeCluster;
    private String _dynamicAddress;
    private int _dynamicPort;
    
    private String _clusterSystemKey;
    
    private String _stage = "production";
  
    private Socket _pingSocket;
    
    private long _shutdownWaitMax = 60000L;
    
    private ResinDelegate _resinDelegate;
  
    private Lifecycle _lifecycle;
  
    private BootConfig _bootConfig;
    private BootResinConfig _bootResinConfig;
    private BootServerConfig _bootServerConfig;
    
    private CloudServer _selfServer;
    
    private ServletContainerConfig _servletContainerConfig;
    private ServletService _servletSystem;
  
    private long _initialStartTime;
    private long _startTime;
  
    private ClassLoader _systemClassLoader;
  
    private Thread _mainThread;
  
    protected Management _management;
  
    private ResinAdmin _resinAdmin;
  
    private InputStream _waitIn;
    
    private boolean _isRestart;
    private String _restartMessage;
    private Path _licenseDirectory;
    
    private ResinWaitForExitService _waitForExitService;
    
    private final ArrayList<StartInfoListener> _startInfoListeners
      = new ArrayList<StartInfoListener>();
  
    /**
     * Creates a new resin server.
     */
    public Resin(String []args)
    {
      this(new ResinArgs(args));
    }
  
    /**
     * Creates a new resin server.
     */
    public Resin(ResinArgs args)
    {
      _startTime = CurrentTime.getCurrentTime();
      
      _args = args;
      
      _resinHome = args.getResinHome();
      
      _resinConf = args.getResinConfPath();
      
      if (_resinConf != null)
        _confDirectory = _resinConf.getParent();
      
      _rootDirectory = args.getRootDirectory();
      _resinDataDirectory = args.getDataDirectory();
  
      _licenseDirectory = args.getLicenseDirectory();
      
      _homeCluster = args.getHomeCluster();
      _dynamicAddress = args.getServerAddress();
      _dynamicPort = args.getServerPort();
      
      _stage = args.getStage();
   
      _pingSocket = _args.getPingSocket();
  
      String displayServerId = args.getServerId();
      
      if (displayServerId == null || displayServerId.equals("")) {
        displayServerId = "default";
      }
      
      String serverId;
  
      if (args.isElasticServer()) {
        serverId = args.getServerId();
        
        if (serverId == null || "".equals(serverId)) {
          serverId = "dyn";
          displayServerId = "dyn";
        }
        
        serverId = serverId + "-"+ getDynamicDisplayAddress() + ':' + getDynamicServerPort();
      }
      else {
        serverId = displayServerId;
      }
      
      _resinSystem = new ResinSystem(serverId);
  
      // _licenseErrorMessage = licenseErrorMessage;
      
      _resinLocal.set(this, _resinSystem.getClassLoader());
  
      Environment.init();
   
      _serverId = serverId;//args.getServerId();
      _displayServerId = displayServerId;
      
      preConfigureInit();
      
      if (! isWatchdog()) {
        configureFile(_resinConf);
      }
    }
  
    /**
     * Returns the resin server.
     */
    public static Resin getCurrent()
    {
      return _resinLocal.get();
    }
  
    public ResinSystem getResinSystem()
    {
      return _resinSystem;
    }
  
    /**
     * Returns the classLoader
     */
    public EnvironmentClassLoader getClassLoader()
    {
      return _resinSystem.getClassLoader();
    }
    
    ResinArgs getArgs()
    {
      return _args;
    }
  
    /**
     * Returns the server id.
     */
    public String getServerId()
    {
      return _serverId;
    }
  
    public String getServerIdFilePart() 
    {
      if (_serverId == null || _serverId.isEmpty())
        return "default";
      else 
        return _serverId.replace(':', '_');
    }
  
    /**
     * Sets the server id.
     */
    /*
    void setServerId(String serverId)
    {
      if (serverId == null || "".equals(serverId))
        return;//      serverId = "default";
  
      _serverId = serverId;
  
      _resinSystem.setId(_serverId);
    }
    */
  
    public String getUniqueServerName()
    {
      String name;
      
      String serverId = getDisplayServerId();
  
      if (isWatchdog())
        name = serverId + "_watchdog";
      else
        name = serverId;
  
      name = name.replace('-', '_');
  
      return name;
    }
  
    /**
     * Returns the server id.
     */
    public String getDisplayServerId()
    {
      return _displayServerId;
    }
  
    public static String getCurrentServerId()
    {
      Resin resin = getCurrent();
  
      if (resin != null)
        return resin.getServerId();
      else
        return "";
    }
  
    /**
     * Returns true for a Resin server, false for a watchdog.
     */
    public boolean isResinServer()
    {
      return ! isWatchdog();
    }
  
    public boolean isWatchdog()
    {
      return false;
    }
  
    /**
     * The configuration file used to start the server.
     */
    public Path getResinConf()
    {
      return _resinConf;
    }
    
    public Path getConfDirectory()
    {
      return _confDirectory;
    }
  
    protected String getResinName()
    {
      return getDelegate().getResinName();
    }
  
    /**
     * Returns resin.home.
     */
    public Path getResinHome()
    {
      return _resinHome;
    }
  
    /**
     * Set true for Resin pro.
     */
    public boolean isProfessional()
    {
      return getDelegate().isProfessional();
    }
    
    //
    // configuration
    //
    
    public boolean isEmbedded()
    {
      return false;
    }
  
    /**
     * Gets the root directory.
     */
    public Path getRootDirectory()
    {
      return _rootDirectory;
    }
    
    public void setRootDirectory(Path path)
    {
      _rootDirectory = path;
    }
  
    public Path getLicenseDirectory()
    {
      return _licenseDirectory;
    }
  
    public void setLicenseDirectory(Path licenseDirectory)
    {
      _licenseDirectory = licenseDirectory;
    }
  
    /**
     * Sets the cluster for a dynamic cluster join.
     */
    void setHomeCluster(String clusterId)
    {
      _homeCluster = clusterId;
    }
    
    /**
     * Returns the cluster to join for a dynamic cluster join.
     */
    public String getHomeCluster()
    {
      if (_homeCluster != null)
        return _homeCluster;
      else if (_bootResinConfig != null)
        return _bootResinConfig.getHomeCluster();
      else
        return null;
    }
    
    public boolean isElasticServer()
    {
      return _bootResinConfig.isElasticServer(_args);
    }
    
    public String getClusterSystemKey()
    {
      return _clusterSystemKey;
    }
    
    void setClusterSystemKey(String key)
    {
      _clusterSystemKey = key;
    }
    
    /**
     * Returns the server stage.
     */
    public String getStage()
    {
      return _stage;
    }
    
    public String getServerAddress()
    {
      return _dynamicAddress;
    }
    
    public String getDynamicServerAddress()
    {
      String address = getServerAddress();
  
      if (address == null) {
        address = _bootConfig.getBootResin().getElasticServerAddress(_args);
      }
      
      if (address != null)
        return address;
      else
        return getLocalHostAddress();
    }
    
    public String getDynamicDisplayAddress()
    {
      String address = getServerAddress();
  
      if (address != null)
        return address;
      else if (CurrentTime.isTest())
        return "192.168.1.x";
      else
        return getLocalHostAddress();
    }
    
    public int getServerPort()
    {
      return _dynamicPort;
    }
    
    public int getDynamicServerPort()
    {
      int port = getServerPort();
      
      if (port > 0)
        return port;
      else if (_bootConfig != null) {
        port = _bootConfig.getBootResin().getElasticServerPort(_args);
        
        if (port > 0)
          return port;
      }
  
      if (_args.getElasticServerPort() > 0)
        return _args.getElasticServerPort();
      else
        return 6830;
    }
  
    public Path getLogDirectory()
    {
      if (_logDirectory != null)
        return _logDirectory;
      else
        return _rootDirectory.lookup("log");
    }
  
    /**
     * Returns the resin-data directory
     */
    public Path getResinDataDirectory()
    {
      Path path;
  
      Path root = getRootDirectory();
      
      if (_resinDataDirectory != null)
        root = _resinDataDirectory;
  
      if (isWatchdog())
        path = root.lookup("watchdog-data");
      else
        path = root.lookup("resin-data");
  
      if (path instanceof MemoryPath) { // QA
        path = WorkDir.getTmpWorkDir().lookup("qa/resin-data");
      }
  
      return path;
    }
    
    protected Path getServerDataDirectory()
    {
      synchronized (this) {
        Path dataDirectory = getResinDataDirectory();
  
        if (_serverDataDirectory == null) {
          String serverName = getDisplayServerId();
  
          serverName = serverName.replace(':', '_');
    
          _serverDataDirectory = dataDirectory.lookup("./" + serverName);
        }
      }
  
      return _serverDataDirectory;
    }
  
    public long getShutdownWaitMax()
    {
      return _shutdownWaitMax;
    }
    
    public void setShutdownWaitTime(long shutdownTime)
    {
      _shutdownWaitMax = shutdownTime;
    }
    
    //
    // deprecated configuration
    //
  
    public Management createResinManagement()
    {
      if (_management == null) 
        _management = new Management(this);
  
      return _management;
    }
  
    public StatSystem createStatSystem()
    {
      return getDelegate().createStatSystem();
    }
    
    public LogSystem createLogSystem()
    {
      return getDelegate().createLogSystem();
    }
  
    /**
     * Returns the initial start time.
     */
    public Date getInitialStartTime()
    {
      return new Date(_initialStartTime);
    }
  
    /**
     * Returns the start time.
     */
    public Date getStartTime()
    {
      return new Date(_startTime);
    }
  
    /**
     * Returns the current lifecycle state.
     */
    public LifecycleState getLifecycleState()
    {
      return _lifecycle.getState();
    }
  
    /**
     * Returns the active server.
     */
    public ServletService getServer()
    {
      return _servletSystem;
    }
  
    /**
     * Returns the management api.
     */
    public Management getManagement()
    {
      return _management;
    }
    
    public CloudServer getSelfServer()
    {
      return _selfServer;
    }
  
    public ServletService createServer()
    {
      if (_servletSystem == null) {
        configure();
  
        // _server.start();
      }
  
      return _servletSystem;
    }
    
    //
    // lifecycle
    //
  
    /**
     * Returns true if active.
     */
    public boolean isActive()
    {
      return _resinSystem.isActive();
    }
  
    /**
     * Returns true if the server is closing.
     */
    public boolean isClosing()
    {
      return _lifecycle.isDestroying();
    }
  
    /**
     * Returns true if the server is closed.
     */
    public boolean isClosed()
    {
      return _lifecycle.isDestroyed();
    }
  
    public void addStartInfoListener(StartInfoListener listener)
    {
      _startInfoListeners.add(listener);
    }
    
    void setStartInfo(boolean isRestart, 
                      String startMessage,
                      ExitCode exitCode)
    {
      _isRestart = isRestart;
      _restartMessage = startMessage;
      
      for (StartInfoListener listener : _startInfoListeners) {
        listener.setStartInfo(isRestart, startMessage, exitCode);
      }
    }
    
    public boolean isRestart()
    {
      return _isRestart;
    }
    
    public String getRestartMessage()
    {
      return _restartMessage;
    }
    
    //
    // initialization code
    //
    
    /**
     * Must be called after the Resin.create()
     */
    private void preConfigureInit()
    {
      Thread thread = Thread.currentThread();
      ClassLoader oldLoader = thread.getContextClassLoader();
  
      try {
        /*
        String serverName = getServerId();
        if (serverName == null || "".equals(serverName))
          serverName = "default";
        
        _resinSystem = new ResinSystem(serverName);
        */
        
        thread.setContextClassLoader(getClassLoader());
  
        _resinLocal.set(this, getClassLoader());
  
        _lifecycle = new Lifecycle(log(), "Resin[]");
  
        if (getRootDirectory() == null)
          throw new NullPointerException();
        
        _resinDelegate = ResinDelegate.create(this);
  
        getDelegate().addPreTopologyServices();
  
        if (! isWatchdog()) {
          // server/p603
          initRepository();
        }
  
        // watchdog/0212
        // else
        //  setRootDirectory(Vfs.getPwd());
        
        if (! isWatchdog()) {
          Environment.addChildLoaderListener(new ListenerPersistenceEnvironment());
          Environment.addChildLoaderListener(new WebBeansAddLoaderListener());
          Environment.addChildLoaderListener(new EjbEnvironmentListener());
        }
  
        readUserProperties();
        
        Config.setProperty("rvar0", getDisplayServerId());
        
        _bootConfig
          = new BootConfig(_resinSystem,
                           getDisplayServerId(),
                           getResinHome(),
                           getRootDirectory(),
                           getLogDirectory(),
                           getResinConf(),
                           isProfessional(),
                           isWatchdog() ? BootType.WATCHDOG : BootType.RESIN);
  
       _bootResinConfig = _bootConfig.getBootResin();
       
        _resinAdmin = new ResinAdmin(this);
      } catch (RuntimeException e) {
        throw e;
      } catch (Exception e) {
        throw ConfigException.create(e);
      } finally {
        thread.setContextClassLoader(oldLoader);
      }
    }
  
    // read $HOME/.resin
    private void readUserProperties()
    {
      if (_args.getUserProperties() != null && _args.getUserProperties().canRead()) {
        ResinProperties properties = new ResinProperties();
        properties.setPath(_args.getUserProperties());
      
        properties.setMode(_args.getMode());
      
        try {
          properties.init();
        } catch (Exception e) {
          log().info(e.toString());
        }
      }
    }
  
    /*
    private String findLocalServerId()
    {
      BootServerConfig server = _bootResinConfig.findLocalServer();
      
      if (server != null)
        return server.getId();
      else
        return null;
    }
    */
  
    /**
     * Starts the server.
     */
    public void start()
      throws Exception
    {
      if (! _lifecycle.toActive())
        return;
  
      Thread thread = Thread.currentThread();
      ClassLoader oldLoader = thread.getContextClassLoader();
  
      try {
        thread.setContextClassLoader(_resinSystem.getClassLoader());
  
        // force a GC on start
        System.gc();
  
        _servletSystem = createServer();
  
        ShutdownSystem shutdown = ShutdownSystem.getCurrent();
  
        shutdown.setShutdownWaitTime(_servletSystem.getShutdownWaitMax());
  
        NetworkListenSystem listenService 
          = _resinSystem.getService(NetworkListenSystem.class);
  
        if (_args != null) {
          for (BoundPort port : _args.getBoundPortList()) {
            listenService.bind(port.getAddress(),
                               port.getPort(),
                               port.getServerSocket());
          }
        }
        
        _resinSystem.start();
  
        log().info(this + " started in " + (CurrentTime.getExactTime() - _startTime) + "ms");
      } finally {
        thread.setContextClassLoader(oldLoader);
      }
    }
    
    private void initRepository()
    {
      GitSystem.createAndAddService();
      
      LocalRepositoryService localRepositoryService = 
        LocalRepositoryService.createAndAddService();
  
      RepositorySpi localRepository = localRepositoryService.getRepositorySpi();
  
      AbstractRepository repository = getDelegate().createRepository(localRepository);
      RepositorySystem.createAndAddService(repository);
      
      // add the cluster repository
      try {
        RepositoryScheme.create("cloud", 
                                getStage() + "/config/resin",
                                getServerDataDirectory().lookup("config"));
      } catch (Exception e) {
        log().log(Level.WARNING, e.toString(), e);
      }
    }
    
    String getLocalHostAddress()
    {
      try {
        InetAddress addr = InetAddress.getLocalHost();
      
        if (addr.isLinkLocalAddress() || addr.isLoopbackAddress()) {
          addr = findLocalHost();
        }
        return addr.getHostAddress();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  
    private InetAddress findLocalHost() throws SocketException
    {
      for (NetworkInterface iface : HostUtil.getNetworkInterfaces()) {
        if (iface.isLoopback() || ! iface.isUp())
          continue;
        
        Enumeration<InetAddress> eInet = iface.getInetAddresses();
        while (eInet.hasMoreElements()) {
          InetAddress iAddr = eInet.nextElement();
  
          if (! iAddr.isLinkLocalAddress() && ! iAddr.isLoopbackAddress())
            return iAddr;
        }
      }
      
      throw new ConfigException(L().l("Cannot find active interface for the server. Check the network configuration."));
    }
  
    /**
     * Starts the server.
     */
    public void stop()
    {
      _resinSystem.stop();
    }
  
    public void destroy()
    {
      _resinSystem.destroy();
    }
    
    //
    // JMX/Admin
    //
  
    public ResinAdmin getAdmin()
    {
      return _resinAdmin;
    }
  
    /**
     * Initialize the server, binding to TCP and starting the threads.
     */
    public void initMain()
      throws Throwable
    {
      _mainThread = Thread.currentThread();
      _mainThread.setContextClassLoader(_systemClassLoader);
      
      // preConfigureInit();
  
      System.out.println(VersionFactory.getFullVersion());
      System.out.println(VersionFactory.getCopyright());
      System.out.println();
  
      String licenseMessage = getDelegate().getLicenseMessage();
  
      if (licenseMessage != null) {
        log().warning(licenseMessage);
        System.out.println(licenseMessage);
      }
  
      String licenseErrorMessage = getDelegate().getLicenseErrorMessage();
  
      if (licenseErrorMessage != null) {
        log().warning(licenseErrorMessage);
        System.err.println(licenseErrorMessage);
      }
  
      System.out.println("Starting " + getResinName()
                         + " on " + QDate.formatLocal(_startTime));
      System.out.println();
  
      Environment.init();
  
      // buildResinClassLoader();
  
      // validateEnvironment();
  
      Thread thread = Thread.currentThread();
  
      thread.setContextClassLoader(_systemClassLoader);
  
      if (_rootDirectory == null)
        _rootDirectory = _resinHome;
  
      configure();
  
      start();
    }
    
    private void configure()
    {
      Thread thread = Thread.currentThread();
      ClassLoader oldLoader = thread.getContextClassLoader();
      
      try {
        thread.setContextClassLoader(_resinSystem.getClassLoader());
        
        if (_servletSystem == null) {
          BootResinConfig bootResin = _bootResinConfig;
    
          configureRootDirectory(bootResin);
          
          initServletSystem();
        }
        
      } catch (Exception e) {
        throw ConfigException.create(e);
      } finally {
        thread.setContextClassLoader(oldLoader);
      }
    }
    
    public void configureFile(Path path)
    {
      _bootConfig.configureFile(path);
    }
    
    public void configureProgram(ConfigProgram program)
    {
      program.configure(_bootResinConfig);
    }
    
    /**
     * Configures the root directory and dataDirectory.
     */
    private void configureRootDirectory(BootResinConfig bootConfig) 
      throws IOException
    {
      Path dataDirectory = getServerDataDirectory();
  
      RootDirectorySystem.createAndAddService(_rootDirectory, dataDirectory);
    }
    
    /**
     * Configures the selected server from the boot config.
     */
    private synchronized void initTopology()
    {
      if (_selfServer != null)
        return;
      
      BootResinConfig bootResin = _bootResinConfig;
      
      _clusterSystemKey = bootResin.getClusterSystemKey();
      
      String serverId = _serverId;
      
      if ("".equals(serverId)) {
        serverId = "default";
      }
  
      if (serverId != null) {
        _bootServerConfig = bootResin.findServer(serverId);
      }
      
      if (serverId == null && ! bootResin.isElasticServer(_args)) {
        // server/2s00
        _bootServerConfig = bootResin.findServer("default");
      }
  
      CloudSystem cloudSystem = bootResin.initTopology();
  
      if (_bootServerConfig != null) {
      }
      else if (isEmbedded()) { 
        _bootServerConfig = joinEmbed();
      }
      else if (CurrentTime.isTest()) {
        _bootServerConfig = joinTest();
      }
      else if (isWatchdog()) {
        _bootServerConfig = joinWatchdog();
      }
      else if (_serverId != null && ! isElasticServer()) {
        throw new ConfigException(L().l("-server '{0}' is an unknown server in the configuration file.",
                                        _serverId));
      }
      else if (! isElasticServer()
               && (_bootServerConfig = bootResin.findLocalServer()) != null) {
      }
      
      if (_bootServerConfig == null && isElasticServer()) {
        _bootServerConfig = joinCluster(cloudSystem);
      }
  
      if (_bootServerConfig == null) {
        throw new ConfigException(L().l("unknown server {0} in unknown cluster",
                                        _serverId));
      }
      
      _selfServer = cloudSystem.findServer(_bootServerConfig.getId());
      
      if (_selfServer ==  null) {
        throw new ConfigException(L().l("unexpected empty server '{0}'",
                                        _bootServerConfig));
      }
      
      // Config.setProperty("rvar0", _selfServer.getId());
      Config.setProperty("rvar0", getDisplayServerId());
      Config.setProperty("rvar1", _selfServer.getCluster().getId());
  
      getDelegate().validateServerCluster();
      
      // NetworkClusterSystem.createAndAddService(_selfServer);
      
      // ClusterServer server = _selfServer.getData(ClusterServer.class);
    }
    
    private BootServerConfig joinCluster(CloudSystem cloudSystem)
    {
      String clusterId = getHomeCluster();
      
      BootResinConfig bootResin = _bootResinConfig;
      
      BootClusterConfig bootCluster = bootResin.findCluster(clusterId);
      
      if (bootCluster == null) {
        throw new ConfigException(L().l("-cluster {0} is an unknown cluster.",
                                        clusterId));
      }
      
      CloudServer cloudServer = getDelegate().joinCluster(cloudSystem, bootCluster);
  
      if (cloudServer == null) {
        String cause = null;
        
        if (bootCluster.getPodList().isEmpty() || 
            bootCluster.getPodList().get(0).getServerList().isEmpty()) {
          cause = L().l("No triad servers are configured in {0}.", bootCluster);
        } else {
          ArrayList<BootServerConfig> servers = 
            bootCluster.getPodList().get(0).getServerList();
          
          boolean isFirst = true;
          StringBuilder sb = new StringBuilder();
          for (BootServerConfig server : servers) {
            if (! isFirst)
              sb.append(", ");
  
            sb.append(server.getFullAddress());
  
            isFirst = false;
          }
          
          cause = L().l("No triad servers were reachable.\n" +
                        "  Triad servers are {0}", sb);
        }
        
        throw new ConfigException(L().l("{0} unable to join cluster {1}: {2}",
                                        getDelegate(), clusterId, cause));
      }
      
      return bootCluster.addDynamicServer(cloudServer);
    }
    
    private BootServerConfig joinWatchdog()
    {
      BootResinConfig bootResin = _bootResinConfig;
      
      BootClusterConfig bootCluster = bootResin.addClusterById("watchdog");
      
      BootServerConfig bootServer = bootCluster.createServer();
      bootServer.setId("default");
      bootServer.setAddress("127.0.0.1");
      bootCluster.addServer(bootServer);
      
      bootServer.initTopology();
      
      return bootServer;
    }
    
    private BootServerConfig joinTest()
    {
      BootClusterConfig bootCluster = findDefaultCluster();
      
      if (bootCluster.getPodList().size() == 0) {
      }
      else if (bootCluster.getPodList().get(0).getServerList().size() == 1) {
        // server/6b07
        return bootCluster.getPodList().get(0).getServerList().get(0);
      }
      else if (bootCluster.getPodList().get(0).getServerList().size() > 0) {
          // server/0342
          return null;
      }
      
      BootServerConfig bootServer = bootCluster.createServer();
      bootServer.setId("default");
      bootServer.setAddress("127.0.0.1");
      bootCluster.addServer(bootServer);
      
      bootServer.initTopology();
      
      return bootServer;
    }
    
    private BootClusterConfig findDefaultCluster()
    {
      BootResinConfig bootResin = _bootResinConfig;
      
      if (bootResin.getClusterList().size() == 1)
        return bootResin.getClusterList().get(0);
      
      return bootResin.findCluster("");
    }
    
    private BootServerConfig joinEmbed()
    {
      BootResinConfig bootResin = _bootResinConfig;
      
      BootClusterConfig bootCluster = null;
      
      if (bootResin.getClusterList().size() == 1)
        bootCluster = bootResin.getClusterList().get(0);
      
      if (bootCluster == null)
        bootCluster = bootResin.findCluster("");
  
      if (bootCluster == null)
        return null;
      
      if (bootCluster.getPodList().size() == 0) {
        
      }
      else if (bootCluster.getPodList().get(0).getServerList().size() == 1) {
        // server/1e06
        BootServerConfig server
          = bootCluster.getPodList().get(0).getServerList().get(0);
        
        if (! server.isRequireExplicitId()) {
          return server;
        }
        else {
          // cloud/12c0
          return null;
        }
      }
      else if (bootCluster.getPodList().get(0).getServerList().size() > 0) {
        // server/0342
        return null;
      }
      
      BootServerConfig bootServer = bootCluster.createServer();
      bootServer.setId("default");
      bootServer.setAddress("127.0.0.1");
      bootCluster.addServer(bootServer);
      
      bootServer.initTopology();
  
      return bootServer;
    }
  
    private void initClusterNetwork()
    {
      NetworkClusterSystem clusterSystem
        = getDelegate().createNetworkSystem(_selfServer);
      
      NetworkClusterSystem.createAndAddService(clusterSystem);
      
      ClusterServer server = _selfServer.getData(ClusterServer.class);
  
      if (server.getPort() == 0 && getServerPort() > 0) {
        server.setPort(getServerPort());
      }
      // initRepository();
      
      LoadBalanceService.createAndAddService(getDelegate().createLoadBalanceFactory());
      
      BamSystem.createAndAddService(server.getBamAdminName());
    }
    
    /**
     * Configures the selected server from the boot config.
     */
    private void initServletSystem()
      throws IOException
    {
      if (_servletSystem != null)
        return;
  
      initTopology();
      
      initClusterNetwork();
      
      _servletSystem = getDelegate().createServer();
  
      if (_args != null && _args.getStage() != null)
        _servletSystem.setStage(_args.getStage());
      else if (_stage != null)
        _servletSystem.setStage(_stage);
      
      NetworkListenSystem.createAndAddService(_selfServer);
      
      DeployControllerService.createAndAddService();
      
      if (! isWatchdog()) {
        getDelegate().addServices();
      }
       
      ServletSystem.createAndAddService(_servletSystem);
      
      ResinConfig resinConfig = new ResinConfig(this);
      
      BootResinConfig bootResin = _bootResinConfig;
      
      bootResin.getProgram().configure(resinConfig);
      
      _servletContainerConfig = new ServletContainerConfig(_servletSystem);
      
      BootClusterConfig cluster = _bootServerConfig.getPod().getCluster();
  
      cluster.getProgram().configure(_servletContainerConfig);
        
      ServerConfig config = new ServerConfig(_servletContainerConfig);
      cluster.getServerDefault().configure(config);
      _bootServerConfig.getServerProgram().configure(config);
      
      _servletContainerConfig.init();
      
      _servletSystem.init();
    }
    
    ResinDelegate getDelegate()
    {
      return _resinDelegate;
    }
  
    public LicenseStore getLicenseStore()
    {
      return _resinDelegate.getLicenseStore();
    }
  
    public LicenseCheck getLicenseCheck() {
      return _resinDelegate.getLicenseCheck();
    }
    
    protected boolean loadCloudLicenses()
    {
      try {
        Class<?> cl = Class.forName("com.caucho.cloud.license.LicenseClientImpl");
        LicenseClient client = (LicenseClient) cl.newInstance();
        
        Path licenses = getServerDataDirectory().lookup("licenses");
        
        return client.loadLicenses(licenses, _selfServer.getPod());
      } catch (ClassNotFoundException e) {
        log().log(Level.ALL, e.toString(), e);
      } catch (Exception e) {
        log().log(Level.FINER, e.toString(), e);
      }
      
      return false;
    }
  
    /**
     * Returns the admin broker
     */
    public Broker getAdminBroker()
    {
      return _management.getAdminBroker();
    }
  
    protected String getLicenseMessage()
    {
      return getDelegate().getLicenseMessage();
    }
  
    protected String getLicenseErrorMessage()
    {
      return getDelegate().getLicenseMessage();
    }
    
    public ServletContainerConfig getServletContainerConfig()
    {
      return _servletContainerConfig;
    }
  
    //
    // statistics
    //
    
    public double getCpuLoad()
    {
      return 0;
    }
  
    /**
     * Thread to wait until Resin should be stopped.
     */
    public void waitForExit()
      throws IOException
    {
      _waitForExitService = new ResinWaitForExitService(this, _resinSystem,
                                                        _waitIn, _pingSocket);
      
      _waitForExitService.startResinActor();
      
      _waitForExitService.waitForExit();
    }
    
    /**
     * Called from the embedded server
     */
    public void close()
    {
      log().info("Resin closed from the embedded server");
      
      _resinSystem.destroy();
    }
  
    @Override
    public String toString()
    {
      StringBuilder sb = new StringBuilder();
      
      if (isProfessional())
        sb.append("Pro");
  
      sb.append(getClass().getSimpleName());
      
      sb.append("[id=" + getDisplayServerId() + "]");
      
      return sb.toString();
    }
  
    /**
     * The main start of the web server.
     *
     * <pre>
     * -conf resin.xml   : alternate configuration file
     * -port port        : set the server's portt
     * <pre>
     */
    public static void main(String []argv)
    {
      try {
        Environment.init();
        
        validateEnvironment();
        
        final Resin resin = new Resin(argv);
  
        resin.initMain();
  
        resin.getServer();
  
        resin.waitForExit();
  
        if (! resin.isClosing()) {
          ShutdownSystem.shutdownActive(ExitCode.FAIL_SAFE_HALT,
                                         "Resin shutdown from unknown reason");
        }
      } catch (Throwable e) {
        Throwable cause;
  
        e.printStackTrace();
        
        for (cause = e;
  