KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > requestmanager > RequestManager


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite, Greg Ward, Nicolas Modrzyk, Vadim Kassin, Jean-Bernard van Zuylen.
23  */

24
25 package org.objectweb.cjdbc.controller.requestmanager;
26
27 import java.sql.SQLException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.Vector JavaDoc;
33
34 import javax.management.NotCompliantMBeanException JavaDoc;
35
36 import org.objectweb.cjdbc.common.exceptions.BackupException;
37 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
38 import org.objectweb.cjdbc.common.exceptions.RollbackException;
39 import org.objectweb.cjdbc.common.i18n.Translate;
40 import org.objectweb.cjdbc.common.jmx.JmxConstants;
41 import org.objectweb.cjdbc.common.jmx.mbeans.RequestManagerMBean;
42 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList;
43 import org.objectweb.cjdbc.common.log.Trace;
44 import org.objectweb.cjdbc.common.shared.BackendState;
45 import org.objectweb.cjdbc.common.shared.DumpInfo;
46 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
47 import org.objectweb.cjdbc.common.sql.AlterRequest;
48 import org.objectweb.cjdbc.common.sql.CreateRequest;
49 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
50 import org.objectweb.cjdbc.common.sql.RequestType;
51 import org.objectweb.cjdbc.common.sql.SelectRequest;
52 import org.objectweb.cjdbc.common.sql.StoredProcedure;
53 import org.objectweb.cjdbc.common.sql.UpdateRequest;
54 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema;
55 import org.objectweb.cjdbc.common.sql.schema.DatabaseTable;
56 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
57 import org.objectweb.cjdbc.common.xml.XmlComponent;
58 import org.objectweb.cjdbc.controller.backend.BackendStateListener;
59 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
60 import org.objectweb.cjdbc.controller.backup.BackupManager;
61 import org.objectweb.cjdbc.controller.backup.Backuper;
62 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
63 import org.objectweb.cjdbc.controller.cache.parsing.ParsingCache;
64 import org.objectweb.cjdbc.controller.cache.result.AbstractResultCache;
65 import org.objectweb.cjdbc.controller.cache.result.entries.AbstractResultCacheEntry;
66 import org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean;
67 import org.objectweb.cjdbc.controller.jmx.MBeanServerManager;
68 import org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer;
69 import org.objectweb.cjdbc.controller.loadbalancer.AllBackendsFailedException;
70 import org.objectweb.cjdbc.controller.recoverylog.BackendRecoveryInfo;
71 import org.objectweb.cjdbc.controller.recoverylog.RecoverThread;
72 import org.objectweb.cjdbc.controller.recoverylog.RecoveryLog;
73 import org.objectweb.cjdbc.controller.scheduler.AbstractScheduler;
74 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
75 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
76
77 /**
78  * This class defines the Request Manager.
79  * <p>
80  * The RM is composed of a Request Scheduler, an optional Query Cache, and a
81  * Load Balancer and an optional Recovery Log.
82  *
83  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
84  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
85  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
86  * @author <a HREF="mailto:vadim@kase.kz">Vadim Kassin </a>
87  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
88  * </a>
89  * @version 1.0
90  */

91 public class RequestManager extends AbstractStandardMBean
92     implements
93       XmlComponent,
94       RequestManagerMBean
95 {
96
97   //
98
// How the code is organized ?
99
//
100
// 1. Member variables
101
// 2. Constructor(s)
102
// 3. Request handling
103
// 4. Transaction handling
104
// 5. Database backend management
105
// 6. Getter/Setter (possibly in alphabetical order)
106
//
107

108   /** begin timeout in ms */
109   protected long beginTimeout;
110
111   /** commit timeout in ms */
112   protected long commitTimeout;
113
114   /** rollback timeout in ms */
115   protected long rollbackTimeout;
116
117   /** The virtual database owning this Request Manager */
118   protected VirtualDatabase vdb;
119
120   /** The request scheduler to order and schedule requests */
121   protected AbstractScheduler scheduler;
122
123   /** An optional request cache to cache responses to SQL requests */
124   protected AbstractResultCache resultCache;
125
126   /** The request load balancer to use to send requests to the databases */
127   protected AbstractLoadBalancer loadBalancer;
128
129   /** An optional recovery log */
130   protected RecoveryLog recoveryLog;
131
132   /** The backup manager responsible for backup and restore of backends */
133   protected BackupManager backupManager;
134
135   // The virtual dabase schema
136
protected DatabaseSchema dbs;
137
138   /** <code>true</code> if schema is no more up-to-date and needs a refresh */
139   private boolean schemaIsDirty = false;
140
141   private boolean schemaIsStatic = false;
142
143   private boolean isCaseSensitiveParsing = false;
144
145   protected ParsingCache parsingCache = null;
146
147   private MetadataCache metadataCache = null;
148
149   // SQL queries parsing granularity according to Scheduler, ResultCache and
150
// LoadBalancer required granularity
151
protected int schedulerParsingranularity = ParsingGranularities.NO_PARSING;
152
153   private int cacheParsingranularity = ParsingGranularities.NO_PARSING;
154
155   private int loadBalancerParsingranularity = ParsingGranularities.NO_PARSING;
156
157   protected int requiredParsingGranularity = ParsingGranularities.NO_PARSING;
158
159   /** Transaction id/Login mapping */
160   protected Hashtable JavaDoc tidLoginTable;
161
162   /** Transaction id/Savepoints mapping */
163   protected Hashtable JavaDoc tidSavepoints;
164
165   protected static Trace logger = null;
166
167   private BackendStateListener backendStateListener;
168
169   //
170
// Constructors
171
//
172

173   /**
174    * Creates a new <code>RequestManager</code> instance.
175    *
176    * @param vdb the virtual database this request manager belongs to
177    * @param scheduler the Request Scheduler to use
178    * @param cache a Query Cache implementation
179    * @param loadBalancer the Request Load Balancer to use
180    * @param recoveryLog the Log Recovery to use
181    * @param beginTimeout timeout in seconds for begin
182    * @param commitTimeout timeout in seconds for commit
183    * @param rollbackTimeout timeout in seconds for rollback
184    * @throws SQLException if an error occurs
185    * @throws NotCompliantMBeanException if the MBean is not JMX compliant
186    */

187   public RequestManager(VirtualDatabase vdb, AbstractScheduler scheduler,
188       AbstractResultCache cache, AbstractLoadBalancer loadBalancer,
189       RecoveryLog recoveryLog, long beginTimeout, long commitTimeout,
190       long rollbackTimeout) throws SQLException JavaDoc, NotCompliantMBeanException JavaDoc
191   {
192     super(RequestManagerMBean.class);
193     this.vdb = vdb;
194     assignAndCheckSchedulerLoadBalancerValidity(scheduler, loadBalancer);
195     // requiredParsingGranularity is the maximum of each component granularity
196
this.resultCache = cache;
197     if (resultCache != null)
198     {
199       cacheParsingranularity = cache.getParsingGranularity();
200       if (cacheParsingranularity > requiredParsingGranularity)
201         requiredParsingGranularity = cacheParsingranularity;
202     }
203     setRecoveryLog(recoveryLog);
204     initRequestManagerVariables(vdb, beginTimeout, commitTimeout,
205         rollbackTimeout);
206     setBackendsLastKnownCheckpointFromRecoveryLog();
207     logger.info(Translate.get("requestmanager.parsing.granularity",
208         ParsingGranularities.getInformation(requiredParsingGranularity)));
209
210     if (MBeanServerManager.isJmxEnabled())
211     {
212       try
213       {
214         MBeanServerManager.registerMBean(this, JmxConstants
215             .getRequestManagerObjectName(vdb.getVirtualDatabaseName()));
216
217       }
218       catch (Exception JavaDoc e)
219       {
220         logger.error(Translate.get("jmx.failed.register.mbean.requestmanager"));
221       }
222     }
223   }
224
225   /**
226    * Retrieve the last known checkpoint from the recovery log and set it for
227    * each backend.
228    */

229   private void setBackendsLastKnownCheckpointFromRecoveryLog()
230   {
231
232     if (recoveryLog == null)
233       return;
234     String JavaDoc databaseName = vdb.getVirtualDatabaseName();
235     ArrayList JavaDoc backends = vdb.getBackends();
236     int size = backends.size();
237     DatabaseBackend backend;
238     BackendRecoveryInfo info;
239     for (int i = 0; i < size; i++)
240     {
241       backend = (DatabaseBackend) backends.get(i);
242       info = recoveryLog
243           .getBackendRecoveryInfo(databaseName, backend.getName());
244       backend.setLastKnownCheckpoint(info.getCheckpoint());
245     }
246
247   }
248
249   /**
250    * Check that Scheduler and Load Balancer are not null and have compatible
251    * RAIDb levels.
252    *
253    * @param scheduler
254    * @param loadBalancer
255    * @throws SQLException if an error occurs
256    */

257   private void assignAndCheckSchedulerLoadBalancerValidity(
258       AbstractScheduler scheduler, AbstractLoadBalancer loadBalancer)
259       throws SQLException JavaDoc
260   {
261     if (scheduler == null)
262       throw new SQLException JavaDoc(Translate.get("requestmanager.null.scheduler"));
263
264     if (loadBalancer == null)
265       throw new SQLException JavaDoc(Translate.get("requestmanager.null.loadbalancer"));
266
267     if (scheduler.getRAIDbLevel() != loadBalancer.getRAIDbLevel())
268       throw new SQLException JavaDoc(Translate.get(
269           "requestmanager.incompatible.raidb.levels",
270           new String JavaDoc[]{"" + scheduler.getRAIDbLevel(),
271               "" + loadBalancer.getRAIDbLevel()}));
272
273     // requiredParsingGranularity is the maximum of each component granularity
274
setScheduler(scheduler);
275     schedulerParsingranularity = scheduler.getParsingGranularity();
276     requiredParsingGranularity = schedulerParsingranularity;
277     setLoadBalancer(loadBalancer);
278     loadBalancerParsingranularity = loadBalancer.getParsingGranularity();
279     if (loadBalancerParsingranularity > requiredParsingGranularity)
280       requiredParsingGranularity = loadBalancerParsingranularity;
281   }
282
283   /**
284    * Method initRequestManagerVariables.
285    *
286    * @param vdb
287    * @param beginTimeout
288    * @param commitTimeout
289    * @param rollbackTimeout
290    */

291   private void initRequestManagerVariables(VirtualDatabase vdb,
292       long beginTimeout, long commitTimeout, long rollbackTimeout)
293   {
294     this.tidLoginTable = new Hashtable JavaDoc();
295     this.tidSavepoints = new Hashtable JavaDoc();
296     this.beginTimeout = beginTimeout;
297     this.commitTimeout = commitTimeout;
298     this.rollbackTimeout = rollbackTimeout;
299     this.vdb = vdb;
300     logger = Trace.getLogger("org.objectweb.cjdbc.controller.RequestManager."
301         + vdb.getDatabaseName());
302   }
303
304   //
305
// Request Handling
306
//
307

308   /**
309    * Perform a read request and return the reply. Call first the scheduler, then
310    * the cache (if defined) and finally the load balancer.
311    *
312    * @param request the request to execute
313    * @return a <code>ControllerResultSet</code> value
314    * @exception SQLException if an error occurs
315    */

316   public ControllerResultSet execReadRequest(SelectRequest request)
317       throws SQLException JavaDoc
318   {
319     // Sanity check
320
if (!request.isAutoCommit())
321     { // Check that the transaction has been
322
// started
323
long tid = request.getTransactionId();
324       if (!tidLoginTable.containsKey(new Long JavaDoc(tid)))
325         throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
326     }
327
328     // If we need to parse the request, try to get the parsing from the
329
// cache.
330
// Note that if we have a cache miss but backgroundParsing has been
331
// turned
332
// on, then this call will start a ParsedThread in background.
333
if ((requiredParsingGranularity != ParsingGranularities.NO_PARSING)
334         && (!request.isParsed()))
335     {
336       if (parsingCache == null)
337         request.parse(getDatabaseSchema(), requiredParsingGranularity,
338             isCaseSensitiveParsing);
339       else
340         parsingCache.getParsingFromCache(request);
341     }
342
343     //
344
// SCHEDULER
345
//
346

347     // Get the parsing now if the request is not yet parsed. The parsing is
348
// handled by the ParsingCache that may already have parsed the request
349
// in background (if backgroundParsing is set).
350
if ((schedulerParsingranularity != ParsingGranularities.NO_PARSING)
351         && !request.isParsed())
352     {
353       if (parsingCache == null)
354         request.parse(dbs, requiredParsingGranularity, isCaseSensitiveParsing);
355       else
356         parsingCache.getParsingFromCacheAndParseIfMissing(request);
357     }
358
359     if (logger.isDebugEnabled())
360       logger.debug(Translate.get("requestmanager.read.request.schedule",
361           new String JavaDoc[]{String.valueOf(request.getId()),
362               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
363
364     // Wait for the scheduler to give us the authorization to execute
365
scheduler.scheduleReadRequest(request);
366
367     //
368
// CACHE
369
//
370

371     ControllerResultSet result = null;
372     try
373     { // Check cache if any
374
if (resultCache != null)
375       {
376         if (logger.isDebugEnabled())
377           logger.debug(Translate.get("requestmanager.read.request.cache.get",
378               new String JavaDoc[]{String.valueOf(request.getId()),
379                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
380
381         AbstractResultCacheEntry qce = resultCache.getFromCache(request, true);
382         if (qce != null)
383         {
384           result = qce.getResult();
385           if (result != null)
386           { // Cache hit !
387
if (vdb.getSQLMonitor() != null)
388               vdb.getSQLMonitor().logCacheHit(request);
389
390             scheduler.readCompleted(request);
391             return result;
392           }
393         }
394       }
395
396       //
397
// LOAD BALANCER
398
//
399

400       if (logger.isDebugEnabled())
401         logger.debug(Translate.get("requestmanager.read.request.balance",
402             new String JavaDoc[]{String.valueOf(request.getId()),
403                 request.getSQLShortForm(vdb.getSQLShortFormLength())}));
404
405       // At this point, we have a result cache miss.
406
// If we had a parsing cache miss too, wait for the parsing to be
407
// done if
408
// needed.
409
if ((loadBalancerParsingranularity != ParsingGranularities.NO_PARSING)
410           && !request.isParsed())
411       {
412         if (parsingCache == null)
413           request
414               .parse(dbs, requiredParsingGranularity, isCaseSensitiveParsing);
415         else
416           parsingCache.getParsingFromCacheAndParseIfMissing(request);
417       }
418
419       // Send the request to the load balancer
420
result = loadBalancer.execReadRequest(request, metadataCache);
421
422       //
423
// UPDATES & NOTIFICATIONS
424
//
425

426       // Update cache
427
if ((resultCache != null)
428           && (request.getCacheAbility() != RequestType.UNCACHEABLE))
429       {
430         if (logger.isDebugEnabled())
431           logger.debug(Translate.get(
432               "requestmanager.read.request.cache.update", new String JavaDoc[]{
433                   String.valueOf(request.getId()),
434                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
435
436         if (!request.isParsed()
437             && (cacheParsingranularity != ParsingGranularities.NO_PARSING))
438         { // The cache was the only one to need parsing and the request was not
439
// previously in the cache
440
if (parsingCache == null)
441             request.parse(dbs, requiredParsingGranularity,
442                 isCaseSensitiveParsing);
443           else
444             parsingCache.getParsingFromCacheAndParseIfMissing(request);
445         }
446         resultCache.addToCache(request, result);
447       }
448     }
449     catch (Exception JavaDoc failed)
450     {
451       if (resultCache != null)
452         resultCache.removeFromPendingQueries(request);
453       scheduler.readCompleted(request);
454       if (failed instanceof NoMoreBackendException)
455         throw (NoMoreBackendException) failed;
456       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
457           request.getSQLShortForm(vdb.getSQLShortFormLength()),
458           failed.getMessage()});
459       if (failed instanceof RuntimeException JavaDoc)
460         logger.warn(msg, failed);
461       else
462         logger.warn(msg);
463       if (failed instanceof SQLException JavaDoc)
464         throw (SQLException JavaDoc) failed;
465
466       throw new SQLException JavaDoc(msg);
467     }
468
469     // Notify scheduler of completion
470
scheduler.readCompleted(request);
471
472     return result;
473   }
474
475   /**
476    * Perform a write request and return the number of rows affected Call first
477    * the scheduler (if defined), then notify the cache (if defined) and finally
478    * call the load balancer.
479    *
480    * @param request the request to execute
481    * @return number of rows affected
482    * @exception SQLException if an error occurs
483    */

484   public int execWriteRequest(AbstractWriteRequest request) throws SQLException JavaDoc
485   {
486     scheduleExecWriteRequest(request);
487     int execWriteRequestResult = 0;
488     try
489     {
490       execWriteRequestResult = loadBalanceExecWriteRequest(request);
491     }
492     catch (AllBackendsFailedException e)
493     {
494       String JavaDoc msg = Translate
495           .get("requestmanager.write.request.failed.unexpected");
496       logger.fatal(msg, e);
497       throw new RuntimeException JavaDoc(msg, e);
498     }
499     updateAndNotifyExecWriteRequest(request);
500     return execWriteRequestResult;
501   }
502
503   /**
504    * Perform a write request and return the auto generated keys. Call first the
505    * scheduler (if defined), then notify the cache (if defined) and finally call
506    * the load balancer.
507    *
508    * @param request the request to execute
509    * @return auto generated keys.
510    * @exception SQLException if an error occurs
511    */

512   public ControllerResultSet execWriteRequestWithKeys(
513       AbstractWriteRequest request) throws SQLException JavaDoc
514   {
515     scheduleExecWriteRequest(request);
516     ControllerResultSet execWriteRequestWithKeysResult = null;
517     try
518     {
519       execWriteRequestWithKeysResult = loadBalanceExecWriteRequestWithKeys(request);
520     }
521     catch (AllBackendsFailedException e)
522     {
523       String JavaDoc msg = Translate
524           .get("requestmanager.write.request.keys.failed.unexpected");
525       logger.fatal(msg, e);
526       throw new RuntimeException JavaDoc(msg, e);
527     }
528     updateAndNotifyExecWriteRequest(request);
529     return execWriteRequestWithKeysResult;
530   }
531
532   /**
533    * Schedule a request for execution.
534    *
535    * @param request the request to execute
536    * @throws SQLException if an error occurs
537    */

538   public void scheduleExecWriteRequest(AbstractWriteRequest request)
539       throws SQLException JavaDoc
540   {
541     // Sanity check
542
if (!request.isAutoCommit())
543     { // Check that the transaction has been
544
// started
545
long tid = request.getTransactionId();
546       if (!tidLoginTable.containsKey(new Long JavaDoc(tid)))
547         throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
548     }
549
550     // If we need to parse the request, try to get the parsing from the
551
// cache.
552
// Note that if we have a cache miss but backgroundParsing has been
553
// turned
554
// on, then this call will start a ParsedThread in background.
555
if ((requiredParsingGranularity != ParsingGranularities.NO_PARSING)
556         && (!request.isParsed()))
557     {
558       if (parsingCache == null)
559         request.parse(getDatabaseSchema(), requiredParsingGranularity,
560             isCaseSensitiveParsing);
561       else
562         parsingCache.getParsingFromCache(request);
563     }
564
565     //
566
// SCHEDULER
567
//
568

569     // Get the parsing now if the request is not yet parsed. The parsing is
570
// handled by the ParsingCache that may already have parsed the request
571
// in background (if backgroundParsing is set).
572
if ((schedulerParsingranularity != ParsingGranularities.NO_PARSING)
573         && !request.isParsed())
574     {
575       if (parsingCache == null)
576         request.parse(getDatabaseSchema(), requiredParsingGranularity,
577             isCaseSensitiveParsing);
578       else
579         parsingCache.getParsingFromCacheAndParseIfMissing(request);
580     }
581
582     if (logger.isDebugEnabled())
583       logger.debug(Translate.get("requestmanager.write.request.schedule",
584           new String JavaDoc[]{String.valueOf(request.getId()),
585               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
586
587     // Wait for the scheduler to give us the authorization to execute
588
try
589     {
590       scheduler.scheduleWriteRequest(request);
591     }
592     catch (RollbackException e)
593     { // Something bad happened and we need to rollback this transaction
594
rollback(request.getTransactionId(), true);
595       throw new SQLException JavaDoc(e.getMessage());
596     }
597
598     // If we have a parsing cache miss, wait for the parsing to be done if
599
// needed. Note that even if the cache was the only one to require
600
// parsing,
601
// we wait for the parsing result here, because if it fails, we must not
602
// execute the query.
603
try
604     {
605       if ((requiredParsingGranularity != ParsingGranularities.NO_PARSING)
606           && !request.isParsed())
607       {
608         if (parsingCache == null)
609           request.parse(getDatabaseSchema(), requiredParsingGranularity,
610               isCaseSensitiveParsing);
611         else
612           parsingCache.getParsingFromCacheAndParseIfMissing(request);
613       }
614     }
615     catch (SQLException JavaDoc e)
616     {
617       // If the parsing fail, we must release the lock acquired ...
618
scheduler.writeCompleted(request);
619       throw e;
620     }
621   }
622
623   /**
624    * Send the given query to the load balancer. If the request fails, the
625    * scheduler is properly notified.
626    *
627    * @param request the request to execute
628    * @throws AllBackendsFailedException if all backends failed to execute the
629    * query
630    * @throws SQLException if an error occurs
631    * @return auto-generated keys
632    */

633   public ControllerResultSet loadBalanceExecWriteRequestWithKeys(
634       AbstractWriteRequest request) throws AllBackendsFailedException,
635       SQLException JavaDoc
636   {
637     if (logger.isDebugEnabled())
638       logger.debug(Translate.get("requestmanager.write.request.balance",
639           new String JavaDoc[]{String.valueOf(request.getId()),
640               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
641
642     try
643     { // Send the request to the load balancer
644
return loadBalancer.execWriteRequestWithKeys(request, metadataCache);
645     }
646     catch (Exception JavaDoc failed)
647     {
648       scheduler.writeCompleted(request);
649       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
650           request.getSQLShortForm(vdb.getSQLShortFormLength()),
651           failed.getMessage()});
652       if (failed instanceof RuntimeException JavaDoc)
653         logger.warn(msg, failed);
654       else
655         logger.warn(msg);
656       if (failed instanceof AllBackendsFailedException)
657         throw (AllBackendsFailedException) failed;
658       else
659         throw new SQLException JavaDoc(msg);
660     }
661   }
662
663   /**
664    * Send the given query to the load balancer. If the request fails, the
665    * scheduler is properly notified.
666    *
667    * @param request the request to execute
668    * @throws AllBackendsFailedException if all backends failed to execute the
669    * query
670    * @throws SQLException if an error occurs
671    * @return number of modified lines
672    */

673   public int loadBalanceExecWriteRequest(AbstractWriteRequest request)
674       throws AllBackendsFailedException, SQLException JavaDoc
675   {
676     if (logger.isDebugEnabled())
677       logger.debug(Translate.get("requestmanager.write.request.balance",
678           new String JavaDoc[]{String.valueOf(request.getId()),
679               request.getSQLShortForm(vdb.getSQLShortFormLength())}));
680
681     try
682     { // Send the request to the load balancer
683
if (request.isUpdate() && (resultCache != null))
684       { // Try the optimization if we try to update values that are already
685
// up-to-date.
686
if (!resultCache.isUpdateNecessary((UpdateRequest) request))
687           return 0;
688       }
689       return loadBalancer.execWriteRequest(request);
690     }
691     catch (Exception JavaDoc failed)
692     {
693       scheduler.writeCompleted(request);
694       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
695           request.getSQLShortForm(vdb.getSQLShortFormLength()),
696           failed.getMessage()});
697
698       // Logging
699
if (failed instanceof RuntimeException JavaDoc)
700         logger.warn(msg, failed);
701       else
702         logger.warn(msg);
703
704       // Rethrow exception
705
if (failed instanceof AllBackendsFailedException)
706         throw (AllBackendsFailedException) failed;
707       else if (failed instanceof SQLException JavaDoc)
708         throw (SQLException JavaDoc) failed;
709       else
710         throw new SQLException JavaDoc(msg);
711     }
712   }
713
714   /**
715    * Update the cache, notify the recovery log, update the database schema if
716    * needed and finally notify the scheduler. Note that if an error occurs, the
717    * scheduler is always notified.
718    *
719    * @param request the request to execute
720    * @throws SQLException if an error occurs
721    */

722   public void updateAndNotifyExecWriteRequest(AbstractWriteRequest request)
723       throws SQLException JavaDoc
724   {
725     try
726     { // Notify cache if any
727
if (resultCache != null)
728       { // Update cache
729
if (logger.isDebugEnabled())
730           logger.debug(Translate.get(
731               "requestmanager.write.request.cache.update", new String JavaDoc[]{
732                   String.valueOf(request.getId()),
733                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
734
735         resultCache.writeNotify(request);
736       }
737
738       if (metadataCache != null)
739       {
740         if (request.isAlter() || request.isDrop())
741           // Note that we also have to flush on Drop in case the same table is
742
// re-created with another schema.
743
metadataCache.flushCache();
744       }
745
746       // Log the request
747
if (recoveryLog != null)
748       {
749         if (logger.isDebugEnabled())
750           logger.debug(Translate.get("requestmanager.write.request.log",
751               new String JavaDoc[]{String.valueOf(request.getId()),
752                   request.getSQLShortForm(vdb.getSQLShortFormLength())}));
753
754         recoveryLog.logRequest(request);
755       }
756
757       // Update the schema if needed
758
if ((request.isDDL())
759           && (requiredParsingGranularity != ParsingGranularities.NO_PARSING))
760       {
761         if (request.isCreate())
762         { // Add the table to the schema
763
CreateRequest createRequest = (CreateRequest) request;
764           if (createRequest.altersDatabaseSchema())
765           {
766             if (createRequest.getDatabaseTable() != null)
767             {
768               getDatabaseSchema().addTable(
769                   ((CreateRequest) request).getDatabaseTable());
770               if (logger.isDebugEnabled())
771                 logger.debug(Translate.get("requestmanager.schema.add.table",
772                     request.getTableName()));
773             }
774             else
775               // Some other create statement that modifies the schema, force
776
// refresh
777
schemaIsDirty = true;
778           }
779         }
780         else if (request.isDrop())
781         { // Delete the table from the schema
782
if (getDatabaseSchema().removeTable(
783               getDatabaseSchema().getTable(request.getTableName())))
784             if (logger.isDebugEnabled())
785               logger.debug(Translate.get("requestmanager.schema.remove.table",
786                   request.getTableName()));
787             else
788               // Table not found, force refresh
789
schemaIsDirty = true;
790         }
791         else if (request.isAlter()
792             && (requiredParsingGranularity > ParsingGranularities.TABLE))
793         { // Add or drop the column from the table
794
AlterRequest req = (AlterRequest) request;
795           DatabaseTable alteredTable = getDatabaseSchema().getTable(
796               req.getTableName());
797           if ((alteredTable != null) && (req.getColumn() != null))
798           {
799             if (req.isDrop())
800               alteredTable.remove(req.getColumn().getName());
801             else if (req.isAdd())
802               alteredTable.addColumn(req.getColumn());
803             else
804               // Unsupported, force refresh
805
schemaIsDirty = true;
806           }
807           else
808             // Table not found, force refresh
809
schemaIsDirty = true;
810         }
811         else
812           // Unsupported, force refresh
813
schemaIsDirty = true;
814       }
815     }
816     catch (Exception JavaDoc failed)
817     {
818       scheduler.writeCompleted(request);
819       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
820           request.getSQLShortForm(vdb.getSQLShortFormLength()),
821           failed.getMessage()});
822       if (failed instanceof RuntimeException JavaDoc)
823         logger.warn(msg, failed);
824       else
825         logger.warn(msg);
826       throw new SQLException JavaDoc(msg);
827     }
828     finally
829     {
830       // Notify scheduler
831
scheduler.writeCompleted(request);
832     }
833   }
834
835   /**
836    * Call a stored procedure that returns a ResultSet.
837    *
838    * @param proc the stored procedure call
839    * @return a <code>ControllerResultSet</code> value
840    * @throws AllBackendsFailedException if all backends failed to execute the
841    * stored procedure
842    * @exception SQLException if an error occurs
843    */

844   public ControllerResultSet execReadStoredProcedure(StoredProcedure proc)
845       throws AllBackendsFailedException, SQLException JavaDoc
846   {
847     ControllerResultSet result = null;
848     try
849     {
850       scheduleStoredProcedure(proc);
851
852       if (logger.isDebugEnabled())
853         logger.debug(Translate.get("requestmanager.read.stored.procedure",
854             new String JavaDoc[]{String.valueOf(proc.getId()),
855                 proc.getSQLShortForm(vdb.getSQLShortFormLength())}));
856
857       result = loadBalanceReadStoredProcedure(proc);
858
859       flushCacheAndLogStoredProcedure(proc, true);
860
861       // Notify scheduler of completion
862
scheduler.storedProcedureCompleted(proc);
863
864       return result;
865     }
866     catch (AllBackendsFailedException e)
867     {
868       throw e;
869     }
870     catch (Exception JavaDoc failed)
871     {
872       scheduler.storedProcedureCompleted(proc);
873       String JavaDoc msg = Translate.get("requestmanager.stored.procedure.failed",
874           new String JavaDoc[]{proc.getSQLShortForm(vdb.getSQLShortFormLength()),
875               failed.getMessage()});
876       logger.warn(msg);
877       if (failed instanceof SQLException JavaDoc)
878       {
879         throw (SQLException JavaDoc) failed;
880       }
881       throw new SQLException JavaDoc(msg);
882     }
883   }
884
885   /**
886    * Call a stored procedure that performs an update.
887    *
888    * @param proc the stored procedure call
889    * @return number of rows affected
890    * @throws AllBackendsFailedException if all backends failed to execute the
891    * stored procedure
892    * @exception SQLException if an error occurs
893    */

894   public int execWriteStoredProcedure(StoredProcedure proc)
895       throws AllBackendsFailedException, SQLException JavaDoc
896   {
897     int result;
898     try
899     {
900       // Wait for the scheduler to give us the authorization to execute
901
scheduleStoredProcedure(proc);
902
903       if (logger.isDebugEnabled())
904         logger.debug(Translate.get("requestmanager.write.stored.procedure",
905             new String JavaDoc[]{String.valueOf(proc.getId()),
906                 proc.getSQLShortForm(vdb.getSQLShortFormLength())}));
907
908       result = loadBalanceWriteStoredProcedure(proc);
909
910       flushCacheAndLogStoredProcedure(proc, false);
911
912       // Notify scheduler of completion
913
scheduler.storedProcedureCompleted(proc);
914
915       return result;
916     }
917     catch (AllBackendsFailedException e)
918     {
919       throw e;
920     }
921     catch (Exception JavaDoc failed)
922     {
923       scheduler.storedProcedureCompleted(proc);
924       String JavaDoc msg = Translate.get("requestmanager.stored.procedure.failed",
925           new String JavaDoc[]{proc.getSQLShortForm(vdb.getSQLShortFormLength()),
926               failed.getMessage()});
927       logger.warn(msg);
928       throw new SQLException JavaDoc(msg);
929     }
930   }
931
932   /**
933    * This method does some sanity check on the given stored procedure and then
934    * tries to schedule it. Note that it is more likely that on a stored
935    * procedure the scheduler will lock in write the entire database as it does
936    * not know which tables are accessed by the procedure.
937    *
938    * @param proc the stored procedure to schedule
939    * @throws SQLException if an error occurs
940    */

941   public void scheduleStoredProcedure(StoredProcedure proc) throws SQLException JavaDoc
942   {
943     // Sanity check
944
if (!proc.isAutoCommit())
945     { // Check that the transaction has been
946
// started
947
long tid = proc.getTransactionId();
948       if (!tidLoginTable.containsKey(new Long JavaDoc(tid)))
949         throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
950     }
951
952     //
953
// SCHEDULER
954
//
955

956     // Wait for the scheduler to give us the authorization to execute
957
try
958     {
959       scheduler.scheduleStoredProcedure(proc);
960     }
961     catch (RollbackException e)
962     { // Something bad happened and we need to rollback this transaction
963
rollback(proc.getTransactionId(), true);
964       throw new SQLException JavaDoc(e.getMessage());
965     }
966   }
967
968   /**
969    * Execute a read stored procedure on the load balancer. Note that we flush
970    * the cache before calling the load balancer.
971    *
972    * @param proc the stored procedure to call
973    * @return the corresponding ControllerResultSet
974    * @throws SQLException if an error occurs
975    * @throws AllBackendsFailedException if all backends failed to execute the
976    * stored procedure
977    */

978   public ControllerResultSet loadBalanceReadStoredProcedure(StoredProcedure proc)
979       throws SQLException JavaDoc, AllBackendsFailedException
980   {
981     ControllerResultSet result;
982     //
983
// CACHE
984
//
985

986     // Cache is always flushed unless the user has explicitely set the
987
// connection to read-only mode in which case we assume that the
988
// users deliberately forces the cache not to be flushed when calling
989
// this stored procedure.
990
if ((resultCache != null) && (!proc.isReadOnly()))
991       resultCache.flushCache();
992
993     //
994
// LOAD BALANCER
995
//
996

997     // Send the request to the load balancer
998
if (proc.isReadOnly())
999       result = loadBalancer
1000          .execReadOnlyReadStoredProcedure(proc, metadataCache);
1001    else
1002      result = loadBalancer.execReadStoredProcedure(proc, metadataCache);
1003    return result;
1004  }
1005
1006  /**
1007   * Execute a write stored procedure on the load balancer. Note that we flush
1008   * the cache before calling the load balancer.
1009   *
1010   * @param proc the stored procedure to call
1011   * @return the number of updated rows
1012   * @throws SQLException if an error occurs
1013   * @throws AllBackendsFailedException if all backends failed to execute the
1014   * stored procedure
1015   */

1016  public int loadBalanceWriteStoredProcedure(StoredProcedure proc)
1017      throws AllBackendsFailedException, SQLException JavaDoc
1018  {
1019    int result;
1020    //
1021
// CACHE
1022
//
1023

1024    // Flush cache (if any) before as we don't properly lock the tables
1025
if (resultCache != null)
1026      resultCache.flushCache();
1027
1028    //
1029
// LOAD BALANCER
1030
//
1031

1032    // Send the request to the load balancer
1033
result = loadBalancer.execWriteStoredProcedure(proc);
1034    return result;
1035  }
1036
1037  /**
1038   * Flush the cache and log the stored procedure execution in the recovery log.
1039   *
1040   * @param proc the stored procedure to log
1041   * @param isRead true is this is an execReadStoredProcedure, false for an
1042   * execWriteStoredProcedure
1043   */

1044  public void flushCacheAndLogStoredProcedure(StoredProcedure proc,
1045      boolean isRead)
1046  {
1047    // Schema might have been updated, force refresh
1048
schemaIsDirty = true;
1049
1050    //
1051
// CACHE
1052
//
1053

1054    // Flush cache (if any) after for consistency (we don't know what has been
1055
// modified by the stored procedure)
1056
if (resultCache != null)
1057      resultCache.flushCache();
1058
1059    if (metadataCache != null)
1060      metadataCache.flushCache();
1061
1062    //
1063
// RECOVERY LOG
1064
//
1065

1066    if (recoveryLog != null)
1067      recoveryLog.logRequest(proc, isRead);
1068  }
1069
1070  //
1071
// Transaction management
1072
//
1073

1074  /**
1075   * Begin a new transaction and return the corresponding transaction
1076   * identifier. This method is called from the driver when setAutoCommit(false)
1077   * is called.
1078   * <p>
1079   * Note that the transaction begin is not logged in the recovery log by this
1080   * method, you will have to call logLazyTransactionBegin.
1081   *
1082   * @param login the login used by the connection
1083   * @return int a unique transaction identifier
1084   * @throws SQLException if an error occurs
1085   * @see #logLazyTransactionBegin(long)
1086   */

1087  public long begin(String JavaDoc login) throws SQLException JavaDoc
1088  {
1089    try
1090    {
1091      TransactionMarkerMetaData tm = new TransactionMarkerMetaData(0,
1092          beginTimeout, login);
1093
1094      // Wait for the scheduler to give us the authorization to execute
1095
long tid = scheduler.begin(tm);
1096      tm.setTransactionId(tid);
1097
1098      if (logger.isDebugEnabled())
1099        logger.debug(Translate.get("transaction.begin", String.valueOf(tid)));
1100
1101      try
1102      {
1103        // Send to load balancer
1104
loadBalancer.begin(tm);
1105      }
1106      catch (SQLException JavaDoc e)
1107      {
1108        throw e;
1109      }
1110      finally
1111      {
1112        // Notify scheduler for completion in any case
1113
scheduler.beginCompleted(tid);
1114      }
1115
1116      tidLoginTable.put(new Long JavaDoc(tid), tm);
1117      return tid;
1118    }
1119    catch (RuntimeException JavaDoc e)
1120    {
1121      logger.fatal(Translate
1122          .get("fatal.runtime.exception.requestmanager.begin"), e);
1123      throw new SQLException JavaDoc(e.getMessage());
1124    }
1125  }
1126
1127  /**
1128   * Log the begin of a transaction that is started lazily. In fact, we just log
1129   * the begin when we execute the first write request in a transaction to
1130   * prevent logging begin/commit for read-only transactions. This also prevents
1131   * a problem with backends that are disabled with a checkpoint when no request
1132   * has been played in the transaction but the begin statement has already been
1133   * logged. In that case, the transaction would not be properly replayed at
1134   * restore time.
1135   *
1136   * @param transactionId the transaction id begin to log
1137   * @throws SQLException if an error occurs
1138   */

1139  public void logLazyTransactionBegin(long transactionId) throws SQLException JavaDoc
1140  {
1141    try
1142    {
1143      Long JavaDoc tid = new Long JavaDoc(transactionId);
1144      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1145
1146      if (logger.isDebugEnabled())
1147        logger.debug(Translate.get("transaction.begin.log", String
1148            .valueOf(transactionId)));
1149
1150      // Log the begin
1151
if (recoveryLog != null)
1152        recoveryLog.logBegin(tm);
1153    }
1154    catch (RuntimeException JavaDoc e)
1155    {
1156      logger.fatal(Translate
1157          .get("fatal.runtime.exception.requestmanager.begin.log"), e);
1158      throw new SQLException JavaDoc(e.getMessage());
1159    }
1160  }
1161
1162  /**
1163   * Abort a transaction that has been started but in which no query was
1164   * executed. As we use lazy transaction begin, there is no need to rollback
1165   * such transaction but just to cleanup the metadata associated with this not
1166   * effectively started transaction.
1167   *
1168   * @param transactionId id of the transaction to abort
1169   * @param logAbort true if the abort (in fact rollback) should be logged in
1170   * the recovery log
1171   * @throws SQLException if an error occurs
1172   */

1173  public void abort(long transactionId, boolean logAbort) throws SQLException JavaDoc
1174  {
1175    try
1176    {
1177      Long JavaDoc tid = new Long JavaDoc(transactionId);
1178      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1179
1180      if (logger.isDebugEnabled())
1181        logger.debug(Translate.get("transaction.abort", String
1182            .valueOf(transactionId)));
1183
1184      try
1185      {
1186        // Notify the scheduler to abort which is the same as a rollback
1187
// from a
1188
// scheduler point of view.
1189
scheduler.rollback(tm);
1190
1191        // Notify the recovery log manager
1192
if (logAbort && (recoveryLog != null))
1193        {
1194          recoveryLog.logAbort(tm);
1195        }
1196      }
1197      catch (SQLException JavaDoc e)
1198      {
1199        throw e;
1200      }
1201      finally
1202      {
1203        // Notify scheduler for completion
1204
scheduler.rollbackCompleted(transactionId);
1205
1206        // Scheduler will add the rollback in the total order queue if any, so
1207
// we have to remove it from the queue to prevent blocking subsequent
1208
// queries.
1209
loadBalancer.removeHeadFromAndNotifyTotalOrderQueue();
1210
1211        completeTransaction(tid);
1212      }
1213    }
1214    catch (RuntimeException JavaDoc e)
1215    {
1216      logger.fatal(Translate
1217          .get("fatal.runtime.exception.requestmanager.abort"), e);
1218      throw new SQLException JavaDoc(e.getMessage());
1219    }
1220  }
1221
1222  /**
1223   * Get the TransactionMarkerMetaData for the given transaction id.
1224   *
1225   * @param tid transaction id
1226   * @return the TransactionMarkerMetaData
1227   * @throws SQLException if no marker has been found for this transaction
1228   */

1229  public TransactionMarkerMetaData getTransactionMarker(Long JavaDoc tid)
1230      throws SQLException JavaDoc
1231  {
1232    TransactionMarkerMetaData tm = (TransactionMarkerMetaData) tidLoginTable
1233        .get(tid);
1234
1235    if (tm == null)
1236      throw new SQLException JavaDoc(Translate.get("transaction.marker.not.found", ""
1237          + tid));
1238
1239    tm.setTimeout(commitTimeout);
1240    return tm;
1241  }
1242
1243  /**
1244   * Complete the transaction by removing it from the tidLoginTable.
1245   *
1246   * @param tid transaction id
1247   */

1248  public void completeTransaction(Long JavaDoc tid)
1249  {
1250    tidLoginTable.remove(tid);
1251    tidSavepoints.remove(tid);
1252  }
1253
1254  /**
1255   * Commit a transaction given its id.
1256   *
1257   * @param transactionId the transaction id
1258   * @param logCommit true if the commit should be logged in the recovery log
1259   * @throws SQLException if an error occurs
1260   */

1261  public void commit(long transactionId, boolean logCommit) throws SQLException JavaDoc
1262  {
1263    try
1264    {
1265      Long JavaDoc tid = new Long JavaDoc(transactionId);
1266      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1267
1268      // Wait for the scheduler to give us the authorization to execute
1269
scheduler.commit(tm);
1270
1271      if (logger.isDebugEnabled())
1272        logger.debug(Translate.get("transaction.commit", String.valueOf(tid)));
1273
1274      try
1275      {
1276        // Send to load balancer
1277
loadBalancer.commit(tm);
1278
1279        // Notify the cache
1280
if (resultCache != null)
1281          resultCache.commit(tm.getTransactionId());
1282
1283        // Notify the recovery log manager
1284
if (logCommit && (recoveryLog != null))
1285          recoveryLog.logCommit(tm);
1286      }
1287      catch (SQLException JavaDoc e)
1288      {
1289        throw e;
1290      }
1291      catch (AllBackendsFailedException e)
1292      {
1293        String JavaDoc msg = "All backends failed to commit transaction "
1294            + transactionId + " (" + e + ")";
1295        logger.error(msg);
1296        throw new SQLException JavaDoc(msg);
1297      }
1298      finally
1299      {
1300        // Notify scheduler for completion
1301
scheduler.commitCompleted(transactionId);
1302
1303        completeTransaction(tid);
1304      }
1305    }
1306    catch (RuntimeException JavaDoc e)
1307    {
1308      logger.fatal(Translate
1309          .get("fatal.runtime.exception.requestmanager.commit"), e);
1310      throw new SQLException JavaDoc(e.getMessage());
1311    }
1312  }
1313
1314  /**
1315   * Rollback a transaction given its id.
1316   *
1317   * @param transactionId the transaction id
1318   * @param logRollback true if the rollback should be logged in the recovery
1319   * log
1320   * @throws SQLException if an error occurs
1321   */

1322  public void rollback(long transactionId, boolean logRollback)
1323      throws SQLException JavaDoc
1324  {
1325    try
1326    {
1327      Long JavaDoc tid = new Long JavaDoc(transactionId);
1328      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1329
1330      // Wait for the scheduler to give us the authorization to execute
1331
scheduler.rollback(tm);
1332
1333      if (logger.isDebugEnabled())
1334        logger.debug(Translate.get("transaction.rollback", String
1335            .valueOf(transactionId)));
1336
1337      try
1338      {
1339        // Send to load balancer
1340
loadBalancer.rollback(tm);
1341
1342        // Send to cache
1343
if (this.resultCache != null)
1344          resultCache.rollback(transactionId);
1345
1346        // Notify the recovery log manager
1347
if (logRollback && (recoveryLog != null))
1348        {
1349          recoveryLog.logRollback(tm);
1350        }
1351      }
1352      catch (SQLException JavaDoc e)
1353      {
1354        throw e;
1355      }
1356      catch (AllBackendsFailedException e)
1357      {
1358        String JavaDoc msg = Translate.get("requestmanager.rollback.failed.all",
1359            new String JavaDoc[]{String.valueOf(transactionId), e.getMessage()});
1360        logger.error(msg);
1361        throw new SQLException JavaDoc(msg);
1362      }
1363      finally
1364      {
1365        // Notify scheduler for completion
1366
scheduler.rollbackCompleted(transactionId);
1367
1368        completeTransaction(tid);
1369      }
1370    }
1371    catch (RuntimeException JavaDoc e)
1372    {
1373      logger.fatal(Translate
1374          .get("fatal.runtime.exception.requestmanager.rollback"), e);
1375      throw new SQLException JavaDoc(e.getMessage());
1376    }
1377  }
1378
1379  /**
1380   * Rollback a transaction given its id to a savepoint given its name.
1381   *
1382   * @param transactionId the transaction id
1383   * @param savepointName the name of the savepoint
1384   * @throws SQLException if an error occurs
1385   */

1386  public void rollback(long transactionId, String JavaDoc savepointName)
1387      throws SQLException JavaDoc
1388  {
1389    try
1390    {
1391      Long JavaDoc tid = new Long JavaDoc(transactionId);
1392      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1393
1394      // Check that a savepoint with given name has been set
1395
if (!hasSavepoint(tid, savepointName))
1396        throw new SQLException JavaDoc(Translate.get("transaction.savepoint.not.found",
1397            new String JavaDoc[]{savepointName, String.valueOf(transactionId)}));
1398
1399      // Wait for the scheduler to give us the authorization to execute
1400
scheduler.rollback(tm, savepointName);
1401
1402      if (logger.isDebugEnabled())
1403        logger.debug(Translate.get("transaction.rollbacksavepoint",
1404            new String JavaDoc[]{String.valueOf(transactionId), savepointName}));
1405      try
1406      {
1407        // Send to loadbalancer
1408
loadBalancer.rollback(tm, savepointName);
1409
1410        // Notify the recovery log manager
1411
if (recoveryLog != null)
1412        {
1413          recoveryLog.logRollback(tm, savepointName);
1414        }
1415      }
1416      catch (AllBackendsFailedException e)
1417      {
1418        String JavaDoc msg = Translate.get(
1419            "requestmanager.rollbackavepoint.failed.all", new String JavaDoc[]{
1420                String.valueOf(transactionId), savepointName, e.getMessage()});
1421        logger.error(msg);
1422        throw new SQLException JavaDoc(msg);
1423      }
1424      finally
1425      {
1426        // Notify scheduler for completion
1427
scheduler.savepointCompleted(transactionId);
1428
1429        // Remove all the savepoints set after the savepoint we rollback to
1430
removeSavepoints(tid, savepointName);
1431      }
1432    }
1433    catch (RuntimeException JavaDoc e)
1434    {
1435      logger.fatal(Translate
1436          .get("fatal.runtime.exception.requestmanager.rollbacksavepoint"), e);
1437      throw new SQLException JavaDoc(e.getMessage());
1438    }
1439  }
1440
1441  /**
1442   * Sets a unnamed savepoint to a transaction given its id.
1443   *
1444   * @param transactionId the transaction id
1445   * @return the generated id of the new savepoint
1446   * @throws SQLException if an error occurs
1447   */

1448  public int setSavepoint(long transactionId) throws SQLException JavaDoc
1449  {
1450    try
1451    {
1452      Long JavaDoc tid = new Long JavaDoc(transactionId);
1453      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1454
1455      // Wait for the scheduler to give us the authorization to execute
1456
int savepointId = scheduler.setSavepoint(tm);
1457      String JavaDoc savepointName = String.valueOf(savepointId);
1458
1459      if (logger.isDebugEnabled())
1460        logger.debug(Translate.get("transaction.setsavepoint", new String JavaDoc[]{
1461            savepointName, String.valueOf(transactionId)}));
1462
1463      try
1464      {
1465        // Send to loadbalancer
1466
loadBalancer.setSavepoint(tm, savepointName);
1467
1468        // Notify the recovery log manager
1469
if (recoveryLog != null)
1470        {
1471          recoveryLog.logSetSavepoint(tm, savepointName);
1472        }
1473      }
1474      catch (AllBackendsFailedException e)
1475      {
1476        String JavaDoc msg = Translate.get("requestmanager.setsavepoint.failed.all",
1477            new String JavaDoc[]{savepointName, String.valueOf(transactionId),
1478                e.getMessage()});
1479        logger.error(msg);
1480        throw new SQLException JavaDoc(msg);
1481      }
1482      finally
1483      {
1484        // Notify scheduler for completion
1485
scheduler.savepointCompleted(transactionId);
1486      }
1487
1488      // Add savepoint name to list of savepoints for this transaction
1489
addSavepoint(tid, savepointName);
1490      return savepointId;
1491    }
1492    catch (RuntimeException JavaDoc e)
1493    {
1494      logger.fatal(Translate
1495          .get("fatal.runtime.exception.requestmanager.setsavepoint"), e);
1496      throw new SQLException JavaDoc(e.getMessage());
1497    }
1498  }
1499
1500  /**
1501   * Sets a savepoint given its desired name to a transaction given its id.
1502   *
1503   * @param transactionId the transaction id
1504   * @param name the desired name of the savepoint
1505   * @throws SQLException if an error occurs
1506   */

1507  public void setSavepoint(long transactionId, String JavaDoc name) throws SQLException JavaDoc
1508  {
1509    try
1510    {
1511      Long JavaDoc tid = new Long JavaDoc(transactionId);
1512      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1513
1514      // Wait for the scheduler to give us the authorization to execute
1515
scheduler.setSavepoint(tm, name);
1516
1517      if (logger.isDebugEnabled())
1518        logger.debug(Translate.get("transaction.setsavepoint", new String JavaDoc[]{
1519            name, String.valueOf(transactionId)}));
1520
1521      try
1522      {
1523        // Send to loadbalancer
1524
loadBalancer.setSavepoint(tm, name);
1525
1526        // Notify the recovery log manager
1527
if (recoveryLog != null)
1528        {
1529          recoveryLog.logSetSavepoint(tm, name);
1530        }
1531      }
1532      catch (AllBackendsFailedException e)
1533      {
1534        String JavaDoc msg = Translate.get("requestmanager.setsavepoint.failed.all",
1535            new String JavaDoc[]{name, String.valueOf(transactionId), e.getMessage()});
1536        logger.error(msg);
1537        throw new SQLException JavaDoc(msg);
1538      }
1539      finally
1540      {
1541        // Notify scheduler for completion
1542
scheduler.savepointCompleted(transactionId);
1543      }
1544
1545      // Add savepoint name to list of savepoints for this transaction
1546
addSavepoint(tid, name);
1547    }
1548    catch (RuntimeException JavaDoc e)
1549    {
1550      logger.fatal(Translate
1551          .get("fatal.runtime.exception.requestmanager.setsavepoint"), e);
1552      throw new SQLException JavaDoc(e.getMessage());
1553    }
1554  }
1555
1556  /**
1557   * Releases a savepoint given its name from a transaction given its id.
1558   *
1559   * @param transactionId the transaction id
1560   * @param name the name of the savepoint
1561   * @exception SQLException if an error occurs
1562   */

1563  public void releaseSavepoint(long transactionId, String JavaDoc name)
1564      throws SQLException JavaDoc
1565  {
1566    try
1567    {
1568      Long JavaDoc tid = new Long JavaDoc(transactionId);
1569      TransactionMarkerMetaData tm = getTransactionMarker(tid);
1570
1571      // Check that a savepoint with given name has been set
1572
if (!hasSavepoint(tid, name))
1573        throw new SQLException JavaDoc(Translate.get("transaction.savepoint.not.found",
1574            new String JavaDoc[]{name, String.valueOf(transactionId)}));
1575
1576      // Wait for the scheduler to give us the authorization to execute
1577
scheduler.releaseSavepoint(tm, name);
1578
1579      if (logger.isDebugEnabled())
1580        logger.debug(Translate.get("transaction.releasesavepoint",
1581            new String JavaDoc[]{name, String.valueOf(transactionId)}));
1582
1583      try
1584      {
1585        // Send to loadbalancer
1586
loadBalancer.releaseSavepoint(tm, name);
1587
1588        // Notify the recovery log manager
1589
if (recoveryLog != null)
1590        {
1591          recoveryLog.logReleaseSavepoint(tm, name);
1592        }
1593      }
1594      catch (AllBackendsFailedException e)
1595      {
1596        String JavaDoc msg = Translate.get(
1597            "requestmanager.releasesavepoint.failed.all", new String JavaDoc[]{name,
1598                String.valueOf(transactionId), e.getMessage()});
1599        logger.error(msg);
1600        throw new SQLException JavaDoc(msg);
1601      }
1602      finally
1603      {
1604        // Notify scheduler for completion
1605
scheduler.savepointCompleted(transactionId);
1606
1607        // Remove savepoint for the transaction
1608
removeSavepoint(tid, name);
1609      }
1610    }
1611    catch (RuntimeException JavaDoc e)
1612    {
1613      logger.fatal(Translate
1614          .get("fatal.runtime.exception.requestmanager.releasesavepoint"), e);
1615      throw new SQLException JavaDoc(e.getMessage());
1616    }
1617  }
1618
1619  /**
1620   * Adds a given savepoint to a given transaction
1621   *
1622   * @param tid transaction id
1623   * @param savepointName name of the savepoint
1624   */

1625  public void addSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
1626  {
1627    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
1628    if (savepoints == null)
1629    { // Lazy list creation
1630
savepoints = new LinkedList JavaDoc();
1631      tidSavepoints.put(tid, savepoints);
1632    }
1633
1634    savepoints.addLast(savepointName);
1635  }
1636
1637  /**
1638   * Removes a given savepoint for a given transaction
1639   *
1640   * @param tid transaction id
1641   * @param savepointName name of the savepoint
1642   */

1643  public void removeSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
1644  {
1645    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
1646    if (savepoints == null)
1647      logger.error("No savepoints found for transaction " + tid);
1648    else
1649      savepoints.remove(savepointName);
1650  }
1651
1652  /**
1653   * Removes all the savepoints set after a given savepoint for a given
1654   * transaction
1655   *
1656   * @param tid transaction id
1657   * @param savepointName name of the savepoint
1658   */

1659  public void removeSavepoints(Long JavaDoc tid, String JavaDoc savepointName)
1660  {
1661    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
1662    if (savepoints == null)
1663    {
1664      logger.error("No savepoints found for transaction " + tid);
1665      return;
1666    }
1667
1668    int index = savepoints.indexOf(savepointName);
1669    if (index == -1)
1670      logger.error("No savepoint with name " + savepointName + " found "
1671          + "for transaction " + tid);
1672    else
1673      savepoints.subList(index, savepoints.size()).clear();
1674  }
1675
1676  /**
1677   * Check if a given savepoint has been set for a given transaction
1678   *
1679   * @param tid transaction id
1680   * @param savepointName name of the savepoint
1681   * @return true if the savepoint exists
1682   */

1683  public boolean hasSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
1684  {
1685    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
1686    if (savepoints == null)
1687      return false;
1688
1689    return savepoints.contains(savepointName);
1690  }
1691
1692  //
1693
// Database Backends management
1694
//
1695

1696  /**
1697   * Enable a backend that has been previously added to this virtual database
1698   * and that is in the disabled state.
1699   * <p>
1700   * The backend is enabled without further check.
1701   * <p>
1702   * The enableBackend method of the load balancer is called.
1703   *
1704   * @param db The database backend to enable
1705   * @throws SQLException if an error occurs
1706   */

1707  public void enableBackend(DatabaseBackend db) throws SQLException JavaDoc
1708  {
1709    db
1710        .setSchemaIsNeededByVdb(requiredParsingGranularity != ParsingGranularities.NO_PARSING);
1711    loadBalancer.enableBackend(db, true);
1712    logger.info(Translate.get("backend.state.enabled", db.getName()));
1713  }
1714
1715  /**
1716   * The backend must have been previously added to this virtual database and be
1717   * in the disabled state.
1718   * <p>
1719   * All the queries since the given checkpoint are played and the backend state
1720   * is set to enabled when it is completely synchronized.
1721   * <p>
1722   * Note that the job is performed in background by a
1723   * <code>RecoverThread</code>. You can synchronize on thread termination if
1724   * you need to wait for completion of this task and listen to JMX
1725   * notifications for the success status.
1726   *
1727   * @param db The database backend to enable
1728   * @param checkpointName The checkpoint name to restart from
1729   * @return the JDBC reocver thread synchronizing the backend
1730   * @throws SQLException if an error occurs
1731   */

1732  public RecoverThread enableBackendFromCheckpoint(DatabaseBackend db,
1733      String JavaDoc checkpointName) throws SQLException JavaDoc
1734  {
1735    // Sanity checks
1736
if (recoveryLog == null)
1737    {
1738      String JavaDoc msg = Translate.get(
1739          "recovery.restore.checkpoint.failed.cause.null", checkpointName);
1740      logger.error(msg);
1741      throw new SQLException JavaDoc(msg);
1742    }
1743
1744    if (db.getStateValue() != BackendState.DISABLED)
1745      throw new SQLException JavaDoc(Translate.get(
1746          "recovery.restore.backend.state.invalid", db.getName()));
1747
1748    db
1749        .setSchemaIsNeededByVdb(requiredParsingGranularity != ParsingGranularities.NO_PARSING);
1750
1751    RecoverThread recoverThread = new RecoverThread(scheduler, recoveryLog, db,
1752        loadBalancer, checkpointName);
1753
1754    // fire the thread and forget
1755
// exception will be reported in a jmx notification on the backend.
1756
recoverThread.start();
1757    return recoverThread;
1758  }
1759
1760  /**
1761   * Disable a backend that is currently enabled on this virtual database.
1762   * <p>
1763   * The backend is disabled without further check.
1764   * <p>
1765   * The load balancer disabled method is called on the specified backend.
1766   *
1767   * @param db The database backend to disable
1768   * @throws SQLException if an error occurs
1769   */

1770  public void disableBackend(DatabaseBackend db) throws SQLException JavaDoc
1771  {
1772    if (db.isReadEnabled() || db.isWriteEnabled())
1773    {
1774      loadBalancer.disableBackend(db);
1775      logger.info(Translate.get("backend.state.disabled", db.getName()));
1776    }
1777    else
1778    {
1779      throw new SQLException JavaDoc(Translate.get("backend.already.disabled", db
1780          .getName()));
1781    }
1782  }
1783
1784  /**
1785   * The backend must belong to this virtual database and be in the enabled
1786   * state.
1787   * <p>
1788   * The backend is disabled once all the pending write queries are executed. A
1789   * checkpoint is inserted in the recovery log.
1790   *
1791   * @param db The database backend to enable
1792   * @param checkpointName The checkpoint name to restart from
1793   * @throws SQLException if an error occurs
1794   */

1795  public void disableBackendForCheckpoint(DatabaseBackend db,
1796      String JavaDoc checkpointName) throws SQLException JavaDoc
1797  {
1798    // Sanity checks
1799
if (recoveryLog == null)
1800    {
1801      String JavaDoc msg = Translate.get("recovery.store.checkpoint.failed.cause.null",
1802          checkpointName);
1803      logger.error(msg);
1804      throw new SQLException JavaDoc(msg);
1805    }
1806
1807    // Wait for all pending writes to finish
1808
logger.info(Translate.get("requestmanager.wait.pending.writes"));
1809    scheduler.suspendWrites();
1810
1811    // Store checkpoint
1812
recoveryLog.storeCheckpoint(checkpointName);
1813    logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
1814
1815    // Signal the backend should not begin any new transaction
1816
db.setState(BackendState.DISABLING);
1817    logger.info(Translate.get("backend.state.disabling", db.getName()));
1818
1819    // Resume writes
1820
logger.info(Translate.get("requestmanager.resume.pending.writes"));
1821    scheduler.resumeWrites();
1822
1823    // Wait for all current transactions on the backend to finish
1824
db.waitForAllTransactionsToComplete();
1825
1826    // Now we can safely disable the backend
1827
db.setLastKnownCheckpoint(checkpointName);
1828    loadBalancer.disableBackend(db);
1829    logger.info(Translate.get("backend.state.disabled", db.getName()));
1830  }
1831
1832  /**
1833   * Disable a list of backends. Only to store only one checkpoint, and to
1834   * disable all the backends at the same time so the the system is in a
1835   * coherent state. Consider only the backends that were enabled. The others
1836   * are left in the state they were before.
1837   *
1838   * @param backendsArrayList backends to disable
1839   * @param checkpointName to store
1840   * @throws SQLException if an error occurs
1841   */

1842  public void disableBackendsForCheckpoint(ArrayList JavaDoc backendsArrayList,
1843      String JavaDoc checkpointName) throws SQLException JavaDoc
1844  {
1845    // Sanity checks
1846
if (recoveryLog == null)
1847    {
1848      String JavaDoc msg = Translate.get("recovery.store.checkpoint.failed.cause.null",
1849          checkpointName);
1850      logger.error(msg);
1851      throw new SQLException JavaDoc(msg);
1852    }
1853
1854    // Wait for all pending writes to finish
1855
logger.info(Translate.get("requestmanager.wait.pending.writes"));
1856    scheduler.suspendWrites();
1857
1858    // Store checkpoint
1859
recoveryLog.storeCheckpoint(checkpointName);
1860    logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
1861
1862    // Copy the list and consider only the backends that are enabled
1863
DatabaseBackend db;
1864    ArrayList JavaDoc backendList = (ArrayList JavaDoc) backendsArrayList.clone();
1865    for (int i = 0; i < backendList.size(); i++)
1866    {
1867      db = (DatabaseBackend) backendList.get(i);
1868      if (!db.isWriteEnabled())
1869        backendList.remove(i);
1870    }
1871
1872    // Signal all backends that they should not begin any new transaction
1873
int size = backendList.size();
1874    for (int i = 0; i < size; i++)
1875    {
1876      db = (DatabaseBackend) backendList.get(i);
1877      db.setState(BackendState.DISABLING);
1878      logger.info(Translate.get("backend.state.disabling", db.getName()));
1879    }
1880
1881    // Resume writes
1882
logger.info(Translate.get("requestmanager.resume.pending.writes"));
1883    scheduler.resumeWrites();
1884
1885    // Wait for all current transactions on backends to finish
1886
for (int i = 0; i < size; i++)
1887    {
1888      db = (DatabaseBackend) backendList.get(i);
1889      db.waitForAllTransactionsToComplete();
1890    }
1891
1892    // Now we can safely disable all backends
1893
for (int i = 0; i < size; i++)
1894    {
1895      db = (DatabaseBackend) backendList.get(i);
1896      db.setLastKnownCheckpoint(checkpointName);
1897      loadBalancer.disableBackend(db);
1898      logger.info(Translate.get("backend.state.disabled", db.getName()));
1899    }
1900  }
1901
1902  /**
1903   * Create a backup from the content of a backend.
1904   *
1905   * @param backend the target backend to backup
1906   * @param login the login to use to connect to the database for the backup
1907   * operation
1908   * @param password the password to use to connect to the database for the
1909   * backup operation
1910   * @param dumpName the name of the dump to create
1911   * @param backuperName the logical name of the backuper to use
1912   * @param path the path where to store the dump
1913   * @param tables the list of tables to backup, null means all tables
1914   * @throws SQLException if the backup fails
1915   */

1916  public void backupBackend(DatabaseBackend backend, String JavaDoc login,
1917      String JavaDoc password, String JavaDoc dumpName, String JavaDoc backuperName, String JavaDoc path,
1918      ArrayList JavaDoc tables) throws SQLException JavaDoc
1919  {
1920    Backuper backuper = backupManager.getBackuperByName(backuperName);
1921    if (backuper == null)
1922      throw new SQLException JavaDoc("No backuper named " + backuperName
1923          + " was found.");
1924
1925    boolean enableAfter = false;
1926    String JavaDoc checkpointName = null;
1927    if (backend.isReadEnabled())
1928    { // Disable backend and store checkpoint
1929
checkpointName = "cp_for_" + dumpName + "_"
1930          + new Date JavaDoc(System.currentTimeMillis()).toString();
1931      disableBackendForCheckpoint(backend, checkpointName);
1932      logger.info(Translate.get("backend.state.disabled", backend.getName()));
1933      enableAfter = true;
1934    }
1935    else
1936    {
1937      if (backend.getLastKnownCheckpoint() == null)
1938      {
1939        throw new SQLException JavaDoc(Translate.get(
1940            "controller.backup.no.lastknown.checkpoint", backend.getName()));
1941      }
1942    }
1943    // else backend is already disabled, no checkpoint is stored here, it should
1944
// have been done at disable time.
1945

1946    try
1947    {
1948      logger.info(Translate.get("controller.backup.start", backend.getName()));
1949
1950      // Sanity check to be sure that no pending request is in the pipe for the
1951
// backend
1952
Vector JavaDoc pending = backend.getPendingRequests();
1953      if (pending.size() != 0)
1954      {
1955        if (logger.isDebugEnabled())
1956        {
1957          for (int i = 0; i < pending.size(); i++)
1958            logger.debug("Pending:" + pending.get(i).toString());
1959
1960          logger.debug("Pending Requests:"
1961              + backend.getPendingRequests().size());
1962          logger.debug("Read enabled:" + backend.isReadEnabled());
1963          logger.debug("Write enabled:" + backend.isWriteEnabled());
1964        }
1965        throw new BackupException(Translate.get("backend.not.ready.for.backup"));
1966      }
1967
1968      // Let's start the backup
1969
backend.setState(BackendState.BACKUPING);
1970      Date JavaDoc dumpDate = backuper.backup(backend, login, password, dumpName, path,
1971          tables);
1972      if (recoveryLog != null)
1973      {
1974        DumpInfo dump = new DumpInfo(dumpName, dumpDate.toString(), path,
1975            backuper.getDumpFormat(), backend.getLastKnownCheckpoint(), backend
1976                .getName(), "*");
1977        recoveryLog.storeDump(dump);
1978      }
1979
1980      // Notify that a new dump is available
1981
backend.notifyJmx(CjdbcNotificationList.VIRTUALDATABASE_NEW_DUMP_LIST);
1982
1983      // Swtich from BACKUPING to DISABLED STATE
1984
backend.setState(BackendState.DISABLED);
1985    }
1986    catch (BackupException be)
1987    {
1988      // Backend is now in an unknown state
1989
backend.setState(BackendState.UNKNOWN);
1990      logger.error(Translate.get("controller.backup.failed"), be);
1991      throw new SQLException JavaDoc(be.getMessage());
1992    }
1993
1994    logger.info(Translate.get("controller.backup.complete", backend.getName()));
1995
1996    if (enableAfter)
1997    {
1998      RecoverThread thread = enableBackendFromCheckpoint(backend,
1999          checkpointName);
2000      try
2001      {
2002        thread.join();
2003      }
2004      catch (InterruptedException JavaDoc e)
2005      {
2006        logger.error("Recovery thread has been interrupted", e);
2007      }
2008    }
2009
2010  }
2011
2012  /**
2013   * Restore a dump on a specific backend. The proper Backuper is retrieved
2014   * automatically according to the dump format stored in the recovery log dump
2015   * table.
2016   * <p>
2017   * This method disables the backend and leave it disabled after recovery
2018   * process. The user has to call the <code>enableBackendFromCheckpoint</code>
2019   * after this.
2020   *
2021   * @param backend the backend to restore
2022   * @param login the login to use to connect to the database for the restore
2023   * operation
2024   * @param password the password to use to connect to the database for the
2025   * restore operation
2026   * @param dumpName the name of the dump to restore
2027   * @param tables the list of tables to restore, null means all tables
2028   * @throws BackupException if the restore operation failed
2029   */

2030  public void restoreBackendFromBackupCheckpoint(DatabaseBackend backend,
2031      String JavaDoc login, String JavaDoc password, String JavaDoc dumpName, ArrayList JavaDoc tables)
2032      throws BackupException
2033  {
2034    DumpInfo dumpInfo;
2035    try
2036    {
2037      dumpInfo = recoveryLog.getDumpInfo(dumpName);
2038    }
2039    catch (SQLException JavaDoc e)
2040    {
2041      throw new BackupException(
2042          "Recovery log error access occured while retrieving information for dump "
2043              + dumpName, e);
2044    }
2045    if (dumpInfo == null)
2046      throw new BackupException(
2047          "No information was found in the dump table for dump " + dumpName);
2048
2049    Backuper backuper = backupManager.getBackuperByFormat(dumpInfo
2050        .getDumpFormat());
2051    if (backuper == null)
2052      throw new BackupException("No backuper was found to handle dump format "
2053          + dumpInfo.getDumpFormat());
2054
2055    try
2056    {
2057      // no check for disable as we are going to overwrite
2058
// all the database data
2059
if (backend.isReadEnabled())
2060        loadBalancer.disableBackend(backend);
2061
2062      backend.setState(BackendState.RECOVERING);
2063
2064      backuper.restore(backend, login, password, dumpName, dumpInfo
2065          .getDumpPath(), tables);
2066
2067      // Set the checkpoint name corresponding to this database dump
2068
backend.setLastKnownCheckpoint(dumpInfo.getCheckpointName());
2069      backend.setState(BackendState.DISABLED);
2070    }
2071    catch (SQLException JavaDoc e1)
2072    {
2073      // This comes from the loadbalancer
2074
backend.setState(BackendState.UNKNOWN);
2075      throw new BackupException("Backend cannot be enabled", e1);
2076    }
2077    catch (BackupException be)
2078    {
2079      backend.setState(BackendState.UNKNOWN);
2080      logger.error(Translate.get("controller.backup.recovery.failed"), be);
2081      throw be;
2082    }
2083    finally
2084    {
2085      logger.info(Translate.get("controller.backup.recovery.done", backend
2086          .getName()));
2087    }
2088  }
2089
2090  /**
2091   * Store all the backends checkpoint in the recoverylog
2092   *
2093   * @param databaseName the virtual database name
2094   * @param backends the <code>Arraylist</code> of backends
2095   */

2096  public void storeBackendsInfo(String JavaDoc databaseName, ArrayList JavaDoc backends)
2097  {
2098    if (recoveryLog == null)
2099      return;
2100    int size = backends.size();
2101    DatabaseBackend backend;
2102    for (int i = 0; i < size; i++)
2103    {
2104      backend = (DatabaseBackend) backends.get(i);
2105      try
2106      {
2107        recoveryLog.storeBackendRecoveryInfo(databaseName,
2108            new BackendRecoveryInfo(backend.getName(), backend
2109                .getLastKnownCheckpoint(), backend.getStateValue(),
2110                databaseName));
2111      }
2112      catch (SQLException JavaDoc e)
2113      {
2114        logger.error(Translate.get("recovery.store.checkpoint.failed",
2115            new String JavaDoc[]{backend.getName(), e.getMessage()}), e);
2116      }
2117    }
2118  }
2119
2120  /**
2121   * Remove a checkpoint and corresponding entries from the log table
2122   *
2123   * @param checkpointName to remove
2124   * @throws SQLException if fails
2125   */

2126  public void removeCheckpoint(String JavaDoc checkpointName) throws SQLException JavaDoc
2127  {
2128    recoveryLog.removeCheckpoint(checkpointName);
2129  }
2130
2131  //
2132
// Database schema management
2133
//
2134

2135  /**
2136   * Get the <code>DatabaseSchema</code> used by this Request Manager.
2137   *
2138   * @return a <code>DatabaseSchema</code> value
2139   */

2140  public synchronized DatabaseSchema getDatabaseSchema()
2141  {
2142    try
2143    {
2144      // Refresh schema if needed. Note that this will break static schemas if
2145
// any
2146
if (schemaIsDirty)
2147      {
2148        dbs = vdb.getDatabaseSchemaFromActiveBackends();
2149        schemaIsDirty = false;
2150      }
2151    }
2152    catch (SQLException JavaDoc e)
2153    {
2154      logger.error("Unable to refresh schema", e);
2155    }
2156    return dbs;
2157  }
2158
2159  /**
2160   * Merge the given schema with the existing database schema.
2161   *
2162   * @param backendSchema The virtual database schema to merge.
2163   */

2164  public synchronized void mergeDatabaseSchema(DatabaseSchema backendSchema)
2165  {
2166    try
2167    {
2168      if (dbs == null)
2169        setDatabaseSchema(new DatabaseSchema(backendSchema), false);
2170      else
2171      {
2172        dbs.mergeSchema(backendSchema);
2173        logger.info(Translate
2174            .get("requestmanager.schema.virtualdatabase.merged.new"));
2175
2176        if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
2177          scheduler.mergeDatabaseSchema(dbs);
2178
2179        if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
2180          resultCache.mergeDatabaseSchema(dbs);
2181      }
2182    }
2183    catch (SQLException JavaDoc e)
2184    {
2185      logger.error(Translate.get("requestmanager.schema.merge.failed", e
2186          .getMessage()), e);
2187    }
2188  }
2189
2190  /**
2191   * Sets the <code>DatabaseSchema</code> to be able to parse the requests and
2192   * find dependencies.
2193   *
2194   * @param schema a <code>DatabaseSchema</code> value
2195   * @param isStatic true if the given schema is static
2196   */

2197  public synchronized void setDatabaseSchema(DatabaseSchema schema,
2198      boolean isStatic)
2199  {
2200    if (schemaIsStatic)
2201    {
2202      if (isStatic)
2203      {
2204        logger.warn(Translate
2205            .get("requestmanager.schema.replace.static.with.new"));
2206        this.dbs = schema;
2207      }
2208      else
2209        logger.info(Translate.get("requestmanager.schema.ignore.new.dynamic"));
2210    }
2211    else
2212    {
2213      schemaIsStatic = isStatic;
2214      this.dbs = schema;
2215      logger.info(Translate
2216          .get("requestmanager.schema.set.new.virtualdatabase"));
2217    }
2218
2219    if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
2220      scheduler.setDatabaseSchema(dbs);
2221
2222    if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
2223      resultCache.setDatabaseSchema(dbs);
2224
2225    // Load balancers do not have a specific database schema to update
2226
}
2227
2228  /**
2229   * Sets the schemaIsDirty value if the backend schema needs to be refreshed.
2230   *
2231   * @param schemaIsDirty The schemaIsDirty to set.
2232   */

2233  public void setSchemaIsDirty(boolean schemaIsDirty)
2234  {
2235    this.schemaIsDirty = schemaIsDirty;
2236  }
2237
2238  //
2239
// Getter/Setter methods
2240
//
2241

2242  /**
2243   * Returns the vdb value.
2244   *
2245   * @return Returns the vdb.
2246   */

2247  public VirtualDatabase getVirtualDatabase()
2248  {
2249    return vdb;
2250  }
2251
2252  /**
2253   * Sets the backup manager for this recovery log
2254   *
2255   * @param currentBackupManager an instance of <code>BackupManager</code>
2256   */

2257  public void setBackupManager(BackupManager currentBackupManager)
2258  {
2259    this.backupManager = currentBackupManager;
2260  }
2261
2262  /**
2263   * Returns the backupManager value.
2264   *
2265   * @return Returns the backupManager.
2266   */

2267  public BackupManager getBackupManager()
2268  {
2269    return backupManager;
2270  }
2271
2272  /**
2273   * Get the Request Load Balancer used in this Request Controller.
2274   *
2275   * @return an <code>AbstractLoadBalancer</code> value
2276   */

2277  public AbstractLoadBalancer getLoadBalancer()
2278  {
2279    return loadBalancer;
2280  }
2281
2282  /**
2283   * Set the Request Load Balancer to use in this Request Controller.
2284   *
2285   * @param loadBalancer a Request Load Balancer implementation
2286   */

2287  public void setLoadBalancer(AbstractLoadBalancer loadBalancer)
2288  {
2289    if (this.loadBalancer != null)
2290      throw new RuntimeException JavaDoc(
2291          "It is not possible to dynamically change a load balancer.");
2292    this.loadBalancer = loadBalancer;
2293    if (loadBalancer == null)
2294      return;
2295    loadBalancerParsingranularity = loadBalancer.getParsingGranularity();
2296    if (loadBalancerParsingranularity > requiredParsingGranularity)
2297      requiredParsingGranularity = loadBalancerParsingranularity;
2298
2299    if (MBeanServerManager.isJmxEnabled())
2300    {
2301      try
2302      {
2303        MBeanServerManager.registerMBean(loadBalancer, JmxConstants
2304            .getLoadBalancerObjectName(vdb.getVirtualDatabaseName()));
2305      }
2306      catch (Exception JavaDoc e)
2307      {
2308        logger.error(Translate.get("jmx.failed.register.mbean.loadbalancer"));
2309      }
2310    }
2311
2312  }
2313
2314  /**
2315   * Get the result cache (if any) used in this Request Manager.
2316   *
2317   * @return an <code>AbstractResultCache</code> value or null if no Reqsult
2318   * Cache has been defined
2319   */

2320  public AbstractResultCache getResultCache()
2321  {
2322    return resultCache;
2323  }
2324
2325  /**
2326   * Returns the metadataCache value.
2327   *
2328   * @return Returns the metadataCache.
2329   */

2330  public MetadataCache getMetadataCache()
2331  {
2332    return metadataCache;
2333  }
2334
2335  /**
2336   * Sets the metadataCache value.
2337   *
2338   * @param metadataCache The metadataCache to set.
2339   */

2340  public void setMetadataCache(MetadataCache metadataCache)
2341  {
2342    this.metadataCache = metadataCache;
2343  }
2344
2345  /**
2346   * Sets the ParsingCache.
2347   *
2348   * @param parsingCache The parsingCache to set.
2349   */

2350  public void setParsingCache(ParsingCache parsingCache)
2351  {
2352    parsingCache.setRequestManager(this);
2353    parsingCache.setGranularity(requiredParsingGranularity);
2354    parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
2355    this.parsingCache = parsingCache;
2356  }
2357
2358  /**
2359   * Returns the Recovery Log Manager.
2360   *
2361   * @return RecoveryLog
2362   */

2363  public RecoveryLog getRecoveryLog()
2364  {
2365    return recoveryLog;
2366  }
2367
2368  /**
2369   * Sets the Recovery Log Manager.
2370   *
2371   * @param recoveryLog The log recovery to set
2372   */

2373  public void setRecoveryLog(RecoveryLog recoveryLog)
2374  {
2375    if (recoveryLog == null)
2376      return;
2377    this.recoveryLog = recoveryLog;
2378    ArrayList JavaDoc backends = vdb.getBackends();
2379    int size = backends.size();
2380    backendStateListener = new BackendStateListener(vdb
2381        .getVirtualDatabaseName(), recoveryLog);
2382    for (int i = 0; i < size; i++)
2383      ((DatabaseBackend) backends.get(i))
2384          .setStateListener(backendStateListener);
2385  }
2386
2387  /**
2388   * Set the Request Cache to use in this Request Controller.
2389   *
2390   * @param cache a Request Cache implementation
2391   */

2392  public void setResultCache(AbstractResultCache cache)
2393  {
2394    resultCache = cache;
2395    cacheParsingranularity = cache.getParsingGranularity();
2396    if (cacheParsingranularity > requiredParsingGranularity)
2397      requiredParsingGranularity = cacheParsingranularity;
2398  }
2399
2400  /**
2401   * Get the Request Scheduler (if any) used in this Request Controller.
2402   *
2403   * @return an <code>AbstractScheduler</code> value or null if no Request
2404   * Scheduler has been defined
2405   */

2406  public AbstractScheduler getScheduler()
2407  {
2408    return scheduler;
2409  }
2410
2411  /**
2412   * Set the Request Scheduler to use in this Request Controller.
2413   *
2414   * @param scheduler a Request Scheduler implementation
2415   */

2416  public void setScheduler(AbstractScheduler scheduler)
2417  {
2418    this.scheduler = scheduler;
2419    schedulerParsingranularity = scheduler.getParsingGranularity();
2420    if (schedulerParsingranularity > requiredParsingGranularity)
2421      requiredParsingGranularity = schedulerParsingranularity;
2422  }
2423
2424  /**
2425   * Sets the parsing case sensitivity. If true the request are parsed in a case
2426   * sensitive way (table/column name must match exactly the case of the names
2427   * fetched from the database or enforced by a static schema).
2428   *
2429   * @param isCaseSensitiveParsing true if parsing is case sensitive
2430   */

2431  public void setCaseSensitiveParsing(boolean isCaseSensitiveParsing)
2432  {
2433    this.isCaseSensitiveParsing = isCaseSensitiveParsing;
2434    if (parsingCache != null)
2435      parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
2436  }
2437
2438  //
2439
// Debug/Monitoring
2440
//
2441

2442  /**
2443   * Get xml information about this Request Manager
2444   *
2445   * @return <code>String</code> in xml formatted text
2446   */

2447  public String JavaDoc getXml()
2448  {
2449    StringBuffer JavaDoc info = new StringBuffer JavaDoc();
2450    info.append("<" + DatabasesXmlTags.ELT_RequestManager + " "
2451        + DatabasesXmlTags.ATT_caseSensitiveParsing + "=\""
2452        + isCaseSensitiveParsing + "\" " + DatabasesXmlTags.ATT_beginTimeout
2453        + "=\"" + beginTimeout / 1000 + "\" "
2454        + DatabasesXmlTags.ATT_commitTimeout + "=\"" + commitTimeout / 1000
2455        + "\" " + DatabasesXmlTags.ATT_rollbackTimeout + "=\""
2456        + rollbackTimeout / 1000 + "\">");
2457    if (scheduler != null)
2458      info.append(scheduler.getXml());
2459
2460    if (metadataCache != null || parsingCache != null || resultCache != null)
2461    {
2462      info.append("<" + DatabasesXmlTags.ELT_RequestCache + ">");
2463      if (metadataCache != null)
2464        info.append(metadataCache.getXml());
2465      if (parsingCache != null)
2466        info.append(parsingCache.getXml());
2467      if (resultCache != null)
2468        info.append(resultCache.getXml());
2469      info.append("</" + DatabasesXmlTags.ELT_RequestCache + ">");
2470    }
2471
2472    if (loadBalancer != null)
2473      info.append(loadBalancer.getXml());
2474    if (recoveryLog != null)
2475      info.append(this.recoveryLog.getXml());
2476    info.append("</" + DatabasesXmlTags.ELT_RequestManager + ">");
2477    return info.toString();
2478  }
2479
2480  /**
2481   * Returns the backendStateListener value.
2482   *
2483   * @return Returns the backendStateListener.
2484   */

2485  public BackendStateListener getBackendStateListener()
2486  {
2487    return backendStateListener;
2488  }
2489
2490  /**
2491   * Returns the beginTimeout value.
2492   *
2493   * @return Returns the beginTimeout.
2494   */

2495  public long getBeginTimeout()
2496  {
2497    return beginTimeout;
2498  }
2499
2500  /**
2501   * Sets the beginTimeout value.
2502   *
2503   * @param beginTimeout The beginTimeout to set.
2504   */

2505  public void setBeginTimeout(long beginTimeout)
2506  {
2507    this.beginTimeout = beginTimeout;
2508  }
2509
2510  /**
2511   * Returns the cacheParsingranularity value.
2512   *
2513   * @return Returns the cacheParsingranularity.
2514   */

2515  public int getCacheParsingranularity()
2516  {
2517    return cacheParsingranularity;
2518  }
2519
2520  /**
2521   * Sets the cacheParsingranularity value.
2522   *
2523   * @param cacheParsingranularity The cacheParsingranularity to set.
2524   */

2525  public void setCacheParsingranularity(int cacheParsingranularity)
2526  {
2527    this.cacheParsingranularity = cacheParsingranularity;
2528  }
2529
2530  /**
2531   * Returns the commitTimeout value.
2532   *
2533   * @return Returns the commitTimeout.
2534   */

2535  public long getCommitTimeout()
2536  {
2537    return commitTimeout;
2538  }
2539
2540  /**
2541   * Sets the commitTimeout value.
2542   *
2543   * @param commitTimeout The commitTimeout to set.
2544   */

2545  public void setCommitTimeout(long commitTimeout)
2546  {
2547    this.commitTimeout = commitTimeout;
2548  }
2549
2550  /**
2551   * Returns the loadBalancerParsingranularity value.
2552   *
2553   * @return Returns the loadBalancerParsingranularity.
2554   */

2555  public int getLoadBalancerParsingranularity()
2556  {
2557    return loadBalancerParsingranularity;
2558  }
2559
2560  /**
2561   * Sets the loadBalancerParsingranularity value.
2562   *
2563   * @param loadBalancerParsingranularity The loadBalancerParsingranularity to
2564   * set.
2565   */

2566  public void setLoadBalancerParsingranularity(int loadBalancerParsingranularity)
2567  {
2568    this.loadBalancerParsingranularity = loadBalancerParsingranularity;
2569  }
2570
2571  /**
2572   * Returns the requiredParsingGranularity value.
2573   *
2574   * @return Returns the requiredParsingGranularity.
2575   */

2576  public int getRequiredParsingGranularity()
2577  {
2578    return requiredParsingGranularity;
2579  }
2580
2581  /**
2582   * Sets the requiredParsingGranularity value.
2583   *
2584   * @param requiredGranularity The requiredParsingGranularity to set.
2585   */

2586  public void setRequiredParsingGranularity(int requiredGranularity)
2587  {
2588    this.requiredParsingGranularity = requiredGranularity;
2589  }
2590
2591  /**
2592   * Returns the rollbackTimeout value.
2593   *
2594   * @return Returns the rollbackTimeout.
2595   */

2596  public long getRollbackTimeout()
2597  {
2598    return rollbackTimeout;
2599  }
2600
2601  /**
2602   * Sets the rollbackTimeout value.
2603   *
2604   * @param rollbackTimeout The rollbackTimeout to set.
2605   */

2606  public void setRollbackTimeout(long rollbackTimeout)
2607  {
2608    this.rollbackTimeout = rollbackTimeout;
2609  }
2610
2611  /**
2612   * Returns the schedulerParsingranularity value.
2613   *
2614   * @return Returns the schedulerParsingranularity.
2615   */

2616  public int getSchedulerParsingranularity()
2617  {
2618    return schedulerParsingranularity;
2619  }
2620
2621  /**
2622   * Sets the schedulerParsingranularity value.
2623   *
2624   * @param schedulerParsingranularity The schedulerParsingranularity to set.
2625   */

2626  public void setSchedulerParsingranularity(int schedulerParsingranularity)
2627  {
2628    this.schedulerParsingranularity = schedulerParsingranularity;
2629  }
2630
2631  /**
2632   * Returns the schemaIsStatic value.
2633   *
2634   * @return Returns the schemaIsStatic.
2635   */

2636  public boolean isSchemaIsStatic()
2637  {
2638    return schemaIsStatic;
2639  }
2640
2641  /**
2642   * Sets the schemaIsStatic value.
2643   *
2644   * @param schemaIsStatic The schemaIsStatic to set.
2645   */

2646  public void setSchemaIsStatic(boolean schemaIsStatic)
2647  {
2648    this.schemaIsStatic = schemaIsStatic;
2649  }
2650
2651  /**
2652   * Returns the isCaseSensitiveParsing value.
2653   *
2654   * @return Returns the isCaseSensitiveParsing.
2655   */

2656  public boolean isCaseSensitiveParsing()
2657  {
2658    return isCaseSensitiveParsing;
2659  }
2660
2661  /**
2662   * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
2663   */

2664  public String JavaDoc getAssociatedString()
2665  {
2666    return "requestmanager";
2667  }
2668}
2669
Popular Tags