0001: /**
0002: * Sequoia: Database clustering technology.
0003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
0004: * Science And Control (INRIA).
0005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
0006: * Contact: sequoia@continuent.org
0007: *
0008: * Licensed under the Apache License, Version 2.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: * Free Software Foundation; either version 2.1 of the License, or any later
0021: * version.
0022: *
0023: * This library is distributed in the hope that it will be useful, but WITHOUT
0024: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0025: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General public final License
0026: * for more details.
0027: *
0028: * You should have received a copy of the GNU Lesser General public final License
0029: * along with this library; if not, write to the Free Software Foundation,
0030: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
0031: *
0032: * Initial developer(s): Emmanuel Cecchet.
0033: * Contributor(s): Mathieu Peltier, Nicolas Modrzyk.
0034: */package org.continuent.sequoia.controller.core;
0035:
0036: import java.io.BufferedOutputStream;
0037: import java.io.BufferedReader;
0038: import java.io.BufferedWriter;
0039: import java.io.DataOutputStream;
0040: import java.io.File;
0041: import java.io.FileNotFoundException;
0042: import java.io.FileOutputStream;
0043: import java.io.FileReader;
0044: import java.io.FileWriter;
0045: import java.io.IOException;
0046: import java.io.InputStream;
0047: import java.net.URL;
0048: import java.net.URLDecoder;
0049: import java.text.SimpleDateFormat;
0050: import java.util.ArrayList;
0051: import java.util.Date;
0052: import java.util.Enumeration;
0053: import java.util.Hashtable;
0054: import java.util.Iterator;
0055: import java.util.List;
0056: import java.util.Locale;
0057: import java.util.zip.ZipEntry;
0058: import java.util.zip.ZipFile;
0059:
0060: import javax.management.MalformedObjectNameException;
0061: import javax.management.Notification;
0062: import javax.management.NotificationBroadcasterSupport;
0063:
0064: import org.continuent.sequoia.common.exceptions.ControllerException;
0065: import org.continuent.sequoia.common.exceptions.VirtualDatabaseException;
0066: import org.continuent.sequoia.common.i18n.Translate;
0067: import org.continuent.sequoia.common.jmx.JmxConstants;
0068: import org.continuent.sequoia.common.jmx.notifications.SequoiaNotificationList;
0069: import org.continuent.sequoia.common.log.LogManager;
0070: import org.continuent.sequoia.common.log.Trace;
0071: import org.continuent.sequoia.common.util.Constants;
0072: import org.continuent.sequoia.common.xml.ControllerXmlTags;
0073: import org.continuent.sequoia.common.xml.XmlComponent;
0074: import org.continuent.sequoia.common.xml.XmlTools;
0075: import org.continuent.sequoia.controller.core.security.ControllerSecurityManager;
0076: import org.continuent.sequoia.controller.core.shutdown.ControllerShutdownThread;
0077: import org.continuent.sequoia.controller.jmx.MBeanServerManager;
0078: import org.continuent.sequoia.controller.jmx.RmiConnector;
0079: import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabase;
0080: import org.continuent.sequoia.controller.xml.DatabasesParser;
0081:
0082: /**
0083: * The Sequoia controller main class. It loads its configuration file and wait
0084: * for virtual database to be loaded.
0085: *
0086: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
0087: * @author <a href="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
0088: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
0089: * @author <a href="mailto:duncan@mightybot.com">Duncan Smith </a>
0090: * @version 1.0
0091: */
0092: public final class Controller implements XmlComponent {
0093:
0094: /** Sequoia controller port number listening for driver connections */
0095: private int portNumber;
0096: private int backlogSize;
0097:
0098: /**
0099: * The IP address to bind the controller to. Useful for machines that contain
0100: * multiple network interface cards and wish to bind to a specific card.
0101: * Default evaluates to localhost IP address (127.0.0.1).
0102: */
0103: private String ipAddress;
0104:
0105: /** Thread that listens for driver connections */
0106: private ControllerServerThread connectionThread;
0107:
0108: /** Logger instances. */
0109: static Trace logger = Trace
0110: .getLogger("org.continuent.sequoia.controller.core.Controller");
0111: static Trace endUserLogger = Trace
0112: .getLogger("org.continuent.sequoia.enduser");
0113:
0114: /** Hashtable of <code>VirtualDatabase</code> objects. */
0115: private Hashtable virtualDatabases;
0116:
0117: /** Hashtable of options */
0118: private Hashtable configuration;
0119:
0120: /** Security Manager */
0121: private ControllerSecurityManager security;
0122:
0123: /** Report Manager */
0124: private ReportManager report;
0125:
0126: private boolean isShuttingDown;
0127:
0128: protected NotificationBroadcasterSupport notificationBroadcasterSupport;
0129:
0130: protected int notificationSequence = 0;
0131:
0132: /* Constructor(s) */
0133:
0134: /**
0135: * Creates a new <code>Controller</code> instance.
0136: *
0137: * @param ipAddress bind the controller to this ipAddress
0138: * @param port bind the controller to listen to this port
0139: * @param backlog backlog connection size
0140: */
0141: public Controller(String ipAddress, int port, int backlog) {
0142: virtualDatabases = new Hashtable();
0143: this .ipAddress = ipAddress;
0144: this .portNumber = port;
0145: this .backlogSize = backlog;
0146: }
0147:
0148: /**
0149: * Sets the NotificationBroadcasterSupport associated with the MBean managing
0150: * this controller.
0151: *
0152: * @param notificationBroadcasterSupport the notificationBroadcasterSupport
0153: * associated with the mbean managing this controller
0154: */
0155: public void setNotificationBroadcasterSupport(
0156: NotificationBroadcasterSupport notificationBroadcasterSupport) {
0157: this .notificationBroadcasterSupport = notificationBroadcasterSupport;
0158: }
0159:
0160: /**
0161: * Sends a JMX Notification on behalf of the MBean associated with this
0162: * controller
0163: *
0164: * @param type type of the JMX notification
0165: * @param message message associated with the notification
0166: * @see SequoiaNotificationList
0167: */
0168: protected void sendJmxNotification(String type, String message) {
0169: if (!MBeanServerManager.isJmxEnabled()) {
0170: // do not send jmx notification if jmx is not enabled
0171: return;
0172: }
0173: try {
0174: notificationBroadcasterSupport
0175: .sendNotification(new Notification(type,
0176: JmxConstants.getControllerObjectName(),
0177: notificationSequence++, message));
0178: } catch (MalformedObjectNameException e) {
0179: // unable to get a correct controller object name: do nothing
0180: logger.warn("Unable to send JMX notification", e);
0181: }
0182: }
0183:
0184: //
0185: // Virtual databases management
0186: //
0187:
0188: /**
0189: * Adds virtual databases contained in the XML document given as a String. If
0190: * a virtual database name is provided, only this database is loaded with the
0191: * provided autoLoad and checkpoint information.
0192: *
0193: * @param xml XML configuration file content
0194: * @param vdbName optional virtual database name to autoload
0195: * @param autoEnable autoenable backend mode for virtual database (or init)
0196: * @param checkpoint checkpoint name if autoEnable is set to force
0197: * @throws ControllerException if an error occurs
0198: */
0199: public void addVirtualDatabases(String xml, String vdbName,
0200: int autoEnable, String checkpoint)
0201: throws ControllerException {
0202: if (logger.isDebugEnabled())
0203: logger.debug(Translate.get(
0204: "controller.add.virtualdatabase", vdbName));
0205: if (vdbName != null && this .hasVirtualDatabase(vdbName)) {
0206: throw new ControllerException(Translate.get(
0207: "controller.add.virtualdatabase.already.used",
0208: vdbName));
0209: }
0210: try {
0211: DatabasesParser parser = new DatabasesParser(this , vdbName,
0212: autoEnable, checkpoint);
0213: parser.readXML(xml, true);
0214: } catch (Exception e) {
0215: String msg = Translate.get(
0216: "controller.add.virtualdatabases.failed", e
0217: .getMessage());
0218: logger.warn(msg, e);
0219: throw new ControllerException(msg);
0220: }
0221: }
0222:
0223: /**
0224: * Register a VirtualDatabase with default options
0225: *
0226: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#addVirtualDatabases(String)
0227: */
0228: public void addVirtualDatabases(String xml)
0229: throws ControllerException {
0230: if (logger.isDebugEnabled()) {
0231: logger.debug(Translate
0232: .get("controller.loading.virtualdatabase"));
0233: }
0234: this .addVirtualDatabases(xml, null,
0235: ControllerConstants.AUTO_ENABLE_FALSE, null);
0236: }
0237:
0238: /**
0239: * Registers a new <code>VirtualDatabase</code> in this controller.
0240: *
0241: * @param vdb the <code>VirtualDatabase</code> to register
0242: * @throws ControllerException if an error occurs
0243: */
0244: public void addVirtualDatabase(VirtualDatabase vdb)
0245: throws ControllerException {
0246: this .addVirtualDatabase(vdb,
0247: ControllerConstants.AUTO_ENABLE_FALSE, null);
0248: }
0249:
0250: /**
0251: * Add the virtual database with the specified options
0252: *
0253: * @param vdb the <code>VirtualDatabase</code> object to add
0254: * @param autoLoad specified if backends should be enabled
0255: * @param checkPoint specified the checkPoint to recover from, leave null if
0256: * no recovery speficied
0257: * @throws ControllerException if database already exists on the specified
0258: * <code>Controller</code> object
0259: */
0260: public synchronized void addVirtualDatabase(VirtualDatabase vdb,
0261: int autoLoad, String checkPoint) throws ControllerException {
0262: // Add the database or retrieve it if it already exists
0263: if (hasVirtualDatabase(vdb.getDatabaseName())) {
0264: String msg = Translate.get(
0265: "controller.add.virtualdatabase.already.used", vdb
0266: .getDatabaseName());
0267: logger.warn(msg);
0268: throw new ControllerException(msg);
0269: } else {
0270: virtualDatabases.put(vdb.getDatabaseName(), vdb);
0271: }
0272:
0273: // Enable backends with the proper states
0274: try {
0275: if (logger.isDebugEnabled())
0276: logger.debug(Translate.get(
0277: "controller.database.autoenable", autoLoad));
0278:
0279: switch (autoLoad) {
0280: case ControllerConstants.AUTO_ENABLE_TRUE:
0281: vdb.enableAllBackendsFromCheckpoint();
0282: break;
0283: case ControllerConstants.AUTO_ENABLE_FALSE:
0284: break;
0285: case ControllerConstants.AUTO_ENABLE_FORCE:
0286: logger
0287: .warn("Backends enabled in force mode from checkpoint "
0288: + checkPoint);
0289: vdb.forceEnableAllBackendsFromCheckpoint(checkPoint);
0290: break;
0291: case ControllerConstants.AUTO_ENABLE_INIT:
0292: case ControllerConstants.AUTO_ENABLE_FORCE_LOAD:
0293: break;
0294: default:
0295: logger
0296: .error("Unsupported autoEnabledBackends mode in controller configuration");
0297: break;
0298: }
0299: } catch (VirtualDatabaseException e) {
0300: logger
0301: .warn("Failed to automatically enable backends, manual resynchronization is probably needed");
0302: }
0303:
0304: logger.info(Translate.get("controller.add.virtualdatabase", vdb
0305: .getDatabaseName()));
0306: endUserLogger.info(Translate.get(
0307: "controller.add.virtualdatabase.success", vdb
0308: .getDatabaseName()));
0309: sendJmxNotification(
0310: SequoiaNotificationList.CONTROLLER_VIRTUALDATABASE_ADDED,
0311: Translate.get("notification.virtualdatabase.added", vdb
0312: .getDatabaseName()));
0313: }
0314:
0315: /**
0316: * Gets the <code>VirtualDatabase</code> object corresponding to a virtual
0317: * database name.
0318: *
0319: * @param virtualDatabaseName the virtual database name
0320: * @return a <code>VirtualDatabase</code> object or null if not found
0321: */
0322: public VirtualDatabase getVirtualDatabase(String virtualDatabaseName) {
0323: return (VirtualDatabase) virtualDatabases
0324: .get(virtualDatabaseName);
0325: }
0326:
0327: /**
0328: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#getVirtualDatabaseNames()
0329: */
0330: public ArrayList getVirtualDatabaseNames() {
0331: ArrayList result = new ArrayList();
0332: for (Iterator iter = virtualDatabases.values().iterator(); iter
0333: .hasNext();)
0334: result.add(((VirtualDatabase) iter.next())
0335: .getVirtualDatabaseName());
0336: return result;
0337: }
0338:
0339: /**
0340: * Returns information about the available virtual databases.
0341: *
0342: * @return ArrayList of information about virtual databases.
0343: */
0344: public ArrayList getVirtualDatabases() {
0345: return new ArrayList(virtualDatabases.values());
0346: }
0347:
0348: /**
0349: * Tests if a <code>VirtualDatabase</code> of a given name exists in this
0350: * controller.
0351: *
0352: * @param name the virtual database name
0353: * @return <code>true</code> if the virtual database exists
0354: */
0355: public boolean hasVirtualDatabase(String name) {
0356: return virtualDatabases.containsKey(name);
0357: }
0358:
0359: /**
0360: * Removes the virtual database with the given name (if any)
0361: *
0362: * @param virtualname name of the virtual database
0363: * @return a translated string for success message
0364: */
0365: public String removeVirtualDatabase(String virtualname) {
0366: if (hasVirtualDatabase(virtualname)) {
0367: if (this .virtualDatabases.remove(virtualname) == null) {
0368: logger
0369: .warn("Unexpected missing virtual database named "
0370: + virtualname
0371: + " while removing from virtual database list");
0372: }
0373:
0374: // Send notification
0375: sendJmxNotification(
0376: SequoiaNotificationList.CONTROLLER_VIRTUALDATABASE_REMOVED,
0377: Translate.get(
0378: "notification.virtualdatabase.shutdown",
0379: virtualname));
0380: }
0381: return Translate
0382: .get("controller.removeVirtualDatabase.success",
0383: virtualname);
0384: }
0385:
0386: //
0387: // Controller operations
0388: //
0389:
0390: /**
0391: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#addDriver(byte[])
0392: */
0393: public void addDriver(byte[] bytes) throws Exception {
0394: // Try to find drivers directory in the classpath
0395: File driversDirectory = null;
0396: URL url = Controller.class
0397: .getResource(ControllerConstants.SEQUOIA_DRIVER_JAR_FILE);
0398: boolean error = false;
0399: if (url != null) {
0400: driversDirectory = (new File(URLDecoder.decode(url
0401: .getFile()))).getParentFile();
0402: error = (driversDirectory == null)
0403: || !driversDirectory.exists();
0404: }
0405:
0406: if (error) {
0407: String msg = Translate.get(
0408: "controller.driver.dir.not.found", driversDirectory
0409: .toString());
0410: logger.error(msg);
0411: endUserLogger.error(Translate.get(
0412: "controller.add.driver.failed", msg));
0413: throw new ControllerException(msg);
0414: }
0415:
0416: // Read the array of bytes to a file
0417: File temp = null;
0418: try {
0419: temp = File.createTempFile("driver", "zip",
0420: driversDirectory);
0421: FileOutputStream output = new FileOutputStream(temp);
0422: output.write(bytes);
0423: output.close();
0424: } catch (IOException e) {
0425: String msg = Translate.get(
0426: "controller.add.jar.write.failed", e);
0427: logger.error(msg);
0428: endUserLogger.error(Translate.get(
0429: "controller.add.driver.failed", msg));
0430: throw new ControllerException(msg);
0431: }
0432:
0433: // Unzip the file content
0434: try {
0435: Enumeration entries;
0436: ZipFile zipFile = new ZipFile(temp);
0437:
0438: // Read the file
0439: int lenght;
0440: InputStream in;
0441: BufferedOutputStream out;
0442: byte[] buffer = new byte[1024];
0443:
0444: entries = zipFile.entries();
0445: while (entries.hasMoreElements()) {
0446: ZipEntry entry = (ZipEntry) entries.nextElement();
0447:
0448: if (entry.isDirectory()) {
0449: // Create the directory
0450: if (logger.isDebugEnabled())
0451: logger.debug(Translate.get(
0452: "controller.add.jar.extract.dir", entry
0453: .getName()));
0454:
0455: (new File(driversDirectory, entry.getName()))
0456: .mkdir();
0457: continue;
0458: }
0459:
0460: // Extract the file
0461: if (logger.isDebugEnabled())
0462: logger.debug(Translate.get(
0463: "controller.add.jar.extract.file", entry
0464: .getName()));
0465:
0466: in = zipFile.getInputStream(entry);
0467: out = new BufferedOutputStream(new FileOutputStream(
0468: driversDirectory
0469: + System.getProperty("file.separator")
0470: + entry.getName()));
0471: while ((lenght = in.read(buffer)) >= 0)
0472: out.write(buffer, 0, lenght);
0473:
0474: in.close();
0475: out.close();
0476: }
0477:
0478: zipFile.close();
0479: temp.delete();
0480: String msg = Translate.get("controller.add.driver.success",
0481: driversDirectory.toString());
0482: logger.info(msg);
0483: endUserLogger.info(msg);
0484: } catch (IOException e) {
0485: String msg = Translate.get(
0486: "controller.driver.extract.failed", new String[] {
0487: temp.getCanonicalPath(), e.getMessage() });
0488: logger.error(msg);
0489: endUserLogger.error(Translate.get(
0490: "controller.add.driver.failed", msg));
0491: throw new ControllerException(msg);
0492: }
0493: }
0494:
0495: /**
0496: * Read a XML configuration file and load only the
0497: * <code>VirtualDatabase</code> specified in the arguments list
0498: *
0499: * @param filename XML configuration file name to take info on
0500: * <code>VirtualDatabase</code>
0501: * @param virtualName the only database to load, null if should load all
0502: * @param autoLoad specifies if the backends should be enabled automatically
0503: * after loading
0504: * @param checkPoint checkPoint to recover from when enabling backends. Leave
0505: * <code>null</code> if no recovery option is needed.
0506: * @return a diagnostic message (success or error)
0507: * @throws Exception if an error occurs
0508: */
0509: public String loadXmlConfiguration(String filename,
0510: String virtualName, int autoLoad, String checkPoint)
0511: throws Exception {
0512: FileReader fileReader = null;
0513: try {
0514: filename = filename.trim();
0515: try {
0516: fileReader = new FileReader(filename);
0517: } catch (FileNotFoundException fnf) {
0518: return Translate.get("controller.file.not.found",
0519: filename);
0520: }
0521:
0522: if (logger.isDebugEnabled())
0523: logger.debug("Loading virtual database configuration "
0524: + filename);
0525: // Read the file
0526: BufferedReader in = new BufferedReader(fileReader);
0527: StringBuffer xml = new StringBuffer();
0528: String line;
0529: do {
0530: line = in.readLine();
0531: if (line != null)
0532: xml.append(line);
0533: } while (line != null);
0534:
0535: // Send it to the controller
0536: addVirtualDatabases(xml.toString(), virtualName, autoLoad,
0537: checkPoint);
0538: return Translate.get("controller.file.send", filename);
0539: } catch (Exception e) {
0540: logger.error(Translate.get("controller.loadXml.failed",
0541: ControllerConstants.PRODUCT_NAME, e), e);
0542: throw new ControllerException(Translate.get(
0543: "controller.loadXml.failed",
0544: ControllerConstants.PRODUCT_NAME, e));
0545: } finally {
0546: if (fileReader != null)
0547: fileReader.close();
0548: }
0549: }
0550:
0551: /**
0552: * Save current configuration of the controller to a default file
0553: *
0554: * @return Status message
0555: * @throws Exception if an error occurs
0556: * @see org.continuent.sequoia.controller.core.ControllerConstants#getSaveFile
0557: */
0558: public String saveConfiguration() throws Exception {
0559: String msg;
0560: try {
0561: String configurationFile = ControllerConstants
0562: .getSaveFile(new SimpleDateFormat(
0563: "yyyy-MM-dd-HH-mm").format(new Date()));
0564: DataOutputStream dos = new DataOutputStream(
0565: new BufferedOutputStream(new FileOutputStream(
0566: configurationFile)));
0567: StringBuffer xml = new StringBuffer();
0568: xml.append(XmlTools.prettyXml(getXmlVirtualDatabases()));
0569: String prettyXml = xml.toString();
0570: // ugly hack to insert the doctype which has been stripped
0571: // when prettyfying the xml
0572: prettyXml = XmlTools.insertDoctype(prettyXml,
0573: ControllerConstants.VIRTUAL_DATABASE_DOCTYPE);
0574: dos.write(prettyXml.getBytes());
0575: dos.close();
0576: msg = Translate.get("controller.save.configuration",
0577: configurationFile);
0578: return msg;
0579: } catch (Exception e) {
0580: msg = Translate.get("controller.save.configuration.failed",
0581: e);
0582: logger.error(msg);
0583: throw new ControllerException(msg, e);
0584: }
0585: }
0586:
0587: //
0588: // Controller shutdown
0589: //
0590: /**
0591: * Create report about fatal error
0592: *
0593: * @param fatal the cause of the fatal error
0594: */
0595: public void endOfController(Exception fatal) {
0596: endUserLogger.fatal(Translate.get("fatal.error"));
0597: logger.fatal(Translate.get("fatal.error"));
0598: if (report.isGenerateOnFatal()) {
0599: new ReportManager(this ).generateAndWriteException(true,
0600: fatal);
0601: logger
0602: .info(Translate.get("fatal.report.generated",
0603: report.getReportLocation() + File.separator
0604: + ControllerConstants.REPORT_FILE));
0605: }
0606: Runtime.getRuntime().exit(1);
0607: }
0608:
0609: /**
0610: * Access the connection thread. Need this for shutting down
0611: *
0612: * @return <code>connectionThread</code>
0613: */
0614: public ControllerServerThread getConnectionThread() {
0615: return connectionThread;
0616: }
0617:
0618: /**
0619: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#isShuttingDown()
0620: */
0621: public boolean isShuttingDown() {
0622: return isShuttingDown;
0623: }
0624:
0625: /**
0626: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#shutdown()
0627: */
0628: public void shutdown() throws ControllerException {
0629: if (virtualDatabases.size() != 0) {
0630: String listOfActiveVdbs = "";
0631: for (Enumeration e = virtualDatabases.keys(); e
0632: .hasMoreElements();) {
0633: if (listOfActiveVdbs.length() != 0)
0634: listOfActiveVdbs = listOfActiveVdbs + ",";
0635: listOfActiveVdbs = listOfActiveVdbs + e.nextElement();
0636: }
0637: String msg = Translate
0638: .get(
0639: "controller.shutdown.error.existing.virtualdatabases",
0640: new String[] { getControllerName(),
0641: listOfActiveVdbs });
0642: throw new ControllerException(msg);
0643: }
0644:
0645: ControllerShutdownThread shutdownThread = null;
0646: synchronized (this ) {
0647: if (isShuttingDown()) {
0648: logger.info(Translate.get(
0649: "controller.already.shutting.down", this
0650: .getControllerName()));
0651: return;
0652: }
0653:
0654: isShuttingDown = true;
0655: shutdownThread = new ControllerShutdownThread(this );
0656: logger.info(Translate.get("controller.shutdown", this
0657: .getControllerName()));
0658: }
0659:
0660: Thread thread = new Thread(shutdownThread.getShutdownGroup(),
0661: shutdownThread, "Controller Shutdown Thread");
0662: thread.start();
0663:
0664: try {
0665: logger.info("Waiting for controller shutdown");
0666: thread.join();
0667: logger.info(Translate.get("controller.shutdown.completed",
0668: this .getControllerName()));
0669: } catch (InterruptedException e) {
0670: e.printStackTrace();
0671: }
0672: }
0673:
0674: /**
0675: * Launches the Sequoia controller. The available options are:
0676: * <ul>
0677: * <li><code>-h</code> or <code>--help</code> <code><port></code>:
0678: * displays usage informations.</li>
0679: * <li><code>-j</code> or <code>--jmx</code> <code><port></code>:
0680: * optinal JMX server HTTP adaptor port number.</li>
0681: * <li><code>-n</code> or <code>--name</code> <code><name></code>:
0682: * optional controller name.</li>
0683: * <li><code>-i</code> or <code>--ip</code> <code><ip></code>:
0684: * optional IP address to beind the controller to.</li>
0685: * <li><code>-r</code> or <code>--rmi</code> <code><port></code>:
0686: * optional RMI registry port number.</li>
0687: * <li><code>-v</code> or <code>--version</code>: displays version
0688: * informations.</li>
0689: * </ul>
0690: * <p>
0691: * The controller starts listening for socket connections on the default port.
0692: * Jmx is configured, and a virtual database can be added.
0693: * <p>
0694: * {@link org.continuent.sequoia.controller.core.ControllerConstants#DEFAULT_PORT}
0695: * Default Listening port
0696: *
0697: * @param args command line arguments (see above)
0698: * @throws Exception when everything goes wrong
0699: */
0700: public static void main(String[] args) throws Exception {
0701: System
0702: .setProperty(
0703: "javax.management.builder.initial",
0704: org.continuent.sequoia.controller.jmx.MBeanServerBuilder.class
0705: .getName());
0706:
0707: if (ControllerConstants.CONTROLLER_FACTORY == null) {
0708: System.err
0709: .println("Impossible to start Controller with an invalid controller.properties file.");
0710: System.exit(1);
0711: }
0712:
0713: // This parses command line arguments and exits for
0714: // --help and --version
0715: ControllerConfiguration conf = new ControllerConfiguration(args);
0716:
0717: logger.info(getVersion());
0718:
0719: Controller controller = conf.getController();
0720: if (controller != null)
0721: controller.launch();
0722: else {
0723: endUserLogger.error(Translate
0724: .get("controller.configure.failed"));
0725: throw new Exception(Translate
0726: .get("controller.configure.failed"));
0727: }
0728: }
0729:
0730: /**
0731: * Actively launch the <code>controller</code>. Add startup actions here to
0732: * avoid them in <method>main </method>
0733: */
0734: public void launch() {
0735: connectionThread = new ControllerServerThread(this );
0736: connectionThread.start();
0737:
0738: SimpleDateFormat formatter = new SimpleDateFormat(
0739: "yyyy.MM.dd ww 'at' hh:mm:ss a zzz");
0740: Date day = new Date();
0741: String date = formatter.format(day);
0742: logger.info(Translate.get("controller.date", date));
0743: logger.info(Translate.get("controller.ready",
0744: getControllerName()));
0745: endUserLogger.info(Translate.get("controller.ready",
0746: getControllerName()));
0747: }
0748:
0749: //
0750: // Controller information
0751: //
0752:
0753: /**
0754: * Returns the controller name.
0755: *
0756: * @return String
0757: */
0758: public String getControllerName() {
0759: return ipAddress + ":" + portNumber;
0760: }
0761:
0762: /**
0763: * Get the IP address to bind the controller to
0764: *
0765: * @return the IP address
0766: */
0767: public String getIPAddress() {
0768: return ipAddress;
0769: }
0770:
0771: /**
0772: * Set the IP address to bind the controller to
0773: *
0774: * @param ipAddress the IP address to use
0775: */
0776: public void setIPAddress(String ipAddress) {
0777: this .ipAddress = ipAddress;
0778: }
0779:
0780: /**
0781: * Get the controller port number
0782: *
0783: * @return the port number
0784: */
0785: public int getPortNumber() {
0786: return portNumber;
0787: }
0788:
0789: /**
0790: * Set the controller backlog size.
0791: *
0792: * @param port the port number to set
0793: */
0794: public void setPortNumber(int port) {
0795: portNumber = port;
0796: }
0797:
0798: /**
0799: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#getBacklogSize()
0800: */
0801: public int getBacklogSize() {
0802: return backlogSize;
0803: }
0804:
0805: /**
0806: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#setBacklogSize(int)
0807: */
0808: public void setBacklogSize(int size) {
0809: backlogSize = size;
0810: }
0811:
0812: /**
0813: * Returns jmx enable
0814: *
0815: * @return jmxEnabled
0816: */
0817: public boolean getJmxEnable() {
0818: return MBeanServerManager.isJmxEnabled();
0819: }
0820:
0821: /**
0822: * Return the jmx name of this controller (hostname:rmiport)
0823: *
0824: * @return jmx name
0825: */
0826: public String getJmxName() {
0827: if (getJmxEnable()) {
0828: List rmiConnectors = RmiConnector.getRmiConnectors();
0829: if ((rmiConnectors != null) && (rmiConnectors.size() > 0)) {
0830: RmiConnector connector = ((RmiConnector) rmiConnectors
0831: .get(0));
0832: return connector.getHostName() + ":"
0833: + connector.getPort();
0834: }
0835: }
0836: return getControllerName();
0837: }
0838:
0839: /**
0840: * set enable JMX
0841: *
0842: * @param enable true if jmx should be enable.
0843: */
0844: public void setJmxEnable(boolean enable) {
0845: configuration.put(ControllerConfiguration.JMX_ENABLE, ""
0846: + enable);
0847: }
0848:
0849: /**
0850: * Returns Version as a long String
0851: *
0852: * @return version
0853: */
0854: public static String getVersion() {
0855: return Translate.get("controller.info", new String[] {
0856: ControllerConstants.PRODUCT_NAME, Constants.VERSION });
0857: }
0858:
0859: /**
0860: * Get current configuration options
0861: *
0862: * @return configure a <code>Hashtable</code> with controller options
0863: */
0864: public Hashtable getConfiguration() {
0865: return configuration;
0866: }
0867:
0868: /**
0869: * Check whether security is enabled or not
0870: *
0871: * @return true if there is not null controller security manager
0872: */
0873: public boolean isSecurityEnabled() {
0874: return security != null;
0875: }
0876:
0877: /**
0878: * @return Returns the security.
0879: */
0880: public ControllerSecurityManager getSecurity() {
0881: return security;
0882: }
0883:
0884: /**
0885: * @param security The security to set.
0886: */
0887: public void setSecurity(ControllerSecurityManager security) {
0888: this .security = security;
0889: }
0890:
0891: /**
0892: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#generateReport()
0893: */
0894: public void generateReport() throws Exception {
0895: report.generate(true);
0896: }
0897:
0898: /**
0899: * Sets the configuration value.
0900: *
0901: * @param configuration The configuration to set.
0902: */
0903: public void setConfiguration(Hashtable configuration) {
0904: this .configuration = configuration;
0905: }
0906:
0907: /**
0908: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#getVersionNumber()
0909: */
0910: public String getVersionNumber() {
0911: return Constants.VERSION;
0912: }
0913:
0914: /**
0915: * @see org.continuent.sequoia.common.xml.XmlComponent#getXml()
0916: */
0917: public String getXml() {
0918: try {
0919: String prettyXml = XmlTools.prettyXml(getXmlController());
0920: return XmlTools.insertDoctype(prettyXml,
0921: ControllerConstants.CONTROLLER_DOCTYPE);
0922: } catch (Exception e) {
0923: logger.error(Translate.get(
0924: "controller.xml.transformation.failed", e));
0925: return e.getMessage();
0926: }
0927: }
0928:
0929: /**
0930: * Return the xml version of the controller.xml file without doc type
0931: * declaration, just data.
0932: *
0933: * @return controller xml data
0934: */
0935: public String getXmlController() {
0936: StringBuffer info = new StringBuffer();
0937: info
0938: .append("<"
0939: + ControllerConstants.CONTROLLER_XML_ROOT_ELEMENT
0940: + ">");
0941: info.append("<" + ControllerXmlTags.ELT_CONTROLLER + " "
0942: + ControllerXmlTags.ATT_CONTROLLER_IP + "=\""
0943: + this .getIPAddress() + "\" "
0944: + ControllerXmlTags.ATT_CONTROLLER_PORT + "=\""
0945: + this .getPortNumber() + "\" " + ">");
0946:
0947: info.append("<" + ControllerXmlTags.ELT_INTERNATIONALIZATION
0948: + " " + ControllerXmlTags.ATT_LANGUAGE + "=\""
0949: + Locale.getDefault().getLanguage() + "\"/>");
0950:
0951: if (report.isReportEnabled()) {
0952: info.append("<" + ControllerXmlTags.ELT_REPORT + " "
0953: + ControllerXmlTags.ATT_REPORT_ENABLE_FILE_LOGGING
0954: + "=\"" + report.isEnableFileLogging() + "\" "
0955: + ControllerXmlTags.ATT_REPORT_HIDE_SENSITIVE_DATA
0956: + "=\"" + report.isHideSensitiveData() + "\" "
0957: + ControllerXmlTags.ATT_REPORT_GENERATE_ON_FATAL
0958: + "=\"" + report.isGenerateOnFatal() + "\" "
0959: + ControllerXmlTags.ATT_REPORT_GENERATE_ON_SHUTDOWN
0960: + "=\"" + report.isGenerateOnShutdown() + "\" "
0961: + ControllerXmlTags.ATT_REPORT_REPORT_LOCATION
0962: + "=\"" + report.getReportLocation() + "\" />");
0963: }
0964:
0965: if (getJmxEnable()) {
0966: info.append("<" + ControllerXmlTags.ELT_JMX + ">");
0967: if (configuration
0968: .containsKey(JmxConstants.ADAPTOR_TYPE_HTTP)) {
0969: info.append("<"
0970: + ControllerXmlTags.ELT_HTTP_JMX_ADAPTOR
0971: + " "
0972: + ControllerXmlTags.ATT_JMX_ADAPTOR_PORT
0973: + "=\""
0974: + configuration
0975: .get(JmxConstants.ADAPTOR_TYPE_HTTP)
0976: + "\" />");
0977: }
0978: if (configuration
0979: .containsKey(JmxConstants.ADAPTOR_TYPE_RMI)) {
0980: info.append("<"
0981: + ControllerXmlTags.ELT_RMI_JMX_ADAPTOR
0982: + " "
0983: + ControllerXmlTags.ATT_JMX_ADAPTOR_PORT
0984: + "=\""
0985: + configuration
0986: .get(JmxConstants.ADAPTOR_TYPE_RMI)
0987: + "\" />");
0988: }
0989:
0990: info.append("</" + ControllerXmlTags.ELT_JMX + ">");
0991: }
0992:
0993: if (this .isSecurityEnabled())
0994: info.append(this .getSecurity().getXml());
0995: info.append("</" + ControllerXmlTags.ELT_CONTROLLER + ">");
0996: info
0997: .append("</"
0998: + ControllerConstants.CONTROLLER_XML_ROOT_ELEMENT
0999: + ">");
1000: return info.toString();
1001: }
1002:
1003: /**
1004: * Same as above but for the virtual databases.
1005: *
1006: * @return xml virtual databases data.
1007: */
1008: public String getXmlVirtualDatabases() {
1009: try {
1010: StringBuffer info = new StringBuffer();
1011: info.append(XML_VERSION);
1012: info.append("\n");
1013: info
1014: .append("<"
1015: + ControllerConstants.VIRTUAL_DATABASE_XML_ROOT_ELEMENT
1016: + ">");
1017: List vdbs = this .getVirtualDatabases();
1018: for (int i = 0; i < vdbs.size(); i++) {
1019: info.append(((XmlComponent) vdbs.get(i)).getXml());
1020: }
1021: info
1022: .append("</"
1023: + ControllerConstants.VIRTUAL_DATABASE_XML_ROOT_ELEMENT
1024: + ">");
1025: return info.toString();
1026: } catch (Exception e) {
1027: logger.error(e.getMessage(), e);
1028: return e.getMessage();
1029: }
1030: }
1031:
1032: //
1033: // Logging system
1034: //
1035:
1036: /**
1037: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#refreshLogConfiguration()
1038: */
1039: public void refreshLogConfiguration() throws ControllerException {
1040: try {
1041: LogManager.configure(URLDecoder.decode(this .getClass()
1042: .getResource(ControllerConstants.LOG4J_RESOURCE)
1043: .getFile()));
1044: if (logger.isDebugEnabled())
1045: logger.info(Translate
1046: .get("controller.refresh.log.success"));
1047: } catch (Exception e) {
1048: throw new ControllerException(Translate
1049: .get("controller.logconfigfile.not.found"));
1050: }
1051: }
1052:
1053: /**
1054: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#updateLogConfigurationFile(java.lang.String)
1055: */
1056: public void updateLogConfigurationFile(String newConfiguration)
1057: throws IOException, ControllerException {
1058: File logFile = new File(URLDecoder.decode(getClass()
1059: .getResource(ControllerConstants.LOG4J_RESOURCE)
1060: .getFile()));
1061: BufferedWriter writer = new BufferedWriter(new FileWriter(
1062: logFile));
1063: writer.write(newConfiguration);
1064: writer.flush();
1065: writer.close();
1066: refreshLogConfiguration();
1067: }
1068:
1069: /**
1070: * @see org.continuent.sequoia.common.jmx.mbeans.ControllerMBean#viewLogConfigurationFile()
1071: */
1072: public String viewLogConfigurationFile() throws IOException {
1073: File logFile = new File(URLDecoder.decode(getClass()
1074: .getResource(ControllerConstants.LOG4J_RESOURCE)
1075: .getFile()));
1076: BufferedReader reader = new BufferedReader(new FileReader(
1077: logFile));
1078: StringBuffer buffer = new StringBuffer();
1079: String line;
1080: while ((line = reader.readLine()) != null)
1081: buffer.append(line + System.getProperty("line.separator"));
1082: reader.close();
1083: return buffer.toString();
1084: }
1085:
1086: /**
1087: * Returns the report value.
1088: *
1089: * @return Returns the report.
1090: */
1091: public ReportManager getReport() {
1092: return report;
1093: }
1094:
1095: /**
1096: * Sets the report value.
1097: *
1098: * @param report The report to set.
1099: */
1100: public void setReport(ReportManager report) {
1101: this.report = report;
1102: }
1103:
1104: }
|