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

RequestManager.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 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 License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public 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): Julie Marguerite, Greg Ward, Nicolas Modrzyk, Vadim Kassin.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.requestmanager;
00026 
00027 import java.sql.SQLException;
00028 import java.util.ArrayList;
00029 import java.util.Hashtable;
00030 
00031 import javax.management.NotCompliantMBeanException;
00032 
00033 import org.objectweb.cjdbc.common.exceptions.BackupException;
00034 import org.objectweb.cjdbc.common.exceptions.ExceptionTypes;
00035 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
00036 import org.objectweb.cjdbc.common.exceptions.OctopusException;
00037 import org.objectweb.cjdbc.common.exceptions.RollbackException;
00038 import org.objectweb.cjdbc.common.i18n.Translate;
00039 import org.objectweb.cjdbc.common.jmx.JmxConstants;
00040 import org.objectweb.cjdbc.common.jmx.mbeans.RequestManagerMBean;
00041 import org.objectweb.cjdbc.common.log.Trace;
00042 import org.objectweb.cjdbc.common.shared.BackendState;
00043 import org.objectweb.cjdbc.common.shared.BackupListener;
00044 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
00045 import org.objectweb.cjdbc.common.sql.AlterRequest;
00046 import org.objectweb.cjdbc.common.sql.CreateRequest;
00047 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
00048 import org.objectweb.cjdbc.common.sql.RequestType;
00049 import org.objectweb.cjdbc.common.sql.SelectRequest;
00050 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00051 import org.objectweb.cjdbc.common.sql.UpdateRequest;
00052 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema;
00053 import org.objectweb.cjdbc.common.sql.schema.DatabaseTable;
00054 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00055 import org.objectweb.cjdbc.common.xml.XmlComponent;
00056 import org.objectweb.cjdbc.controller.backend.BackendStateListener;
00057 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00058 import org.objectweb.cjdbc.controller.backup.BackupManager;
00059 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
00060 import org.objectweb.cjdbc.controller.cache.parsing.ParsingCache;
00061 import org.objectweb.cjdbc.controller.cache.result.AbstractResultCache;
00062 import org.objectweb.cjdbc.controller.cache.result.entries.CacheEntry;
00063 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean;
00064 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager;
00065 import org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer;
00066 import org.objectweb.cjdbc.controller.loadbalancer.AllBackendsFailedException;
00067 import org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog;
00068 import org.objectweb.cjdbc.controller.recoverylog.BackendRecoveryInfo;
00069 import org.objectweb.cjdbc.controller.recoverylog.JDBCRecoverThread;
00070 import org.objectweb.cjdbc.controller.scheduler.AbstractScheduler;
00071 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
00072 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
00073 
00074 /**
00075  * This class defines the Request Manager.
00076  * <p>
00077  * The RM is composed of a Request Scheduler, an optional Query Cache, and a
00078  * Load Balancer and an optional Recovery Log.
00079  * 
00080  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00081  * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
00082  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
00083  * @author <a href="mailto:vadim@kase.kz">Vadim Kassin </a>
00084  * @version 1.0
00085  */
00086 public class RequestManager extends AbstractStandardMBean
00087     implements
00088       XmlComponent,
00089       RequestManagerMBean
00090 {
00091 
00092   //
00093   // How the code is organized ?
00094   //
00095   // 1. Member variables
00096   // 2. Constructor(s)
00097   // 3. Request handling
00098   // 4. Transaction handling
00099   // 5. Database backend management
00100   // 6. Getter/Setter (possibly in alphabetical order)
00101   //
00102 
00103   /** begin timeout in ms */
00104   protected long                 beginTimeout;
00105 
00106   /** commit timeout in ms */
00107   protected long                 commitTimeout;
00108 
00109   /** rollback timeout in ms */
00110   protected long                 rollbackTimeout;
00111 
00112   /** The virtual database owning this Request Manager */
00113   protected VirtualDatabase      vdb;
00114 
00115   /** The request scheduler to order and schedule requests */
00116   protected AbstractScheduler    scheduler;
00117 
00118   /** An optional request cache to cache responses to SQL requests */
00119   protected AbstractResultCache  resultCache;
00120 
00121   /** The request load balancer to use to send requests to the databases */
00122   protected AbstractLoadBalancer loadBalancer;
00123 
00124   /** An optional recovery log */
00125   protected AbstractRecoveryLog  recoveryLog;
00126 
00127   /** The backup manager responsible for backup and restore of backends */
00128   protected BackupManager        backupManager;
00129 
00130   // Database schema pointing to the virtual dabase schema
00131   protected DatabaseSchema       dbs;
00132 
00133   private boolean                schemaIsStatic                = false;
00134 
00135   private boolean                isCaseSensitiveParsing        = false;
00136 
00137   protected ParsingCache         parsingCache                  = null;
00138 
00139   private MetadataCache          metadataCache                 = null;
00140 
00141   // SQL queries parsing granularity according to Scheduler, ResultCache and
00142   // LoadBalancer required granularity
00143   protected int                  schedulerParsingranularity    = ParsingGranularities.NO_PARSING;
00144 
00145   private int                    cacheParsingranularity        = ParsingGranularities.NO_PARSING;
00146 
00147   private int                    loadBalancerParsingranularity = ParsingGranularities.NO_PARSING;
00148 
00149   protected int                  requiredGranularity           = ParsingGranularities.NO_PARSING;
00150 
00151   /** Transaction id/Login mapping */
00152   protected Hashtable            tidLoginTable;
00153 
00154   protected static Trace         logger                        = null;
00155 
00156   private BackendStateListener   backendStateListener;
00157 
00158   //
00159   // Constructors
00160   //
00161 
00162   /**
00163    * Creates a new <code>RequestManager</code> instance.
00164    * 
00165    * @param vdb the virtual database this request manager belongs to
00166    * @param scheduler the Request Scheduler to use
00167    * @param cache a Query Cache implementation
00168    * @param loadBalancer the Request Load Balancer to use
00169    * @param recoveryLog the Log Recovery to use
00170    * @param beginTimeout timeout in seconds for begin
00171    * @param commitTimeout timeout in seconds for commit
00172    * @param rollbackTimeout timeout in seconds for rollback
00173    * @throws SQLException if an error occurs
00174    * @throws NotCompliantMBeanException if the MBean is not JMX compliant
00175    */
00176   public RequestManager(VirtualDatabase vdb, AbstractScheduler scheduler,
00177       AbstractResultCache cache, AbstractLoadBalancer loadBalancer,
00178       AbstractRecoveryLog recoveryLog, long beginTimeout, long commitTimeout,
00179       long rollbackTimeout) throws SQLException, NotCompliantMBeanException
00180   {
00181     super(RequestManagerMBean.class);
00182     this.vdb = vdb;
00183     assignAndCheckSchedulerLoadBalancerValidity(scheduler, loadBalancer);
00184     // requiredGranularity is the maximum of each component granularity
00185     this.resultCache = cache;
00186     if (resultCache != null)
00187     {
00188       cacheParsingranularity = cache.getParsingGranularity();
00189       if (cacheParsingranularity > requiredGranularity)
00190         requiredGranularity = cacheParsingranularity;
00191     }
00192     setRecoveryLog(recoveryLog);
00193     initRequestManagerVariables(vdb, beginTimeout, commitTimeout,
00194         rollbackTimeout);
00195     setBackendsLastKnownCheckpointFromRecoveryLog();
00196     logger.info(Translate.get("requestmanager.parsing.granularity",
00197         ParsingGranularities.getInformation(requiredGranularity)));
00198 
00199     if (MBeanServerManager.isJmxEnabled())
00200     {
00201       try
00202       {
00203         MBeanServerManager.registerMBean(this, JmxConstants
00204             .getRequestManagerObjectName(vdb.getVirtualDatabaseName()));
00205 
00206       }
00207       catch (Exception e)
00208       {
00209         logger.error(Translate.get("jmx.failed.register.mbean.requestmanager"));
00210       }
00211     }
00212   }
00213 
00214   /**
00215    * Retrieve the last known checkpoint from the recovery log and set it for
00216    * each backend.
00217    */
00218   private void setBackendsLastKnownCheckpointFromRecoveryLog()
00219   {
00220 
00221     if (recoveryLog == null)
00222       return;
00223     String databaseName = vdb.getVirtualDatabaseName();
00224     ArrayList backends = vdb.getBackends();
00225     int size = backends.size();
00226     DatabaseBackend backend;
00227     BackendRecoveryInfo info;
00228     for (int i = 0; i < size; i++)
00229     {
00230       backend = (DatabaseBackend) backends.get(i);
00231       try
00232       {
00233         info = recoveryLog.getBackendRecoveryInfo(databaseName, backend
00234             .getName());
00235         backend.setLastKnownCheckpoint(info.getCheckpoint());
00236       }
00237       catch (SQLException e)
00238       {
00239         logger.error(Translate.get("requestmanager.checkpoint.not.found",
00240             backend.getName()), e);
00241       }
00242     }
00243 
00244   }
00245 
00246   /**
00247    * Check that Scheduler and Load Balancer are not null and have compatible
00248    * RAIDb levels.
00249    * 
00250    * @param scheduler
00251    * @param loadBalancer
00252    * @throws SQLException if an error occurs
00253    */
00254   private void assignAndCheckSchedulerLoadBalancerValidity(
00255       AbstractScheduler scheduler, AbstractLoadBalancer loadBalancer)
00256       throws SQLException
00257   {
00258     if (scheduler == null)
00259       throw new SQLException(Translate.get("requestmanager.null.scheduler"));
00260 
00261     if (loadBalancer == null)
00262       throw new SQLException(Translate.get("requestmanager.null.loadbalancer"));
00263 
00264     if (scheduler.getRAIDbLevel() != loadBalancer.getRAIDbLevel())
00265       throw new SQLException(Translate.get(
00266           "requestmanager.incompatible.raidb.levels",
00267           new String[]{"" + scheduler.getRAIDbLevel(),
00268               "" + loadBalancer.getRAIDbLevel()}));
00269 
00270     // requiredGranularity is the maximum of each component granularity
00271     setScheduler(scheduler);
00272     schedulerParsingranularity = scheduler.getParsingGranularity();
00273     requiredGranularity = schedulerParsingranularity;
00274     setLoadBalancer(loadBalancer);
00275     loadBalancerParsingranularity = loadBalancer.getParsingGranularity();
00276     if (loadBalancerParsingranularity > requiredGranularity)
00277       requiredGranularity = loadBalancerParsingranularity;
00278   }
00279 
00280   /**
00281    * Method initRequestManagerVariables.
00282    * 
00283    * @param vdb
00284    * @param beginTimeout
00285    * @param commitTimeout
00286    * @param rollbackTimeout
00287    */
00288   private void initRequestManagerVariables(VirtualDatabase vdb,
00289       long beginTimeout, long commitTimeout, long rollbackTimeout)
00290   {
00291     this.tidLoginTable = new Hashtable();
00292     this.beginTimeout = beginTimeout;
00293     this.commitTimeout = commitTimeout;
00294     this.rollbackTimeout = rollbackTimeout;
00295     this.vdb = vdb;
00296     logger = Trace.getLogger("org.objectweb.cjdbc.controller.RequestManager."
00297         + vdb.getDatabaseName());
00298   }
00299 
00300   //
00301   // Request Handling
00302   //
00303 
00304   /**
00305    * Perform a read request and return the reply. Call first the scheduler, then
00306    * the cache (if defined) and finally the load balancer.
00307    * 
00308    * @param request the request to execute
00309    * @return a <code>ControllerResultSet</code> value
00310    * @exception SQLException if an error occurs
00311    */
00312   public ControllerResultSet execReadRequest(SelectRequest request)
00313       throws SQLException
00314   {
00315     // Sanity check
00316     if (!request.isAutoCommit())
00317     { // Check that the transaction has been
00318       // started
00319       long tid = request.getTransactionId();
00320       if (!tidLoginTable.containsKey(new Long(tid)))
00321         throw new SQLException(Translate.get("transaction.not.started", tid));
00322     }
00323 
00324     // If we need to parse the request, try to get the parsing from the
00325     // cache.
00326     // Note that if we have a cache miss but backgroundParsing has been
00327     // turned
00328     // on, then this call will start a ParsedThread in background.
00329     if ((requiredGranularity != ParsingGranularities.NO_PARSING)
00330         && (!request.isParsed()))
00331     {
00332       if (parsingCache == null)
00333         request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00334       else
00335         parsingCache.getParsingFromCache(request);
00336     }
00337 
00338     //
00339     // SCHEDULER
00340     //
00341 
00342     // Get the parsing now if the request is not yet parsed. The parsing is
00343     // handled by the ParsingCache that may already have parsed the request
00344     // in background (if backgroundParsing is set).
00345     if ((schedulerParsingranularity != ParsingGranularities.NO_PARSING)
00346         && !request.isParsed())
00347     {
00348       if (parsingCache == null)
00349         request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00350       else
00351         parsingCache.getParsingFromCacheAndParseIfMissing(request);
00352     }
00353 
00354     if (logger.isDebugEnabled())
00355       logger.debug(Translate.get("requestmanager.read.request.schedule",
00356           new String[]{String.valueOf(request.getId()),
00357               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00358 
00359     // Wait for the scheduler to give us the authorization to execute
00360     scheduler.scheduleReadRequest(request);
00361 
00362     //
00363     // CACHE
00364     //
00365 
00366     ControllerResultSet result = null;
00367     try
00368     { // Check cache if any
00369       if (resultCache != null)
00370       {
00371         if (logger.isDebugEnabled())
00372           logger.debug(Translate.get("requestmanager.read.request.cache.get",
00373               new String[]{String.valueOf(request.getId()),
00374                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00375 
00376         CacheEntry qce = resultCache.getFromCache(request, true);
00377         if (qce != null)
00378         {
00379           result = qce.getResult();
00380           if (result != null)
00381           { // Cache hit !
00382             if (vdb.getSQLMonitor() != null)
00383               vdb.getSQLMonitor().logCacheHit(request);
00384 
00385             scheduler.readCompleted(request);
00386             return result;
00387           }
00388         }
00389       }
00390 
00391       //
00392       // LOAD BALANCER
00393       //
00394 
00395       if (logger.isDebugEnabled())
00396         logger.debug(Translate.get("requestmanager.read.request.balance",
00397             new String[]{String.valueOf(request.getId()),
00398                 request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00399 
00400       // At this point, we have a result cache miss.
00401       // If we had a parsing cache miss too, wait for the parsing to be
00402       // done if
00403       // needed.
00404       if ((loadBalancerParsingranularity != ParsingGranularities.NO_PARSING)
00405           && !request.isParsed())
00406       {
00407         if (parsingCache == null)
00408           request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00409         else
00410           parsingCache.getParsingFromCacheAndParseIfMissing(request);
00411       }
00412 
00413       // Send the request to the load balancer
00414       result = loadBalancer.execReadRequest(request, metadataCache);
00415 
00416       //
00417       // UPDATES & NOTIFICATIONS
00418       //
00419 
00420       // Update cache
00421       if ((resultCache != null)
00422           && (request.getCacheAbility() != RequestType.UNCACHEABLE))
00423       {
00424         if (logger.isDebugEnabled())
00425           logger.debug(Translate.get(
00426               "requestmanager.read.request.cache.update", new String[]{
00427                   String.valueOf(request.getId()),
00428                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00429 
00430         if (!request.isParsed()
00431             && (cacheParsingranularity != ParsingGranularities.NO_PARSING))
00432         { // The cache was the only one to need parsing and the request was not
00433           // previously in the cache
00434           {
00435             if (parsingCache == null)
00436               request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00437             else
00438               parsingCache.getParsingFromCacheAndParseIfMissing(request);
00439           }
00440         }
00441         resultCache.addToCache(request, result);
00442       }
00443     }
00444     catch (Exception failed)
00445     {
00446       if (resultCache != null)
00447         resultCache.removeFromPendingQueries(request);
00448       scheduler.readCompleted(request);
00449       if (failed instanceof NoMoreBackendException)
00450         throw (NoMoreBackendException) failed;
00451       String msg = Translate.get("requestmanager.request.failed", new String[]{
00452           request.getSQLShortForm(vdb.getSQLShortFormLength()),
00453           failed.getMessage()});
00454       if (failed instanceof RuntimeException)
00455         logger.warn(msg, failed);
00456       else
00457         logger.warn(msg);
00458       if (failed instanceof SQLException)
00459         throw (SQLException) failed;
00460 
00461       throw new SQLException(msg);
00462     }
00463 
00464     // Notify scheduler of completion
00465     scheduler.readCompleted(request);
00466 
00467     return result;
00468   }
00469 
00470   /**
00471    * Perform a write request and return the number of rows affected Call first
00472    * the scheduler (if defined), then notify the cache (if defined) and finally
00473    * call the load balancer.
00474    * 
00475    * @param request the request to execute
00476    * @return number of rows affected
00477    * @exception SQLException if an error occurs
00478    */
00479   public int execWriteRequest(AbstractWriteRequest request) throws SQLException
00480   {
00481     scheduleExecWriteRequest(request);
00482     int execWriteRequestResult = 0;
00483     try
00484     {
00485       execWriteRequestResult = loadBalanceExecWriteRequest(request);
00486     }
00487     catch (AllBackendsFailedException e)
00488     {
00489       String msg = Translate
00490           .get("requestmanager.write.request.failed.unexpected");
00491       logger.fatal(msg, e);
00492       throw new RuntimeException(msg, e);
00493     }
00494     updateAndNotifyExecWriteRequest(request, true);
00495     return execWriteRequestResult;
00496   }
00497 
00498   /**
00499    * Perform a write request and return the auto generated keys. Call first the
00500    * scheduler (if defined), then notify the cache (if defined) and finally call
00501    * the load balancer.
00502    * 
00503    * @param request the request to execute
00504    * @return auto generated keys.
00505    * @exception SQLException if an error occurs
00506    */
00507   public ControllerResultSet execWriteRequestWithKeys(
00508       AbstractWriteRequest request) throws SQLException
00509   {
00510     scheduleExecWriteRequest(request);
00511     ControllerResultSet execWriteRequestWithKeysResult = null;
00512     try
00513     {
00514       execWriteRequestWithKeysResult = loadBalanceExecWriteRequestWithKeys(request);
00515     }
00516     catch (AllBackendsFailedException e)
00517     {
00518       String msg = Translate
00519           .get("requestmanager.write.request.keys.failed.unexpected");
00520       logger.fatal(msg, e);
00521       throw new RuntimeException(msg, e);
00522     }
00523     updateAndNotifyExecWriteRequest(request, true);
00524     return execWriteRequestWithKeysResult;
00525   }
00526 
00527   /**
00528    * Schedule a request for execution.
00529    * 
00530    * @param request the request to execute
00531    * @throws SQLException if an error occurs
00532    */
00533   public void scheduleExecWriteRequest(AbstractWriteRequest request)
00534       throws SQLException
00535   {
00536     // Sanity check
00537     if (!request.isAutoCommit())
00538     { // Check that the transaction has been
00539       // started
00540       long tid = request.getTransactionId();
00541       if (!tidLoginTable.containsKey(new Long(tid)))
00542         throw new SQLException(Translate.get("transaction.not.started", tid));
00543     }
00544 
00545     // If we need to parse the request, try to get the parsing from the
00546     // cache.
00547     // Note that if we have a cache miss but backgroundParsing has been
00548     // turned
00549     // on, then this call will start a ParsedThread in background.
00550     if ((requiredGranularity != ParsingGranularities.NO_PARSING)
00551         && (!request.isParsed()))
00552     {
00553       if (parsingCache == null)
00554         request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00555       else
00556         parsingCache.getParsingFromCache(request);
00557     }
00558 
00559     //
00560     // SCHEDULER
00561     //
00562 
00563     // Get the parsing now if the request is not yet parsed. The parsing is
00564     // handled by the ParsingCache that may already have parsed the request
00565     // in background (if backgroundParsing is set).
00566     if ((schedulerParsingranularity != ParsingGranularities.NO_PARSING)
00567         && !request.isParsed())
00568     {
00569       if (parsingCache == null)
00570         request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00571       else
00572         parsingCache.getParsingFromCacheAndParseIfMissing(request);
00573     }
00574 
00575     if (logger.isDebugEnabled())
00576       logger.debug(Translate.get("requestmanager.write.request.schedule",
00577           new String[]{String.valueOf(request.getId()),
00578               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00579 
00580     // Wait for the scheduler to give us the authorization to execute
00581     try
00582     {
00583       scheduler.scheduleWriteRequest(request);
00584     }
00585     catch (RollbackException e)
00586     { // Something bad happened and we need to
00587       // rollback this transaction
00588       rollback(request.getTransactionId());
00589       throw new SQLException(e.getMessage());
00590     }
00591 
00592     // If we have a parsing cache miss, wait for the parsing to be done if
00593     // needed. Note that even if the cache was the only one to require
00594     // parsing,
00595     // we wait for the parsing result here, because if it fails, we must not
00596     // execute the query.
00597     try
00598     {
00599       if ((requiredGranularity != ParsingGranularities.NO_PARSING)
00600           && !request.isParsed())
00601       {
00602         if (parsingCache == null)
00603           request.parse(dbs, requiredGranularity, isCaseSensitiveParsing);
00604         else
00605           parsingCache.getParsingFromCacheAndParseIfMissing(request);
00606       }
00607     }
00608     catch (SQLException e)
00609     {
00610       // If the parsing fail, we must release the lock acquired ...
00611       scheduler.writeCompleted(request);
00612       throw e;
00613     }
00614   }
00615 
00616   /**
00617    * Send the given query to the load balancer. If the request fails, the
00618    * scheduler is properly notified.
00619    * 
00620    * @param request the request to execute
00621    * @throws AllBackendsFailedException if all backends failed to execute the
00622    *           query
00623    * @throws SQLException if an error occurs
00624    * @return auto-generated keys
00625    */
00626   public ControllerResultSet loadBalanceExecWriteRequestWithKeys(
00627       AbstractWriteRequest request) throws AllBackendsFailedException,
00628       SQLException
00629   {
00630     if (logger.isDebugEnabled())
00631       logger.debug(Translate.get("requestmanager.write.request.balance",
00632           new String[]{String.valueOf(request.getId()),
00633               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00634 
00635     try
00636     { // Send the request to the load balancer
00637       return loadBalancer.execWriteRequestWithKeys(request, metadataCache);
00638     }
00639     catch (Exception failed)
00640     {
00641       scheduler.writeCompleted(request);
00642       String msg = Translate.get("requestmanager.request.failed", new String[]{
00643           request.getSQLShortForm(vdb.getSQLShortFormLength()),
00644           failed.getMessage()});
00645       if (failed instanceof RuntimeException)
00646         logger.warn(msg, failed);
00647       else
00648         logger.warn(msg);
00649       if (failed instanceof AllBackendsFailedException)
00650         throw (AllBackendsFailedException) failed;
00651       else
00652         throw new SQLException(msg);
00653     }
00654   }
00655 
00656   /**
00657    * Send the given query to the load balancer. If the request fails, the
00658    * scheduler is properly notified.
00659    * 
00660    * @param request the request to execute
00661    * @throws AllBackendsFailedException if all backends failed to execute the
00662    *           query
00663    * @throws SQLException if an error occurs
00664    * @return number of modified lines
00665    */
00666   public int loadBalanceExecWriteRequest(AbstractWriteRequest request)
00667       throws AllBackendsFailedException, SQLException
00668   {
00669     if (logger.isDebugEnabled())
00670       logger.debug(Translate.get("requestmanager.write.request.balance",
00671           new String[]{String.valueOf(request.getId()),
00672               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00673 
00674     try
00675     { // Send the request to the load balancer
00676       if (request.isUpdate() && (resultCache != null))
00677       { // Try the optimization if we try to update values that are already
00678         // up-to-date.
00679         if (!resultCache.isUpdateNecessary((UpdateRequest) request))
00680           return 0;
00681       }
00682       return loadBalancer.execWriteRequest(request);
00683     }
00684     catch (Exception failed)
00685     {
00686       scheduler.writeCompleted(request);
00687       String msg = Translate.get("requestmanager.request.failed", new String[]{
00688           request.getSQLShortForm(vdb.getSQLShortFormLength()),
00689           failed.getMessage()});
00690       if (failed instanceof RuntimeException)
00691         logger.warn(msg, failed);
00692       else
00693         logger.warn(msg);
00694       if (failed instanceof AllBackendsFailedException)
00695         throw (AllBackendsFailedException) failed;
00696       else if (failed instanceof SQLException)
00697         throw (SQLException) failed;
00698       else
00699         throw new SQLException(msg);
00700     }
00701   }
00702 
00703   /**
00704    * Update the cache, notify the recovery log and finally the scheduler. It is
00705    * possible to disable scheduler notifications (unless an error occurs in
00706    * which case the scheduler is always notified). This is especially useful for
00707    * distributed schedulers when all backends failed at one controller but we
00708    * have to wait for the confirmation that all other controllers failed. This
00709    * piece of code is then generic and reusable.
00710    * 
00711    * @param request the request to execute
00712    * @param notifyRecoveryLogAndScheduler true if the recovery log and the
00713    *          scheduler must be notified
00714    * @throws SQLException if an error occurs
00715    */
00716   public void updateAndNotifyExecWriteRequest(AbstractWriteRequest request,
00717       boolean notifyRecoveryLogAndScheduler) throws SQLException
00718   {
00719 
00720     try
00721     { // Notify cache if any
00722       if (resultCache != null)
00723       { // Update cache
00724         if (logger.isDebugEnabled())
00725           logger.debug(Translate.get(
00726               "requestmanager.write.request.cache.update", new String[]{
00727                   String.valueOf(request.getId()),
00728                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00729 
00730         resultCache.writeNotify(request);
00731       }
00732 
00733       // Log the request
00734       if (notifyRecoveryLogAndScheduler && (recoveryLog != null))
00735       {
00736         if (logger.isDebugEnabled())
00737           logger.debug(Translate.get("requestmanager.write.request.log",
00738               new String[]{String.valueOf(request.getId()),
00739                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
00740 
00741         recoveryLog.logRequest(request);
00742       }
00743 
00744       // Update the schema if needed
00745       if (requiredGranularity != ParsingGranularities.NO_PARSING)
00746       {
00747         if (request.isCreate())
00748         { // Add the table to the schema
00749           dbs.addTable(((CreateRequest) request).getDatabaseTable());
00750           if (logger.isDebugEnabled())
00751             logger.debug(Translate.get("requestmanager.schema.add.table",
00752                 request.getTableName()));
00753         }
00754         else if (request.isDrop())
00755         { // Delete the table from the
00756           // schema
00757           dbs.removeTable(dbs.getTable(request.getTableName()));
00758           if (logger.isDebugEnabled())
00759             logger.debug(Translate.get("requestmanager.schema.remove.table",
00760                 request.getTableName()));
00761         }
00762         else if (request.isAlter()
00763             && (requiredGranularity > ParsingGranularities.TABLE))
00764         {
00765           // Add or drop the column from the table
00766           AlterRequest req = (AlterRequest) request;
00767           if (req.isDrop())
00768             dbs.getTable(req.getTableName()).remove(req.getColumn().getName());
00769           else if (req.isAdd())
00770           {
00771             dbs.getTable(req.getTableName()).addColumn(req.getColumn());
00772           }
00773         }
00774       }
00775 
00776       // Notify scheduler
00777       if (notifyRecoveryLogAndScheduler)
00778         scheduler.writeCompleted(request);
00779 
00780     }
00781     catch (Exception failed)
00782     {
00783       scheduler.writeCompleted(request);
00784       String msg = Translate.get("requestmanager.request.failed", new String[]{
00785           request.getSQLShortForm(vdb.getSQLShortFormLength()),
00786           failed.getMessage()});
00787       if (failed instanceof RuntimeException)
00788         logger.warn(msg, failed);
00789       else
00790         logger.warn(msg);
00791       throw new SQLException(msg);
00792     }
00793   }
00794 
00795   /**
00796    * Call a stored procedure that returns a ResultSet.
00797    * 
00798    * @param proc the stored procedure call
00799    * @return a <code>ControllerResultSet</code> value
00800    * @exception SQLException if an error occurs
00801    */
00802   public ControllerResultSet execReadStoredProcedure(StoredProcedure proc)
00803       throws SQLException
00804   {
00805     // Sanity check
00806     if (!proc.isAutoCommit())
00807     { // Check that the transaction has been
00808       // started
00809       long tid = proc.getTransactionId();
00810       if (!tidLoginTable.containsKey(new Long(tid)))
00811         throw new SQLException(Translate.get("transaction.not.started", tid));
00812     }
00813 
00814     //
00815     // SCHEDULER
00816     //
00817 
00818     // Note that no parsing is required for stored procedures.
00819     // We build a fake request that span over all tables to be sure
00820     // that the scheduler will lock everything.
00821     SelectRequest spanOnAllTables = null;
00822     if (requiredGranularity != ParsingGranularities.NO_PARSING)
00823     {
00824       String sql = "SELECT * FROM";
00825       ArrayList tables = dbs.getTables();
00826       int size = tables.size();
00827       // The following code does not generate a syntaxically correct SQL
00828       // request (an extra coma at the beginning) but that is not an
00829       // issue.
00830       for (int i = 0; i < size; i++)
00831         sql += " ," + ((DatabaseTable) tables.get(i)).getName();
00832 
00833       spanOnAllTables = new SelectRequest(sql, false, 0, proc
00834           .getLineSeparator());
00835     }
00836     else
00837       spanOnAllTables = new SelectRequest("select * from x", false, 0, proc
00838           .getLineSeparator());
00839 
00840     // Wait for the scheduler to give us the authorization to execute
00841     scheduler.scheduleReadRequest(spanOnAllTables);
00842 
00843     if (logger.isDebugEnabled())
00844       logger.debug(Translate.get("requestmanager.read.store.procedure",
00845           new String[]{String.valueOf(proc.getId()),
00846               proc.getSQLShortForm(vdb.getSQLShortFormLength())}));
00847 
00848     //
00849     // CACHE
00850     //
00851 
00852     // Cache is always flushed unless the user has explicitely set the
00853     // connection to read-only mode in which case we assume that the
00854     // users deliberately forces the cache not to be flushed when calling
00855     // this stored procedure.
00856     if ((resultCache != null) && (!proc.isReadOnly()))
00857       resultCache.flushCache();
00858 
00859     ControllerResultSet result = null;
00860     try
00861     {
00862       //
00863       // LOAD BALANCER
00864       //
00865 
00866       // Send the request to the load balancer
00867       if (proc.isReadOnly())
00868         result = loadBalancer.execReadOnlyReadStoredProcedure(proc,
00869             metadataCache);
00870       else
00871         result = loadBalancer.execReadStoredProcedure(proc, metadataCache);
00872 
00873       //
00874       // RECOVERY LOG
00875       //
00876 
00877       if (recoveryLog != null)
00878         recoveryLog.logRequest(proc, true);
00879 
00880     }
00881     catch (Exception failed)
00882     {
00883       scheduler.readCompleted(spanOnAllTables);
00884       String msg = Translate.get("requestmanager.store.procedure.failed",
00885           new String[]{proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00886               failed.getMessage()});
00887       logger.warn(msg);
00888       if (failed instanceof SQLException)
00889       {
00890         throw (SQLException) failed;
00891       }
00892       throw new SQLException(msg);
00893     }
00894 
00895     // Notify scheduler of completion
00896     scheduler.readCompleted(spanOnAllTables);
00897 
00898     return result;
00899   }
00900 
00901   /**
00902    * Call a stored procedure that performs an update.
00903    * 
00904    * @param proc the stored procedure call
00905    * @return number of rows affected
00906    * @exception SQLException if an error occurs
00907    */
00908   public int execWriteStoredProcedure(StoredProcedure proc) throws SQLException
00909   {
00910     // Sanity check
00911     if (!proc.isAutoCommit())
00912     { // Check that the transaction has been
00913       // started
00914       long tid = proc.getTransactionId();
00915       if (!tidLoginTable.containsKey(new Long(tid)))
00916         throw new SQLException(Translate.get("transaction.not.started", String
00917             .valueOf(tid)));
00918     }
00919 
00920     //
00921     // SCHEDULER
00922     //
00923 
00924     // Note that no parsing is required for stored procedures.
00925     // We build a fake request that span over all tables to be sure
00926     // that the scheduler will lock everything.
00927     SelectRequest spanOnAllTables = null;
00928     if (requiredGranularity != ParsingGranularities.NO_PARSING)
00929     {
00930       String sql = "SELECT * FROM";
00931       ArrayList tables = dbs.getTables();
00932       int size = tables.size();
00933       // The following code does not generate a syntaxically correct SQL
00934       // request (an extra coma at the beginning) but that is not an
00935       // issue.
00936       for (int i = 0; i < size; i++)
00937         sql += " ," + ((DatabaseTable) tables.get(i)).getName();
00938 
00939       spanOnAllTables = new SelectRequest(sql, false, 0, proc
00940           .getLineSeparator());
00941     }
00942     else
00943       spanOnAllTables = new SelectRequest("select * from x", false, 0, proc
00944           .getLineSeparator());
00945 
00946     // Wait for the scheduler to give us the authorization to execute
00947     // We schedule a read even if it is a write because there is no
00948     // way to lock all tables for write in a single query. FIXME!
00949     scheduler.scheduleReadRequest(spanOnAllTables);
00950 
00951     if (logger.isDebugEnabled())
00952       logger.debug(Translate.get("requestmanager.write.store.procedure",
00953           new String[]{String.valueOf(proc.getId()),
00954               proc.getSQLShortForm(vdb.getSQLShortFormLength())}));
00955 
00956     //
00957     // CACHE
00958     //
00959 
00960     // Flush cache if any
00961     if (resultCache != null)
00962       resultCache.flushCache();
00963 
00964     int result;
00965     try
00966     {
00967       //
00968       // LOAD BALANCER
00969       //
00970 
00971       // Send the request to the load balancer
00972       result = loadBalancer.execWriteStoredProcedure(proc);
00973 
00974       //
00975       // RECOVERY LOG
00976       //
00977 
00978       if (recoveryLog != null)
00979         recoveryLog.logRequest(proc, false);
00980 
00981     }
00982     catch (Exception failed)
00983     {
00984       scheduler.readCompleted(spanOnAllTables);
00985       String msg = Translate.get("requestmanager.store.procedure.failed",
00986           new String[]{proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00987               failed.getMessage()});
00988       logger.warn(msg);
00989       throw new SQLException(msg);
00990     }
00991 
00992     // Notify scheduler of completion
00993     scheduler.readCompleted(spanOnAllTables);
00994 
00995     return result;
00996   }
00997 
00998   //
00999   // Transaction management
01000   //
01001 
01002   /**
01003    * Begin a new transaction and return the corresponding transaction
01004    * identifier. This method is called from the driver when setAutoCommit(false)
01005    * is called.
01006    * 
01007    * @param login the login used by the connection
01008    * @return int a unique transaction identifier
01009    * @throws SQLException if an error occurs
01010    */
01011   public long begin(String login) throws SQLException
01012   {
01013     try
01014     {
01015       TransactionMarkerMetaData tm = new TransactionMarkerMetaData(0,
01016           beginTimeout, login);
01017 
01018       // Wait for the scheduler to give us the authorization to execute
01019       long tid = scheduler.begin(tm);
01020       tm.setTransactionId(tid);
01021 
01022       if (logger.isDebugEnabled())
01023         logger.debug(Translate.get("transaction.begin", String.valueOf(tid)));
01024 
01025       try
01026       {
01027         // Send to load balancer
01028         loadBalancer.begin(tm);
01029 
01030         // Log the begin
01031         if (recoveryLog != null)
01032         {
01033           recoveryLog.begin(tm);
01034         }
01035       }
01036       catch (SQLException e)
01037       {
01038         throw e;
01039       }
01040       finally
01041       {
01042         // Notify scheduler for completion in any case
01043         scheduler.beginCompleted(tid);
01044       }
01045 
01046       tidLoginTable.put(new Long(tid), tm);
01047       return tid;
01048     }
01049     catch (RuntimeException e)
01050     {
01051       logger.fatal(Translate.get(
01052           "fatal.runtime.exception.requestmanager.begin", e));
01053       throw new SQLException(e.getMessage());
01054     }
01055   }
01056 
01057   /**
01058    * Abort a transaction that has been started but in which no query was
01059    * executed. As we use lazy transaction begin, there is no need to rollback
01060    * such transaction but just to cleanup the metadata associated with this not
01061    * effectively started transaction.
01062    * 
01063    * @param transactionId id of the transaction to abort
01064    * @throws SQLException if an error occurs
01065    */
01066   public void abort(long transactionId) throws SQLException
01067   {
01068     try
01069     {
01070       Long tid = new Long(transactionId);
01071       TransactionMarkerMetaData tm = getTransactionMarker(tid);
01072 
01073       if (logger.isDebugEnabled())
01074         logger.debug(Translate.get("transaction.abort", String
01075             .valueOf(transactionId)));
01076 
01077       try
01078       {
01079         // Notify the scheduler to abort which is the same as a rollback
01080         // from a
01081         // scheduler point of view.
01082         scheduler.rollback(tm);
01083 
01084         // Notify the recovery log manager
01085         if (recoveryLog != null)
01086         {
01087           recoveryLog.abort(tm);
01088         }
01089       }
01090       catch (SQLException e)
01091       {
01092         throw e;
01093       }
01094       finally
01095       {
01096         // Notify scheduler for completion
01097         scheduler.rollbackCompleted(transactionId);
01098 
01099         completeTransaction(tid);
01100       }
01101     }
01102     catch (RuntimeException e)
01103     {
01104       logger.fatal(Translate
01105           .get("fatal.runtime.exception.requestmanager.rollback"), e);
01106       throw new SQLException(e.getMessage());
01107     }
01108   }
01109 
01110   /**
01111    * Get the TransactionMarkerMetaData for the given transaction id.
01112    * 
01113    * @param tid transaction id
01114    * @return the TransactionMarkerMetaData
01115    * @throws SQLException if no marker has been found for this transaction
01116    */
01117   public TransactionMarkerMetaData getTransactionMarker(Long tid)
01118       throws SQLException
01119   {
01120     TransactionMarkerMetaData tm = (TransactionMarkerMetaData) tidLoginTable
01121         .get(tid);
01122 
01123     if (tm == null)
01124       throw new SQLException(Translate.get("transaction.marker.not.found", ""
01125           + tid));
01126 
01127     tm.setTimeout(commitTimeout);
01128     return tm;
01129   }
01130 
01131   /**
01132    * Complete the transaction by removing it from the tidLoginTable.
01133    * 
01134    * @param tid transaction id
01135    */
01136   public void completeTransaction(Long tid)
01137   {
01138     tidLoginTable.remove(tid);
01139   }
01140 
01141   /**
01142    * Commit a transaction given its id.
01143    * 
01144    * @param transactionId the transaction id
01145    * @throws SQLException if an error occurs
01146    */
01147   public void commit(long transactionId) throws SQLException
01148   {
01149     try
01150     {
01151       Long tid = new Long(transactionId);
01152       TransactionMarkerMetaData tm = getTransactionMarker(tid);
01153 
01154       // Wait for the scheduler to give us the authorization to execute
01155       scheduler.commit(tm);
01156 
01157       if (logger.isDebugEnabled())
01158         logger.debug(Translate.get("transaction.commit", String.valueOf(tid)));
01159 
01160       try
01161       {
01162         // Send to load balancer
01163         loadBalancer.commit(tm);
01164 
01165         // Notify the cache
01166         if (resultCache != null)
01167           resultCache.commit(tm.getTransactionId());
01168 
01169         // Notify the recovery log manager
01170         if (recoveryLog != null)
01171           recoveryLog.commit(tm);
01172       }
01173       catch (SQLException e)
01174       {
01175         throw e;
01176       }
01177       catch (AllBackendsFailedException e)
01178       {
01179         String msg = "All backends failed to commit transaction "
01180             + transactionId + " (" + e + ")";
01181         logger.error(msg);
01182         throw new SQLException(msg);
01183       }
01184       finally
01185       {
01186         // Notify scheduler for completion
01187         scheduler.commitCompleted(transactionId);
01188 
01189         completeTransaction(tid);
01190       }
01191     }
01192     catch (RuntimeException e)
01193     {
01194       logger.fatal(Translate.get(
01195           "fatal.runtime.exception.requestmanager.commit", e));
01196       throw new SQLException(e.getMessage());
01197     }
01198   }
01199 
01200   /**
01201    * Rollback a transaction given its id.
01202    * 
01203    * @param transactionId the transaction id
01204    * @throws SQLException if an error occurs
01205    */
01206   public void rollback(long transactionId) throws SQLException
01207   {
01208     try
01209     {
01210       Long tid = new Long(transactionId);
01211       TransactionMarkerMetaData tm = getTransactionMarker(tid);
01212 
01213       // Wait for the scheduler to give us the authorization to execute
01214       scheduler.rollback(tm);
01215 
01216       if (logger.isDebugEnabled())
01217         logger.debug(Translate.get("transaction.rollback", String
01218             .valueOf(transactionId)));
01219 
01220       try
01221       {
01222         // Send to load balancer
01223         loadBalancer.rollback(tm);
01224 
01225         // Send to cache
01226         if (this.resultCache != null)
01227           resultCache.rollback(transactionId);
01228 
01229         // Notify the recovery log manager
01230         if (recoveryLog != null)
01231         {
01232           recoveryLog.rollback(tm);
01233         }
01234       }
01235       catch (SQLException e)
01236       {
01237         throw e;
01238       }
01239       catch (AllBackendsFailedException e)
01240       {
01241         String msg = Translate.get("requestmanager.rollback.failed.all",
01242             new String[]{String.valueOf(transactionId), e.getMessage()});
01243         logger.error(msg);
01244         throw new SQLException(msg);
01245       }
01246       finally
01247       {
01248         // Notify scheduler for completion
01249         scheduler.rollbackCompleted(transactionId);
01250 
01251         completeTransaction(tid);
01252       }
01253     }
01254     catch (RuntimeException e)
01255     {
01256       logger.fatal(Translate
01257           .get("fatal.runtime.exception.requestmanager.rollback"), e);
01258       throw new SQLException(e.getMessage());
01259     }
01260   }
01261 
01262   //
01263   // Database Backends management
01264   //
01265 
01266   /**
01267    * Enable a backend that has been previously added to this virtual database
01268    * and that is in the disabled state.
01269    * <p>
01270    * The backend is enabled without further check.
01271    * <p>
01272    * The enableBackend method of the load balancer is called.
01273    * 
01274    * @param db The database backend to enable
01275    * @throws SQLException if an error occurs
01276    */
01277   public void enableBackend(DatabaseBackend db) throws SQLException
01278   {
01279     loadBalancer.enableBackend(db, true);
01280     logger.info(Translate.get("backend.state.enabled", db.getName()));
01281   }
01282 
01283   /**
01284    * The backend must have been previously added to this virtual database and be
01285    * in the disabled state.
01286    * <p>
01287    * All the queries since the given checkpoint are played and the backend state
01288    * is set to enabled when it is completely synchronized.
01289    * <p>
01290    * Note that the job is performed in background by a
01291    * <code>JDBCRecoverThread</code>. You can synchronize on thread
01292    * termination if you need to wait for completion of this task and listen to
01293    * JMX notifications for the success status.
01294    * 
01295    * @param db The database backend to enable
01296    * @param checkpointName The checkpoint name to restart from
01297    * @return the JDBC reocver thread synchronizing the backend
01298    * @throws SQLException if an error occurs
01299    */
01300   public JDBCRecoverThread enableBackendFromCheckpoint(DatabaseBackend db,
01301       String checkpointName) throws SQLException
01302   {
01303     // Sanity check
01304     if (recoveryLog == null)
01305     {
01306       String msg = Translate.get(
01307           "recovery.restore.checkpoint.failed.cause.null", checkpointName);
01308       logger.error(msg);
01309       throw new SQLException(msg);
01310     }
01311 
01312     if (db.getStateValue() == BackendState.REPLAYING
01313         || db.getStateValue() == BackendState.RECOVERING)
01314       throw new SQLException(Translate.get(
01315           "recovery.restore.backend.state.invalid", db.getName()));
01316 
01317     JDBCRecoverThread recoverThread = new JDBCRecoverThread(scheduler,
01318         recoveryLog, db, loadBalancer, checkpointName);
01319 
01320     // fire the thread and forget
01321     // exception will be reported in a jmx notification on the
01322     // backend.
01323     recoverThread.start();
01324     return recoverThread;
01325   }
01326 
01327   /**
01328    * Disable a backend that is currently enabled on this virtual database.
01329    * <p>
01330    * The backend is disabled without further check.
01331    * <p>
01332    * The load balancer disabled method is called on the specified backend.
01333    * 
01334    * @param db The database backend to disable
01335    * @throws SQLException if an error occurs
01336    */
01337   public void disableBackend(DatabaseBackend db) throws SQLException
01338   {
01339     if (db.isReadEnabled() || db.isWriteEnabled())
01340     {
01341       loadBalancer.disableBackend(db);
01342       logger.info(Translate.get("backend.state.disabled", db.getName()));
01343     }
01344     else
01345     {
01346       throw new SQLException(Translate.get("backend.already.disabled", db
01347           .getName()));
01348     }
01349   }
01350 
01351   /**
01352    * The backend must belong to this virtual database and be in the enabled
01353    * state.
01354    * <p>
01355    * The backend is disabled once all the pending write queries are executed. A
01356    * checkpoint is inserted in the recovery log.
01357    * 
01358    * @param db The database backend to enable
01359    * @param checkpointName The checkpoint name to restart from
01360    * @throws SQLException if an error occurs
01361    */
01362   public void disableBackendForCheckpoint(DatabaseBackend db,
01363       String checkpointName) throws SQLException
01364   {
01365     // Sanity checks
01366     if (recoveryLog == null)
01367     {
01368       String msg = Translate.get("recovery.store.checkpoint.failed.cause.null",
01369           checkpointName);
01370       logger.error(msg);
01371       throw new SQLException(msg);
01372     }
01373 
01374     // Wait for all pending writes to finish
01375     logger.info(Translate.get("requestmanager.wait.pending.writes"));
01376     scheduler.suspendWrites();
01377 
01378     // Store checkpoint
01379     recoveryLog.storeCheckpoint(checkpointName);
01380     logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
01381 
01382     // Signal the backend should not begin any new transaction
01383     db.setState(BackendState.DISABLING);
01384     logger.info(Translate.get("backend.state.disabling", db.getName()));
01385 
01386     // Resume writes
01387     logger.info(Translate.get("requestmanager.resume.pending.writes"));
01388     scheduler.resumeWrites();
01389 
01390     // Wait for all current transactions on the backend to finish
01391     db.waitForAllTransactionsToComplete();
01392 
01393     // Now we can safely disable the backend
01394     db.setLastKnownCheckpoint(checkpointName);
01395     loadBalancer.disableBackend(db);
01396     logger.info(Translate.get("backend.state.disabled", db.getName()));
01397   }
01398 
01399   /**
01400    * Disable a list of backends. Only to store only one checkpoint, and to
01401    * disable all the backends at the same time so the the system is in a
01402    * coherent state. Consider only the backends that were enabled. The others
01403    * are left in the state they were before.
01404    * 
01405    * @param backendsArrayList backends to disable
01406    * @param checkpointName to store
01407    * @throws SQLException if an error occurs
01408    */
01409   public void disableBackendsForCheckpoint(ArrayList backendsArrayList,
01410       String checkpointName) throws SQLException
01411   {
01412     // Sanity checks
01413     if (recoveryLog == null)
01414     {
01415       String msg = Translate.get("recovery.store.checkpoint.failed.cause.null",
01416           checkpointName);
01417       logger.error(msg);
01418       throw new SQLException(msg);
01419     }
01420 
01421     // Wait for all pending writes to finish
01422     logger.info(Translate.get("requestmanager.wait.pending.writes"));
01423     scheduler.suspendWrites();
01424 
01425     // Store checkpoint
01426     recoveryLog.storeCheckpoint(checkpointName);
01427     logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
01428 
01429     // Copy the list and consider only the backends that are enabled
01430     DatabaseBackend db;
01431     ArrayList backendList = (ArrayList) backendsArrayList.clone();
01432     for (int i = 0; i < backendList.size(); i++)
01433     {
01434       db = (DatabaseBackend) backendList.get(i);
01435       if (!db.isWriteEnabled())
01436         backendList.remove(i);
01437     }
01438 
01439     // Signal all backends that they should not begin any new transaction
01440     int size = backendList.size();
01441     for (int i = 0; i < size; i++)
01442     {
01443       db = (DatabaseBackend) backendList.get(i);
01444       db.setState(BackendState.DISABLING);
01445       logger.info(Translate.get("backend.state.disabling", db.getName()));
01446     }
01447 
01448     // Resume writes
01449     logger.info(Translate.get("requestmanager.resume.pending.writes"));
01450     scheduler.resumeWrites();
01451 
01452     // Wait for all current transactions on backends to finish
01453     for (int i = 0; i < size; i++)
01454     {
01455       db = (DatabaseBackend) backendList.get(i);
01456       db.waitForAllTransactionsToComplete();
01457     }
01458 
01459     // Now we can safely disable all backends
01460     for (int i = 0; i < size; i++)
01461     {
01462       db = (DatabaseBackend) backendList.get(i);
01463       db.setLastKnownCheckpoint(checkpointName);
01464       loadBalancer.disableBackend(db);
01465       logger.info(Translate.get("backend.state.disabled", db.getName()));
01466     }
01467   }
01468 
01469   /**
01470    * Call the backup manager on the given backend. Start a fire and forget
01471    * thread for backup or recovery. The only way to get the results is to wait
01472    * for jmx notifications
01473    * 
01474    * @param backup true if this is a backup, false if this is a restore process
01475    * @param db backend object
01476    * @param checkpoint the name of the checkpoint associated with the process
01477    * @param tables the tables to consider while performing the copy
01478    * @param enableAfter should be enable the backend automatically after
01479    * @param listener backup listener for callback
01480    * @throws Exception if fails
01481    */
01482   public void callBackupManager(boolean backup, DatabaseBackend db,
01483       String checkpoint, ArrayList tables, boolean enableAfter,
01484       BackupListener listener) throws Exception
01485   {
01486     if (backup)
01487       backupBackendWithCheckpoint(db, checkpoint, tables, enableAfter, false,
01488           listener);
01489     else
01490       restoreBackendFromBackupCheckpoint(db, checkpoint, false, listener);
01491   }
01492 
01493   /**
01494    * Creates a new backup with the corresponding checkpoint Note that this will
01495    * disable the backend for the time of the backup
01496    * 
01497    * @param backend the backend to backup
01498    * @param checkpointName the checkpoint to insert in the log
01499    * @param tables to copy or null for copy all
01500    * @param enableAfter if the backend should be enable after backup
01501    * @param wait4Result if the method should block until the backup is over
01502    * @param listener object for callback
01503    * @throws SQLException if fails
01504    */
01505   public void backupBackendWithCheckpoint(DatabaseBackend backend,
01506       String checkpointName, ArrayList tables, boolean enableAfter,
01507       boolean wait4Result, BackupListener listener) throws SQLException
01508   {
01509     if (backend.isReadEnabled())
01510     { // Disable backend and store checkpoint
01511       disableBackendForCheckpoint(backend, checkpointName);
01512       logger.info(Translate.get("backend.state.disabled", backend.getName()));
01513     }
01514     // else backend is already disabled, no checkpoint is stored here, it should
01515     // have been done at disable time.
01516 
01517     try
01518     {
01519       logger.info(Translate.get("controller.backup.octopus.start", backend
01520           .getName()));
01521       backupManager.backup(backend, checkpointName, tables, listener);
01522       if (wait4Result)
01523         backupManager.getResult(backend, 0);
01524     }
01525     catch (OctopusException e)
01526     {
01527       logger.error(Translate.get("controller.backup.octopus.failed"), e);
01528       throw new SQLException(e.getMessage());
01529     }
01530     catch (BackupException be)
01531     {
01532       logger.error(Translate.get("controller.backup.failed"), be);
01533       throw new SQLException(be.getMessage());
01534     }
01535     logger.info(Translate.get("controller.backup.complete", backend.getName()));
01536 
01537     if (enableAfter)
01538     {
01539       JDBCRecoverThread thread = enableBackendFromCheckpoint(backend,
01540           checkpointName);
01541       if (wait4Result)
01542         try
01543         {
01544           thread.join();
01545         }
01546         catch (InterruptedException e)
01547         {
01548           logger.error("Recovery thread has been interrupted", e);
01549         }
01550     }
01551 
01552   }
01553 
01554   /**
01555    * Recopy all the data of a previous dump recorded by octopus into the named
01556    * backend. This disables the backend and leave it disable after recovery
01557    * process. The user has to call the <code>enableBackendFromCheckpoint</code>
01558    * after this.
01559    * 
01560    * @param db the backend to restore
01561    * @param checkpointName the name of the checkpoint that has a dump
01562    * @param wait4Result should we blocked this method until the restore is
01563    *          finished
01564    * @param listener object for callback
01565    * @throws OctopusException if backup failed while in octopus mode
01566    * @throws BackupException if backup failed for other reasons
01567    */
01568   public void restoreBackendFromBackupCheckpoint(DatabaseBackend db,
01569       String checkpointName, boolean wait4Result, BackupListener listener)
01570       throws OctopusException, BackupException
01571   {
01572     try
01573     {
01574       // no check for disable as we are going to overwrite
01575       // all the database data
01576       if (db.isReadEnabled())
01577         loadBalancer.disableBackend(db);
01578 
01579       backupManager.restore(db, checkpointName, null, listener);
01580       if (wait4Result)
01581         backupManager.getResult(db, 0);
01582     }
01583     catch (SQLException e1)
01584     {
01585       // This comes from the loadbalancer
01586       throw new BackupException(ExceptionTypes.BACKEND_CANNOT_BE_DISABLED);
01587     }
01588     catch (OctopusException e)
01589     {
01590       logger.error(Translate.get("controller.octopus.recovery.failed"), e);
01591       throw e;
01592     }
01593     catch (BackupException be)
01594     {
01595       logger.error(Translate.get("controller.backup.recovery.failed"), be);
01596       throw be;
01597     }
01598     finally
01599     {
01600       logger.info(Translate
01601           .get("controller.backup.recovery.done", db.getName()));
01602     }
01603   }
01604 
01605   /**
01606    * Store all the backends checkpoint in the recoverylog
01607    * 
01608    * @param databaseName the virtual database name
01609    * @param backends the <code>Arraylist</code> of backends
01610    */
01611   public void storeBackendsInfo(String databaseName, ArrayList backends)
01612   {
01613     if (recoveryLog == null)
01614       return;
01615     int size = backends.size();
01616     DatabaseBackend backend;
01617     for (int i = 0; i < size; i++)
01618     {
01619       backend = (DatabaseBackend) backends.get(i);
01620       try
01621       {
01622         recoveryLog.storeBackendRecoveryInfo(databaseName,
01623             new BackendRecoveryInfo(backend.getName(), backend
01624                 .getLastKnownCheckpoint(), backend.getStateValue(),
01625                 databaseName));
01626       }
01627       catch (SQLException e)
01628       {
01629         logger.error(Translate.get("recovery.store.checkpoint.failed",
01630             new String[]{backend.getName(), e.getMessage()}), e);
01631       }
01632     }
01633   }
01634 
01635   /**
01636    * Remove a checkpoint and corresponding entries from the log table
01637    * 
01638    * @param checkpointName to remove
01639    * @throws SQLException if fails
01640    */
01641   public void removeCheckpoint(String checkpointName) throws SQLException
01642   {
01643     recoveryLog.removeCheckpoint(checkpointName);
01644   }
01645 
01646   //
01647   // Getter/Setter methods
01648   //
01649 
01650   /**
01651    * Returns the vdb value.
01652    * 
01653    * @return Returns the vdb.
01654    */
01655   public VirtualDatabase getVirtualDatabase()
01656   {
01657     return vdb;
01658   }
01659 
01660   /**
01661    * Sets the <code>DatabaseSchema</code> to be able to parse the requests and
01662    * find dependencies.
01663    * 
01664    * @param schema a <code>DatabaseSchema</code> value
01665    * @param isStatic true if the given schema is static
01666    */
01667   public void setDatabaseSchema(DatabaseSchema schema, boolean isStatic)
01668   {
01669     if (schemaIsStatic)
01670     {
01671       if (isStatic)
01672       {
01673         logger.warn(Translate
01674             .get("requestmanager.schema.replace.static.with.new"));
01675         this.dbs = schema;
01676       }
01677       else
01678         logger.info(Translate.get("requestmanager.schema.ignore.new.dynamic"));
01679     }
01680     else
01681     {
01682       schemaIsStatic = isStatic;
01683       this.dbs = schema;
01684       logger.info(Translate
01685           .get("requestmanager.schema.set.new.virtualdatabase"));
01686     }
01687 
01688     if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
01689       scheduler.setDatabaseSchema(dbs);
01690 
01691     if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
01692       resultCache.setDatabaseSchema(dbs);
01693 
01694     // Load balancers do not have a specific database schema to update
01695   }
01696 
01697   /**
01698    * Merge the given schema with the existing database schema.
01699    * 
01700    * @param backendSchema The virtual database schema to merge.
01701    */
01702   public void mergeDatabaseSchema(DatabaseSchema backendSchema)
01703   {
01704     try
01705     {
01706       if (dbs == null)
01707         setDatabaseSchema(new DatabaseSchema(backendSchema), false);
01708       else
01709       {
01710         dbs.mergeSchema(backendSchema);
01711         logger.info(Translate
01712             .get("requestmanager.schema.virtualdatabase.merged.new"));
01713 
01714         if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
01715           scheduler.mergeDatabaseSchema(dbs);
01716 
01717         if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
01718           resultCache.mergeDatabaseSchema(dbs);
01719       }
01720     }
01721     catch (SQLException e)
01722     {
01723       logger.error(Translate.get("requestmanager.schema.merge.failed", e
01724           .getMessage()), e);
01725     }
01726   }
01727 
01728   /**
01729    * Sets the backup manager for this recovery log
01730    * 
01731    * @param currentBackupManager an instance of <code>BackupManager</code>
01732    */
01733   public void setBackupManager(BackupManager currentBackupManager)
01734   {
01735     this.backupManager = currentBackupManager;
01736   }
01737 
01738   /**
01739    * Returns the backupManager value.
01740    * 
01741    * @return Returns the backupManager.
01742    */
01743   public BackupManager getBackupManager()
01744   {
01745     return backupManager;
01746   }
01747 
01748   /**
01749    * Get the <code>DatabaseSchema</code> used by this Request Manager.
01750    * 
01751    * @return a <code>DatabaseSchema</code> value
01752    */
01753   public DatabaseSchema getDatabaseSchema()
01754   {
01755     return dbs;
01756   }
01757 
01758   /**
01759    * Get the Request Load Balancer used in this Request Controller.
01760    * 
01761    * @return an <code>AbstractLoadBalancer</code> value
01762    */
01763   public AbstractLoadBalancer getLoadBalancer()
01764   {
01765     return loadBalancer;
01766   }
01767 
01768   /**
01769    * Set the Request Load Balancer to use in this Request Controller.
01770    * 
01771    * @param loadBalancer a Request Load Balancer implementation
01772    */
01773   public void setLoadBalancer(AbstractLoadBalancer loadBalancer)
01774   {
01775     if (this.loadBalancer != null)
01776       throw new RuntimeException(
01777           "It is not possible to dynamically change a load balancer.");
01778     this.loadBalancer = loadBalancer;
01779     if (loadBalancer == null)
01780       return;
01781     loadBalancerParsingranularity = loadBalancer.getParsingGranularity();
01782     if (loadBalancerParsingranularity > requiredGranularity)
01783       requiredGranularity = loadBalancerParsingranularity;
01784 
01785     if (MBeanServerManager.isJmxEnabled())
01786     {
01787       try
01788       {
01789         MBeanServerManager.registerMBean(loadBalancer, JmxConstants
01790             .getLoadBalancerObjectName(vdb.getVirtualDatabaseName()));
01791       }
01792       catch (Exception e)
01793       {
01794         logger.error(Translate.get("jmx.failed.register.mbean.loadbalancer"));
01795       }
01796     }
01797 
01798   }
01799 
01800   /**
01801    * Get the result cache (if any) used in this Request Manager.
01802    * 
01803    * @return an <code>AbstractResultCache</code> value or null if no Reqsult
01804    *         Cache has been defined
01805    */
01806   public AbstractResultCache getResultCache()
01807   {
01808     return resultCache;
01809   }
01810 
01811   /**
01812    * Returns the metadataCache value.
01813    * 
01814    * @return Returns the metadataCache.
01815    */
01816   public MetadataCache getMetadataCache()
01817   {
01818     return metadataCache;
01819   }
01820 
01821   /**
01822    * Sets the metadataCache value.
01823    * 
01824    * @param metadataCache The metadataCache to set.
01825    */
01826   public void setMetadataCache(MetadataCache metadataCache)
01827   {
01828     this.metadataCache = metadataCache;
01829   }
01830 
01831   /**
01832    * Sets the ParsingCache.
01833    * 
01834    * @param parsingCache The parsingCache to set.
01835    */
01836   public void setParsingCache(ParsingCache parsingCache)
01837   {
01838     parsingCache.setRequestManager(this);
01839     parsingCache.setGranularity(requiredGranularity);
01840     parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
01841     this.parsingCache = parsingCache;
01842   }
01843 
01844   /**
01845    * Returns the Recovery Log Manager.
01846    * 
01847    * @return AbstractRecoveryLog
01848    */
01849   public AbstractRecoveryLog getRecoveryLog()
01850   {
01851     return recoveryLog;
01852   }
01853 
01854   /**
01855    * Sets the Recovery Log Manager.
01856    * 
01857    * @param recoveryLog The log recovery to set
01858    */
01859   public void setRecoveryLog(AbstractRecoveryLog recoveryLog)
01860   {
01861     if (recoveryLog == null)
01862       return;
01863     this.recoveryLog = recoveryLog;
01864     ArrayList backends = vdb.getBackends();
01865     int size = backends.size();
01866     backendStateListener = new BackendStateListener(vdb
01867         .getVirtualDatabaseName(), recoveryLog);
01868     for (int i = 0; i < size; i++)
01869       ((DatabaseBackend) backends.get(i))
01870           .setStateListener(backendStateListener);
01871 
01872     if (MBeanServerManager.isJmxEnabled())
01873     {
01874       try
01875       {
01876         MBeanServerManager.registerMBean(recoveryLog, JmxConstants
01877             .getRecoveryLogObjectName(vdb.getVirtualDatabaseName()));
01878       }
01879       catch (Exception e)
01880       {
01881         logger.error(Translate.get("jmx.failed.register.mbean.recoverylog"));
01882       }
01883     }
01884   }
01885 
01886   /**
01887    * Set the Request Cache to use in this Request Controller.
01888    * 
01889    * @param cache a Request Cache implementation
01890    */
01891   public void setResultCache(AbstractResultCache cache)
01892   {
01893     resultCache = cache;
01894     cacheParsingranularity = cache.getParsingGranularity();
01895     if (cacheParsingranularity > requiredGranularity)
01896       requiredGranularity = cacheParsingranularity;
01897   }
01898 
01899   /**
01900    * Get the Request Scheduler (if any) used in this Request Controller.
01901    * 
01902    * @return an <code>AbstractScheduler</code> value or null if no Request
01903    *         Scheduler has been defined
01904    */
01905   public AbstractScheduler getScheduler()
01906   {
01907     return scheduler;
01908   }
01909 
01910   /**
01911    * Set the Request Scheduler to use in this Request Controller.
01912    * 
01913    * @param scheduler a Request Scheduler implementation
01914    */
01915   public void setScheduler(AbstractScheduler scheduler)
01916   {
01917     this.scheduler = scheduler;
01918     schedulerParsingranularity = scheduler.getParsingGranularity();
01919     if (schedulerParsingranularity > requiredGranularity)
01920       requiredGranularity = schedulerParsingranularity;
01921   }
01922 
01923   /**
01924    * Sets the parsing case sensitivity. If true the request are parsed in a case
01925    * sensitive way (table/column name must match exactly the case of the names
01926    * fetched from the database or enforced by a static schema).
01927    * 
01928    * @param isCaseSensitiveParsing true if parsing is case sensitive
01929    */
01930   public void setCaseSensitiveParsing(boolean isCaseSensitiveParsing)
01931   {
01932     this.isCaseSensitiveParsing = isCaseSensitiveParsing;
01933     if (parsingCache != null)
01934       parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
01935   }
01936 
01937   /**
01938    * @return the parsing granularity required by the current configuration
01939    */
01940   public int getRequiredParsingGranularity()
01941   {
01942     return requiredGranularity;
01943   }
01944 
01945   //
01946   // Debug/Monitoring
01947   //
01948 
01949   /**
01950    * Get xml information about this Request Manager
01951    * 
01952    * @return <code>String</code> in xml formatted text
01953    */
01954   public String getXml()
01955   {
01956     StringBuffer info = new StringBuffer();
01957     info.append("<" + DatabasesXmlTags.ELT_RequestManager + " "
01958         + DatabasesXmlTags.ATT_caseSensitiveParsing + "=\""
01959         + isCaseSensitiveParsing + "\" " + DatabasesXmlTags.ATT_beginTimeout
01960         + "=\"" + beginTimeout / 1000 + "\" "
01961         + DatabasesXmlTags.ATT_commitTimeout + "=\"" + commitTimeout / 1000
01962         + "\" " + DatabasesXmlTags.ATT_rollbackTimeout + "=\""
01963         + rollbackTimeout / 1000 + "\">");
01964     if (scheduler != null)
01965       info.append(scheduler.getXml());
01966 
01967     if (metadataCache != null || parsingCache != null || resultCache != null)
01968     {
01969       info.append("<" + DatabasesXmlTags.ELT_RequestCache + ">");
01970       if (metadataCache != null)
01971         info.append(metadataCache.getXml());
01972       if (parsingCache != null)
01973         info.append(parsingCache.getXml());
01974       if (resultCache != null)
01975         info.append(resultCache.getXml());
01976       info.append("</" + DatabasesXmlTags.ELT_RequestCache + ">");
01977     }
01978 
01979     if (loadBalancer != null)
01980       info.append(loadBalancer.getXml());
01981     if (recoveryLog != null)
01982       info.append(this.recoveryLog.getXml());
01983     info.append("</" + DatabasesXmlTags.ELT_RequestManager + ">");
01984     return info.toString();
01985   }
01986 
01987   /**
01988    * Returns the backendStateListener value.
01989    * 
01990    * @return Returns the backendStateListener.
01991    */
01992   public BackendStateListener getBackendStateListener()
01993   {
01994     return backendStateListener;
01995   }
01996 
01997   /**
01998    * Returns the beginTimeout value.
01999    * 
02000    * @return Returns the beginTimeout.
02001    */
02002   public long getBeginTimeout()
02003   {
02004     return beginTimeout;
02005   }
02006 
02007   /**
02008    * Sets the beginTimeout value.
02009    * 
02010    * @param beginTimeout The beginTimeout to set.
02011    */
02012   public void setBeginTimeout(long beginTimeout)
02013   {
02014     this.beginTimeout = beginTimeout;
02015   }
02016 
02017   /**
02018    * Returns the cacheParsingranularity value.
02019    * 
02020    * @return Returns the cacheParsingranularity.
02021    */
02022   public int getCacheParsingranularity()
02023   {
02024     return cacheParsingranularity;
02025   }
02026 
02027   /**
02028    * Sets the cacheParsingranularity value.
02029    * 
02030    * @param cacheParsingranularity The cacheParsingranularity to set.
02031    */
02032   public void setCacheParsingranularity(int cacheParsingranularity)
02033   {
02034     this.cacheParsingranularity = cacheParsingranularity;
02035   }
02036 
02037   /**
02038    * Returns the commitTimeout value.
02039    * 
02040    * @return Returns the commitTimeout.
02041    */
02042   public long getCommitTimeout()
02043   {
02044     return commitTimeout;
02045   }
02046 
02047   /**
02048    * Sets the commitTimeout value.
02049    * 
02050    * @param commitTimeout The commitTimeout to set.
02051    */
02052   public void setCommitTimeout(long commitTimeout)
02053   {
02054     this.commitTimeout = commitTimeout;
02055   }
02056 
02057   /**
02058    * Returns the loadBalancerParsingranularity value.
02059    * 
02060    * @return Returns the loadBalancerParsingranularity.
02061    */
02062   public int getLoadBalancerParsingranularity()
02063   {
02064     return loadBalancerParsingranularity;
02065   }
02066 
02067   /**
02068    * Sets the loadBalancerParsingranularity value.
02069    * 
02070    * @param loadBalancerParsingranularity The loadBalancerParsingranularity to
02071    *          set.
02072    */
02073   public void setLoadBalancerParsingranularity(int loadBalancerParsingranularity)
02074   {
02075     this.loadBalancerParsingranularity = loadBalancerParsingranularity;
02076   }
02077 
02078   /**
02079    * Returns the requiredGranularity value.
02080    * 
02081    * @return Returns the requiredGranularity.
02082    */
02083   public int getRequiredGranularity()
02084   {
02085     return requiredGranularity;
02086   }
02087 
02088   /**
02089    * Sets the requiredGranularity value.
02090    * 
02091    * @param requiredGranularity The requiredGranularity to set.
02092    */
02093   public void setRequiredGranularity(int requiredGranularity)
02094   {
02095     this.requiredGranularity = requiredGranularity;
02096   }
02097 
02098   /**
02099    * Returns the rollbackTimeout value.
02100    * 
02101    * @return Returns the rollbackTimeout.
02102    */
02103   public long getRollbackTimeout()
02104   {
02105     return rollbackTimeout;
02106   }
02107 
02108   /**
02109    * Sets the rollbackTimeout value.
02110    * 
02111    * @param rollbackTimeout The rollbackTimeout to set.
02112    */
02113   public void setRollbackTimeout(long rollbackTimeout)
02114   {
02115     this.rollbackTimeout = rollbackTimeout;
02116   }
02117 
02118   /**
02119    * Returns the schedulerParsingranularity value.
02120    * 
02121    * @return Returns the schedulerParsingranularity.
02122    */
02123   public int getSchedulerParsingranularity()
02124   {
02125     return schedulerParsingranularity;
02126   }
02127 
02128   /**
02129    * Sets the schedulerParsingranularity value.
02130    * 
02131    * @param schedulerParsingranularity The schedulerParsingranularity to set.
02132    */
02133   public void setSchedulerParsingranularity(int schedulerParsingranularity)
02134   {
02135     this.schedulerParsingranularity = schedulerParsingranularity;
02136   }
02137 
02138   /**
02139    * Returns the schemaIsStatic value.
02140    * 
02141    * @return Returns the schemaIsStatic.
02142    */
02143   public boolean isSchemaIsStatic()
02144   {
02145     return schemaIsStatic;
02146   }
02147 
02148   /**
02149    * Sets the schemaIsStatic value.
02150    * 
02151    * @param schemaIsStatic The schemaIsStatic to set.
02152    */
02153   public void setSchemaIsStatic(boolean schemaIsStatic)
02154   {
02155     this.schemaIsStatic = schemaIsStatic;
02156   }
02157 
02158   /**
02159    * Returns the isCaseSensitiveParsing value.
02160    * 
02161    * @return Returns the isCaseSensitiveParsing.
02162    */
02163   public boolean isCaseSensitiveParsing()
02164   {
02165     return isCaseSensitiveParsing;
02166   }
02167 
02168   /**
02169    * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
02170    */
02171   public String getAssociatedString()
02172   {
02173     return "requestmanager";
02174   }
02175 }

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