Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

Controller.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General public final License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General public final License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General public final License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet. 
00022  * Contributor(s): Mathieu Peltier, Nicolas Modrzyk.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.core;
00026 
00027 import java.io.BufferedOutputStream;
00028 import java.io.BufferedReader;
00029 import java.io.BufferedWriter;
00030 import java.io.DataOutputStream;
00031 import java.io.File;
00032 import java.io.FileNotFoundException;
00033 import java.io.FileOutputStream;
00034 import java.io.FileReader;
00035 import java.io.FileWriter;
00036 import java.io.IOException;
00037 import java.io.InputStream;
00038 import java.net.URL;
00039 import java.net.URLDecoder;
00040 import java.text.SimpleDateFormat;
00041 import java.util.ArrayList;
00042 import java.util.Date;
00043 import java.util.Enumeration;
00044 import java.util.Hashtable;
00045 import java.util.Iterator;
00046 import java.util.Locale;
00047 import java.util.zip.ZipEntry;
00048 import java.util.zip.ZipFile;
00049 
00050 import javax.management.NotCompliantMBeanException;
00051 import javax.management.ObjectName;
00052 
00053 import org.objectweb.cjdbc.common.exceptions.ControllerException;
00054 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException;
00055 import org.objectweb.cjdbc.common.i18n.Translate;
00056 import org.objectweb.cjdbc.common.jmx.JmxConstants;
00057 import org.objectweb.cjdbc.common.jmx.JmxException;
00058 import org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean;
00059 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList;
00060 import org.objectweb.cjdbc.common.log.LogManager;
00061 import org.objectweb.cjdbc.common.log.Trace;
00062 import org.objectweb.cjdbc.common.util.Constants;
00063 import org.objectweb.cjdbc.common.xml.ControllerXmlTags;
00064 import org.objectweb.cjdbc.common.xml.XmlComponent;
00065 import org.objectweb.cjdbc.common.xml.XmlTools;
00066 import org.objectweb.cjdbc.controller.core.security.ControllerSecurityManager;
00067 import org.objectweb.cjdbc.controller.core.shutdown.ControllerForceShutdownThread;
00068 import org.objectweb.cjdbc.controller.core.shutdown.ControllerSafeShutdownThread;
00069 import org.objectweb.cjdbc.controller.core.shutdown.ControllerShutdownThread;
00070 import org.objectweb.cjdbc.controller.core.shutdown.ControllerWaitShutdownThread;
00071 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean;
00072 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager;
00073 import org.objectweb.cjdbc.controller.jmx.RmiConnector;
00074 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
00075 import org.objectweb.cjdbc.controller.xml.DatabasesParser;
00076 
00077 /**
00078  * The C-JDBC controller main class. It registers itself in the RMI registry and
00079  * waits for C-JDBC driver requests.
00080  * 
00081  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00082  * @author <a href="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
00083  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
00084  * @author <a href="mailto:duncan@mightybot.com">Duncan Smith </a>
00085  * @version 1.0
00086  */
00087 public final class Controller extends AbstractStandardMBean
00088     implements
00089       ControllerMBean,
00090       XmlComponent
00091 {
00092 
00093   /** C-JDBC controller port number listening for driver connections */
00094   private int                       portNumber;
00095   private int                       backlogSize;
00096 
00097   /**
00098    * The IP address to bind the controller to. Useful for machines that contain
00099    * multiple network interface cards and wish to bind to a specific card.
00100    * Default evaluates to localhost IP address (127.0.0.1).
00101    */
00102   private String                    ipAddress;
00103 
00104   /** Thread that listens for driver connections */
00105   private ControllerServerThread    connectionThread;
00106 
00107   /** Logger instance. */
00108   static Trace                      logger = Trace
00109                                                .getLogger("org.objectweb.cjdbc.controller.core.Controller");
00110 
00111   /** Hashtable of <code>VirtualDatabase</code> objects. */
00112   private Hashtable                 virtualDatabases;
00113 
00114   /** Hashtable of options */
00115   private Hashtable                 configuration;
00116 
00117   /** Security Manager */
00118   private ControllerSecurityManager security;
00119 
00120   /** Report Manager */
00121   private ReportManager             report;
00122 
00123   private boolean                   isShuttingDown;
00124 
00125   /* Constructor(s) */
00126 
00127   /**
00128    * Creates a new <code>Controller</code> instance.
00129    * 
00130    * @param ipAddress bind the controller to this ipAddress
00131    * @param port bind the controller to listen to this port
00132    * @param backlog backlog connection size
00133    * @exception NotCompliantMBeanException in case the bean does not comply with
00134    *              jmx
00135    * @exception JmxException the bean could not be registered
00136    */
00137   public Controller(String ipAddress, int port, int backlog)
00138       throws NotCompliantMBeanException, JmxException
00139   {
00140     super(ControllerMBean.class);
00141     virtualDatabases = new Hashtable();
00142     this.ipAddress = ipAddress;
00143     this.portNumber = port;
00144     this.backlogSize = backlog;
00145     ObjectName name = JmxConstants.getControllerObjectName();
00146     MBeanServerManager.registerMBean(this, name);
00147   }
00148 
00149   //
00150   // Virtual databases management
00151   //
00152 
00153   /**
00154    * Adds virtual databases contained in the XML document given as a String. If
00155    * a virtual database name is provided, only this database is loaded with the
00156    * provided autoLoad and checkpoint information.
00157    * 
00158    * @param xml XML configuration file content
00159    * @param vdbName optional virtual database name to autoload
00160    * @param autoEnable autoenable backend mode for virtual database
00161    * @param checkpoint checkpoint name if autoEnable is set to force
00162    * @throws ControllerException if an error occurs
00163    */
00164   public void addVirtualDatabases(String xml, String vdbName, int autoEnable,
00165       String checkpoint) throws ControllerException
00166   {
00167     if (logger.isDebugEnabled())
00168       logger.debug(Translate.get("controller.add.virtualdatabase", vdbName));
00169     if (vdbName != null && this.hasVirtualDatabase(vdbName))
00170     {
00171       throw new ControllerException(Translate
00172           .get("controller.add.virtualdatabase.already.used"));
00173     }
00174     try
00175     {
00176       DatabasesParser parser = new DatabasesParser(this, vdbName, autoEnable,
00177           checkpoint);
00178       parser.readXML(xml, true);
00179     }
00180     catch (Exception e)
00181     {
00182       String msg = Translate.get("controller.add.virtualdatabase.failed", e);
00183       logger.warn(msg, e);
00184       throw new ControllerException(msg);
00185     }
00186   }
00187 
00188   /**
00189    * Register a VirtualDatabase with default options
00190    * 
00191    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#addVirtualDatabases(String)
00192    */
00193   public void addVirtualDatabases(String xml) throws ControllerException
00194   {
00195     if (logger.isDebugEnabled())
00196     {
00197       logger.debug(Translate.get("controller.loading.virtualdatabase"));
00198     }
00199     this.addVirtualDatabases(xml, null,
00200         ControllerConstants.AUTO_ENABLE_BACKEND,
00201         ControllerConstants.DATABASE_DEFAULT_CHECKPOINT);
00202   }
00203 
00204   /**
00205    * Registers a new <code>VirtualDatabase</code> in this controller.
00206    * 
00207    * @param vdb the <code>VirtualDatabase</code> to register
00208    * @throws ControllerException if an error occurs
00209    */
00210   public void addVirtualDatabase(VirtualDatabase vdb)
00211       throws ControllerException
00212   {
00213     this.addVirtualDatabase(vdb, ControllerConstants.AUTO_ENABLE_BACKEND,
00214         ControllerConstants.DATABASE_DEFAULT_CHECKPOINT);
00215   }
00216 
00217   /**
00218    * Add the virtual database with the specified options
00219    * 
00220    * @param vdb the <code>VirtualDatabase</code> object to add
00221    * @param autoLoad specified if backends should be enabled
00222    * @param checkPoint specified the checkPoint to recover from, leave null if
00223    *          no recovery speficied
00224    * @throws ControllerException if database already exists on the specified
00225    *           <code>Controller</code> object
00226    */
00227   public synchronized void addVirtualDatabase(VirtualDatabase vdb,
00228       int autoLoad, String checkPoint) throws ControllerException
00229   {
00230     // Add the database or retrieve it if it already exists
00231     if (hasVirtualDatabase(vdb.getDatabaseName()))
00232     {
00233       String msg = Translate.get("controller.database.already.exists", vdb
00234           .getDatabaseName());
00235       logger.warn(msg);
00236       throw new ControllerException(msg);
00237     }
00238     else
00239     {
00240       virtualDatabases.put(vdb.getDatabaseName(), vdb);
00241 
00242       // Send notification
00243       if (MBeanServerManager.isJmxEnabled())
00244       {
00245         Hashtable databases = new Hashtable();
00246         try
00247         {
00248           databases.put("backends", vdb.getAllBackendNames());
00249         }
00250         catch (VirtualDatabaseException e)
00251         {
00252           // ignore
00253         }
00254         RmiConnector.broadcastNotification(this,
00255             CjdbcNotificationList.CONTROLLER_VIRTUALDATABASE_ADDED,
00256             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
00257                 "notification.virtualdatabase.added", vdb
00258                     .getVirtualDatabaseName()), databases);
00259       }
00260     }
00261 
00262     // Enable backends with the proper states
00263     try
00264     {
00265       if (logger.isDebugEnabled())
00266         logger.debug(Translate.get("controller.database.autoenable", autoLoad));
00267 
00268       switch (autoLoad)
00269       {
00270         case ControllerConstants.AUTO_ENABLE_TRUE :
00271           vdb.enableAllBackendsFromCheckpoint();
00272           break;
00273         case ControllerConstants.AUTO_ENABLE_FALSE :
00274           break;
00275         case ControllerConstants.AUTO_ENABLE_FORCE :
00276           logger.warn("Backends enabled in force mode from checkpoint "
00277               + checkPoint);
00278           vdb.forceEnableAllBackendsFromCheckpoint(checkPoint);
00279           break;
00280         default :
00281           logger
00282               .error("Unsupported autoEnabledBackends mode in controller configuration");
00283           break;
00284       }
00285     }
00286     catch (VirtualDatabaseException e)
00287     {
00288       throw new ControllerException(e);
00289     }
00290 
00291     logger.info(Translate.get("controller.add.virtualdatabase", vdb
00292         .getDatabaseName()));
00293   }
00294 
00295   /**
00296    * Gets the <code>VirtualDatabase</code> object corresponding to a virtual
00297    * database name.
00298    * 
00299    * @param virtualDatabaseName the virtual database name
00300    * @return a <code>VirtualDatabase</code> object or null if not found
00301    */
00302   public VirtualDatabase getVirtualDatabase(String virtualDatabaseName)
00303   {
00304     return (VirtualDatabase) virtualDatabases.get(virtualDatabaseName);
00305   }
00306 
00307   /**
00308    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#getVirtualDatabaseNames()
00309    */
00310   public ArrayList getVirtualDatabaseNames()
00311   {
00312     ArrayList result = new ArrayList();
00313     for (Iterator iter = virtualDatabases.values().iterator(); iter.hasNext();)
00314       result.add(((VirtualDatabase) iter.next()).getVirtualDatabaseName());
00315     return result;
00316   }
00317 
00318   /**
00319    * Returns information about the available virtual databases.
00320    * 
00321    * @return ArrayList of information about virtual databases.
00322    */
00323   public ArrayList getVirtualDatabases()
00324   {
00325     ArrayList result = new ArrayList();
00326     for (Iterator iter = virtualDatabases.values().iterator(); iter.hasNext();)
00327       result.add(iter.next());
00328     return result;
00329   }
00330 
00331   /**
00332    * Tests if a <code>VirtualDatabase</code> of a given name exists in this
00333    * controller.
00334    * 
00335    * @param name the virtual database name
00336    * @return <code>true</code> if the virtual database exists
00337    */
00338   public boolean hasVirtualDatabase(String name)
00339   {
00340     return virtualDatabases.containsKey(name);
00341   }
00342 
00343   /**
00344    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#removeVirtualDatabase(String)
00345    */
00346   public String removeVirtualDatabase(String virtualname)
00347       throws ControllerException
00348   {
00349     if (hasVirtualDatabase(virtualname))
00350     {
00351       VirtualDatabase vdb = (VirtualDatabase) virtualDatabases.get(virtualname);
00352       try
00353       {
00354         vdb.disableAllBackends();
00355       }
00356       catch (VirtualDatabaseException e)
00357       {
00358         throw new ControllerException(e);
00359       }
00360       this.virtualDatabases.remove(virtualname);
00361 
00362       // Send notification
00363       if (MBeanServerManager.isJmxEnabled())
00364       {
00365         Hashtable databases = new Hashtable();
00366         try
00367         {
00368           databases.put("backends", vdb.getAllBackendNames());
00369         }
00370         catch (VirtualDatabaseException e)
00371         {
00372           // ignore
00373         }
00374         RmiConnector.broadcastNotification(this,
00375             CjdbcNotificationList.CONTROLLER_VIRTUALDATABASE_REMOVED,
00376             CjdbcNotificationList.NOTIFICATION_LEVEL_INFO, Translate.get(
00377                 "notification.virtualdatabase.removed", vdb
00378                     .getVirtualDatabaseName()), databases);
00379       }
00380     }
00381     return Translate.get("controller.removeVirtualDatabase.success",
00382         virtualname);
00383   }
00384 
00385   //
00386   // Controller operations
00387   //
00388 
00389   /**
00390    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#addDriver(byte[])
00391    */
00392   public void addDriver(byte[] bytes) throws Exception
00393   {
00394     // Try to find drivers directory in the classpath
00395     File driversDirectory = null;
00396     URL url = Controller.class
00397         .getResource(ControllerConstants.C_JDBC_DRIVER_JAR_FILE);
00398     boolean error = false;
00399     if (url != null)
00400     {
00401       driversDirectory = (new File(URLDecoder.decode(url.getFile())))
00402           .getParentFile();
00403       error = (driversDirectory == null) || !driversDirectory.exists();
00404     }
00405 
00406     if (error)
00407     {
00408       String msg = Translate.get("controller.driver.dir.not.found");
00409       logger.error(msg);
00410       throw new ControllerException(msg);
00411     }
00412 
00413     // Read the array of bytes to a file
00414     File temp = null;
00415     try
00416     {
00417       temp = File.createTempFile("driver", "zip", driversDirectory);
00418       FileOutputStream output = new FileOutputStream(temp);
00419       output.write(bytes);
00420       output.close();
00421     }
00422     catch (IOException e)
00423     {
00424       String msg = Translate.get("controller.add.jar.read.failed", e);
00425       logger.error(msg);
00426       throw new ControllerException(msg);
00427     }
00428 
00429     // Unzip the file content
00430     try
00431     {
00432       Enumeration entries;
00433       ZipFile zipFile = new ZipFile(temp);
00434 
00435       // Read the file
00436       int lenght;
00437       InputStream in;
00438       BufferedOutputStream out;
00439       byte[] buffer = new byte[1024];
00440 
00441       entries = zipFile.entries();
00442       while (entries.hasMoreElements())
00443       {
00444         ZipEntry entry = (ZipEntry) entries.nextElement();
00445 
00446         if (entry.isDirectory())
00447         {
00448           // Create the directory
00449           if (logger.isDebugEnabled())
00450             logger.debug(Translate.get("controller.add.jar.extract.dir", entry
00451                 .getName()));
00452 
00453           (new File(driversDirectory, entry.getName())).mkdir();
00454           continue;
00455         }
00456 
00457         // Extract the file
00458         if (logger.isDebugEnabled())
00459           logger.debug(Translate.get("controller.add.jar.extract.file", entry
00460               .getName()));
00461 
00462         in = zipFile.getInputStream(entry);
00463         out = new BufferedOutputStream(new FileOutputStream(driversDirectory
00464             + System.getProperty("file.separator") + entry.getName()));
00465         while ((lenght = in.read(buffer)) >= 0)
00466           out.write(buffer, 0, lenght);
00467 
00468         in.close();
00469         out.close();
00470       }
00471 
00472       zipFile.close();
00473       temp.delete();
00474       logger.info(Translate.get("controller.add.jar.to.directory",
00475           driversDirectory.toString()));
00476     }
00477     catch (IOException e)
00478     {
00479       String msg = Translate.get("controller.driver.extract.failed", e);
00480       logger.error(msg);
00481       throw new ControllerException(msg);
00482     }
00483   }
00484 
00485   /**
00486    * Read a XML configuration file and load only the
00487    * <code>VirtualDatabase</code> specified in the arguments list
00488    * 
00489    * @param filename XML configuration file name to take info on
00490    *          <code>VirtualDatabase</code>
00491    * @param virtualName the only database to load, null if should load all
00492    * @param autoLoad specifies if the backends should be enabled automatically
00493    *          after loading
00494    * @param checkPoint checkPoint to recover from when enabling backends. Leave
00495    *          <code>null</code> if no recovery option is needed.
00496    * @return a diagnostic message (success or error)
00497    * @throws Exception if an error occurs
00498    */
00499   public String loadXmlConfiguration(String filename, String virtualName,
00500       int autoLoad, String checkPoint) throws Exception
00501   {
00502     FileReader fileReader = null;
00503     try
00504     {
00505       filename = filename.trim();
00506       try
00507       {
00508         fileReader = new FileReader(filename);
00509       }
00510       catch (FileNotFoundException fnf)
00511       {
00512         return Translate.get("controller.file.not.found", filename);
00513       }
00514 
00515       // Read the file
00516       BufferedReader in = new BufferedReader(fileReader);
00517       StringBuffer xml = new StringBuffer();
00518       String line;
00519       do
00520       {
00521         line = in.readLine();
00522         if (line != null)
00523           xml.append(line);
00524       }
00525       while (line != null);
00526 
00527       // Send it to the controller
00528       addVirtualDatabases(xml.toString(), virtualName, autoLoad, checkPoint);
00529       return Translate.get("controller.file.send", filename);
00530     }
00531     catch (Exception e)
00532     {
00533       logger.error(Translate.get("controller.loadXml.failed", e), e);
00534       throw new ControllerException(Translate.get("controller.loadXml.failed",
00535           e));
00536     }
00537     finally
00538     {
00539       if (fileReader != null)
00540         fileReader.close();
00541     }
00542   }
00543 
00544   /**
00545    * Read a XML configuration file for a set of virtual databases
00546    * 
00547    * @param filename XML configuration file name
00548    * @return a diagnostic message (success or error)
00549    * @throws Exception if an error occurs
00550    */
00551   public String loadXml(String filename) throws Exception
00552   {
00553     return this.loadXmlConfiguration(filename, null,
00554         ControllerConstants.AUTO_ENABLE_BACKEND,
00555         ControllerConstants.DATABASE_DEFAULT_CHECKPOINT);
00556   }
00557 
00558   /**
00559    * Save current configuration of the controller to a default file
00560    * 
00561    * @return Status message
00562    * @throws VirtualDatabaseException if an error occurs
00563    * @see org.objectweb.cjdbc.controller.core.ControllerConstants#getSaveFile
00564    */
00565   public String saveConfiguration() throws VirtualDatabaseException
00566   {
00567     String msg = Translate.get("controller.save.configuration");
00568     try
00569     {
00570       String configurationFile = ControllerConstants
00571           .getSaveFile(new SimpleDateFormat("EEE, MMM d, yy")
00572               .format(new Date()));
00573       DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
00574           new FileOutputStream(configurationFile)));
00575       StringBuffer xml = new StringBuffer();
00576       xml.append(XmlTools.prettyXml(getXmlVirtualDatabases().toString()));
00577       dos.write(xml.toString().getBytes());
00578       dos.close();
00579     }
00580     catch (Exception e)
00581     {
00582       msg = Translate.get("controller.save.configuration.failed", e);
00583       logger.error(msg);
00584     }
00585     return msg;
00586   }
00587 
00588   //
00589   // Controller shutdown
00590   //
00591 
00592   /**
00593    * Create report about fatal error
00594    * 
00595    * @param fatal the cause of the fatal error
00596    */
00597   public void endOfController(Exception fatal)
00598   {
00599     logger.fatal(Translate.get("fatal.error"));
00600     if (report.isGenerateOnFatal())
00601     {
00602       new ReportManager(this, fatal).generate();
00603       logger.info(Translate.get("fatal.report.generated", report
00604           .getReportLocation()
00605           + File.separator + ControllerConstants.REPORT_FILE));
00606     }
00607     Runtime.getRuntime().exit(1);
00608   }
00609 
00610   /**
00611    * Access the connection thread. Need this for shutting down
00612    * 
00613    * @return <code>connectionThread</code>
00614    */
00615   public ControllerServerThread getConnectionThread()
00616   {
00617     return connectionThread;
00618   }
00619 
00620   /**
00621    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#isShuttingDown()
00622    */
00623   public boolean isShuttingDown()
00624   {
00625     return isShuttingDown;
00626   }
00627 
00628   /**
00629    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#shutdown(int)
00630    */
00631   public void shutdown(int level) throws ControllerException
00632   {
00633     ControllerShutdownThread shutdownThread = null;
00634     synchronized (this)
00635     {
00636       if (isShuttingDown())
00637       {
00638         logger.info(Translate.get("controller.already.shutting.down", this
00639             .getControllerName()));
00640         return;
00641       }
00642 
00643       if (isSecurityEnabled() && !security.getAllowConsoleShutdown())
00644         throw new ControllerException(Translate
00645             .get("controller.shutdown.refused"));
00646 
00647       switch (level)
00648       {
00649         case Constants.SHUTDOWN_WAIT :
00650           shutdownThread = new ControllerWaitShutdownThread(this);
00651           logger.info(Translate.get("controller.shutdown.type.wait", this
00652               .getControllerName()));
00653           break;
00654         case Constants.SHUTDOWN_SAFE :
00655           isShuttingDown = true;
00656           shutdownThread = new ControllerSafeShutdownThread(this);
00657           logger.info(Translate.get("controller.shutdown.type.safe", this
00658               .getControllerName()));
00659           break;
00660         case Constants.SHUTDOWN_FORCE :
00661           isShuttingDown = true;
00662           shutdownThread = new ControllerForceShutdownThread(this);
00663           logger.warn(Translate.get("controller.shutdown.type.force", this
00664               .getControllerName()));
00665           break;
00666         default :
00667           String msg = Translate
00668               .get("controller.shutdown.unknown.level", level);
00669           logger.error(msg);
00670           throw new RuntimeException(msg);
00671       }
00672     }
00673 
00674     Thread thread = new Thread(shutdownThread.getShutdownGroup(),
00675         shutdownThread, "Controller Shutdown Thread");
00676     thread.start();
00677 
00678     try
00679     {
00680       logger.info("Waiting for shutdown");
00681       thread.join();
00682       logger.info("Shutdown over");
00683     }
00684     catch (InterruptedException e)
00685     {
00686       e.printStackTrace();
00687     }
00688   }
00689 
00690   /**
00691    * Launches the C-JDBC controller and bind it with RMI registry. The available
00692    * options are:
00693    * <ul>
00694    * <li><code>-h</code> or <code>--help</code> <code>&lt;port&gt;</code>:
00695    * displays usage informations.</li>
00696    * <li><code>-j</code> or <code>--jmx</code> <code>&lt;port&gt;</code>:
00697    * optinal JMX server HTTP adaptor port number.</li>
00698    * <li><code>-n</code> or <code>--name</code> <code>&lt;name&gt;</code>:
00699    * optional controller name.</li>
00700    * <li><code>-i</code> or <code>--ip</code> <code>&lt;ip&gt;</code>:
00701    * optional IP address to beind the controller to.</li>
00702    * <li><code>-r</code> or <code>--rmi</code> <code>&lt;port&gt;</code>:
00703    * optional RMI registry port number.</li>
00704    * <li><code>-v</code> or <code>--version</code>: displays version
00705    * informations.</li>
00706    * </ul>
00707    * <p>
00708    * The controller starts listening for socket connections on the default port.
00709    * Jmx is configured, and a virtual database can be added.
00710    * <p>
00711    * {@link org.objectweb.cjdbc.controller.core.ControllerConstants#DEFAULT_PORT}
00712    * Default Listening port
00713    * 
00714    * @param args command line arguments (see above)
00715    * @throws Exception when everything goes wrong
00716    */
00717   public static void main(String args[]) throws Exception
00718   {
00719     logger.info(getVersion());
00720 
00721     System.setProperty("javax.management.builder.initial",
00722         org.objectweb.cjdbc.controller.jmx.MBeanServerBuilder.class.getName());
00723 
00724     ControllerFactory conf = new ControllerFactory(args);
00725     Controller controller = conf.getController();
00726     if (controller != null)
00727       controller.launch();
00728     else
00729       throw new Exception(Translate.get("controller.configure.failed"));
00730   }
00731 
00732   /**
00733    * Actively launch the <code>controller</code>. Add startup actions here to
00734    * avoid them in <method>main </method>
00735    */
00736   public void launch()
00737   {
00738     connectionThread = new ControllerServerThread(this);
00739     connectionThread.start();
00740 
00741     SimpleDateFormat formatter = new SimpleDateFormat(
00742         "yyyy.MM.dd ww 'at' hh:mm:ss a zzz");
00743     Date day = new Date();
00744     String date = formatter.format(day);
00745     logger.info(Translate.get("controller.date", date));
00746     logger.info(Translate.get("controller.ready", getControllerName()));
00747   }
00748 
00749   //
00750   // Controller information
00751   //
00752 
00753   /**
00754    * Returns the controller name.
00755    * 
00756    * @return String
00757    */
00758   public String getControllerName()
00759   {
00760     return ipAddress + ":" + portNumber;
00761   }
00762 
00763   /**
00764    * Get the IP address to bind the controller to
00765    * 
00766    * @return the IP address
00767    */
00768   public String getIPAddress()
00769   {
00770     return ipAddress;
00771   }
00772 
00773   /**
00774    * Set the IP address to bind the controller to
00775    * 
00776    * @param ipAddress the IP address to use
00777    */
00778   public void setIPAddress(String ipAddress)
00779   {
00780     this.ipAddress = ipAddress;
00781   }
00782 
00783   /**
00784    * Get the controller port number
00785    * 
00786    * @return the port number
00787    */
00788   public int getPortNumber()
00789   {
00790     return portNumber;
00791   }
00792 
00793   /**
00794    * Set the controller backlog size.
00795    * 
00796    * @param port the port number to set
00797    */
00798   public void setPortNumber(int port)
00799   {
00800     portNumber = port;
00801   }
00802 
00803   /**
00804    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#getBacklogSize()
00805    */
00806   public int getBacklogSize()
00807   {
00808     return backlogSize;
00809   }
00810 
00811   /**
00812    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#setBacklogSize(int)
00813    */
00814   public void setBacklogSize(int size)
00815   {
00816     backlogSize = size;
00817   }
00818 
00819   /**
00820    * Returns jmx enable
00821    * 
00822    * @return jmxEnabled
00823    */
00824   public boolean getJmxEnable()
00825   {
00826     return MBeanServerManager.isJmxEnabled();
00827   }
00828 
00829   /**
00830    * Return the jmx name of this controller (hostname:rmiport)
00831    * 
00832    * @return jmx name
00833    */
00834   public String getJmxName()
00835   {
00836     if (getJmxEnable())
00837     {
00838       RmiConnector connector = ((RmiConnector) RmiConnector.getRmiConnectors()
00839           .get(0));
00840       return connector.getHostName() + ":" + connector.getPort();
00841     }
00842     else
00843       return getControllerName();
00844   }
00845 
00846   /**
00847    * set enable JMX
00848    * 
00849    * @param enable true if jmx should be enable.
00850    */
00851   public void setJmxEnable(boolean enable)
00852   {
00853     configuration.put(ControllerFactory.JMX_ENABLE, "" + enable);
00854   }
00855 
00856   /**
00857    * Returns Version as a long String
00858    * 
00859    * @return version
00860    */
00861   public static String getVersion()
00862   {
00863     return Translate.get("controller.info", Constants.VERSION);
00864   }
00865 
00866   /**
00867    * Get current configuration options
00868    * 
00869    * @return configure a <code>Hashtable</code> with controller options
00870    */
00871   public Hashtable getConfiguration()
00872   {
00873     return configuration;
00874   }
00875 
00876   /**
00877    * Check whether security is enabled or not
00878    * 
00879    * @return true if there is not null controller security manager
00880    */
00881   public boolean isSecurityEnabled()
00882   {
00883     return security != null;
00884   }
00885 
00886   /**
00887    * @return Returns the security.
00888    */
00889   public ControllerSecurityManager getSecurity()
00890   {
00891     return security;
00892   }
00893 
00894   /**
00895    * @param security The security to set.
00896    */
00897   public void setSecurity(ControllerSecurityManager security)
00898   {
00899     this.security = security;
00900   }
00901 
00902   /**
00903    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#generateReport()
00904    */
00905   public String generateReport() throws Exception
00906   {
00907     report.startReport();
00908     return report.generate();
00909   }
00910 
00911   /**
00912    * Sets the configuration value.
00913    * 
00914    * @param configuration The configuration to set.
00915    */
00916   public void setConfiguration(Hashtable configuration)
00917   {
00918     this.configuration = configuration;
00919   }
00920 
00921   /**
00922    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#getVersionNumber()
00923    */
00924   public String getVersionNumber()
00925   {
00926     return Constants.VERSION;
00927   }
00928 
00929   /**
00930    * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
00931    */
00932   public String getAssociatedString()
00933   {
00934     return "controller";
00935   }
00936 
00937   /**
00938    * @see org.objectweb.cjdbc.common.xml.XmlComponent#getXml()
00939    */
00940   public String getXml()
00941   {
00942     try
00943     {
00944       return XmlTools.prettyXml(getXmlController());
00945     }
00946     catch (Exception e)
00947     {
00948       logger.error(Translate.get("controller.xml.transformation.failed", e));
00949       return e.getMessage();
00950     }
00951   }
00952 
00953   /**
00954    * Return the xml version of the controller.xml file without doc type
00955    * declaration, just data.
00956    * 
00957    * @return controller xml data
00958    */
00959   public String getXmlController()
00960   {
00961     StringBuffer info = new StringBuffer();
00962     info.append("<C-JDBC-CONTROLLER>");
00963     info.append("<" + ControllerXmlTags.ELT_CONTROLLER + " "
00964         + ControllerXmlTags.ATT_CONTROLLER_IP + "=\"" + this.getIPAddress()
00965         + "\" " + ControllerXmlTags.ATT_CONTROLLER_PORT + "=\""
00966         + this.getPortNumber() + "\" " + ">");
00967 
00968     info.append("<" + ControllerXmlTags.ELT_INTERNATIONALIZATION + " "
00969         + ControllerXmlTags.ATT_LANGUAGE + "=\""
00970         + Locale.getDefault().getLanguage() + "\"/>");
00971 
00972     if (report.isReportEnabled())
00973     {
00974       info.append("<" + ControllerXmlTags.ELT_REPORT + " "
00975           + ControllerXmlTags.ATT_REPORT_ENABLE_FILE_LOGGING + "=\""
00976           + report.isEnableFileLogging() + "\" "
00977           + ControllerXmlTags.ATT_REPORT_HIDE_SENSITIVE_DATA + "=\""
00978           + report.isHideSensitiveData() + "\" "
00979           + ControllerXmlTags.ATT_REPORT_GENERATE_ON_FATAL + "=\""
00980           + report.isGenerateOnFatal() + "\" "
00981           + ControllerXmlTags.ATT_REPORT_GENERATE_ON_SHUTDOWN + "=\""
00982           + report.isGenerateOnShutdown() + "\" "
00983           + ControllerXmlTags.ATT_REPORT_REPORT_LOCATION + "=\""
00984           + report.getReportLocation() + "\" />");
00985     }
00986 
00987     if (getJmxEnable())
00988     {
00989       info.append("<" + ControllerXmlTags.ELT_JMX + ">");
00990       if (configuration.containsKey(JmxConstants.ADAPTOR_TYPE_HTTP))
00991       {
00992         info.append("<" + ControllerXmlTags.ELT_HTTP_JMX_ADAPTOR + " "
00993             + ControllerXmlTags.ATT_JMX_ADAPTOR_PORT + "=\""
00994             + configuration.get(JmxConstants.ADAPTOR_TYPE_HTTP) + "\" />");
00995       }
00996       if (configuration.containsKey(JmxConstants.ADAPTOR_TYPE_RMI))
00997       {
00998         info.append("<" + ControllerXmlTags.ELT_RMI_JMX_ADAPTOR + " "
00999             + ControllerXmlTags.ATT_JMX_ADAPTOR_PORT + "=\""
01000             + configuration.get(JmxConstants.ADAPTOR_TYPE_RMI) + "\" />");
01001       }
01002 
01003       info.append("</" + ControllerXmlTags.ELT_JMX + ">");
01004     }
01005 
01006     if (this.isSecurityEnabled())
01007       info.append(this.getSecurity().getXml());
01008     info.append("</" + ControllerXmlTags.ELT_CONTROLLER + ">");
01009     info.append("</C-JDBC-CONTROLLER>");
01010     return info.toString();
01011   }
01012 
01013   /**
01014    * Same as above but for the virtual databases.
01015    * 
01016    * @return xml virtual databases data.
01017    */
01018   public String getXmlVirtualDatabases()
01019   {
01020     try
01021     {
01022       StringBuffer info = new StringBuffer();
01023       info.append("<C-JDBC>");
01024       ArrayList virtualDatabases = this.getVirtualDatabases();
01025       for (int i = 0, size = virtualDatabases.size(); i < size; i++)
01026         info.append(((VirtualDatabase) virtualDatabases.get(i)).getXml());
01027       info.append("</C-JDBC>");
01028       return info.toString();
01029     }
01030     catch (Exception e)
01031     {
01032       logger.error(e.getMessage(), e);
01033       return e.getMessage();
01034     }
01035   }
01036 
01037   /**
01038    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#generateLogReport()
01039    */
01040   public String generateLogReport() throws Exception
01041   {
01042     ReportManager report = new ReportManager(this, true);
01043     return report.generateJustLogs();
01044   }
01045 
01046   // 
01047   // Logging system
01048   //
01049 
01050   /**
01051    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#refreshLogConfiguration()
01052    */
01053   public void refreshLogConfiguration() throws ControllerException
01054   {
01055     try
01056     {
01057       LogManager.configure(URLDecoder.decode(this.getClass().getResource(
01058           ControllerConstants.LOG4J_RESOURCE).getFile()));
01059       if (logger.isDebugEnabled())
01060         logger.info(Translate.get("controller.refresh.log.success"));
01061     }
01062     catch (Exception e)
01063     {
01064       throw new ControllerException(Translate
01065           .get("controller.logconfigfile.not.found"));
01066     }
01067   }
01068 
01069   /**
01070    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#updateLogConfigurationFile(java.lang.String)
01071    */
01072   public void updateLogConfigurationFile(String newConfiguration)
01073       throws IOException, ControllerException
01074   {
01075     File logFile = new File(URLDecoder.decode(getClass().getResource(
01076         ControllerConstants.LOG4J_RESOURCE).getFile()));
01077     BufferedWriter writer = new BufferedWriter(new FileWriter(logFile));
01078     writer.write(newConfiguration);
01079     writer.flush();
01080     writer.close();
01081     refreshLogConfiguration();
01082   }
01083 
01084   /**
01085    * @see org.objectweb.cjdbc.common.jmx.mbeans.ControllerMBean#viewLogConfigurationFile()
01086    */
01087   public String viewLogConfigurationFile() throws IOException
01088   {
01089     File logFile = new File(URLDecoder.decode(getClass().getResource(
01090         ControllerConstants.LOG4J_RESOURCE).getFile()));
01091     BufferedReader reader = new BufferedReader(new FileReader(logFile));
01092     StringBuffer buffer = new StringBuffer();
01093     String line;
01094     while ((line = reader.readLine()) != null)
01095       buffer.append(line + System.getProperty("line.separator"));
01096     reader.close();
01097     return buffer.toString();
01098   }
01099 
01100   /**
01101    * Returns the report value.
01102    * 
01103    * @return Returns the report.
01104    */
01105   public ReportManager getReport()
01106   {
01107     return report;
01108   }
01109 
01110   /**
01111    * Sets the report value.
01112    * 
01113    * @param report The report to set.
01114    */
01115   public void setReport(ReportManager report)
01116   {
01117     this.report = report;
01118   }
01119 }

Generated on Mon Apr 11 22:01:30 2005 for C-JDBC by  doxygen 1.3.9.1