0001: /**
0002: * JOnAS: Java(TM) Open Application Server
0003: * Copyright (C) 2006 Bull S.A.S.
0004: * Contact: jonas-team@objectweb.org
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or 1any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
0019: * USA
0020: *
0021: * --------------------------------------------------------------------------
0022: * $Id: ClusterDaemon.java 9850 2006-11-20 15:50:09Z danesa $
0023: * --------------------------------------------------------------------------
0024: */package org.objectweb.jonas.cluster.daemon;
0025:
0026: import java.io.BufferedReader;
0027: import java.io.BufferedWriter;
0028: import java.io.File;
0029: import java.io.FileWriter;
0030: import java.io.IOException;
0031: import java.io.InputStream;
0032: import java.io.InputStreamReader;
0033: import java.io.Writer;
0034: import java.net.MalformedURLException;
0035: import java.net.URL;
0036: import java.util.HashMap;
0037: import java.util.Map;
0038: import java.util.Vector;
0039:
0040: import javax.management.MalformedObjectNameException;
0041: import javax.management.ObjectName;
0042:
0043: import org.objectweb.carol.jndi.ns.NameServiceException;
0044: import org.objectweb.carol.jndi.ns.NameServiceManager;
0045: import org.objectweb.carol.util.configuration.CarolDefaultValues;
0046: import org.objectweb.carol.util.configuration.ConfigurationException;
0047: import org.objectweb.carol.util.configuration.ConfigurationRepository;
0048: import org.objectweb.carol.util.configuration.TraceCarol;
0049: import org.objectweb.common.Env;
0050: import org.objectweb.common.WinSysEnv;
0051: import org.objectweb.jonas.common.JProp;
0052: import org.objectweb.jonas.common.Log;
0053: import org.objectweb.jonas.jmx.commons.JMXRemoteException;
0054: import org.objectweb.jonas.jmx.commons.JMXRemoteHelper;
0055: import org.objectweb.jonas.jmx.commons.MBeanServerException;
0056: import org.objectweb.jonas.jmx.commons.MBeanServerHelper;
0057: import org.objectweb.jonas_clusterd.xml.Server;
0058: import org.objectweb.jonas_lib.deployment.xml.JLinkedList;
0059: import org.objectweb.util.monolog.api.BasicLevel;
0060: import org.objectweb.util.monolog.api.Logger;
0061:
0062: /**
0063: * ClusterDaemon is provided to control JOnAS instances remotely.
0064: * The ClusterDaemon is an independent Java program, and runs on every node (machine) in the cluster.
0065: * It receives remote commands (JMX), and does something such as starting or stopping JOnAS instances.
0066: * @author zhengzz (initial version for PKUAS)
0067: * @author Benoit Pelletier (integration in JOnAS)
0068: */
0069:
0070: public class ClusterDaemon implements ClusterDaemonMBean {
0071:
0072: /**
0073: * Allow to trace errors/output of a process
0074: */
0075: class CmdReader implements Runnable {
0076:
0077: /**
0078: * Input stream containing information
0079: */
0080: private InputStream is;
0081:
0082: /**
0083: * Should send as error or debug message ?
0084: */
0085: private boolean isErrorMessage = false;
0086:
0087: /**
0088: * Logger to use
0089: */
0090: private Logger logger = null;
0091:
0092: /**
0093: * Cmd associated with the reader
0094: */
0095: private String cmd = null;
0096:
0097: /**
0098: * Constructor
0099: * @param logger logger
0100: * @param cmd command
0101: * @param is given input stream
0102: * @param isErrorMessage Should send as error or debug message
0103: */
0104: public CmdReader(Logger logger, String cmd, InputStream is,
0105: boolean isErrorMessage) {
0106: this .is = is;
0107: this .isErrorMessage = isErrorMessage;
0108: this .logger = logger;
0109: this .cmd = cmd;
0110: }
0111:
0112: /**
0113: * Thread execution printing information received
0114: */
0115: public void run() {
0116: try {
0117: BufferedReader br = new BufferedReader(
0118: new InputStreamReader(is));
0119: String str = null;
0120: while ((str = br.readLine()) != null) {
0121: if (isErrorMessage) {
0122: this .logger.log(BasicLevel.ERROR, "<" + cmd
0123: + "> " + str);
0124: } else {
0125: this .logger.log(BasicLevel.DEBUG, "<" + cmd
0126: + "> " + str);
0127: }
0128: }
0129: // close input stream
0130: is.close();
0131: } catch (Exception e) {
0132: this .logger.log(BasicLevel.ERROR, e.getMessage());
0133: e.printStackTrace();
0134: }
0135: }
0136: }
0137:
0138: /**
0139: * Allow stop the cmd childs
0140: */
0141: class ShutdownHook extends Thread {
0142:
0143: /**
0144: * Action at the shutdown : destroy the process
0145: */
0146: public static final int ACTION_DESTROY = 0;
0147:
0148: /**
0149: * Action at the shutdown : stop the JOnAS instance
0150: */
0151: public static final int ACTION_JONAS_STOP = 1;
0152:
0153: /**
0154: * Cmdkey
0155: */
0156: private String keyCmd = null;
0157:
0158: /**
0159: * Action
0160: */
0161: private int action = -1;
0162:
0163: /**
0164: * JOnAS instance name
0165: */
0166: private String serverName = null;
0167:
0168: /**
0169: * Constructor
0170: * @param keyCmd key command
0171: */
0172: public ShutdownHook(String serverName, String keyCmd, int action) {
0173: super ();
0174: this .keyCmd = keyCmd;
0175: this .action = action;
0176: this .serverName = serverName;
0177: }
0178:
0179: /**
0180: * Thread execution printing information received
0181: */
0182: public void run() {
0183: try {
0184: if (action == ACTION_DESTROY) {
0185: ((Process) ClusterDaemon.this .processMap
0186: .get(this .keyCmd)).destroy();
0187: ClusterDaemon.this .logger.log(BasicLevel.DEBUG,
0188: "destroy cmd" + keyCmd);
0189: } else if (action == ACTION_JONAS_STOP) {
0190: ClusterDaemon.this .logger.log(BasicLevel.DEBUG,
0191: "stop JOnAS instance " + serverName);
0192: ClusterDaemon.this .doStopJOnAS(serverName);
0193: ClusterDaemon.this .logger.log(BasicLevel.DEBUG,
0194: "destroy cmd" + keyCmd);
0195: ((Process) ClusterDaemon.this .processMap
0196: .get(this .keyCmd)).destroy();
0197: }
0198:
0199: } catch (Exception e) {
0200: TraceCarol.error("ShutdownHook problem for key="
0201: + this .keyCmd, e);
0202: }
0203: }
0204: }
0205:
0206: /**
0207: * Monitor is a tool that collects node's performance information regularly if needed
0208: * (only supporting CPU and memory information now).
0209: * @author zhengzz
0210: *
0211: */
0212: class Monitor extends Thread {
0213:
0214: /**
0215: * Logger to use
0216: */
0217: private Logger logger = null;
0218:
0219: int cpurate;
0220: long totalM, availM, percentM;
0221: BufferedWriter out;
0222: int count = 0;
0223:
0224: public Monitor(Logger logger) {
0225: this .logger = logger;
0226: try {
0227: out = new BufferedWriter(new FileWriter("monitor.txt",
0228: true));
0229: } catch (Exception e) {
0230: this .logger.log(BasicLevel.ERROR,
0231: "Fail to new FileWrite from monitor.txt" + e);
0232: }
0233: }
0234:
0235: public void run() {
0236: this .logger.log(BasicLevel.INFO,
0237: "Start monitoring thread....");
0238: while (true) {
0239: try {
0240: //cpurate = SystemInformation.getCurrentCpuUsage();
0241: //totalM = SystemInformation.getMemoryInfo(1) / 1024;
0242: //availM = SystemInformation.getMemoryInfo(2) / 1024;
0243: //percentM = SystemInformation.getMemoryInfo(0);
0244: //out.write(count + " " + cpurate + " " + percentM + "\r\n");
0245: //out.flush();
0246: Thread.currentThread().sleep(10 * 1000);
0247: count = count + 10;
0248: } catch (Exception e) {
0249: e.printStackTrace();
0250: }
0251: }
0252: }
0253: }
0254:
0255: /**
0256: * Sleep time to wait
0257: */
0258: private static final int SLEEP_TIME = 2000;
0259:
0260: /**
0261: * JOnAS interaction mode : tighly coupled
0262: */
0263: private static final String TIGHLY_COUPLED = "tighly-coupled";
0264:
0265: /**
0266: * JOnAS interaction mode : loosely coupled
0267: */
0268: private static final String LOOSELY_COUPLED = "loosely-coupled";
0269:
0270: /**
0271: * jonas admin ping short timeout
0272: */
0273: private static final int PING_SHORT_TIMEOUT = 5;
0274:
0275: /**
0276: * jonas admin ping long timeout
0277: */
0278: private static final int PING_LONG_TIMEOUT = 120;
0279:
0280: /**
0281: * Sleep before exit of JVM
0282: */
0283: public static final int SLEEP_DELAY = 2000;
0284:
0285: /**
0286: * JOnAS command name in windows environmment
0287: */
0288: public static final String JONAS_CMD_NAME_WIN = "jonas.bat";
0289:
0290: /**
0291: * JOnAS command name in unix environmment
0292: */
0293: public static final String JONAS_CMD_NAME_UNIX = "jonas";
0294:
0295: /**
0296: * trace.properties file to use instead of the default file.
0297: */
0298: private static String csTraceFile = null;
0299:
0300: /**
0301: * carol.properties file to use instead of the default file.
0302: */
0303: private static String carolFile = null;
0304:
0305: /**
0306: * clusterd.xml filename (by default $JONAS_ROOT/conf/clusterd.xml)
0307: */
0308: private static String confFile = null;
0309:
0310: /**
0311: * J2EE Management domain
0312: */
0313: private static String domainName = null;
0314:
0315: /**
0316: * Logger to use
0317: */
0318: private Logger logger = null;
0319:
0320: /**
0321: * Object name
0322: */
0323: private String objectName = null;
0324:
0325: /**
0326: * Cluster Daemon name
0327: */
0328: private String name = null;
0329:
0330: /**
0331: * Process list
0332: */
0333: private Map processMap = new HashMap();
0334:
0335: /**
0336: * server state
0337: */
0338: private boolean isStarted = false;
0339:
0340: /**
0341: * Constructor
0342: */
0343: private ClusterDaemon() {
0344: }
0345:
0346: /**
0347: * Execute a JOnAS command in a separate JVM
0348: * @param server JOnAS server to pass the command
0349: * @param cmd command
0350: * @param keyCmd id the of command
0351: * @return Process associated with the command
0352: * @throws Exception exception if an error occurs
0353: */
0354: private Process execJOnASCmd(Server server, String cmd,
0355: String keyCmd) throws Exception {
0356:
0357: // check if the server exists
0358: if (server == null) {
0359: throw new ClusterDaemonException("server=null");
0360: }
0361:
0362: // Check that a same process in not running
0363: Process previousP = (Process) processMap.get(keyCmd);
0364: if (previousP != null) {
0365: try {
0366: this .logger.log(BasicLevel.INFO, "Cmd =" + cmd
0367: + " Process=" + previousP.exitValue());
0368: } catch (IllegalThreadStateException exc) {
0369: // there is already a process running
0370: this .logger.log(BasicLevel.INFO, "Cmd =" + cmd
0371: + " already running (pid="
0372: + previousP.toString() + ").");
0373: throw new IllegalStateException("Cmd =" + cmd
0374: + " already running (pid="
0375: + previousP.toString() + ").");
0376: }
0377: }
0378:
0379: // Set the environment
0380: Vector envv = new Vector();
0381: envv.addElement("JAVA_HOME=" + server.getJavaHome());
0382: envv.addElement("JONAS_ROOT=" + server.getJonasRoot());
0383: envv.addElement("JONAS_BASE=" + server.getJonasBase());
0384:
0385: String catalinaHome = System.getProperty("catalina.home");
0386: if (catalinaHome != null) {
0387: envv.addElement("CATALINA_HOME=" + catalinaHome);
0388: envv.addElement("CATALINA_BASE=" + server.getJonasBase());
0389: }
0390:
0391: String jettyHome = System.getProperty("jetty.home");
0392: if (jettyHome != null) {
0393: envv.addElement("JETTY_HOME=" + jettyHome);
0394: }
0395:
0396: // if OS is of Windows family, set the SystemRoot variable
0397: if (Env.isOsWindows()) {
0398: envv
0399: .addElement("SystemRoot="
0400: + WinSysEnv.get("SystemRoot"));
0401: }
0402:
0403: String[] enva = new String[envv.size()];
0404: envv.copyInto(enva);
0405:
0406: // set the bin dir according to the platform
0407: String binDir = server.getJonasRoot();
0408: if (File.separatorChar == '/') { // unix system
0409: binDir += "/bin/unix/";
0410: } else if (File.separatorChar == '\\') { // windows system
0411: binDir += "\\bin\\nt\\";
0412: }
0413:
0414: String[] cmda = null;
0415: if (cmd != null) {
0416: cmda = (binDir + cmd).split("\\s+");
0417: }
0418: // Set the working directory to the current directory
0419: File workingDirectory = new File(".");
0420:
0421: // Launch the command
0422: this .logger.log(BasicLevel.INFO, "Execute the command " + cmd
0423: + " with the env " + envv);
0424:
0425: Process p = Runtime.getRuntime().exec(cmda, enva,
0426: workingDirectory);
0427:
0428: // if synchronous mode, wait for the end of the execution
0429:
0430: processMap.put(keyCmd, p);
0431:
0432: // wait for starting
0433: Thread.sleep(SLEEP_TIME);
0434:
0435: // trace the start execution
0436: InputStream cmdError = p.getErrorStream();
0437: InputStream cmdOut = p.getInputStream();
0438: Thread err = new Thread(new CmdReader(this .logger, keyCmd,
0439: cmdError, true));
0440: Thread out = new Thread(new CmdReader(this .logger, keyCmd,
0441: cmdOut, false));
0442: out.start();
0443: err.start();
0444:
0445: // add a shudown hook for this process
0446: // if loosely coupled mode, only the current commands are destroyed - the JOnAS instances are not stopped
0447: int action = ShutdownHook.ACTION_DESTROY;
0448: if (keyCmd.startsWith("start") && isTighlyCoupled()) {
0449: action = ShutdownHook.ACTION_JONAS_STOP;
0450: }
0451: Runtime.getRuntime().addShutdownHook(
0452: new ShutdownHook(server.getName(), keyCmd, action));
0453:
0454: this .logger.log(BasicLevel.INFO, "Command " + cmd
0455: + " with the env " + envv + " launched");
0456:
0457: return p;
0458: }
0459:
0460: /**
0461: * @return JOnAS command name according to the os
0462: */
0463: private String getJOnASCmdName() {
0464: if (Env.isOsWindows()) {
0465: return JONAS_CMD_NAME_WIN;
0466: } else {
0467: return JONAS_CMD_NAME_UNIX;
0468: }
0469:
0470: }
0471:
0472: /**
0473: * Start a JOnAS instance
0474: * @param name server name to sart
0475: * @param domainName domain name
0476: * @param prm extra parameters
0477: * @param sync wait until the complete starting
0478: * @throws ClusterDaemonException if an error occurs
0479: */
0480: private void doStartJOnAS(String name, String domainName,
0481: String prm, boolean sync) throws ClusterDaemonException {
0482:
0483: // Get the Server
0484: Server server = getServer(name);
0485:
0486: // check if the server exists
0487: if (server == null) {
0488: //throw new ClusterDaemonException("JOnAS instance " + name + " doesn't exist");
0489: throw new ClusterDaemonException("JOnAS server " + name
0490: + " is not known by the cluster daemon "
0491: + getName());
0492: }
0493:
0494: // get prm if needed
0495: String xprm = null;
0496: if (prm == null) {
0497: xprm = "";
0498: } else {
0499: xprm = prm;
0500: }
0501:
0502: // add the parameters given in the configuration
0503: if (server.getXprm() != null) {
0504: xprm += server.getXprm();
0505: }
0506:
0507: String d = null;
0508: if (domainName == null) {
0509: d = ClusterDaemon.domainName;
0510: } else {
0511: d = domainName;
0512: }
0513:
0514: // Compose the command
0515: String cmd = getJOnASCmdName() + " start -n " + name
0516: + " -Ddomain.name=" + d + " " + xprm;
0517:
0518: // If the interaction mode with JOnAS is tighly coupled, the instance is launched in the foreground
0519: if (isTighlyCoupled()) {
0520: cmd += "-fg";
0521: }
0522:
0523: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0524: + " is starting ...");
0525:
0526: String keyCmd = "start/" + name;
0527:
0528: Process p = null;
0529: try {
0530: p = execJOnASCmd(server, cmd, keyCmd);
0531: } catch (Throwable t) {
0532: this .logger.log(BasicLevel.ERROR,
0533: "Unable to start the JOnAS instance " + name, t);
0534: throw new ClusterDaemonException(
0535: "Unable to start the JOnAS instance " + name, t);
0536: }
0537:
0538: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0539: + " launched");
0540:
0541: // If sync is required, wait for the end of the starting
0542: if (sync) {
0543: if (isLooselyCoupled()) {
0544: int exitCode = waitEndProcess(p);
0545: } else {
0546: if (doPingJOnAS(name, PING_LONG_TIMEOUT) == 1) {
0547: throw new ClusterDaemonException(
0548: "Unable to start the JOnAS instance "
0549: + name + " - unreachable server");
0550: }
0551: }
0552: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0553: + " started");
0554: }
0555: }
0556:
0557: /**
0558: * Stop a JOnAS instance
0559: * @param name instance name
0560: * @throws ClusterDaemonException if an error occurs
0561: */
0562: private void doStopJOnAS(String name) throws ClusterDaemonException {
0563: // Get the Server
0564: Server server = getServer(name);
0565:
0566: // check if the server exists
0567: if (server == null) {
0568: throw new ClusterDaemonException("JOnAS instance " + name
0569: + " doesn't exist");
0570: }
0571:
0572: String cmd = getJOnASCmdName() + " stop -n " + server.getName();
0573:
0574: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0575: + " is stopping ...");
0576:
0577: String keyCmd = "stop/" + server.getName();
0578:
0579: Process p = null;
0580: try {
0581: p = execJOnASCmd(server, cmd, keyCmd);
0582: } catch (Throwable t) {
0583: this .logger.log(BasicLevel.ERROR,
0584: "Unable to stop the JOnAS instance " + name, t);
0585: throw new ClusterDaemonException(
0586: "Unable to stop the JOnAS instance " + name, t);
0587: }
0588: // Wait until command termination and get the exit code
0589: int exitCode = waitEndProcess(p);
0590:
0591: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0592: + " stopped");
0593: }
0594:
0595: /**
0596: * Kill a JOnAS instance (only in tighly coupled mode)
0597: * @param name instance name
0598: * @throws ClusterDaemonException if an error occurs
0599: */
0600: private void doKillJOnAS(String name) throws ClusterDaemonException {
0601: // Get the Server
0602: Server server = getServer(name);
0603:
0604: // check if the server exists
0605: if (server == null) {
0606: throw new ClusterDaemonException("JOnAS instance " + name
0607: + " doesn't exist");
0608: }
0609:
0610: // check the mode
0611: if (isLooselyCoupled()) {
0612: throw new ClusterDaemonException(
0613: "Command not compatible with the loosely-coupled mode");
0614: }
0615:
0616: String keyCmd = "start/" + server.getName();
0617:
0618: // get the process
0619: Process p = (Process) processMap.get(keyCmd);
0620:
0621: // check that the process is alive
0622: boolean alive = false;
0623: try {
0624: this .logger.log(BasicLevel.DEBUG, "Process="
0625: + p.exitValue());
0626:
0627: // Process not alived
0628: this .logger.log(BasicLevel.DEBUG, "JOnAS instance " + name
0629: + " is already dead");
0630:
0631: } catch (IllegalThreadStateException exc) {
0632: // there is already a process running
0633: this .logger.log(BasicLevel.DEBUG, "JOnAS instance "
0634: + server.getName() + " is going to be killed");
0635: alive = true;
0636: }
0637:
0638: if (alive) {
0639: // kill the process
0640: p.destroy();
0641: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0642: + " killed");
0643: } else {
0644: this .logger.log(BasicLevel.INFO, "JOnAS instance " + name
0645: + " already killed");
0646: }
0647:
0648: }
0649:
0650: /**
0651: * Wait until the end of the execution of the process
0652: * @param p process
0653: * @return
0654: */
0655: private int waitEndProcess(Process p) {
0656:
0657: if (p == null) {
0658: return -1;
0659: }
0660: boolean isRunning = true;
0661: int exitCode = -1;
0662: while (isRunning) {
0663: this .logger.log(BasicLevel.DEBUG, "check if the process "
0664: + p + " is running");
0665: try {
0666: exitCode = p.exitValue();
0667: this .logger.log(BasicLevel.DEBUG, "cmd " + p
0668: + " is finished");
0669: isRunning = false;
0670: } catch (IllegalThreadStateException exc) {
0671: // there is already a process running
0672: this .logger.log(BasicLevel.DEBUG, "cmd " + p
0673: + " is still running");
0674: try {
0675: Thread.sleep(1000);
0676: } catch (InterruptedException e) {
0677: this .logger.log(BasicLevel.DEBUG, e);
0678: }
0679: }
0680: }
0681: return exitCode;
0682: }
0683:
0684: /**
0685: * Ping a JOnAS instance
0686: * @param name instance name
0687: * @param timeout timeout
0688: * @return exit code of the ping (0 ok, 1 ko)
0689: * @throws ClusterDaemonException if an error occurs
0690: */
0691: private int doPingJOnAS(String name, int timeout)
0692: throws ClusterDaemonException {
0693:
0694: // Get the Server
0695: Server server = getServer(name);
0696:
0697: // check if the server exists
0698: if (server == null) {
0699: throw new ClusterDaemonException("JOnAS instance " + name
0700: + " doesn't exist");
0701: }
0702:
0703: String cmd = getJOnASCmdName() + " admin -n "
0704: + server.getName() + " -ping -timeout " + timeout;
0705:
0706: this .logger.log(BasicLevel.INFO, "Ping JOnAS instance " + name
0707: + " ...");
0708:
0709: String keyCmd = "ping/" + server.getName();
0710:
0711: try {
0712: execJOnASCmd(server, cmd, keyCmd);
0713: } catch (Throwable t) {
0714: this .logger.log(BasicLevel.ERROR,
0715: "Unable to ping the JOnAS instance " + name, t);
0716: throw new ClusterDaemonException(
0717: "Unable to ping the JOnAS instance " + name, t);
0718: }
0719:
0720: // Wait until command termination and get the exit code
0721: Process p = (Process) processMap.get(keyCmd);
0722: int exitCode = waitEndProcess(p);
0723: this .logger.log(BasicLevel.INFO, "Ping JOnAS instance " + name
0724: + " return " + exitCode);
0725: return exitCode;
0726: }
0727:
0728: /**
0729: * Call the Log class to instanciate the cluster daemon logger
0730: * @throws ClusterDaemonException if an error occurs
0731: */
0732: private void initLogger() throws ClusterDaemonException {
0733:
0734: if (csTraceFile != null) {
0735: File tClient = new File(csTraceFile);
0736:
0737: if (!tClient.exists()) {
0738: throw new ClusterDaemonException("The file '"
0739: + csTraceFile + "' was not found.");
0740: }
0741:
0742: if (!tClient.isFile()) {
0743: throw new ClusterDaemonException("The file '"
0744: + csTraceFile
0745: + "' is not a valid file. Maybe a directory ?");
0746: }
0747:
0748: // Configure log
0749: System.setProperty("jonas.client.trace.file", csTraceFile);
0750: Log.reset();
0751: } else {
0752: csTraceFile = "trace";
0753: }
0754:
0755: // Allow tracing ejb/jms code
0756: try {
0757: System.setProperty("jonas.client.trace.file", csTraceFile);
0758: Log.configure(csTraceFile);
0759: } catch (NoClassDefFoundError ncdfe) {
0760: // 'normal exception' thrown outside the JOnAS containers
0761: }
0762:
0763: // init the logger
0764: this .logger = Log.getLogger(Log.JONAS_CLUSTER_DAEMON);
0765: }
0766:
0767: /**
0768: * Init carol
0769: * @throws ClusterDaemonException if an error occurs
0770: */
0771: private void initCarol() throws ClusterDaemonException {
0772:
0773: System.setProperty("carol.server.mode", "true");
0774: try {
0775: // hack
0776: Writer fw;
0777: File fConf = null;
0778: if (carolFile == null) {
0779:
0780: // 1st, try to use the carol.properties in $JONAS_BASE/conf
0781: URL myCarolFile = Thread
0782: .currentThread()
0783: .getContextClassLoader()
0784: .getResource(
0785: CarolDefaultValues.CAROL_CONFIGURATION_FILE);
0786: if (myCarolFile == null) {
0787:
0788: fConf = new File(System
0789: .getProperty("java.io.tmpdir")
0790: + File.separator + "cs-carol.properties");
0791: try {
0792: fw = new FileWriter(fConf);
0793: fw.write("carol.protocols=irmi\n");
0794: fw
0795: .write("carol.irmi.url=rmi://localhost:1806\n");
0796: // fw.write("carol.jvm.rmi.local.registry=true");
0797: fw.close();
0798: } catch (IOException e) {
0799: throw new ClusterDaemonException(
0800: "Cannot initialize carol", e);
0801: }
0802: } else {
0803: fConf = new File(myCarolFile.getFile());
0804: }
0805: } else {
0806: fConf = new File(carolFile);
0807: }
0808: ConfigurationRepository.init(fConf.toURL());
0809: } catch (ConfigurationException e) {
0810: throw new ClusterDaemonException(
0811: "Cannot initialize registry", e);
0812: } catch (MalformedURLException e) {
0813: throw new ClusterDaemonException(
0814: "Cannot initialize registry", e);
0815: }
0816: // start registry
0817: try {
0818: NameServiceManager.startNS();
0819: } catch (NameServiceException e) {
0820: throw new ClusterDaemonException("Cannot start registry", e);
0821: }
0822:
0823: this .logger.log(BasicLevel.INFO, "Carol initialized");
0824: }
0825:
0826: /**
0827: * Init MBeans
0828: * @throws ClusterDaemonException if an error occurs
0829: */
0830: private void initMBeans() throws ClusterDaemonException {
0831:
0832: try {
0833: MBeanServerHelper.startMBeanServer(domainName);
0834: } catch (MBeanServerException e) {
0835: throw new ClusterDaemonException(
0836: "Cannot start MBean server", e);
0837: }
0838:
0839: try {
0840: this .name = ClusterDaemonTools.getCurrentConfiguration()
0841: .getClusterDaemon().getName();
0842: String url = ClusterDaemonTools.getJmxUrl(name);
0843: String objName = ClusterDaemonTools.getObjectName();
0844: JMXRemoteHelper.startConnector(url, objName);
0845: this .logger.log(BasicLevel.DEBUG, "JMX remote connector ("
0846: + url + ", " + objName + " started ");
0847: } catch (JMXRemoteException e) {
0848: throw new ClusterDaemonException(
0849: "Cannot start JMX Remote connector", e);
0850: }
0851:
0852: // register local MBeans
0853: StringBuffer sb = new StringBuffer(domainName);
0854: sb.append(":type=ClusterDaemon");
0855: ObjectName on = null;
0856: try {
0857: on = new ObjectName(sb.toString());
0858: } catch (MalformedObjectNameException e) {
0859: throw new ClusterDaemonException("Cannot build ObjectName",
0860: e);
0861: }
0862:
0863: // Set the objectname
0864: this .setobjectName(on.toString());
0865:
0866: try {
0867: MBeanServerHelper.getMBeanServer().registerMBean(this , on);
0868: this .logger.log(BasicLevel.DEBUG, "MBean (" + on
0869: + ") registered");
0870:
0871: } catch (Exception e) {
0872: throw new ClusterDaemonException("Cannot register MBean '"
0873: + on + "' in MBeanServer", e);
0874: }
0875:
0876: this .logger.log(BasicLevel.INFO, "MBeans initialized");
0877: }
0878:
0879: /**
0880: *
0881: * @return true is the loosely-coupled mode is enabled
0882: */
0883: private boolean isLooselyCoupled() {
0884:
0885: return ClusterDaemonTools.getCurrentConfiguration()
0886: .getClusterDaemon().getJonasInteractionMode().equals(
0887: ClusterDaemon.LOOSELY_COUPLED);
0888: }
0889:
0890: /**
0891: *
0892: * @return true is the tighly-coupled mode is enabled
0893: */
0894: private boolean isTighlyCoupled() {
0895:
0896: return ClusterDaemonTools.getCurrentConfiguration()
0897: .getClusterDaemon().getJonasInteractionMode().equals(
0898: ClusterDaemon.TIGHLY_COUPLED);
0899: }
0900:
0901: /**
0902: * @param name server name
0903: * @return Configuration of the server
0904: */
0905: private Server getServer(String name) {
0906: JLinkedList servers = ClusterDaemonTools
0907: .getCurrentConfiguration().getClusterDaemon()
0908: .getServerList();
0909:
0910: if (servers != null) {
0911: for (int i = 0; i < servers.size(); i++) {
0912: Server serverElement = (Server) servers.get(i);
0913: if (serverElement.getName().equals(name)) {
0914: return serverElement;
0915: }
0916: }
0917: }
0918: return null;
0919: }
0920:
0921: /**
0922: * Start all the JOnAS instances configured with auto-reboot
0923: * @param domainName domain name
0924: * @param prm extra parameters
0925: * @param force the starting
0926: * @param sync synchronous starting
0927: * @return the nodes list with an indicator started/starting failed
0928: */
0929: private String doStartAllJOnAS(String domainName, String prm,
0930: boolean force, boolean sync) {
0931:
0932: // This feature is valid only if the jonas-interaction-mode is loosely-coupled
0933: if (!force) {
0934: if (isLooselyCoupled()) {
0935: String result = "JOnAS interaction mode is set to loosely-coupled => don't start the JOnAS instances";
0936: this .logger.log(BasicLevel.INFO, result);
0937: return result;
0938: } else {
0939: this .logger
0940: .log(BasicLevel.INFO,
0941: "JOnAS interaction mode is set to tighly-coupled => start the JOnAS instances");
0942: }
0943: }
0944:
0945: int nbInst = 0;
0946: int nbInstStarted = 0;
0947: String instStarted = "";
0948: int nbInstStartingError = 0;
0949: String instStartingError = "";
0950:
0951: JLinkedList servers = ClusterDaemonTools
0952: .getCurrentConfiguration().getClusterDaemon()
0953: .getServerList();
0954:
0955: if (servers != null) {
0956: for (int i = 0; i < servers.size(); i++) {
0957: nbInst++;
0958: Server serverElement = (Server) servers.get(i);
0959: if (serverElement.isAutoBoot() || force) {
0960: try {
0961: doStartJOnAS(serverElement.getName(),
0962: domainName, prm, sync);
0963: nbInstStarted++;
0964: instStarted += serverElement.getName() + " ";
0965: } catch (ClusterDaemonException e) {
0966: this .logger.log(BasicLevel.ERROR,
0967: "Error during the launching of JOnAS instance "
0968: + serverElement.getName(), e);
0969: nbInstStartingError++;
0970: instStartingError += serverElement.getName()
0971: + " ex=" + e + " ";
0972: }
0973: }
0974: }
0975: }
0976: String result = "Instances started (" + nbInstStarted + "/"
0977: + nbInst + ", " + instStarted + ")";
0978: result += " Instances starting failed (" + nbInstStartingError
0979: + "/" + nbInst + ", " + instStartingError + ")";
0980:
0981: this .logger.log(BasicLevel.INFO, result);
0982: return result;
0983: }
0984:
0985: /**
0986: * Stop all the JOnAS instances
0987: * @return the nodes list with an indicator stopped/stopping failed
0988: */
0989: private String doStopAllJOnAS() {
0990:
0991: int nbInst = 0;
0992: int nbInstStopped = 0;
0993: String instStopped = "";
0994: int nbInstStoppingError = 0;
0995: String instStoppingError = "";
0996:
0997: JLinkedList servers = ClusterDaemonTools
0998: .getCurrentConfiguration().getClusterDaemon()
0999: .getServerList();
1000:
1001: if (servers != null) {
1002: for (int i = 0; i < servers.size(); i++) {
1003: Server serverElement = (Server) servers.get(i);
1004: try {
1005: doStopJOnAS(serverElement.getName());
1006: nbInstStopped++;
1007: instStopped += serverElement.getName() + " ";
1008: } catch (ClusterDaemonException e) {
1009: this .logger.log(BasicLevel.ERROR,
1010: "Error during the stopping of JOnAS instance "
1011: + serverElement.getName(), e);
1012: nbInstStoppingError++;
1013: instStoppingError += serverElement.getName()
1014: + " ex=" + e + " ";
1015: }
1016: }
1017: }
1018: String result = "Instances stopped (" + nbInstStopped + "/"
1019: + nbInst + ", " + instStopped + ")";
1020: result += " Instances stopping failed (" + nbInstStoppingError
1021: + "/" + nbInst + ", " + instStoppingError + ")";
1022:
1023: this .logger.log(BasicLevel.INFO, result);
1024: return result;
1025: }
1026:
1027: /**
1028: * Kill all the JOnAS instances
1029: * @return the nodes list with an indicator killed/killing failed
1030: */
1031: private String doKillAllJOnAS() {
1032:
1033: int nbInst = 0;
1034: int nbInstKilled = 0;
1035: String instKilled = "";
1036: int nbInstKillingError = 0;
1037: String instKillingError = "";
1038:
1039: JLinkedList servers = ClusterDaemonTools
1040: .getCurrentConfiguration().getClusterDaemon()
1041: .getServerList();
1042:
1043: if (servers != null) {
1044: for (int i = 0; i < servers.size(); i++) {
1045: Server serverElement = (Server) servers.get(i);
1046: try {
1047: doKillJOnAS(serverElement.getName());
1048: nbInstKilled++;
1049: instKilled += serverElement.getName() + " ";
1050: } catch (ClusterDaemonException e) {
1051: this .logger.log(BasicLevel.ERROR,
1052: "Error during the killing of JOnAS instance "
1053: + serverElement.getName(), e);
1054: nbInstKillingError++;
1055: instKillingError += serverElement.getName()
1056: + " ex=" + e + " ";
1057: }
1058: }
1059: }
1060: String result = "Instances killed (" + nbInstKilled + "/"
1061: + nbInst + ", " + instKilled + ")";
1062: result += " Instances killing failed (" + nbInstKillingError
1063: + "/" + nbInst + ", " + instKillingError + ")";
1064:
1065: this .logger.log(BasicLevel.INFO, result);
1066: return result;
1067: }
1068:
1069: /**
1070: * Main function
1071: * @param args arguments list
1072: * @throws ClusterDaemonException if an error occurs
1073: */
1074: public void start(String[] args) throws ClusterDaemonException {
1075:
1076: boolean useMonitor = false;
1077:
1078: /**
1079: * Analyse the arguments list
1080: */
1081: for (int argn = 0; argn < args.length; argn++) {
1082: String arg = args[argn];
1083:
1084: try {
1085: if (arg.equals("-useMonitor")) {
1086: useMonitor = true;
1087: continue;
1088: }
1089:
1090: if (arg.equals("-traceFile")) {
1091: csTraceFile = args[++argn];
1092: continue;
1093: }
1094:
1095: if (arg.equals("-carolFile")) {
1096: carolFile = args[++argn];
1097: continue;
1098: }
1099:
1100: if (arg.equals("-confFile")) {
1101: confFile = args[++argn];
1102: continue;
1103: }
1104:
1105: if (arg.equals("--help") || arg.equals("-help")
1106: || arg.equals("-h") || arg.equals("-?")) {
1107: usage();
1108: System.exit(1);
1109: }
1110:
1111: } catch (ArrayIndexOutOfBoundsException aioobe) {
1112: // The next argument is not in the array
1113: throw new ClusterDaemonException(
1114: "A required parameter was missing after the argument"
1115: + arg);
1116: }
1117: }
1118:
1119: /**
1120: * Init the trace logger
1121: */
1122: initLogger();
1123:
1124: /**
1125: * Load clusterd.xml
1126: */
1127: ClusterDaemonTools.loadClusterDaemonConfiguration(confFile);
1128:
1129: /**
1130: * Get the domain name
1131: */
1132: domainName = (String) System.getProperty(JProp.DOMAIN_NAME,
1133: JProp.JONAS_DEF_NAME);
1134: String confDomainName = ClusterDaemonTools
1135: .getCurrentConfiguration().getClusterDaemon()
1136: .getDomainName();
1137:
1138: if (domainName.equals(JProp.JONAS_DEF_NAME)) {
1139: if (confDomainName == null) {
1140: this .logger
1141: .log(BasicLevel.INFO,
1142: "Domain set with default value : "
1143: + domainName);
1144: } else {
1145: domainName = confDomainName;
1146: this .logger.log(BasicLevel.INFO,
1147: "Domain set with the value defined in the clusterd.xml : "
1148: + domainName);
1149: }
1150: } else if (domainName.equals(confDomainName)) {
1151: this .logger.log(BasicLevel.INFO,
1152: "Domain set with the value defined in the clusterd.xml : "
1153: + domainName);
1154: } else {
1155: throw new ClusterDaemonException(
1156: "Incorrect domain name set in clusterd.xml file and in the property "
1157: + confDomainName + "/" + domainName);
1158: }
1159:
1160: /**
1161: * Init Carol
1162: */
1163: initCarol();
1164:
1165: /**
1166: * Init MBean server
1167: */
1168: initMBeans();
1169:
1170: this .logger.log(BasicLevel.INFO,
1171: "JOnAS Cluster Daemon Started!");
1172:
1173: /**
1174: * start the monitor thread if required
1175: */
1176: if (useMonitor) {
1177: new Monitor(this .logger).start();
1178: }
1179:
1180: /**
1181: * Launches the JOnAS instances configured with auto-reboot
1182: */
1183: doStartAllJOnAS(domainName, null, false, false);
1184:
1185: /**
1186: * The server is started now
1187: */
1188: isStarted = true;
1189:
1190: }
1191:
1192: /**
1193: * Usage
1194: */
1195: private static void usage() {
1196: System.out
1197: .println("Usage : jclusterd [-DdomainName=<name>] [-useMonitor] [-confFile my_clusterd.xml] [-carolFile=<my_carol.properties>]");
1198: }
1199:
1200: /**
1201: * Main method
1202: * @param args the arguments of the cluster daemon
1203: */
1204: public static void main(String[] args) {
1205: // Retrieve command line parameters
1206: ClusterDaemon cs = new ClusterDaemon();
1207:
1208: try {
1209: cs.start(args);
1210: } catch (Exception e) {
1211: System.err.println("There was the following exception : "
1212: + e.getMessage());
1213: e.printStackTrace();
1214: System.exit(-1);
1215: }
1216: }
1217:
1218: //////////////////////////////////////////////////////////////////////////////////////////
1219: ///
1220: /// MBEAN INTERFACES
1221: ///
1222: //////////////////////////////////////////////////////////////////////////////////////////
1223: /**
1224: * @return Object Name
1225: */
1226: public String getobjectName() {
1227: return objectName;
1228: }
1229:
1230: /**
1231: * Sets the object name of this mbean
1232: * @param name the Object Name
1233: */
1234: public void setobjectName(String name) {
1235: this .objectName = name;
1236: }
1237:
1238: /**
1239: * @return true if it is an event provider
1240: */
1241: public boolean iseventProvider() {
1242: return false;
1243: }
1244:
1245: /**
1246: * @return true if this managed object implements J2EE State Management
1247: * Model
1248: */
1249: public boolean isstateManageable() {
1250: return false;
1251: }
1252:
1253: /**
1254: * @return true if this managed object implements the J2EE StatisticProvider
1255: * Model
1256: */
1257: public boolean isstatisticsProvider() {
1258: return false;
1259: }
1260:
1261: /**
1262: * (MBean interface)
1263: * @return the JAVA_HOME for a specified server name
1264: * @param name JOnAS instance name
1265: */
1266: public String getJavaHome4Server(String name) {
1267: Server server = getServer(name);
1268: if (server != null) {
1269: return server.getJavaHome();
1270: } else {
1271: return null;
1272: }
1273: }
1274:
1275: /**
1276: * (MBean interface)
1277: * @return the JONAS_BASE for a specified server name
1278: * @param name JOnAS instance name
1279: */
1280: public String getJonasBase4Server(String name) {
1281: Server server = getServer(name);
1282: if (server != null) {
1283: return server.getJonasBase();
1284: } else {
1285: return null;
1286: }
1287: }
1288:
1289: /**
1290: * (MBean interface)
1291: * @return the JONAS_ROOT for a specified server name
1292: * @param name JOnAS instance name
1293: */
1294: public String getJonasRoot4Server(String name) {
1295: Server server = getServer(name);
1296: if (server != null) {
1297: return server.getJonasRoot();
1298: } else {
1299: return null;
1300: }
1301: }
1302:
1303: /**
1304: * @return the list of JOnAS instances
1305: */
1306: public String getServersList() {
1307: StringBuffer sb = new StringBuffer();
1308: JLinkedList servers = ClusterDaemonTools
1309: .getCurrentConfiguration().getClusterDaemon()
1310: .getServerList();
1311:
1312: if (servers != null) {
1313: for (int i = 0; i < servers.size(); i++) {
1314: Server serverElement = (Server) servers.get(i);
1315: if (i != 0) {
1316: sb.append(",");
1317: }
1318: sb.append(serverElement.getName());
1319: }
1320: }
1321: return sb.toString();
1322: }
1323:
1324: /**
1325: * Flush the configuration to the clusterd.xml file
1326: * @throws ClusterDaemonException if an error occurs
1327: */
1328: private void flushConfiguration() throws ClusterDaemonException {
1329: String buf = ClusterDaemonTools.getCurrentConfiguration()
1330: .toXML();
1331: FileWriter fw;
1332: try {
1333: fw = new FileWriter(confFile);
1334: fw.write(buf);
1335: fw.close();
1336: } catch (IOException e) {
1337: throw new ClusterDaemonException(e);
1338: }
1339:
1340: this .logger.log(BasicLevel.DEBUG,
1341: "Configuration flushed in the file " + confFile + "\n"
1342: + buf);
1343: }
1344:
1345: /**
1346: * Add a server configuration (MBean interface)
1347: * @param name server name
1348: * @param description server description
1349: * @param javaHome JAVA_HOME dir
1350: * @param jonasBase JONAS_BASE dir
1351: * @param jonasRoot JONAS_ROOT dir
1352: * @throws ClusterDaemonException if an error occurs
1353: */
1354: public void addServer(String name, String description,
1355: String javaHome, String jonasRoot, String jonasBase)
1356: throws ClusterDaemonException {
1357:
1358: // check if the server already exist
1359: if (getServer(name) != null) {
1360: throw new ClusterDaemonException("JOnAS instance " + name
1361: + "already exists");
1362: }
1363: JLinkedList servers = ClusterDaemonTools
1364: .getCurrentConfiguration().getClusterDaemon()
1365: .getServerList();
1366:
1367: // add a server
1368: Server myServer = new Server();
1369: myServer.setName(name);
1370: myServer.setDescription(description);
1371: myServer.setJavaHome(javaHome);
1372: myServer.setJonasBase(jonasBase);
1373: myServer.setJonasRoot(jonasRoot);
1374: servers.add(myServer);
1375: ClusterDaemonTools.getCurrentConfiguration().getClusterDaemon()
1376: .setServerList(servers);
1377:
1378: // flush the configuration
1379: flushConfiguration();
1380:
1381: this .logger.log(BasicLevel.DEBUG, "Server " + name + " added");
1382:
1383: }
1384:
1385: /**
1386: * Remove a server configuration (MBean interface)
1387: * @param name server name
1388: * @throws ClusterDaemonException if an error occurs
1389: */
1390: public void removeServer(String name) throws ClusterDaemonException {
1391:
1392: // Get the Server
1393: Server server = getServer(name);
1394:
1395: // check if the server exists
1396: if (server == null) {
1397: throw new ClusterDaemonException("JOnAS instance " + name
1398: + " doesn't exist");
1399: }
1400:
1401: // Remove the server
1402: JLinkedList servers = ClusterDaemonTools
1403: .getCurrentConfiguration().getClusterDaemon()
1404: .getServerList();
1405: servers.remove(server);
1406: ClusterDaemonTools.getCurrentConfiguration().getClusterDaemon()
1407: .setServerList(servers);
1408:
1409: // flush the configuration
1410: flushConfiguration();
1411:
1412: this .logger
1413: .log(BasicLevel.DEBUG, "Server " + name + " removed");
1414:
1415: }
1416:
1417: /**
1418: * Modify a server configuration (MBean interface)
1419: * @param name server name
1420: * @param description server description
1421: * @param javaHome JAVA_HOME dir
1422: * @param jonasBase JONAS_BASE dir
1423: * @param jonasRoot JONAS_ROOT dir
1424: * @throws ClusterDaemonException if an error occurs
1425: */
1426: public void modifyServer(String name, String description,
1427: String javaHome, String jonasRoot, String jonasBase)
1428: throws ClusterDaemonException {
1429:
1430: // Get the Server
1431: Server myServer = getServer(name);
1432:
1433: // check if the server exists
1434: if (myServer == null) {
1435: throw new ClusterDaemonException("JOnAS instance " + name
1436: + " doesn't exist");
1437: }
1438:
1439: // Update the server
1440: myServer.setDescription(description);
1441: myServer.setJavaHome(javaHome);
1442: myServer.setJonasBase(jonasBase);
1443: myServer.setJonasRoot(jonasRoot);
1444:
1445: // flush the configuration
1446: flushConfiguration();
1447:
1448: this .logger
1449: .log(BasicLevel.DEBUG, "Server " + name + " updated");
1450: }
1451:
1452: /**
1453: * Reload the configuration (MBean interface)
1454: * @throws ClusterDaemonException if an error occurs
1455: */
1456: public void reloadConfiguration() throws ClusterDaemonException {
1457:
1458: ClusterDaemonTools.loadClusterDaemonConfiguration(confFile);
1459:
1460: }
1461:
1462: /**
1463: * Start a JOnAS instance (MBean interface)
1464: * @param name instance name
1465: * @param domainName domain name
1466: * @param prm extra parameters
1467: * @throws ClusterDaemonException if an error occurs
1468: */
1469: public void startJOnAS(String name, String domainName, String prm)
1470: throws ClusterDaemonException {
1471: doStartJOnAS(name, domainName, prm, true);
1472: }
1473:
1474: /**
1475: * Stop a JOnAS instance (MBean interface)
1476: * @param name instance name
1477: * @throws ClusterDaemonException if an error occurs
1478: */
1479: public void stopJOnAS(String name) throws ClusterDaemonException {
1480: doStopJOnAS(name);
1481: }
1482:
1483: /**
1484: * Ping a JOnAS instance (MBean interface)
1485: * @param name instance name
1486: * @return exit code of the ping (0 ok, 1 ko)
1487: * @throws ClusterDaemonException if an error occurs
1488: */
1489: public int pingJOnAS(String name) throws ClusterDaemonException {
1490: return doPingJOnAS(name, PING_SHORT_TIMEOUT);
1491: }
1492:
1493: /**
1494: * Start all the JOnAS instances configured with auto-reboot
1495: * @param domainName domain name
1496: * @param prm extra parameters
1497: * @return the nodes list with an indicator started/starting failed
1498: */
1499: public String startAllJOnAS(String domainName, String prm) {
1500: return doStartAllJOnAS(domainName, prm, true, true);
1501: }
1502:
1503: /**
1504: * Stop all the JOnAS instances
1505: * @return the nodes list with an indicator stopped/stopping failed
1506: */
1507: public String stopAllJOnAS() {
1508: return doStopAllJOnAS();
1509: }
1510:
1511: /**
1512: * Stop the current instance
1513: */
1514: public void stopClusterDaemon() {
1515:
1516: if (isStarted) {
1517: this .logger.log(BasicLevel.DEBUG, "Server not started");
1518: }
1519:
1520: isStarted = false;
1521:
1522: // Delay the exit of the JVM so the link client/server is not broken.
1523: // client call will return before the end of the JVM on server side.
1524: new Thread(new Runnable() {
1525:
1526: public void run() {
1527: try {
1528: // Wait before exit
1529: Thread.sleep(SLEEP_DELAY);
1530: } catch (InterruptedException ie) {
1531: ie.printStackTrace();
1532: throw new IllegalStateException("Cannot wait: "
1533: + ie.getMessage());
1534: }
1535: System.exit(0);
1536: }
1537: }).start();
1538:
1539: this .logger.log(BasicLevel.INFO, "Server stopped");
1540: }
1541:
1542: public String getName() {
1543: return name;
1544: }
1545: }
|