KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > requestmanager > RequestManager


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
6  * Copyright (C) 2005-2006 Continuent, Inc.
7  * Contact: sequoia@continuent.org
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite, Greg Ward, Nicolas Modrzyk, Vadim Kassin,
23  * Jean-Bernard van Zuylen, Peter Royal, Stephane Giron.
24  */

25
26 package org.continuent.sequoia.controller.requestmanager;
27
28 import java.sql.SQLException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Date JavaDoc;
31 import java.util.Hashtable JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.LinkedList JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Vector JavaDoc;
36
37 import javax.management.NotCompliantMBeanException JavaDoc;
38
39 import org.continuent.sequoia.common.exceptions.BackupException;
40 import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
41 import org.continuent.sequoia.common.exceptions.RollbackException;
42 import org.continuent.sequoia.common.exceptions.VirtualDatabaseException;
43 import org.continuent.sequoia.common.i18n.Translate;
44 import org.continuent.sequoia.common.jmx.JmxConstants;
45 import org.continuent.sequoia.common.jmx.management.BackendInfo;
46 import org.continuent.sequoia.common.jmx.management.BackendState;
47 import org.continuent.sequoia.common.jmx.management.DumpInfo;
48 import org.continuent.sequoia.common.jmx.mbeans.RequestManagerMBean;
49 import org.continuent.sequoia.common.jmx.notifications.SequoiaNotificationList;
50 import org.continuent.sequoia.common.log.Trace;
51 import org.continuent.sequoia.common.xml.DatabasesXmlTags;
52 import org.continuent.sequoia.common.xml.XmlComponent;
53 import org.continuent.sequoia.controller.backend.BackendStateListener;
54 import org.continuent.sequoia.controller.backend.DatabaseBackend;
55 import org.continuent.sequoia.controller.backend.result.ControllerResultSet;
56 import org.continuent.sequoia.controller.backend.result.ExecuteResult;
57 import org.continuent.sequoia.controller.backend.result.ExecuteUpdateResult;
58 import org.continuent.sequoia.controller.backend.result.GeneratedKeysResult;
59 import org.continuent.sequoia.controller.backup.BackupManager;
60 import org.continuent.sequoia.controller.backup.Backuper;
61 import org.continuent.sequoia.controller.cache.metadata.MetadataCache;
62 import org.continuent.sequoia.controller.cache.parsing.ParsingCache;
63 import org.continuent.sequoia.controller.cache.result.AbstractResultCache;
64 import org.continuent.sequoia.controller.cache.result.entries.AbstractResultCacheEntry;
65 import org.continuent.sequoia.controller.core.ControllerConstants;
66 import org.continuent.sequoia.controller.jmx.AbstractStandardMBean;
67 import org.continuent.sequoia.controller.jmx.MBeanServerManager;
68 import org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer;
69 import org.continuent.sequoia.controller.loadbalancer.AllBackendsFailedException;
70 import org.continuent.sequoia.controller.loadbalancer.LoadBalancerControl;
71 import org.continuent.sequoia.controller.loadbalancer.policies.WaitForCompletionPolicy;
72 import org.continuent.sequoia.controller.recoverylog.BackendRecoveryInfo;
73 import org.continuent.sequoia.controller.recoverylog.RecoverThread;
74 import org.continuent.sequoia.controller.recoverylog.RecoveryLog;
75 import org.continuent.sequoia.controller.requests.AbstractRequest;
76 import org.continuent.sequoia.controller.requests.AbstractWriteRequest;
77 import org.continuent.sequoia.controller.requests.ParsingGranularities;
78 import org.continuent.sequoia.controller.requests.RequestFactory;
79 import org.continuent.sequoia.controller.requests.RequestType;
80 import org.continuent.sequoia.controller.requests.SelectRequest;
81 import org.continuent.sequoia.controller.requests.StoredProcedure;
82 import org.continuent.sequoia.controller.requests.UpdateRequest;
83 import org.continuent.sequoia.controller.scheduler.AbstractScheduler;
84 import org.continuent.sequoia.controller.semantic.SemanticBehavior;
85 import org.continuent.sequoia.controller.semantic.SemanticManager;
86 import org.continuent.sequoia.controller.sql.schema.DatabaseSchema;
87 import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabase;
88 import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabaseWorkerThread;
89 import org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedCommit;
90 import org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedReleaseSavepoint;
91 import org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedRollback;
92 import org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedRollbackToSavepoint;
93 import org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedSetSavepoint;
94
95 /**
96  * This class defines the Request Manager.
97  * <p>
98  * The RM is composed of a Request Scheduler, an optional Query Cache, and a
99  * Load Balancer and an optional Recovery Log.
100  *
101  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
102  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
103  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
104  * @author <a HREF="mailto:vadim@kase.kz">Vadim Kassin </a>
105  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
106  * </a>
107  * @version 1.0
108  */

109 public class RequestManager extends AbstractStandardMBean
110     implements
111       XmlComponent,
112       RequestManagerMBean
113 {
114
115   //
116
// How the code is organized ?
117
//
118
// 1. Member variables
119
// 2. Constructor(s)
120
// 3. Request handling
121
// 4. Transaction handling
122
// 5. Database backend management
123
// 6. Getter/Setter (possibly in alphabetical order)
124
//
125

126   /** begin timeout in ms */
127   protected long beginTimeout;
128
129   /** commit timeout in ms */
130   protected long commitTimeout;
131
132   /** rollback timeout in ms */
133   protected long rollbackTimeout;
134
135   /** The virtual database owning this Request Manager */
136   protected VirtualDatabase vdb;
137
138   /** The request scheduler to order and schedule requests */
139   protected AbstractScheduler scheduler;
140
141   /** An optional request cache to cache responses to SQL requests */
142   protected AbstractResultCache resultCache;
143
144   /** The request load balancer to use to send requests to the databases */
145   protected AbstractLoadBalancer loadBalancer;
146
147   /** An optional recovery log */
148   protected RecoveryLog recoveryLog;
149
150   /** The backup manager responsible for backup and restore of backends */
151   protected BackupManager backupManager;
152
153   // The virtual dabase schema
154
protected DatabaseSchema dbs;
155
156   /** <code>true</code> if schema is no more up-to-date and needs a refresh */
157   private boolean schemaIsDirty = false;
158
159   private boolean isCaseSensitiveParsing = false;
160
161   protected ParsingCache parsingCache = null;
162
163   private MetadataCache metadataCache = null;
164
165   // SQL queries parsing granularity according to Scheduler, ResultCache and
166
// LoadBalancer required granularity
167
protected int schedulerParsingranularity = ParsingGranularities.NO_PARSING;
168
169   private int cacheParsingranularity = ParsingGranularities.NO_PARSING;
170
171   private int loadBalancerParsingranularity = ParsingGranularities.NO_PARSING;
172
173   protected int requiredParsingGranularity = ParsingGranularities.NO_PARSING;
174
175   private long requestId = 0;
176
177   /* end user logger */
178   protected static Trace endUserLogger = Trace
179                                                                    .getLogger("org.continuent.sequoia.enduser");
180
181   /**
182    * Hashtable&lt;Long, TransactionMetaData&gt;, the <code>Long</code> key is
183    * the same transaction ID as the <code>transactionId</code> field of its
184    * corresponding <code>TransactionMetaData</code> value.
185    */

186   // <pedantic>This Hashtable should be called transactionMetaData and the
187
// TransactionMetaData class should be renamed TransactionMetaDatum but
188
// 1/ I don't care
189
// 2/ I don't spell check my code :-)
190
// Jeff.
191
// </pedantic>
192
protected Hashtable JavaDoc transactionMetaDatas;
193
194   /**
195    * Hashtable&lt;Long, String&gt;, the <code>Long</code> being a transaction
196    * ID and its corresponding <code>String</code> being the name of a
197    * savepoint.
198    */

199   protected Hashtable JavaDoc tidSavepoints;
200
201   protected Trace logger = null;
202
203   private BackendStateListener backendStateListener;
204
205   //
206
// Constructors
207
//
208

209   /**
210    * Creates a new <code>RequestManager</code> instance.
211    *
212    * @param vdb the virtual database this request manager belongs to
213    * @param scheduler the Request Scheduler to use
214    * @param cache a Query Cache implementation
215    * @param loadBalancer the Request Load Balancer to use
216    * @param recoveryLog the Log Recovery to use
217    * @param beginTimeout timeout in seconds for begin
218    * @param commitTimeout timeout in seconds for commit
219    * @param rollbackTimeout timeout in seconds for rollback
220    * @throws SQLException if an error occurs
221    * @throws NotCompliantMBeanException if the MBean is not JMX compliant
222    */

223   public RequestManager(VirtualDatabase vdb, AbstractScheduler scheduler,
224       AbstractResultCache cache, AbstractLoadBalancer loadBalancer,
225       RecoveryLog recoveryLog, long beginTimeout, long commitTimeout,
226       long rollbackTimeout) throws SQLException JavaDoc, NotCompliantMBeanException JavaDoc
227   {
228     super(RequestManagerMBean.class);
229     this.vdb = vdb;
230     assignAndCheckSchedulerLoadBalancerValidity(scheduler, loadBalancer);
231     // requiredParsingGranularity is the maximum of each component granularity
232
this.resultCache = cache;
233     if (resultCache != null)
234     {
235       cacheParsingranularity = cache.getParsingGranularity();
236       if (cacheParsingranularity > requiredParsingGranularity)
237         requiredParsingGranularity = cacheParsingranularity;
238     }
239     setRecoveryLog(recoveryLog);
240     initRequestManagerVariables(vdb, beginTimeout, commitTimeout,
241         rollbackTimeout);
242     logger.info(Translate.get("requestmanager.parsing.granularity",
243         ParsingGranularities.getInformation(requiredParsingGranularity)));
244
245     try
246     {
247       MBeanServerManager.registerMBean(this, JmxConstants
248           .getRequestManagerObjectName(vdb.getVirtualDatabaseName()));
249     }
250     catch (Exception JavaDoc e)
251     {
252       logger.error(Translate.get("jmx.failed.register.mbean.requestmanager"));
253     }
254   }
255
256   /**
257    * Retrieve the last known checkpoint from the recovery log and set it for
258    * each backend.
259    */

260   public void initBackendsLastKnownCheckpointFromRecoveryLog()
261   {
262     if (recoveryLog == null)
263       return;
264     String JavaDoc databaseName = vdb.getVirtualDatabaseName();
265     ArrayList JavaDoc backends = vdb.getBackends();
266     int size = backends.size();
267     DatabaseBackend backend;
268     BackendRecoveryInfo info;
269     for (int i = 0; i < size; i++)
270     {
271       backend = (DatabaseBackend) backends.get(i);
272       try
273       {
274         info = recoveryLog.getBackendRecoveryInfo(databaseName, backend
275             .getName());
276         String JavaDoc checkpoint = info.getCheckpoint();
277         if ((checkpoint == null) || ("".equals(checkpoint)))
278         { // No last known checkpoint
279
if (info.getBackendState() != BackendState.UNKNOWN)
280           {
281             String JavaDoc msg = "Backend " + backend.getName()
282                 + " was not properly stopped, manual recovery will be needed ("
283                 + info + ")";
284             logger.warn(msg);
285             throw new SQLException JavaDoc(msg);
286           }
287         }
288         backend.setLastKnownCheckpoint(checkpoint);
289       }
290       catch (SQLException JavaDoc e)
291       {
292         logger.warn(e.getMessage(), e);
293         backend.setState(BackendState.UNKNOWN);
294         // Also sets backend.setLastKnownCheckpoint(null);
295
}
296     }
297   }
298
299   /**
300    * Check that Scheduler and Load Balancer are not null and have compatible
301    * RAIDb levels.
302    *
303    * @param aScheduler the scheduler to set
304    * @param aLoadBalancer the load balancer to set
305    * @throws SQLException if an error occurs
306    */

307   private void assignAndCheckSchedulerLoadBalancerValidity(
308       AbstractScheduler aScheduler, AbstractLoadBalancer aLoadBalancer)
309       throws SQLException JavaDoc
310   {
311     if (aScheduler == null)
312       throw new SQLException JavaDoc(Translate.get("requestmanager.null.scheduler"));
313
314     if (aLoadBalancer == null)
315       throw new SQLException JavaDoc(Translate.get("requestmanager.null.loadbalancer"));
316
317     if (aScheduler.getRAIDbLevel() != aLoadBalancer.getRAIDbLevel())
318       throw new SQLException JavaDoc(Translate.get(
319           "requestmanager.incompatible.raidb.levels", new String JavaDoc[]{
320               "" + aScheduler.getRAIDbLevel(),
321               "" + aLoadBalancer.getRAIDbLevel()}));
322
323     // requiredParsingGranularity is the maximum of each component granularity
324
setScheduler(aScheduler);
325     schedulerParsingranularity = aScheduler.getParsingGranularity();
326     requiredParsingGranularity = schedulerParsingranularity;
327     setLoadBalancer(aLoadBalancer);
328     loadBalancerParsingranularity = aLoadBalancer.getParsingGranularity();
329     if (loadBalancerParsingranularity > requiredParsingGranularity)
330       requiredParsingGranularity = loadBalancerParsingranularity;
331   }
332
333   /**
334    * Get the parsing from the parsing cache or parse the query.
335    *
336    * @param request the request to be parsed (request will be parsed after this
337    * call)
338    * @throws SQLException if an error occurs during parsing
339    */

340   public void getParsingFromCacheOrParse(AbstractRequest request)
341       throws SQLException JavaDoc
342   {
343     /*
344      * If we need to parse the request, try to get the parsing from the cache.
345      * Note that if we have a cache miss but backgroundParsing has been turned
346      * on, then this call will start a ParsedThread in background.
347      */

348     if ((requiredParsingGranularity != ParsingGranularities.NO_PARSING)
349         && (!request.isParsed()))
350     {
351       if (parsingCache == null)
352       {
353         SemanticManager semanticManager = vdb.getSemanticManager();
354         // Check if there is semantic information
355
request.setSemanticManager(semanticManager);
356         SemanticBehavior semantic = semanticManager.getRequestSemantic(request);
357         if (semantic != null)
358           request.setSemantic(semantic);
359         else
360         { // No semantic, parse it
361
request.parse(getDatabaseSchema(), requiredParsingGranularity,
362               isCaseSensitiveParsing);
363         }
364       }
365       else
366         parsingCache.getParsingFromCacheAndParseIfMissing(request);
367     }
368   }
369
370   /**
371    * Method initRequestManagerVariables.
372    *
373    * @param vdb the virtual database
374    * @param beginTimeout timeout for begin operation
375    * @param commitTimeout timeout for commit operation
376    * @param rollbackTimeout timeout for rollback operation
377    */

378   private void initRequestManagerVariables(VirtualDatabase vdb,
379       long beginTimeout, long commitTimeout, long rollbackTimeout)
380   {
381     this.transactionMetaDatas = new Hashtable JavaDoc();
382     this.tidSavepoints = new Hashtable JavaDoc();
383     this.beginTimeout = beginTimeout;
384     this.commitTimeout = commitTimeout;
385     this.rollbackTimeout = rollbackTimeout;
386     this.vdb = vdb;
387     logger = Trace
388         .getLogger("org.continuent.sequoia.controller.RequestManager."
389             + vdb.getDatabaseName());
390   }
391
392   //
393
// Request Handling
394
//
395

396   /**
397    * Close the given persistent connection.
398    *
399    * @param login login to use to retrieve the right connection pool
400    * @param persistentConnectionId id of the persistent connection to close
401    */

402   public void closePersistentConnection(String JavaDoc login,
403       long persistentConnectionId)
404   {
405     try
406     {
407       scheduler.scheduleClosePersistentConnection();
408
409       // No need to wait for task completion (cannot fail), so log right away.
410
if (recoveryLog != null)
411         recoveryLog.logClosePersistentConnection(login, persistentConnectionId);
412
413       loadBalancer.closePersistentConnection(login, persistentConnectionId);
414     }
415     catch (NoMoreBackendException ignore)
416     {
417       // Removes ugly trace as reported in SEQUOIA-390
418
}
419     catch (SQLException JavaDoc e)
420     {
421       logger.warn("Failed to close persistent connection "
422           + persistentConnectionId, e);
423     }
424     finally
425     {
426       scheduler.closePersistentConnectionCompleted(persistentConnectionId);
427     }
428   }
429
430   /**
431    * Returns true if the virtual database has opened the given persistent
432    * connection.
433    *
434    * @param persistentConnectionId id of the persistent connection to check
435    * @return true if the connection is open
436    */

437   public boolean hasPersistentConnection(long persistentConnectionId)
438   {
439     return scheduler.hasPersistentConnection(persistentConnectionId);
440   }
441
442   /**
443    * Open the given persistent connection.
444    *
445    * @param login login to use to retrieve the right connection pool
446    * @param persistentConnectionId id of the persistent connection to open
447    * @throws SQLException An exception can be thrown when it failed to open a
448    * connection
449    */

450   public void openPersistentConnection(String JavaDoc login, long persistentConnectionId)
451       throws SQLException JavaDoc
452   {
453     boolean success = false;
454     long entryId = -1;
455     try
456     {
457       scheduler.scheduleOpenPersistentConnection(persistentConnectionId, login);
458
459       if (recoveryLog != null)
460         entryId = recoveryLog.logOpenPersistentConnection(login,
461             persistentConnectionId);
462
463       loadBalancer.openPersistentConnection(login, persistentConnectionId);
464       success = true;
465     }
466     catch (NoMoreBackendException e)
467     {
468       throw e;
469     }
470     catch (SQLException JavaDoc e)
471     {
472       logger.warn("Failed to open persistent connection "
473           + persistentConnectionId, e);
474       throw e;
475     }
476     finally
477     {
478       if (recoveryLog != null)
479         recoveryLog.logRequestCompletion(entryId, success, 0);
480       scheduler.openPersistentConnectionCompleted(persistentConnectionId,
481           success);
482     }
483   }
484
485   private static final Object JavaDoc REQUEST_ID_SYNC_OBJECT = new Object JavaDoc();
486
487   /**
488    * Initialize the request id with the given value (usually retrieved from the
489    * recovery log).
490    *
491    * @param requestId new current request identifier
492    */

493   public void initializeRequestId(long requestId)
494   {
495     this.requestId = requestId;
496   }
497
498   /**
499    * Return the next request identifier (monotically increasing number).
500    *
501    * @return a request identifier
502    */

503   public long getNextRequestId()
504   {
505     synchronized (REQUEST_ID_SYNC_OBJECT)
506     {
507       return requestId++;
508     }
509   }
510
511   /**
512    * Perform a read request and return the reply. Call first the scheduler, then
513    * the cache (if defined) and finally the load balancer.
514    *
515    * @param request the request to execute
516    * @return a <code>ControllerResultSet</code> value
517    * @exception SQLException if an error occurs
518    */

519   public ControllerResultSet statementExecuteQuery(SelectRequest request)
520       throws SQLException JavaDoc
521   {
522     // Sanity check
523
TransactionMetaData tm = null;
524     if (!request.isAutoCommit())
525     { // Check that the transaction has been started
526
Long JavaDoc tid = new Long JavaDoc(request.getTransactionId());
527       try
528       {
529         tm = getTransactionMetaData(tid);
530       }
531       catch (SQLException JavaDoc e)
532       {
533         throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
534       }
535     }
536
537     getParsingFromCacheOrParse(request);
538
539     //
540
// SCHEDULER
541
//
542

543     if (logger.isDebugEnabled())
544       logger.debug(Translate.get("requestmanager.read.request.schedule",
545           new String JavaDoc[]{String.valueOf(request.getId()),
546               request.getSqlShortForm(vdb.getSqlShortFormLength())}));
547
548     // Wait for the scheduler to give us the authorization to execute
549
scheduler.scheduleReadRequest(request);
550
551     //
552
// CACHE
553
//
554

555     ControllerResultSet result = null;
556     try
557     { // Check cache if any
558
if ((resultCache != null) && (!request.isMustBroadcast()))
559       {
560         if (logger.isDebugEnabled())
561           logger.debug(Translate.get("requestmanager.read.request.cache.get",
562               new String JavaDoc[]{String.valueOf(request.getId()),
563                   request.getSqlShortForm(vdb.getSqlShortFormLength())}));
564
565         AbstractResultCacheEntry qce = resultCache.getFromCache(request, true);
566         if (qce != null)
567         {
568           result = qce.getResult();
569           if (result != null)
570           { // Cache hit !
571
if (vdb.getSQLMonitor() != null)
572               vdb.getSQLMonitor().logCacheHit(request);
573
574             scheduler.readCompleted(request);
575             return result;
576           }
577         }
578       }
579
580       //
581
// LOAD BALANCER
582
//
583

584       if (logger.isDebugEnabled())
585         logger.debug(Translate.get("requestmanager.read.request.balance",
586             new String JavaDoc[]{String.valueOf(request.getId()),
587                 request.getSqlShortForm(vdb.getSqlShortFormLength())}));
588
589       // At this point, we have a result cache miss.
590

591       // Send the request to the load balancer
592
result = loadBalancer.statementExecuteQuery(request, metadataCache);
593
594       //
595
// UPDATES & NOTIFICATIONS
596
//
597

598       // Update cache
599
if ((resultCache != null)
600           && (request.getCacheAbility() != RequestType.UNCACHEABLE))
601       {
602         if (logger.isDebugEnabled())
603           logger.debug(Translate.get(
604               "requestmanager.read.request.cache.update", new String JavaDoc[]{
605                   String.valueOf(request.getId()),
606                   request.getSqlShortForm(vdb.getSqlShortFormLength())}));
607
608         resultCache.addToCache(request, result);
609         if (tm != null)
610           tm.setAltersQueryResultCache(true);
611       }
612     }
613     catch (Exception JavaDoc failed)
614     {
615       if (resultCache != null)
616         resultCache.removeFromPendingQueries(request);
617       if (failed instanceof NoMoreBackendException)
618         throw (NoMoreBackendException) failed;
619       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
620           request.getSqlShortForm(vdb.getSqlShortFormLength()),
621           failed.getMessage()});
622       if (failed instanceof RuntimeException JavaDoc)
623         logger.warn(msg, failed);
624       else
625         logger.warn(msg);
626       if (failed instanceof SQLException JavaDoc)
627         throw (SQLException JavaDoc) failed;
628
629       throw new SQLException JavaDoc(msg);
630     }
631     finally
632     {
633       // Notify scheduler of completion
634
scheduler.readCompleted(request);
635     }
636     return result;
637   }
638
639   /**
640    * Perform a write request and return the number of rows affected Call first
641    * the scheduler (if defined), then notify the cache (if defined) and finally
642    * call the load balancer.
643    *
644    * @param request the request to execute
645    * @return number of rows affected
646    * @exception SQLException if an error occurs
647    */

648   public ExecuteUpdateResult statementExecuteUpdate(AbstractWriteRequest request)
649       throws SQLException JavaDoc
650   {
651     boolean hasBeenScheduled = false, schedulerHasBeenNotified = false;
652     try
653     {
654       scheduleExecWriteRequest(request);
655       hasBeenScheduled = true;
656       ExecuteUpdateResult execWriteRequestResult = null;
657       try
658       {
659         execWriteRequestResult = loadBalanceStatementExecuteUpdate(request);
660       }
661       catch (AllBackendsFailedException e)
662       {
663         String JavaDoc msg = Translate
664             .get("requestmanager.write.request.failed.unexpected");
665         logger.fatal(msg, e);
666         endUserLogger.fatal(msg);
667         throw new RuntimeException JavaDoc(msg, e);
668       }
669       updateAndNotifyExecWriteRequest(request, execWriteRequestResult
670           .getUpdateCount());
671       schedulerHasBeenNotified = true;
672       return execWriteRequestResult;
673     }
674     finally
675     {
676       if (hasBeenScheduled && !schedulerHasBeenNotified)
677         scheduler.writeCompleted(request);
678     }
679   }
680
681   /**
682    * Perform a write request and return the auto generated keys. Call first the
683    * scheduler (if defined), then notify the cache (if defined) and finally call
684    * the load balancer.
685    *
686    * @param request the request to execute
687    * @return auto generated keys.
688    * @exception SQLException if an error occurs
689    */

690   public GeneratedKeysResult statementExecuteUpdateWithKeys(
691       AbstractWriteRequest request) throws SQLException JavaDoc
692   {
693     boolean hasBeenScheduled = false, schedulerHasBeenNotified = false;
694     try
695     {
696       scheduleExecWriteRequest(request);
697       hasBeenScheduled = true;
698       GeneratedKeysResult execWriteRequestWithKeysResult = null;
699       try
700       {
701         execWriteRequestWithKeysResult = loadBalanceStatementExecuteUpdateWithKeys(request);
702       }
703       catch (AllBackendsFailedException e)
704       {
705         String JavaDoc msg = Translate
706             .get("requestmanager.write.request.keys.failed.unexpected");
707         logger.fatal(msg, e);
708         endUserLogger.fatal(msg);
709         throw new RuntimeException JavaDoc(msg, e);
710       }
711       updateAndNotifyExecWriteRequest(request, execWriteRequestWithKeysResult
712           .getUpdateCount());
713       schedulerHasBeenNotified = true;
714       return execWriteRequestWithKeysResult;
715     }
716     finally
717     {
718       if (hasBeenScheduled && !schedulerHasBeenNotified)
719         scheduler.writeCompleted(request);
720     }
721   }
722
723   /**
724    * Execute a call to CallableStatement.execute() and returns a suite of
725    * updateCount and/or ResultSets.
726    *
727    * @param request the stored procedure to execute
728    * @return an <code>ExecuteResult</code> object
729    * @throws AllBackendsFailedException if all backends failed to execute the
730    * stored procedure
731    * @exception SQLException if an error occurs
732    */

733   public ExecuteResult statementExecute(AbstractRequest request)
734       throws AllBackendsFailedException, SQLException JavaDoc
735   {
736     // Create a fake stored procedure
737
StoredProcedure proc = new StoredProcedure(request.getSqlOrTemplate(),
738         request.getEscapeProcessing(), request.getTimeout(), request
739             .getLineSeparator());
740     proc.setIsAutoCommit(request.isAutoCommit());
741     proc.setTransactionId(request.getTransactionId());
742     proc.setTransactionIsolation(request.getTransactionIsolation());
743     proc.setId(request.getId());
744     proc.setLogin(request.getLogin());
745     proc.setPreparedStatementParameters(request
746         .getPreparedStatementParameters());
747     proc.setTimeout(request.getTimeout());
748     proc.setMaxRows(request.getMaxRows());
749     proc.setPersistentConnection(request.isPersistentConnection());
750     proc.setPersistentConnectionId(request.getPersistentConnectionId());
751
752     boolean hasBeenScheduled = false;
753     try
754     {
755       ExecuteResult result;
756
757       // Schedule as a stored procedure
758
scheduleStoredProcedure(proc);
759       hasBeenScheduled = true;
760
761       if (logger.isDebugEnabled())
762         logger.debug(Translate.get("requestmanager.write.stored.procedure",
763             new String JavaDoc[]{String.valueOf(request.getId()),
764                 request.getSqlShortForm(vdb.getSqlShortFormLength())}));
765
766       result = loadBalanceStatementExecute(proc);
767
768       updateRecoveryLogFlushCacheAndRefreshSchema(proc);
769
770       // Notify scheduler of completion
771
scheduler.storedProcedureCompleted(proc);
772
773       return result;
774     }
775     catch (AllBackendsFailedException e)
776     {
777       throw e;
778     }
779     finally
780     {
781       if (hasBeenScheduled)
782         scheduler.storedProcedureCompleted(proc);
783     }
784   }
785
786   /**
787    * Schedule a request for execution.
788    *
789    * @param request the request to execute
790    * @throws SQLException if an error occurs
791    */

792   public void scheduleExecWriteRequest(AbstractWriteRequest request)
793       throws SQLException JavaDoc
794   {
795     // Sanity check
796
if (!request.isAutoCommit())
797     { // Check that the transaction has been
798
// started
799
long tid = request.getTransactionId();
800       if (!transactionMetaDatas.containsKey(new Long JavaDoc(tid)))
801         throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
802     }
803
804     getParsingFromCacheOrParse(request);
805
806     //
807
// SCHEDULER
808
//
809

810     if (logger.isDebugEnabled())
811       logger.debug(Translate.get("requestmanager.write.request.schedule",
812           new String JavaDoc[]{String.valueOf(request.getId()),
813               request.getSqlShortForm(vdb.getSqlShortFormLength())}));
814
815     // Wait for the scheduler to give us the authorization to execute
816
try
817     {
818       scheduler.scheduleWriteRequest(request);
819     }
820     catch (RollbackException e)
821     { // Something bad happened and we need to rollback this transaction
822
rollback(request.getTransactionId(), true);
823       throw new SQLException JavaDoc(e.getMessage());
824     }
825   }
826
827   /**
828    * Send the given query to the load balancer. If the request fails, the
829    * scheduler is properly notified.
830    *
831    * @param request the request to execute
832    * @return update count and auto-generated keys
833    * @throws AllBackendsFailedException if all backends failed to execute the
834    * query
835    * @throws NoMoreBackendException if no backends are left to execute the
836    * request
837    * @throws SQLException if an error occurs
838    */

839   public GeneratedKeysResult loadBalanceStatementExecuteUpdateWithKeys(
840       AbstractWriteRequest request) throws AllBackendsFailedException,
841       NoMoreBackendException, SQLException JavaDoc
842   {
843     if (logger.isDebugEnabled())
844       logger.debug(Translate.get("requestmanager.write.request.balance",
845           new String JavaDoc[]{String.valueOf(request.getId()),
846               String.valueOf(request.getTransactionId()),
847               request.getSqlShortForm(vdb.getSqlShortFormLength())}));
848
849     try
850     { // Send the request to the load balancer
851
return loadBalancer
852           .statementExecuteUpdateWithKeys(request, metadataCache);
853     }
854     catch (Exception JavaDoc failed)
855     {
856       if (!vdb.isDistributed())
857       { // Notify log of failure
858
if (recoveryLog != null)
859           recoveryLog.logRequestCompletion(request.getLogId(), false, request
860               .getExecTimeInMs());
861       }
862
863       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
864           request.getSqlShortForm(vdb.getSqlShortFormLength()),
865           failed.getMessage()});
866       if (failed instanceof RuntimeException JavaDoc)
867         logger.warn(msg, failed);
868
869       if (failed instanceof AllBackendsFailedException)
870         throw (AllBackendsFailedException) failed;
871       else if (failed instanceof SQLException JavaDoc)
872         throw (SQLException JavaDoc) failed;
873       else if (failed instanceof NoMoreBackendException)
874         throw (NoMoreBackendException) failed;
875       else
876         throw new SQLException JavaDoc(msg);
877     }
878   }
879
880   /**
881    * Send the given query to the load balancer. If the request fails, the
882    * scheduler is properly notified.
883    *
884    * @param request the request to execute
885    * @throws AllBackendsFailedException if all backends failed to execute the
886    * query
887    * @exception NoMoreBackendException if no backends are left to execute the
888    * request
889    * @throws SQLException if an error occurs
890    * @return number of modified lines
891    */

892   public ExecuteUpdateResult loadBalanceStatementExecuteUpdate(
893       AbstractWriteRequest request) throws AllBackendsFailedException,
894       NoMoreBackendException, SQLException JavaDoc
895   {
896     if (logger.isDebugEnabled())
897       logger.debug(Translate.get("requestmanager.write.request.balance",
898           new String JavaDoc[]{String.valueOf(request.getId()),
899               String.valueOf(request.getTransactionId()),
900               request.getSqlShortForm(vdb.getSqlShortFormLength())}));
901
902     try
903     { // Send the request to the load balancer
904
if (request.isUpdate() && (resultCache != null))
905       { // Try the optimization if we try to update values that are already
906
// up-to-date. Warnings will be lost anyway so forget it
907
if (!resultCache.isUpdateNecessary((UpdateRequest) request))
908           return new ExecuteUpdateResult(0);
909       }
910       return loadBalancer.statementExecuteUpdate(request);
911     }
912     catch (Exception JavaDoc failed)
913     {
914       if (!vdb.isDistributed())
915       { // Notify log of failure
916
if (recoveryLog != null)
917           recoveryLog.logRequestCompletion(request.getLogId(), false, request
918               .getExecTimeInMs());
919       }
920
921       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
922           request.getSqlShortForm(vdb.getSqlShortFormLength()),
923           failed.getMessage()});
924
925       // Error logging
926
if (failed instanceof RuntimeException JavaDoc)
927         logger.warn(msg, failed);
928
929       // Rethrow exception
930
if (failed instanceof AllBackendsFailedException)
931         throw (AllBackendsFailedException) failed;
932       else if (failed instanceof SQLException JavaDoc)
933         throw (SQLException JavaDoc) failed;
934       else if (failed instanceof NoMoreBackendException)
935         throw (NoMoreBackendException) failed;
936       else
937         throw new SQLException JavaDoc(msg);
938     }
939   }
940
941   /**
942    * Execute a request using Statement.execute() on the load balancer. Note that
943    * we flush the cache before calling the load balancer.
944    *
945    * @param request the request to execute.
946    * @return an <code>ExecuteResult</code> object
947    * @throws SQLException if an error occurs
948    * @throws AllBackendsFailedException if all backends failed to execute the
949    * stored procedure
950    */

951   public ExecuteResult loadBalanceStatementExecute(AbstractRequest request)
952       throws AllBackendsFailedException, SQLException JavaDoc
953   {
954     ExecuteResult result;
955     //
956
// CACHE
957
//
958

959     // Flush cache (if any) before
960
if (resultCache != null)
961     {
962       resultCache.flushCache();
963     }
964
965     //
966
// LOAD BALANCER
967
//
968

969     try
970     { // Send the request to the load balancer
971
result = loadBalancer.statementExecute(request, metadataCache);
972       return result;
973     }
974     catch (Exception JavaDoc failed)
975     {
976       if (!vdb.isDistributed())
977       { // Notify log of failure
978
if (recoveryLog != null)
979           recoveryLog.logRequestCompletion(request.getLogId(), false, request
980               .getExecTimeInMs());
981       }
982
983       String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
984           request.getSqlShortForm(vdb.getSqlShortFormLength()),
985           failed.getMessage()});
986       if (failed instanceof RuntimeException JavaDoc)
987         logger.warn(msg, failed);
988
989       if (failed instanceof AllBackendsFailedException)
990         throw (AllBackendsFailedException) failed;
991       else if (failed instanceof SQLException JavaDoc)
992         throw (SQLException JavaDoc) failed;
993       else if (failed instanceof NoMoreBackendException)
994         throw (NoMoreBackendException) failed;
995       else
996         throw (SQLException JavaDoc) new SQLException JavaDoc(msg).initCause(failed);
997     }
998   }
999
1000  /**
1001   * Update the recovery log, cache, update the database schema if needed and
1002   * finally notify the scheduler. Note that if an error occurs, the scheduler
1003   * is always notified.
1004   *
1005   * @param request the request to execute
1006   * @param updateCount the update count if query was executed with
1007   * executeUpdate(), -1 otherwise
1008   * @throws SQLException if an error occurs
1009   */

1010  public void updateAndNotifyExecWriteRequest(AbstractWriteRequest request,
1011      int updateCount) throws SQLException JavaDoc
1012  {
1013    try
1014    {
1015      TransactionMetaData tm = null;
1016      if (!request.isAutoCommit())
1017      {
1018        /*
1019         * This is a write transaction, update the transactional context so that
1020         * commit/rollback for that transaction will be logged.
1021         */

1022        tm = getTransactionMetaData(new Long JavaDoc(request.getTransactionId()));
1023        tm.setReadOnly(false);
1024      }
1025
1026      // Update the recovery log (except for SingleDB)
1027
if ((recoveryLog != null)
1028          && (loadBalancer.getRAIDbLevel() != RAIDbLevels.SingleDB))
1029        recoveryLog.logRequestExecuteUpdateCompletion(request.getLogId(), true,
1030            updateCount, request.getExecTimeInMs());
1031
1032      if (requiredParsingGranularity == ParsingGranularities.NO_PARSING)
1033        return;
1034
1035      if (request.altersSomething())
1036      {
1037        SemanticBehavior semantic = request.getSemantic();
1038        if (semantic == null)
1039        { // Sanity check, should never happen
1040
logger.warn("No semantic information found for request '"
1041              + request.getSqlShortForm(vdb.getSqlShortFormLength())
1042              + "' flushing all caches and refreshing schema.");
1043
1044          flushAllCachesAndUpdateTransactionMetadata(tm);
1045          return;
1046        }
1047
1048        // Use semantic information
1049

1050        // Start to update the query result cache first if needed (must be
1051
// done before altering the schema)
1052
if (((semantic.altersDatabaseSchema()) || semantic
1053            .altersQueryResultCache())
1054            && (resultCache != null))
1055        {
1056          if (logger.isDebugEnabled())
1057            logger.debug(Translate.get(
1058                "requestmanager.write.request.cache.update", new String JavaDoc[]{
1059                    String.valueOf(request.getId()),
1060                    request.getSqlShortForm(vdb.getSqlShortFormLength())}));
1061
1062          if (semantic.altersDatabaseSchema() && tm != null)
1063            tm.setAltersQueryResultCache(true);
1064          resultCache.writeNotify(request);
1065        }
1066
1067        if (tm != null)
1068          tm.setAltersDatabaseSchema(true);
1069
1070        synchronized (this)
1071        {
1072          if (getDatabaseSchema() != null)
1073            getDatabaseSchema().updateDatabaseSchema(request, logger, this, tm,
1074                null);
1075        }
1076
1077        if (semantic.altersMetadataCache())
1078        {
1079          flushMetadataCacheAndUpdateTransactionMetadata(tm);
1080        }
1081      }
1082    }
1083    catch (Exception JavaDoc failed)
1084    {
1085      String JavaDoc msg = Translate.get("requestmanager.request.failed", new String JavaDoc[]{
1086          request.getSqlShortForm(vdb.getSqlShortFormLength()),
1087          failed.getMessage()});
1088      if (failed instanceof RuntimeException JavaDoc)
1089        logger.warn(msg, failed);
1090      else
1091        logger.warn(msg);
1092      throw new SQLException JavaDoc(msg);
1093    }
1094    finally
1095    {
1096      // Notify scheduler
1097
scheduler.writeCompleted(request);
1098    }
1099  }
1100
1101  /**
1102   * Call a stored procedure that returns a ResultSet.
1103   *
1104   * @param proc the stored procedure call
1105   * @return a <code>ControllerResultSet</code> value
1106   * @throws AllBackendsFailedException if all backends failed to execute the
1107   * stored procedure
1108   * @exception SQLException if an error occurs
1109   */

1110  public ControllerResultSet callableStatementExecuteQuery(StoredProcedure proc)
1111      throws AllBackendsFailedException, SQLException JavaDoc
1112  {
1113    ControllerResultSet result = null;
1114    boolean hasBeenScheduled = false;
1115    boolean success = false;
1116    try
1117    {
1118      scheduleStoredProcedure(proc);
1119      hasBeenScheduled = true;
1120
1121      if (logger.isDebugEnabled())
1122        logger.debug(Translate.get("requestmanager.read.stored.procedure",
1123            new String JavaDoc[]{String.valueOf(proc.getId()),
1124                proc.getSqlShortForm(vdb.getSqlShortFormLength())}));
1125
1126      result = loadBalanceCallableStatementExecuteQuery(proc);
1127
1128      updateRecoveryLogFlushCacheAndRefreshSchema(proc);
1129
1130      success = true;
1131      return result;
1132    }
1133    catch (AllBackendsFailedException e)
1134    {
1135      throw e;
1136    }
1137    catch (NoMoreBackendException e)
1138    {
1139      throw e;
1140    }
1141    catch (Exception JavaDoc failed)
1142    {
1143      String JavaDoc msg = Translate.get("requestmanager.stored.procedure.failed",
1144          new String JavaDoc[]{proc.getSqlShortForm(vdb.getSqlShortFormLength()),
1145              failed.getMessage()});
1146      logger.warn(msg);
1147      if (failed instanceof SQLException JavaDoc)
1148      {
1149        throw (SQLException JavaDoc) failed;
1150      }
1151      throw new SQLException JavaDoc(msg);
1152    }
1153    finally
1154    {
1155      if (hasBeenScheduled)
1156        scheduler.storedProcedureCompleted(proc);
1157      if (!vdb.isDistributed() && !success)
1158      { // Notify log of failure
1159
if (recoveryLog != null)
1160          recoveryLog.logRequestCompletion(proc.getLogId(), false, proc
1161              .getExecTimeInMs());
1162      }
1163    }
1164  }
1165
1166  /**
1167   * Call a stored procedure that performs an update.
1168   *
1169   * @param proc the stored procedure call
1170   * @return number of rows affected
1171   * @throws AllBackendsFailedException if all backends failed to execute the
1172   * stored procedure
1173   * @exception SQLException if an error occurs
1174   */

1175  public ExecuteUpdateResult callableStatementExecuteUpdate(StoredProcedure proc)
1176      throws AllBackendsFailedException, SQLException JavaDoc
1177  {
1178    ExecuteUpdateResult result;
1179    boolean hasBeenScheduled = false;
1180    boolean success = false;
1181    try
1182    {
1183      // Wait for the scheduler to give us the authorization to execute
1184
scheduleStoredProcedure(proc);
1185      hasBeenScheduled = true;
1186
1187      if (logger.isDebugEnabled())
1188        logger.debug(Translate.get("requestmanager.write.stored.procedure",
1189            new String JavaDoc[]{String.valueOf(proc.getId()),
1190                proc.getSqlShortForm(vdb.getSqlShortFormLength())}));
1191
1192      result = loadBalanceCallableStatementExecuteUpdate(proc);
1193
1194      updateRecoveryLogFlushCacheAndRefreshSchema(proc);
1195
1196      // Notify scheduler of completion
1197
scheduler.storedProcedureCompleted(proc);
1198
1199      success = true;
1200      return result;
1201    }
1202    catch (AllBackendsFailedException e)
1203    {
1204      throw e;
1205    }
1206    catch (Exception JavaDoc failed)
1207    {
1208      String JavaDoc msg = Translate.get("requestmanager.stored.procedure.failed",
1209          new String JavaDoc[]{proc.getSqlShortForm(vdb.getSqlShortFormLength()),
1210              failed.getMessage()});
1211      logger.warn(msg);
1212      if (failed instanceof SQLException JavaDoc)
1213      {
1214        throw (SQLException JavaDoc) failed;
1215      }
1216      throw new SQLException JavaDoc(msg);
1217    }
1218    finally
1219    {
1220      if (hasBeenScheduled)
1221        scheduler.storedProcedureCompleted(proc);
1222      if (!vdb.isDistributed() && !success)
1223      { // Notify log of failure
1224
if (recoveryLog != null)
1225          recoveryLog.logRequestCompletion(proc.getLogId(), false, proc
1226              .getExecTimeInMs());
1227      }
1228    }
1229  }
1230
1231  /**
1232   * Execute a call to CallableStatement.execute() and returns a suite of
1233   * updateCount and/or ResultSets.
1234   *
1235   * @param proc the stored procedure to execute
1236   * @return an <code>ExecuteResult</code> object
1237   * @throws AllBackendsFailedException if all backends failed to execute the
1238   * stored procedure
1239   * @exception SQLException if an error occurs
1240   */

1241  public ExecuteResult callableStatementExecute(StoredProcedure proc)
1242      throws AllBackendsFailedException, SQLException JavaDoc
1243  {
1244    ExecuteResult result;
1245    boolean hasBeenScheduled = false;
1246    boolean success = false;
1247    try
1248    {
1249      scheduleStoredProcedure(proc);
1250      hasBeenScheduled = true;
1251
1252      if (logger.isDebugEnabled())
1253        logger.debug(Translate.get("requestmanager.write.stored.procedure",
1254            new String JavaDoc[]{String.valueOf(proc.getId()),
1255                proc.getSqlShortForm(vdb.getSqlShortFormLength())}));
1256
1257      result = loadBalanceCallableStatementExecute(proc);
1258
1259      updateRecoveryLogFlushCacheAndRefreshSchema(proc);
1260
1261      success = true;
1262      return result;
1263    }
1264    catch (AllBackendsFailedException e)
1265    {
1266      throw e;
1267    }
1268    catch (NoMoreBackendException e)
1269    {
1270      throw e;
1271    }
1272    catch (Exception JavaDoc failed)
1273    {
1274      String JavaDoc msg = Translate.get("requestmanager.stored.procedure.failed",
1275          new String JavaDoc[]{proc.getSqlShortForm(vdb.getSqlShortFormLength()),
1276              failed.getMessage()});
1277      logger.warn(msg);
1278      if (failed instanceof SQLException JavaDoc)
1279      {
1280        throw (SQLException JavaDoc) failed;
1281      }
1282      throw new SQLException JavaDoc(msg);
1283    }
1284    finally
1285    {
1286      if (hasBeenScheduled)
1287        scheduler.storedProcedureCompleted(proc);
1288      if (!vdb.isDistributed() && !success)
1289      { // Notify log of failure
1290
if (recoveryLog != null)
1291          recoveryLog.logRequestCompletion(proc.getLogId(), false, proc
1292              .getExecTimeInMs());
1293      }
1294    }
1295  }
1296
1297  /**
1298   * This method does some sanity check on the given stored procedure and then
1299   * tries to schedule it. Note that it is more likely that on a stored
1300   * procedure the scheduler will lock in write the entire database as it does
1301   * not know which tables are accessed by the procedure.
1302   *
1303   * @param proc the stored procedure to schedule
1304   * @throws SQLException if an error occurs
1305   */

1306  public void scheduleStoredProcedure(StoredProcedure proc) throws SQLException JavaDoc
1307  {
1308    // Sanity check
1309
if (!proc.isAutoCommit())
1310    { // Check that the transaction has been started
1311
long tid = proc.getTransactionId();
1312      if (!transactionMetaDatas.containsKey(new Long JavaDoc(tid)))
1313        throw new SQLException JavaDoc(Translate.get("transaction.not.started", tid));
1314    }
1315
1316    getParsingFromCacheOrParse(proc);
1317
1318    //
1319
// SCHEDULER
1320
//
1321

1322    // Wait for the scheduler to give us the authorization to execute
1323
try
1324    {
1325      scheduler.scheduleStoredProcedure(proc);
1326    }
1327    catch (RollbackException e)
1328    { // Something bad happened and we need to rollback this transaction
1329
rollback(proc.getTransactionId(), true);
1330      throw new SQLException JavaDoc(e.getMessage());
1331    }
1332  }
1333
1334  /**
1335   * Execute a read stored procedure on the load balancer. Note that we flush
1336   * the cache before calling the load balancer.
1337   *
1338   * @param proc the stored procedure to call
1339   * @return the corresponding ControllerResultSet
1340   * @throws SQLException if an error occurs
1341   * @throws AllBackendsFailedException if all backends failed to execute the
1342   * stored procedure
1343   */

1344  public ControllerResultSet loadBalanceCallableStatementExecuteQuery(
1345      StoredProcedure proc) throws SQLException JavaDoc, AllBackendsFailedException
1346  {
1347    SemanticBehavior semantic = proc.getSemantic();
1348    ControllerResultSet result;
1349
1350    //
1351
// CACHE
1352
//
1353

1354    // Cache is always flushed unless the user has explicitely set the
1355
// connection to read-only mode in which case we assume that the
1356
// users deliberately forces the cache not to be flushed when calling
1357
// this stored procedure.
1358
if ((resultCache != null) && (!proc.isReadOnly()))
1359    {
1360      if ((semantic == null) || (!semantic.isReadOnly()))
1361        resultCache.flushCache();
1362    }
1363
1364    //
1365
// LOAD BALANCER
1366
//
1367

1368    // Send the request to the load balancer
1369
if (proc.isReadOnly() || ((semantic != null) && (semantic.isReadOnly())))
1370      result = loadBalancer.readOnlyCallableStatementExecuteQuery(proc,
1371          metadataCache);
1372    else
1373    {
1374      // Disable fetch size with a distributed execution
1375
proc.setFetchSize(0);
1376      result = loadBalancer.callableStatementExecuteQuery(proc, metadataCache);
1377    }
1378    return result;
1379  }
1380
1381  /**
1382   * Execute a write stored procedure on the load balancer. Note that we flush
1383   * the cache before calling the load balancer.
1384   *
1385   * @param proc the stored procedure to call
1386   * @return the number of updated rows
1387   * @throws SQLException if an error occurs
1388   * @throws AllBackendsFailedException if all backends failed to execute the
1389   * stored procedure
1390   */

1391  public ExecuteUpdateResult loadBalanceCallableStatementExecuteUpdate(
1392      StoredProcedure proc) throws AllBackendsFailedException, SQLException JavaDoc
1393  {
1394    ExecuteUpdateResult result;
1395    //
1396
// CACHE
1397
//
1398

1399    // Flush cache (if any) before as we don't properly lock the tables
1400
if (resultCache != null)
1401    {
1402      SemanticBehavior semantic = proc.getSemantic();
1403      if ((semantic == null) || (!semantic.isReadOnly()))
1404        resultCache.flushCache();
1405    }
1406
1407    //
1408
// LOAD BALANCER
1409
//
1410

1411    // Send the request to the load balancer
1412
result = loadBalancer.callableStatementExecuteUpdate(proc);
1413    return result;
1414  }
1415
1416  /**
1417   * Execute a write stored procedure on the load balancer. Note that we flush
1418   * the cache before calling the load balancer.
1419   *
1420   * @param proc the stored procedure to call
1421   * @return an <code>ExecuteResult</code> object
1422   * @throws SQLException if an error occurs
1423   * @throws AllBackendsFailedException if all backends failed to execute the
1424   * stored procedure
1425   */

1426  public ExecuteResult loadBalanceCallableStatementExecute(StoredProcedure proc)
1427      throws AllBackendsFailedException, SQLException JavaDoc
1428  {
1429    SemanticBehavior semantic = proc.getSemantic();
1430    ExecuteResult result;
1431
1432    //
1433
// CACHE
1434
//
1435

1436    // Flush cache (if any) before as we don't properly lock the tables
1437
if (resultCache != null)
1438    {
1439      if ((semantic == null) || (!semantic.isReadOnly()))
1440        resultCache.flushCache();
1441    }
1442
1443    //
1444
// LOAD BALANCER
1445
//
1446

1447    // Send the request to the load balancer
1448
if (proc.isReadOnly() || ((semantic != null) && (semantic.isReadOnly())))
1449      result = loadBalancer.readOnlyCallableStatementExecute(proc,
1450          metadataCache);
1451    else
1452      result = loadBalancer.callableStatementExecute(proc, metadataCache);
1453    return result;
1454  }
1455
1456  /**
1457   * Update the recovery log with successful completion of the query, flush the
1458   * cache and force schema refresh if needed.
1459   *
1460   * @param proc the stored procedure to log
1461   * @throws SQLException if the transaction context could not be updated
1462   */

1463  public void updateRecoveryLogFlushCacheAndRefreshSchema(StoredProcedure proc)
1464      throws SQLException JavaDoc
1465  {
1466    // Stored procedures executing on a read-only connection don't update
1467
// anything
1468
if (proc.isReadOnly())
1469      return;
1470
1471    SemanticBehavior semantic = proc.getSemantic();
1472    TransactionMetaData tm = null;
1473    if (!proc.isAutoCommit())
1474      tm = getTransactionMetaData(new Long JavaDoc(proc.getTransactionId()));
1475
1476    if ((semantic == null) || (semantic.altersDatabaseSchema()))
1477    {
1478      // Schema might have been updated, force refresh
1479
if ((semantic == null) && (logger.isDebugEnabled()))
1480        logger.debug("No semantic found for stored procedure "
1481            + proc.getSqlShortForm(vdb.getSqlShortFormLength())
1482            + ". Forcing a schema refresh.");
1483      setSchemaIsDirty(true);
1484
1485      if (tm != null)
1486        tm.setAltersDatabaseSchema(true);
1487    }
1488
1489    if ((semantic == null) || (!semantic.isReadOnly()))
1490    {
1491      // Update recovery log
1492
if ((recoveryLog != null)
1493          && (loadBalancer.getRAIDbLevel() != RAIDbLevels.SingleDB))
1494        recoveryLog.logRequestCompletion(proc.getLogId(), true, proc
1495            .getExecTimeInMs());
1496
1497      //
1498
// Update transaction context if needed to force commit to be logged
1499
// (occurs if callableStatement.executeQuery() is called without semantic
1500
// information.
1501
//
1502
if (tm != null)
1503      {
1504        tm.setReadOnly(false);
1505        tm.setAltersQueryResultCache(true);
1506      }
1507
1508      //
1509
// CACHE
1510
//
1511

1512      if (resultCache != null)
1513        resultCache.writeNotify(proc);
1514
1515      if ((metadataCache != null)
1516          && ((semantic == null) || (semantic.altersDatabaseSchema())))
1517        metadataCache.flushCache();
1518    }
1519  }
1520
1521  /**
1522   * Return a ControllerResultSet containing the PreparedStatement metaData of
1523   * the given sql template
1524   *
1525   * @param request the request containing the sql template
1526   * @return an empty ControllerResultSet with the metadata
1527   * @throws SQLException if a database error occurs
1528   */

1529  public ControllerResultSet getPreparedStatementGetMetaData(
1530      AbstractRequest request) throws SQLException JavaDoc
1531  {
1532    return loadBalancer.getPreparedStatementGetMetaData(request);
1533  }
1534
1535  //
1536
// Transaction management
1537
//
1538

1539  /**
1540   * Begin a new transaction and return the corresponding transaction
1541   * identifier. This method is called from the driver when setAutoCommit(false)
1542   * is called.
1543   * <p>
1544   * Note that the transaction begin is not logged in the recovery log by this
1545   * method, you will have to call logLazyTransactionBegin.
1546   *
1547   * @param login the login used by the connection
1548   * @param isPersistentConnection true if the transaction is started on a
1549   * persistent connection
1550   * @param persistentConnectionId persistent connection id if the transaction
1551   * must be started on a persistent connection
1552   * @return long a unique transaction identifier
1553   * @throws SQLException if an error occurs
1554   * @see #logLazyTransactionBegin(long)
1555   */

1556  public long begin(String JavaDoc login, boolean isPersistentConnection,
1557      long persistentConnectionId) throws SQLException JavaDoc
1558  {
1559    long tid = scheduler.getNextTransactionId();
1560    doBegin(login, tid, isPersistentConnection, persistentConnectionId);
1561    return tid;
1562  }
1563
1564  /**
1565   * Begins a new transaction for the given <code>login</code> and
1566   * <code>tid</code>. Informs the loadbalancer and the scheduler about this
1567   * begun transaction.
1568   *
1569   * @param login the login used by the connection
1570   * @param tid the tid associed with the transaction to begin
1571   * @param isPersistentConnection true if the transaction is started on a
1572   * persistent connection
1573   * @param persistentConnectionId persistent connection id if the transaction
1574   * must be started on a persistent connection
1575   * @throws SQLException if an error occurs
1576   */

1577  public void doBegin(String JavaDoc login, long tid, boolean isPersistentConnection,
1578      long persistentConnectionId) throws SQLException JavaDoc
1579  {
1580    try
1581    {
1582      TransactionMetaData tm = new TransactionMetaData(tid, beginTimeout,
1583          login, isPersistentConnection, persistentConnectionId);
1584      scheduler.begin(tm, false, null);
1585
1586      if (logger.isDebugEnabled())
1587        logger.debug(Translate.get("transaction.begin", String.valueOf(tid)));
1588
1589      try
1590      {
1591        // Send to load balancer
1592
loadBalancer.begin(tm);
1593        // the tid is stored *now* before notifying the scheduler of begin
1594
// completion to avoid releasing pending write queries before
1595
// transaction list is updated
1596
transactionMetaDatas.put(new Long JavaDoc(tid), tm);
1597      }
1598      catch (SQLException JavaDoc e)
1599      {
1600        throw e;
1601      }
1602      finally
1603      {
1604        // Notify scheduler for completion in any case
1605
scheduler.beginCompleted(tid);
1606      }
1607    }
1608    catch (RuntimeException JavaDoc e)
1609    {
1610      String JavaDoc msg = Translate
1611          .get("fatal.runtime.exception.requestmanager.begin");
1612      logger.fatal(msg, e);
1613      endUserLogger.fatal(msg);
1614      throw new SQLException JavaDoc(e.getMessage());
1615    }
1616  }
1617
1618  /**
1619   * Log the begin of a transaction that is started lazily. In fact, we just log
1620   * the begin when we execute the first write request in a transaction to
1621   * prevent logging begin/commit for read-only transactions. This also prevents
1622   * a problem with backends that are disabled with a checkpoint when no request
1623   * has been played in the transaction but the begin statement has already been
1624   * logged. In that case, the transaction would not be properly replayed at
1625   * restore time.
1626   *
1627   * @param transactionId the transaction id begin to log
1628   * @throws SQLException if an error occurs
1629   */

1630  public void logLazyTransactionBegin(long transactionId) throws SQLException JavaDoc
1631  {
1632    try
1633    {
1634      Long JavaDoc tid = new Long JavaDoc(transactionId);
1635      TransactionMetaData tm = getTransactionMetaData(tid);
1636
1637      if (logger.isDebugEnabled())
1638        logger.debug(Translate.get("transaction.begin.log", String
1639            .valueOf(transactionId)));
1640
1641      // Log the begin
1642
if (recoveryLog != null)
1643        recoveryLog.logBegin(tm);
1644    }
1645    catch (RuntimeException JavaDoc e)
1646    {
1647      String JavaDoc msg = Translate
1648          .get("fatal.runtime.exception.requestmanager.begin.log");
1649      logger.fatal(msg, e);
1650      endUserLogger.fatal(msg);
1651      throw new SQLException JavaDoc(e.getMessage());
1652    }
1653  }
1654
1655  /**
1656   * Abort a transaction that has been started but in which no query was
1657   * executed. As we use lazy transaction begin, there is no need to rollback
1658   * such transaction but just to cleanup the metadata associated with this not
1659   * effectively started transaction.
1660   *
1661   * @param transactionId id of the transaction to abort
1662   * @param logAbort true if the abort (in fact rollback) should be logged in
1663   * the recovery log
1664   * @param forceAbort true if the abort will be forced. Actually, abort will do
1665   * nothing when a transaction has savepoints (we do not abort the
1666   * whole transaction, so that the user can rollback to a previous
1667   * savepoint), except when the connection is closed. In this last
1668   * case, if the transaction is not aborted, it prevents future
1669   * maintenance operations such as shutdowns, enable/disable from
1670   * completing, so we have to force this abort operation. It also
1671   * applies to the DeadlockDetectionThread and the cleanup of the
1672   * VirtualDatabaseWorkerThread.
1673   * @throws SQLException if an error occurs
1674   */

1675  public void abort(long transactionId, boolean logAbort, boolean forceAbort)
1676      throws SQLException JavaDoc
1677  {
1678    TransactionMetaData tm;
1679    try
1680    {
1681      Long JavaDoc tid = new Long JavaDoc(transactionId);
1682      boolean tmIsFake = false;
1683      try
1684      {
1685        tm = getTransactionMetaData(tid);
1686        if (!forceAbort && tidSavepoints.get(tid) != null)
1687        {
1688          if (logger.isDebugEnabled())
1689            logger.debug("Transaction " + transactionId
1690                + " has savepoints, transaction will not be aborted");
1691          return;
1692        }
1693      }
1694      catch (SQLException JavaDoc e1)
1695      {
1696        logger.warn("No transaction metadata found to abort transaction "
1697            + transactionId + ". Creating a fake context for abort.");
1698        // Note that we ignore the persistent connection id (will be retrieved
1699
// by the connection manager)
1700
tm = new TransactionMetaData(transactionId, 0,
1701            RecoveryLog.UNKNOWN_USER, false, 0);
1702        tm.setReadOnly(!logAbort);
1703        tmIsFake = true;
1704        if (tidSavepoints.get(tid) != null)
1705        {
1706          if (logger.isDebugEnabled())
1707            logger.debug("Transaction " + transactionId
1708                + " has savepoints, transaction will not be aborted");
1709          return;
1710        }
1711      }
1712
1713      VirtualDatabaseWorkerThread vdbwt = vdb
1714          .getVirtualDatabaseWorkerThreadForTransaction(transactionId);
1715      if (vdbwt != null)
1716        vdbwt.notifyAbort(transactionId);
1717
1718      if (logger.isDebugEnabled())
1719        logger.debug(Translate.get("transaction.aborting", String
1720            .valueOf(transactionId)));
1721
1722      // Notify the scheduler to abort which is the same as a rollback
1723
// from a scheduler point of view.
1724

1725      boolean abortScheduled = false;
1726      try
1727      {
1728        if (!tmIsFake)
1729        {
1730          scheduler.rollback(tm, null);
1731          abortScheduled = true;
1732        }
1733
1734        loadBalancer.abort(tm);
1735
1736        // Update recovery log
1737
if (recoveryLog != null)
1738          recoveryLog.logRequestCompletion(tm.getLogId(), true, 0);
1739
1740        // Invalidate the query result cache if this transaction has updated the
1741
// cache or altered the schema
1742
if ((resultCache != null)
1743            && (tm.altersQueryResultCache() || tm.altersDatabaseSchema()))
1744          resultCache.rollback(transactionId);
1745
1746        // Check for schema modifications that need to be rollbacked
1747
if (tm.altersDatabaseSchema())
1748        {
1749          if (metadataCache != null)
1750            metadataCache.flushCache();
1751          setSchemaIsDirty(true);
1752        }
1753      }
1754
1755      // FIXME This whole catch block is ugly. In particular the Rollback task
1756
// is re-created. The original task is created and posted on the total
1757
// order queue by the call to loadBalancer.abort() above.
1758
// The aim is to ensure that we log the rollback when no backends are
1759
// enabled even for read only transactions. This is needed since a begin
1760
// could have been previously logged.
1761
catch (NoMoreBackendException e)
1762      {
1763        // Log the query in any case for later recovery (if the request really
1764
// failed, it will be unloged later)
1765
if (getRecoveryLog() != null)
1766        {
1767          if (logger.isDebugEnabled())
1768            logger.debug(Translate
1769                .get("virtualdatabase.distributed.abort.logging.only",
1770                    transactionId));
1771
1772          // Wait to be sure that we log in the proper order
1773
DistributedRollback totalOrderAbort = new DistributedRollback(tm
1774              .getLogin(), transactionId);
1775          if (getLoadBalancer().waitForTotalOrder(totalOrderAbort, false))
1776            getLoadBalancer().removeObjectFromAndNotifyTotalOrderQueue(
1777                totalOrderAbort);
1778
1779          // Update recovery log
1780
this.getRecoveryLog().logRequestCompletion(tm.getLogId(), false, 0);
1781          e.setRecoveryLogId(tm.getLogId());
1782          e.setLogin(tm.getLogin());
1783        }
1784        throw e;
1785      }
1786
1787      catch (SQLException JavaDoc e)
1788      {
1789        /*
1790         * Check if the we have to remove the entry from the total order queue
1791         * (wait to be sure that we do it in the proper order)
1792         */

1793        DistributedRollback totalOrderAbort = new DistributedRollback(tm
1794            .getLogin(), transactionId);
1795        if (loadBalancer.waitForTotalOrder(totalOrderAbort, false))
1796          loadBalancer
1797              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderAbort);
1798
1799        // Update recovery log
1800
if (recoveryLog != null)
1801          recoveryLog.logRequestCompletion(tm.getLogId(), false, 0);
1802
1803        throw e;
1804      }
1805      finally
1806      {
1807        if (abortScheduled) // is not set if tm is fake
1808
{
1809          // Notify scheduler for completion
1810
scheduler.rollbackCompleted(tm, true);
1811        }
1812
1813        completeTransaction(tid);
1814
1815        if (logger.isDebugEnabled())
1816          logger.debug(Translate.get("transaction.aborted", String
1817              .valueOf(transactionId)));
1818      }
1819    }
1820    catch (RuntimeException JavaDoc e)
1821    {
1822      String JavaDoc msg = Translate
1823          .get("fatal.runtime.exception.requestmanager.abort");
1824      logger.fatal(msg, e);
1825      endUserLogger.fatal(msg);
1826      throw new SQLException JavaDoc(e.getMessage());
1827    }
1828  }
1829
1830  /**
1831   * Get the TransactionMetaData for the given transaction id.
1832   *
1833   * @param tid transaction id
1834   * @return the TransactionMetaData
1835   * @throws SQLException if no marker has been found for this transaction
1836   */

1837  public TransactionMetaData getTransactionMetaData(Long JavaDoc tid)
1838      throws SQLException JavaDoc
1839  {
1840    TransactionMetaData tm = (TransactionMetaData) transactionMetaDatas
1841        .get(tid);
1842
1843    if (tm == null)
1844      throw new SQLException JavaDoc(Translate.get("transaction.marker.not.found",
1845          String.valueOf(tid)));
1846
1847    return tm;
1848  }
1849
1850  /**
1851   * Complete the transaction by removing it from the transactionMetaDatas.
1852   *
1853   * @param tid transaction id
1854   */

1855  public void completeTransaction(Long JavaDoc tid)
1856  {
1857    if (loadBalancer.waitForCompletionPolicy.getPolicy() == WaitForCompletionPolicy.ALL)
1858    {
1859      transactionMetaDatas.remove(tid);
1860      tidSavepoints.remove(tid);
1861    }
1862    else
1863    { // Asynchronous execution, wait for the last backend to complete the
1864
// transaction
1865
TransactionMetaData tm = (TransactionMetaData) transactionMetaDatas
1866          .get(tid);
1867      if (tm != null)
1868      { // Check that everyone has release its locks
1869
if (tm.isLockedByBackends())
1870        {
1871          return;
1872        }
1873        transactionMetaDatas.remove(tid);
1874      }
1875      tidSavepoints.remove(tid);
1876    }
1877  }
1878
1879  /**
1880   * Commit a transaction given its id.
1881   *
1882   * @param transactionId the transaction id
1883   * @param logCommit true if the commit should be logged in the recovery log
1884   * @param emptyTransaction true if this transaction has not executed any
1885   * request
1886   * @throws SQLException if an error occurs
1887   */

1888  public void commit(long transactionId, boolean logCommit,
1889      boolean emptyTransaction) throws SQLException JavaDoc
1890  {
1891    long startTime = System.currentTimeMillis();
1892    try
1893    {
1894      Long JavaDoc tid = new Long JavaDoc(transactionId);
1895      TransactionMetaData tm = getTransactionMetaData(tid);
1896
1897      boolean commitScheduled = false;
1898      boolean success = false;
1899      try
1900      {
1901        scheduler.commit(tm, emptyTransaction, null);
1902        commitScheduled = true;
1903        if (!emptyTransaction)
1904        {
1905          if (logger.isDebugEnabled())
1906            logger.debug(Translate.get("transaction.commit", String
1907                .valueOf(tid)));
1908
1909          // Send to load balancer
1910
loadBalancer.commit(tm);
1911
1912          // Notify the cache
1913
if (resultCache != null)
1914            resultCache.commit(tm.getTransactionId());
1915        }
1916        success = true;
1917      }
1918      catch (SQLException JavaDoc e)
1919      {
1920        /*
1921         * Check if the we have to remove the entry from the total order queue
1922         * (wait to be sure that we do it in the proper order)
1923         */

1924        DistributedCommit totalOrderCommit = new DistributedCommit(tm
1925            .getLogin(), transactionId);
1926        if (loadBalancer.waitForTotalOrder(totalOrderCommit, false))
1927          loadBalancer
1928              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderCommit);
1929
1930        throw e;
1931      }
1932      catch (AllBackendsFailedException e)
1933      {
1934        String JavaDoc msg = "All backends failed to commit transaction "
1935            + transactionId + " (" + e + ")";
1936        logger.error(msg);
1937        throw new SQLException JavaDoc(msg);
1938      }
1939      finally
1940      {
1941        // Update the recovery log
1942
// The recovery log will take care of read-only transactions to log
1943
// only what is required.
1944
if (recoveryLog != null && tm.getLogId() != 0)
1945          recoveryLog.logRequestCompletion(tm.getLogId(), success, System
1946              .currentTimeMillis()
1947              - startTime);
1948        if (commitScheduled)
1949        {
1950          // Notify scheduler for completion
1951
scheduler.commitCompleted(tm, success);
1952        }
1953        if (success)
1954        {
1955          completeTransaction(tid);
1956        }
1957      }
1958    }
1959    catch (RuntimeException JavaDoc e)
1960    {
1961      String JavaDoc msg = Translate
1962          .get("fatal.runtime.exception.requestmanager.commit");
1963      logger.fatal(msg, e);
1964      endUserLogger.fatal(msg);
1965      throw new SQLException JavaDoc(e.getMessage());
1966    }
1967  }
1968
1969  /**
1970   * Rollback a transaction given its id.
1971   *
1972   * @param transactionId the transaction id
1973   * @param logRollback true if the rollback should be logged in the recovery
1974   * log
1975   * @throws SQLException if an error occurs
1976   */

1977  public void rollback(long transactionId, boolean logRollback)
1978      throws SQLException JavaDoc
1979  {
1980    long startTime = System.currentTimeMillis();
1981    try
1982    {
1983      Long JavaDoc tid = new Long JavaDoc(transactionId);
1984      TransactionMetaData tm = getTransactionMetaData(tid);
1985
1986      // Wait for the scheduler to give us the authorization to execute
1987

1988      boolean rollbackScheduled = false;
1989      boolean success = false;
1990      try
1991      {
1992        scheduler.rollback(tm, null);
1993        rollbackScheduled = true;
1994
1995        if (logger.isDebugEnabled())
1996          logger.debug(Translate.get("transaction.rollback", String
1997              .valueOf(transactionId)));
1998
1999        // Send to load balancer
2000
loadBalancer.rollback(tm);
2001
2002        // Invalidate the query result cache if this transaction has updated the
2003
// cache or altered the schema
2004
if ((resultCache != null)
2005            && (tm.altersQueryResultCache() || tm.altersDatabaseSchema()))
2006          resultCache.rollback(transactionId);
2007
2008        // Check for schema modifications that need to be rollbacked
2009
if (tm.altersDatabaseSchema())
2010        {
2011          if (metadataCache != null)
2012            metadataCache.flushCache();
2013          setSchemaIsDirty(true);
2014        }
2015
2016        success = true;
2017      }
2018      catch (SQLException JavaDoc e)
2019      {
2020        /*
2021         * Check if the we have to remove the entry from the total order queue
2022         * (wait to be sure that we do it in the proper order)
2023         */

2024        DistributedRollback totalOrderRollback = new DistributedRollback(tm
2025            .getLogin(), transactionId);
2026        if (loadBalancer.waitForTotalOrder(totalOrderRollback, false))
2027        {
2028          loadBalancer
2029              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderRollback);
2030        }
2031        else if (recoveryLog != null)
2032        { // Force rollback logging even in the case of failure. The recovery
2033
// log will take care of empty or read-only transactions that do not
2034
// need to log the rollback.
2035
if (!recoveryLog.findRollbackForTransaction(tm.getTransactionId()))
2036            recoveryLog.logRollback(tm);
2037          if (!rollbackScheduled)
2038            recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2039                .currentTimeMillis()
2040                - startTime);
2041        }
2042
2043        throw e;
2044      }
2045      catch (AllBackendsFailedException e)
2046      {
2047        String JavaDoc msg = Translate.get("requestmanager.rollback.failed.all",
2048            new String JavaDoc[]{String.valueOf(transactionId), e.getMessage()});
2049        logger.error(msg);
2050        throw new SQLException JavaDoc(msg);
2051      }
2052      finally
2053      {
2054        if (rollbackScheduled)
2055        {
2056          // Update the recovery log
2057
if (recoveryLog != null)
2058            recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2059                .currentTimeMillis()
2060                - startTime);
2061
2062          // Notify scheduler for completion
2063
scheduler.rollbackCompleted(tm, success);
2064        }
2065
2066        completeTransaction(tid);
2067      }
2068    }
2069    catch (RuntimeException JavaDoc e)
2070    {
2071      String JavaDoc msg = Translate
2072          .get("fatal.runtime.exception.requestmanager.rollback");
2073      logger.fatal(msg, e);
2074      endUserLogger.fatal(msg);
2075      throw new SQLException JavaDoc(e.getMessage());
2076    }
2077  }
2078
2079  /**
2080   * Rollback a transaction given its id to a savepoint given its name.
2081   *
2082   * @param transactionId the transaction id
2083   * @param savepointName the name of the savepoint
2084   * @throws SQLException if an error occurs
2085   */

2086  public void rollback(long transactionId, String JavaDoc savepointName)
2087      throws SQLException JavaDoc
2088  {
2089    long startTime = System.currentTimeMillis();
2090    try
2091    {
2092      Long JavaDoc tid = new Long JavaDoc(transactionId);
2093      TransactionMetaData tm = getTransactionMetaData(tid);
2094
2095      boolean rollbackScheduled = false;
2096      boolean validSavepoint = false;
2097
2098      try
2099      {
2100        // Check that a savepoint with given name has been set
2101
if (!hasSavepoint(tid, savepointName))
2102          throw new SQLException JavaDoc(Translate.get(
2103              "transaction.savepoint.not.found", new String JavaDoc[]{savepointName,
2104                  String.valueOf(transactionId)}));
2105
2106        validSavepoint = true;
2107
2108        // Wait for the scheduler to give us the authorization to execute
2109
scheduler.rollback(tm, savepointName, null);
2110        rollbackScheduled = true;
2111
2112        if (logger.isDebugEnabled())
2113          logger.debug(Translate.get("transaction.rollbacksavepoint",
2114              new String JavaDoc[]{String.valueOf(transactionId), savepointName}));
2115
2116        // Send to loadbalancer
2117
loadBalancer.rollbackToSavepoint(tm, savepointName);
2118
2119        // Update the recovery log
2120
if (recoveryLog != null)
2121          recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2122              .currentTimeMillis()
2123              - startTime);
2124
2125        // Invalidate the query result cache if this transaction has updated the
2126
// cache or altered the schema
2127
if ((resultCache != null)
2128            && (tm.altersQueryResultCache() || tm.altersDatabaseSchema()))
2129          resultCache.rollback(transactionId);
2130
2131        // Check for schema modifications that need to be rollbacked
2132
if (tm.altersDatabaseSchema())
2133        {
2134          if (metadataCache != null)
2135            metadataCache.flushCache();
2136          setSchemaIsDirty(true);
2137        }
2138      }
2139      catch (SQLException JavaDoc e)
2140      {
2141        /*
2142         * Check if the we have to remove the entry from the total order queue
2143         * (wait to be sure that we do it in the proper order)
2144         */

2145        DistributedRollbackToSavepoint totalOrderRollbackToSavepoint = new DistributedRollbackToSavepoint(
2146            transactionId, savepointName);
2147        if (loadBalancer
2148            .waitForTotalOrder(totalOrderRollbackToSavepoint, false))
2149          loadBalancer
2150              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderRollbackToSavepoint);
2151
2152        throw e;
2153      }
2154      catch (AllBackendsFailedException e)
2155      {
2156        String JavaDoc msg = Translate.get(
2157            "requestmanager.rollbackavepoint.failed.all", new String JavaDoc[]{
2158                String.valueOf(transactionId), savepointName, e.getMessage()});
2159        logger.error(msg);
2160        throw new SQLException JavaDoc(msg);
2161      }
2162      finally
2163      {
2164        if (rollbackScheduled)
2165        {
2166          // Notify scheduler for completion
2167
scheduler.savepointCompleted(transactionId);
2168        }
2169
2170        if (validSavepoint)
2171          // Remove all the savepoints set after the savepoint we rollback to
2172
removeSavepoints(tid, savepointName);
2173      }
2174    }
2175    catch (RuntimeException JavaDoc e)
2176    {
2177      String JavaDoc msg = Translate
2178          .get("fatal.runtime.exception.requestmanager.rollbacksavepoint");
2179      logger.fatal(msg, e);
2180      endUserLogger.fatal(msg);
2181      throw new SQLException JavaDoc(e.getMessage());
2182    }
2183  }
2184
2185  /**
2186   * Sets a unnamed savepoint to a transaction given its id.
2187   *
2188   * @param transactionId the transaction id
2189   * @return the generated id of the new savepoint
2190   * @throws SQLException if an error occurs
2191   */

2192  public int setSavepoint(long transactionId) throws SQLException JavaDoc
2193  {
2194    long startTime = System.currentTimeMillis();
2195    try
2196    {
2197      Long JavaDoc tid = new Long JavaDoc(transactionId);
2198      TransactionMetaData tm = getTransactionMetaData(tid);
2199      String JavaDoc savepointName = "unnamed savepoint";
2200      int savepointId;
2201
2202      boolean setSavepointScheduled = false;
2203      try
2204      {
2205        // Wait for the scheduler to give us the authorization to execute
2206
savepointId = scheduler.setSavepoint(tm);
2207        setSavepointScheduled = true;
2208
2209        savepointName = String.valueOf(savepointId);
2210
2211        if (logger.isDebugEnabled())
2212          logger.debug(Translate.get("transaction.setsavepoint", new String JavaDoc[]{
2213              savepointName, String.valueOf(transactionId)}));
2214
2215        // Send to loadbalancer
2216
loadBalancer.setSavepoint(tm, savepointName);
2217
2218        // Update the recovery log
2219
if (recoveryLog != null)
2220          recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2221              .currentTimeMillis()
2222              - startTime);
2223      }
2224      catch (AllBackendsFailedException e)
2225      {
2226        String JavaDoc msg = Translate.get("requestmanager.setsavepoint.failed.all",
2227            new String JavaDoc[]{savepointName, String.valueOf(transactionId),
2228                e.getMessage()});
2229        logger.error(msg);
2230        throw new SQLException JavaDoc(msg);
2231      }
2232      catch (SQLException JavaDoc e)
2233      {
2234        throw e;
2235      }
2236      finally
2237      {
2238        if (setSavepointScheduled)
2239        {
2240          // Notify scheduler for completion
2241
scheduler.savepointCompleted(transactionId);
2242        }
2243      }
2244
2245      // Add savepoint name to list of savepoints for this transaction
2246
addSavepoint(tid, savepointName);
2247      return savepointId;
2248    }
2249    catch (RuntimeException JavaDoc e)
2250    {
2251      String JavaDoc msg = Translate
2252          .get("fatal.runtime.exception.requestmanager.setsavepoint");
2253      logger.fatal(msg, e);
2254      endUserLogger.fatal(msg);
2255      throw new SQLException JavaDoc(e.getMessage());
2256    }
2257  }
2258
2259  /**
2260   * Sets a savepoint given its desired name to a transaction given its id.
2261   *
2262   * @param transactionId the transaction id
2263   * @param name the desired name of the savepoint
2264   * @throws SQLException if an error occurs
2265   */

2266  public void setSavepoint(long transactionId, String JavaDoc name) throws SQLException JavaDoc
2267  {
2268    long startTime = System.currentTimeMillis();
2269    try
2270    {
2271      Long JavaDoc tid = new Long JavaDoc(transactionId);
2272      TransactionMetaData tm = getTransactionMetaData(tid);
2273
2274      boolean setSavepointScheduled = false;
2275      try
2276      {
2277        // Wait for the scheduler to give us the authorization to execute
2278
scheduler.setSavepoint(tm, name, null);
2279        setSavepointScheduled = true;
2280
2281        if (logger.isDebugEnabled())
2282          logger.debug(Translate.get("transaction.setsavepoint", new String JavaDoc[]{
2283              name, String.valueOf(transactionId)}));
2284
2285        // Send to loadbalancer
2286
loadBalancer.setSavepoint(tm, name);
2287
2288        // Update the recovery log
2289
if (recoveryLog != null)
2290          recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2291              .currentTimeMillis()
2292              - startTime);
2293      }
2294      catch (AllBackendsFailedException e)
2295      {
2296        String JavaDoc msg = Translate.get("requestmanager.setsavepoint.failed.all",
2297            new String JavaDoc[]{name, String.valueOf(transactionId), e.getMessage()});
2298        logger.error(msg);
2299        throw new SQLException JavaDoc(msg);
2300      }
2301      catch (SQLException JavaDoc e)
2302      {
2303        /*
2304         * Check if the we have to remove the entry from the total order queue
2305         * (wait to be sure that we do it in the proper order)
2306         */

2307        DistributedSetSavepoint totalOrderSavePoint = new DistributedSetSavepoint(
2308            tm.getLogin(), transactionId, name);
2309        if (loadBalancer.waitForTotalOrder(totalOrderSavePoint, false))
2310          loadBalancer
2311              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderSavePoint);
2312
2313        throw e;
2314      }
2315      finally
2316      {
2317        if (setSavepointScheduled)
2318        {
2319          // Notify scheduler for completion
2320
scheduler.savepointCompleted(transactionId);
2321        }
2322      }
2323
2324      // Add savepoint name to list of savepoints for this transaction
2325
addSavepoint(tid, name);
2326    }
2327    catch (RuntimeException JavaDoc e)
2328    {
2329      String JavaDoc msg = Translate
2330          .get("fatal.runtime.exception.requestmanager.setsavepoint");
2331      logger.fatal(msg, e);
2332      endUserLogger.fatal(msg);
2333      throw new SQLException JavaDoc(e.getMessage());
2334    }
2335  }
2336
2337  /**
2338   * Releases a savepoint given its name from a transaction given its id.
2339   *
2340   * @param transactionId the transaction id
2341   * @param name the name of the savepoint
2342   * @exception SQLException if an error occurs
2343   */

2344  public void releaseSavepoint(long transactionId, String JavaDoc name)
2345      throws SQLException JavaDoc
2346  {
2347    long startTime = System.currentTimeMillis();
2348    try
2349    {
2350      Long JavaDoc tid = new Long JavaDoc(transactionId);
2351      TransactionMetaData tm = getTransactionMetaData(tid);
2352
2353      boolean releasedSavepointScheduled = false;
2354      try
2355      {
2356        // Check that a savepoint with given name has been set
2357
if (!hasSavepoint(tid, name))
2358          throw new SQLException JavaDoc(Translate.get(
2359              "transaction.savepoint.not.found", new String JavaDoc[]{name,
2360                  String.valueOf(transactionId)}));
2361
2362        // Wait for the scheduler to give us the authorization to execute
2363
scheduler.releaseSavepoint(tm, name, null);
2364        releasedSavepointScheduled = true;
2365
2366        if (logger.isDebugEnabled())
2367          logger.debug(Translate.get("transaction.releasesavepoint",
2368              new String JavaDoc[]{name, String.valueOf(transactionId)}));
2369
2370        // Send to loadbalancer
2371
loadBalancer.releaseSavepoint(tm, name);
2372
2373        // Update the recovery log
2374
if (recoveryLog != null)
2375          recoveryLog.logRequestCompletion(tm.getLogId(), true, System
2376              .currentTimeMillis()
2377              - startTime);
2378      }
2379      catch (SQLException JavaDoc e)
2380      {
2381        /*
2382         * Check if the we have to remove the entry from the total order queue
2383         * (wait to be sure that we do it in the proper order)
2384         */

2385        DistributedReleaseSavepoint totalOrderReleaseSavepoint = new DistributedReleaseSavepoint(
2386            transactionId, name);
2387        if (loadBalancer.waitForTotalOrder(totalOrderReleaseSavepoint, false))
2388          loadBalancer
2389              .removeObjectFromAndNotifyTotalOrderQueue(totalOrderReleaseSavepoint);
2390
2391        throw e;
2392      }
2393      catch (AllBackendsFailedException e)
2394      {
2395        String JavaDoc msg = Translate.get(
2396            "requestmanager.releasesavepoint.failed.all", new String JavaDoc[]{name,
2397                String.valueOf(transactionId), e.getMessage()});
2398        logger.error(msg);
2399        throw new SQLException JavaDoc(msg);
2400      }
2401      finally
2402      {
2403        if (releasedSavepointScheduled)
2404        {
2405          // Notify scheduler for completion
2406
scheduler.savepointCompleted(transactionId);
2407        }
2408
2409        // Remove savepoint for the transaction
2410
// Will log an error in case of an invalid savepoint
2411
removeSavepoint(tid, name);
2412      }
2413    }
2414    catch (RuntimeException JavaDoc e)
2415    {
2416      String JavaDoc msg = Translate
2417          .get("fatal.runtime.exception.requestmanager.releasesavepoint");
2418      logger.fatal(msg, e);
2419      endUserLogger.fatal(msg);
2420      throw new SQLException JavaDoc(e.getMessage());
2421    }
2422  }
2423
2424  /**
2425   * Adds a given savepoint to a given transaction
2426   *
2427   * @param tid transaction id
2428   * @param savepointName name of the savepoint
2429   */

2430  public void addSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
2431  {
2432    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
2433    if (savepoints == null)
2434    { // Lazy list creation
2435
savepoints = new LinkedList JavaDoc();
2436      tidSavepoints.put(tid, savepoints);
2437    }
2438
2439    savepoints.addLast(savepointName);
2440  }
2441
2442  /**
2443   * Removes a given savepoint for a given transaction
2444   *
2445   * @param tid transaction id
2446   * @param savepointName name of the savepoint
2447   */

2448  public void removeSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
2449  {
2450    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
2451    if (savepoints == null)
2452      logger.error("No savepoints found for transaction " + tid);
2453    else
2454      savepoints.remove(savepointName);
2455  }
2456
2457  /**
2458   * Removes all the savepoints set after a given savepoint for a given
2459   * transaction
2460   *
2461   * @param tid transaction id
2462   * @param savepointName name of the savepoint
2463   */

2464  public void removeSavepoints(Long JavaDoc tid, String JavaDoc savepointName)
2465  {
2466    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
2467    if (savepoints == null)
2468    {
2469      logger.error("No savepoints found for transaction " + tid);
2470      return;
2471    }
2472
2473    int index = savepoints.indexOf(savepointName);
2474    if (index == -1)
2475      logger.error("No savepoint with name " + savepointName + " found "
2476          + "for transaction " + tid);
2477    else if (index < savepoints.size())
2478      savepoints.subList(index + 1, savepoints.size()).clear();
2479  }
2480
2481  /**
2482   * Check if a given savepoint has been set for a given transaction
2483   *
2484   * @param tid transaction id
2485   * @param savepointName name of the savepoint
2486   * @return true if the savepoint exists
2487   */

2488  public boolean hasSavepoint(Long JavaDoc tid, String JavaDoc savepointName)
2489  {
2490    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(tid);
2491    if (savepoints == null)
2492      return false;
2493
2494    return savepoints.contains(savepointName);
2495  }
2496
2497  //
2498
// Database Backends management
2499
//
2500

2501  /**
2502   * Enable a backend that has been previously added to this virtual database
2503   * and that is in the disabled state.
2504   * <p>
2505   * The backend is enabled without further check.
2506   * <p>
2507   * The enableBackend method of the load balancer is called.
2508   *
2509   * @param db The database backend to enable
2510   * @throws SQLException if an error occurs
2511   */

2512  public void enableBackend(DatabaseBackend db) throws SQLException JavaDoc
2513  {
2514    loadBalancer.enableBackend(db, true);
2515    logger.info(Translate.get("backend.state.enabled", db.getName()));
2516  }
2517
2518  /**
2519   * The backend must have been previously added to this virtual database and be
2520   * in the disabled state.
2521   * <p>
2522   * All the queries since the given checkpoint are played and the backend state
2523   * is set to enabled when it is completely synchronized.
2524   * <p>
2525   * Note that the job is performed in background by a
2526   * <code>RecoverThread</code>. You can synchronize on thread termination if
2527   * you need to wait for completion of this task and listen to JMX
2528   * notifications for the success status.
2529   *
2530   * @param db The database backend to enable
2531   * @param checkpointName The checkpoint name to restart from
2532   * @return the JDBC reocver thread synchronizing the backend
2533   * @throws SQLException if an error occurs
2534   */

2535  public RecoverThread enableBackendFromCheckpoint(DatabaseBackend db,
2536      String JavaDoc checkpointName) throws SQLException JavaDoc
2537  {
2538    // Sanity checks
2539
if (recoveryLog == null)
2540    {
2541      String JavaDoc msg = Translate.get(
2542          "recovery.restore.checkpoint.failed.cause.null", checkpointName);
2543      logger.error(msg);
2544      throw new SQLException JavaDoc(msg);
2545    }
2546
2547    if (db.getStateValue() != BackendState.DISABLED)
2548      throw new SQLException JavaDoc(Translate.get(
2549          "recovery.restore.backend.state.invalid", db.getName()));
2550
2551    RecoverThread recoverThread = new RecoverThread(scheduler, recoveryLog, db,
2552        this, checkpointName);
2553
2554    // fire the thread and forget
2555
// exception will be reported in a jmx notification on the backend.
2556
recoverThread.start();
2557    return recoverThread;
2558  }
2559
2560  /**
2561   * Disable a backend that is currently enabled on this virtual database.
2562   * <p>
2563   * The backend is disabled without further check.
2564   * <p>
2565   * The load balancer disabled method is called on the specified backend.
2566   *
2567   * @param db The database backend to disable
2568   * @param forceDisable true if disabling must be forced on the backend
2569   * @throws SQLException if an error occurs
2570   */

2571  public void disableBackend(DatabaseBackend db, boolean forceDisable)
2572      throws SQLException JavaDoc
2573  {
2574    if (db.isReadEnabled() || db.isWriteEnabled())
2575    {
2576      loadBalancer.disableBackend(db, forceDisable);
2577      logger.info(Translate.get("backend.state.disabled", db.getName()));
2578    }
2579    else
2580    {
2581      throw new SQLException JavaDoc(Translate.get("backend.already.disabled", db
2582          .getName()));
2583    }
2584  }
2585
2586  /**
2587   * The backend must belong to this virtual database and be in the enabled
2588   * state. A checkpoint is stored with the given name and the backend is set to
2589   * the disabling state. The method then wait for all in-flight transactions to
2590   * complete before disabling completely the backend and storing its last known
2591   * checkpoint (upon success only).
2592   *
2593   * @param db The database backend to disable
2594   * @param checkpointName The checkpoint name to store
2595   * @throws SQLException if an error occurs
2596   */

2597  public void disableBackendWithCheckpoint(DatabaseBackend db,
2598      String JavaDoc checkpointName) throws SQLException JavaDoc
2599  {
2600    ArrayList JavaDoc backendsArrayList = new ArrayList JavaDoc();
2601    backendsArrayList.add(new BackendInfo(db));
2602    disableBackendsWithCheckpoint(backendsArrayList, checkpointName);
2603  }
2604
2605  /**
2606   * Disable a list of backends. Only to store only one checkpoint, and to
2607   * disable all the backends at the same time so the the system is in a
2608   * coherent state. Consider only the backends that were enabled. The others
2609   * are left in the state they were before.
2610   *
2611   * @param backendInfos a List of BackendInfo to disable
2612   * @param checkpointName to store
2613   * @throws SQLException if an error occurs
2614   */

2615  public void disableBackendsWithCheckpoint(
2616      final ArrayList JavaDoc/* <BackendInfo> */backendInfos, String JavaDoc checkpointName)
2617      throws SQLException JavaDoc
2618  {
2619    // Sanity checks
2620
if (recoveryLog == null)
2621    {
2622      String JavaDoc msg = Translate.get("recovery.store.checkpoint.failed.cause.null",
2623          checkpointName);
2624      logger.error(msg);
2625      throw new SQLException JavaDoc(msg);
2626    }
2627
2628    // Wait for all pending writes to finish
2629
logger.info(Translate.get("requestmanager.wait.pending.writes"));
2630    scheduler.suspendNewWrites();
2631    scheduler.waitForSuspendedWritesToComplete();
2632
2633    // Store checkpoint
2634
recoveryLog.storeCheckpoint(checkpointName);
2635    logger.info(Translate.get("recovery.checkpoint.stored", checkpointName));
2636
2637    // Copy the list and consider only the backends that are enabled
2638
List JavaDoc backendsToDisable = new ArrayList JavaDoc();
2639    Iterator JavaDoc iter = backendInfos.iterator();
2640    while (iter.hasNext())
2641    {
2642      BackendInfo backendInfo = (BackendInfo) iter.next();
2643      DatabaseBackend db;
2644      try
2645      {
2646        db = vdb.getAndCheckBackend(backendInfo.getName(),
2647            VirtualDatabase.NO_CHECK_BACKEND);
2648        if (db.isWriteEnabled())
2649        {
2650          backendsToDisable.add(db);
2651        }
2652      }
2653      catch (VirtualDatabaseException e)
2654      {
2655        if (logger.isWarnEnabled())
2656        {
2657          logger.warn(e.getMessage(), e);
2658        }
2659      }
2660    }
2661
2662    // Signal all backends that they should not begin any new transaction
2663
int size = backendsToDisable.size();
2664    for (int i = 0; i < size; i++)
2665    {
2666      DatabaseBackend db = (DatabaseBackend) backendsToDisable.get(i);
2667      db.setState(BackendState.DISABLING);
2668      logger.info(Translate.get("backend.state.disabling", db.getName()));
2669    }
2670
2671    // Resume writes
2672
logger.info(Translate.get("requestmanager.resume.pending.writes"));
2673    scheduler.resumeWrites();
2674
2675    // Wait for all current transactions on backends to finish
2676
for (int i = 0; i < size; i++)
2677    {
2678      DatabaseBackend db = (DatabaseBackend) backendsToDisable.get(i);
2679      db.waitForAllTransactionsAndPersistentConnectionsToComplete();
2680    }
2681
2682    // Now we can safely disable all backends
2683
for (int i = 0; i < size; i++)
2684    {
2685      DatabaseBackend db = (DatabaseBackend) backendsToDisable.get(i);
2686      db.setLastKnownCheckpoint(checkpointName);
2687      loadBalancer.disableBackend(db, true);
2688      logger.info(Translate.get("backend.state.disabled", db.getName()));
2689    }
2690  }
2691
2692  /**
2693   * Create a backup from the content of a backend.
2694   *
2695   * @param backend the target backend to backup
2696   * @param login the login to use to connect to the database for the backup
2697   * operation
2698   * @param password the password to use to connect to the database for the
2699   * backup operation
2700   * @param dumpName the name of the dump to create
2701   * @param backuperName the logical name of the backuper to use
2702   * @param path the path where to store the dump
2703   * @param tables the list of tables to backup, null means all tables
2704   * @throws SQLException if the backup fails
2705   */

2706  public void backupBackend(DatabaseBackend backend, String JavaDoc login,
2707      String JavaDoc password, String JavaDoc dumpName, String JavaDoc backuperName, String JavaDoc path,
2708      ArrayList JavaDoc tables) throws SQLException JavaDoc
2709  {
2710    Backuper backuper = backupManager.getBackuperByName(backuperName);
2711    if (backuper == null)
2712      throw new SQLException JavaDoc("No backuper named " + backuperName
2713          + " was found.");
2714
2715    boolean enableAfter = false;
2716    String JavaDoc checkpointName = vdb.buildCheckpointName("backup " + dumpName);
2717    if (backend.isReadEnabled() || backend.isWriteEnabled())
2718    { // Disable backend and store checkpoint
2719
disableBackendWithCheckpoint(backend, checkpointName);
2720      logger.info(Translate.get("backend.state.disabled", backend.getName()));
2721      enableAfter = true;
2722    }
2723    else
2724    {
2725      if (backend.getLastKnownCheckpoint() == null)
2726      {
2727        throw new SQLException JavaDoc(Translate.get(
2728            "controller.backup.no.lastknown.checkpoint", backend.getName()));
2729      }
2730    }
2731    // else backend is already disabled, no checkpoint is stored here, it should
2732
// have been done at disable time.
2733

2734    try
2735    {
2736      logger.info(Translate.get("controller.backup.start", backend.getName()));
2737
2738      // Sanity check to be sure that no pending request is in the pipe for the
2739
// backend
2740
Vector JavaDoc pending = backend.getPendingRequests();
2741      if (pending.size() != 0)
2742      {
2743        if (logger.isDebugEnabled())
2744        {
2745          int shortFormLength = this.getVirtualDatabase()
2746              .getSqlShortFormLength();
2747          for (int i = 0; i < pending.size(); i++)
2748            logger.debug("Pending:"
2749                + ((AbstractRequest) pending.get(i))
2750                    .toStringShortForm(shortFormLength));
2751
2752          logger.debug("Pending Requests:"
2753              + backend.getPendingRequests().size());
2754          logger.debug("Read enabled:" + backend.isReadEnabled());
2755          logger.debug("Write enabled:" + backend.isWriteEnabled());
2756        }
2757        throw new BackupException(Translate.get("backend.not.ready.for.backup"));
2758      }
2759
2760      // Let's start the backup
2761
backend.setState(BackendState.BACKUPING);
2762      Date JavaDoc dumpDate = backuper.backup(backend, login, password, dumpName, path,
2763          tables);
2764      if (recoveryLog != null)
2765      {
2766        DumpInfo dump = new DumpInfo(dumpName, dumpDate, path, backuper
2767            .getDumpFormat(), backend.getLastKnownCheckpoint(), backend
2768            .getName(), "*");
2769        recoveryLog.storeDump(dump);
2770      }
2771
2772      // Notify that a new dump is available
2773
backend.notifyJmx(SequoiaNotificationList.VIRTUALDATABASE_NEW_DUMP_LIST);
2774    }
2775    catch (BackupException be)
2776    {
2777      logger.error(Translate.get("controller.backup.failed"), be);
2778      throw new SQLException JavaDoc(be.getMessage());
2779    }
2780    catch (RuntimeException JavaDoc e)
2781    {
2782      logger.error(Translate.get("controller.backup.failed"), e);
2783      throw new SQLException JavaDoc(e.getMessage());
2784    }
2785    finally
2786    {
2787      // Switch from BACKUPING to DISABLED STATE
2788
backend.setState(BackendState.DISABLED);
2789    }
2790
2791    logger.info(Translate.get("controller.backup.complete", backend.getName()));
2792
2793    if (enableAfter)
2794    {
2795      RecoverThread thread = enableBackendFromCheckpoint(backend,
2796          checkpointName);
2797      try
2798      {
2799        thread.join();
2800      }
2801      catch (InterruptedException JavaDoc e)
2802      {
2803        logger.error("Recovery thread has been interrupted", e);
2804      }
2805    }
2806
2807  }
2808
2809  /**
2810   * Restore a dump on a specific backend. The proper Backuper is retrieved
2811   * automatically according to the dump format stored in the recovery log dump
2812   * table.
2813   * <p>
2814   * This method disables the backend and leave it disabled after recovery
2815   * process. The user has to call the <code>enableBackendFromCheckpoint</code>
2816   * after this.
2817   *
2818   * @param backend the backend to restore
2819   * @param login the login to use to connect to the database for the restore
2820   * operation
2821   * @param password the password to use to connect to the database for the
2822   * restore operation
2823   * @param dumpName the name of the dump to restore
2824   * @param tables the list of tables to restore, null means all tables
2825   * @throws BackupException if the restore operation failed
2826   */

2827  public void restoreBackendFromBackupCheckpoint(DatabaseBackend backend,
2828      String JavaDoc login, String JavaDoc password, String JavaDoc dumpName, ArrayList JavaDoc tables)
2829      throws BackupException
2830  {
2831    DumpInfo dumpInfo;
2832    try
2833    {
2834      dumpInfo = recoveryLog.getDumpInfo(dumpName);
2835    }
2836    catch (SQLException JavaDoc e)
2837    {
2838      throw new BackupException(
2839          "Recovery log error access occured while retrieving information for dump "
2840              + dumpName, e);
2841    }
2842    if (dumpInfo == null)
2843      throw new BackupException(
2844          "No information was found in the dump table for dump " + dumpName);
2845
2846    Backuper backuper = backupManager.getBackuperByFormat(dumpInfo
2847        .getDumpFormat());
2848    if (backuper == null)
2849      throw new BackupException("No backuper was found to handle dump format "
2850          + dumpInfo.getDumpFormat());
2851
2852    if (backend.isReadEnabled())
2853      throw new BackupException(
2854          "Unable to restore a dump on an active backend. Disable the backend first.");
2855
2856    try
2857    {
2858      backend.setState(BackendState.RESTORING);
2859
2860      backuper.restore(backend, login, password, dumpName, dumpInfo
2861          .getDumpPath(), tables);
2862
2863      // Set the checkpoint name corresponding to this database dump
2864
backend.setLastKnownCheckpoint(dumpInfo.getCheckpointName());
2865      backend.setState(BackendState.DISABLED);
2866    }
2867    catch (BackupException be)
2868    {
2869      backend.setState(BackendState.UNKNOWN);
2870      logger.error(Translate.get("controller.backup.recovery.failed"), be);
2871      throw be;
2872    }
2873    finally
2874    {
2875      logger.info(Translate.get("controller.backup.recovery.done", backend
2876          .getName()));
2877    }
2878  }
2879
2880  /**
2881   * Store all the backends checkpoint in the recoverylog
2882   *
2883   * @param databaseName the virtual database name
2884   * @param backends the <code>Arraylist</code> of backends
2885   */

2886  public void storeBackendsInfo(String JavaDoc databaseName, ArrayList JavaDoc backends)
2887  {
2888    if (recoveryLog == null)
2889      return;
2890    int size = backends.size();
2891    DatabaseBackend backend;
2892    for (int i = 0; i < size; i++)
2893    {
2894      backend = (DatabaseBackend) backends.get(i);
2895      try
2896      {
2897        recoveryLog.storeBackendRecoveryInfo(databaseName,
2898            new BackendRecoveryInfo(backend.getName(), backend
2899                .getLastKnownCheckpoint(), backend.getStateValue(),
2900                databaseName));
2901      }
2902      catch (SQLException JavaDoc e)
2903      {
2904        logger.error(Translate.get("recovery.store.checkpoint.failed",
2905            new String JavaDoc[]{backend.getName(), e.getMessage()}), e);
2906      }
2907    }
2908  }
2909
2910  /**
2911   * Remove a checkpoint and corresponding entries from the log table
2912   *
2913   * @param checkpointName to remove
2914   */

2915  public void removeCheckpoint(String JavaDoc checkpointName)
2916  {
2917    recoveryLog.removeCheckpoint(checkpointName);
2918  }
2919
2920  //
2921
// Database schema management
2922
//
2923

2924  /**
2925   * Get the <code>DatabaseSchema</code> used by this Request Manager.
2926   *
2927   * @return a <code>DatabaseSchema</code> value
2928   */

2929  public synchronized DatabaseSchema getDatabaseSchema()
2930  {
2931    try
2932    {
2933      // Refresh schema if needed. Note that this will break static schemas if
2934
// any
2935
if (schemaIsDirty)
2936      {
2937        if (logger.isDebugEnabled())
2938          logger.debug("Database schema is dirty, refreshing it");
2939        DatabaseSchema activeSchema = vdb.getDatabaseSchemaFromActiveBackends();
2940        if (activeSchema != null)
2941        {
2942          if (dbs == null)
2943            dbs = activeSchema;
2944          else
2945            dbs.mergeSchema(activeSchema);
2946          setSchemaIsDirty(false);
2947        }
2948      }
2949    }
2950    catch (SQLException JavaDoc e)
2951    {
2952      logger.error("Unable to refresh schema", e);
2953    }
2954    return dbs;
2955  }
2956
2957  /**
2958   * Merge the given schema with the existing database schema.
2959   *
2960   * @param backendSchema The virtual database schema to merge.
2961   */

2962  public synchronized void mergeDatabaseSchema(DatabaseSchema backendSchema)
2963  {
2964    try
2965    {
2966      if (dbs == null)
2967        setDatabaseSchema(new DatabaseSchema(backendSchema));
2968      else
2969      {
2970        dbs.mergeSchema(backendSchema);
2971        logger.info(Translate
2972            .get("requestmanager.schema.virtualdatabase.merged.new"));
2973
2974        if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
2975          scheduler.mergeDatabaseSchema(dbs);
2976
2977        if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
2978          resultCache.mergeDatabaseSchema(dbs);
2979      }
2980    }
2981    catch (SQLException JavaDoc e)
2982    {
2983      logger.error(Translate.get("requestmanager.schema.merge.failed", e
2984          .getMessage()), e);
2985    }
2986  }
2987
2988  /**
2989   * Sets the <code>DatabaseSchema</code> to be able to parse the requests and
2990   * find dependencies.
2991   *
2992   * @param schema a <code>DatabaseSchema</code> value
2993   */

2994  public synchronized void setDatabaseSchema(DatabaseSchema schema)
2995  {
2996    this.dbs = schema;
2997    logger.info(Translate.get("requestmanager.schema.set.new.virtualdatabase"));
2998
2999    if (schedulerParsingranularity != ParsingGranularities.NO_PARSING)
3000      scheduler.setDatabaseSchema(dbs);
3001
3002    if (cacheParsingranularity != ParsingGranularities.NO_PARSING)
3003      resultCache.setDatabaseSchema(dbs);
3004
3005    // Load balancers do not have a specific database schema to update
3006
}
3007
3008  /**
3009   * Sets the schemaIsDirty value if the backend schema needs to be refreshed.
3010   *
3011   * @param schemaIsDirty The schemaIsDirty to set.
3012   */

3013  public synchronized void setSchemaIsDirty(boolean schemaIsDirty)
3014  {
3015    this.schemaIsDirty = schemaIsDirty;
3016  }
3017
3018  //
3019
// Getter/Setter methods
3020
//
3021

3022  /**
3023   * Returns the vdb value.
3024   *
3025   * @return Returns the vdb.
3026   */

3027  public VirtualDatabase getVirtualDatabase()
3028  {
3029    return vdb;
3030  }
3031
3032  /**
3033   * Sets the backup manager for this recovery log
3034   *
3035   * @param currentBackupManager an instance of <code>BackupManager</code>
3036   */

3037  public void setBackupManager(BackupManager currentBackupManager)
3038  {
3039    this.backupManager = currentBackupManager;
3040  }
3041
3042  /**
3043   * Returns the backupManager value.
3044   *
3045   * @return Returns the backupManager.
3046   */

3047  public BackupManager getBackupManager()
3048  {
3049    return backupManager;
3050  }
3051
3052  /**
3053   * Get the Request Load Balancer used in this Request Controller.
3054   *
3055   * @return an <code>AbstractLoadBalancer</code> value
3056   */

3057  public AbstractLoadBalancer getLoadBalancer()
3058  {
3059    return loadBalancer;
3060  }
3061
3062  /**
3063   * Set the Request Load Balancer to use in this Request Controller.
3064   *
3065   * @param loadBalancer a Request Load Balancer implementation
3066   */

3067  public void setLoadBalancer(AbstractLoadBalancer loadBalancer)
3068  {
3069    if (this.loadBalancer != null)
3070      throw new RuntimeException JavaDoc(
3071          "It is not possible to dynamically change a load balancer.");
3072    this.loadBalancer = loadBalancer;
3073    if (loadBalancer == null)
3074      return;
3075    loadBalancerParsingranularity = loadBalancer.getParsingGranularity();
3076    if (loadBalancerParsingranularity > requiredParsingGranularity)
3077      requiredParsingGranularity = loadBalancerParsingranularity;
3078
3079    try
3080    {
3081      MBeanServerManager.registerMBean(new LoadBalancerControl(loadBalancer),
3082          JmxConstants.getLoadBalancerObjectName(vdb.getVirtualDatabaseName()));
3083    }
3084    catch (Exception JavaDoc e)
3085    {
3086      logger.error(Translate.get("jmx.failed.register.mbean.loadbalancer"));
3087    }
3088  }
3089
3090  /**
3091   * Get the result cache (if any) used in this Request Manager.
3092   *
3093   * @return an <code>AbstractResultCache</code> value or null if no Reqsult
3094   * Cache has been defined
3095   */

3096  public AbstractResultCache getResultCache()
3097  {
3098    return resultCache;
3099  }
3100
3101  /**
3102   * Returns the metadataCache value.
3103   *
3104   * @return Returns the metadataCache.
3105   */

3106  public MetadataCache getMetadataCache()
3107  {
3108    return metadataCache;
3109  }
3110
3111  /**
3112   * Sets the metadataCache value.
3113   *
3114   * @param metadataCache The metadataCache to set.
3115   */

3116  public void setMetadataCache(MetadataCache metadataCache)
3117  {
3118    this.metadataCache = metadataCache;
3119  }
3120
3121  /**
3122   * Sets the ParsingCache.
3123   *
3124   * @param parsingCache The parsingCache to set.
3125   */

3126  public void setParsingCache(ParsingCache parsingCache)
3127  {
3128    parsingCache.setRequestManager(this);
3129    parsingCache.setGranularity(requiredParsingGranularity);
3130    parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
3131    this.parsingCache = parsingCache;
3132  }
3133
3134  /**
3135   * Returns the Recovery Log Manager.
3136   *
3137   * @return RecoveryLog the current recovery log (null if none)
3138   */

3139  public RecoveryLog getRecoveryLog()
3140  {
3141    return recoveryLog;
3142  }
3143
3144  /**
3145   * Sets the Recovery Log Manager.
3146   *
3147   * @param recoveryLog The log recovery to set
3148   */

3149  public void setRecoveryLog(RecoveryLog recoveryLog)
3150  {
3151    if (recoveryLog == null)
3152      return;
3153    this.recoveryLog = recoveryLog;
3154    loadBalancer.setRecoveryLog(recoveryLog);
3155    ArrayList JavaDoc backends = vdb.getBackends();
3156    int size = backends.size();
3157    backendStateListener = new BackendStateListener(vdb
3158        .getVirtualDatabaseName(), recoveryLog);
3159    for (int i = 0; i < size; i++)
3160      ((DatabaseBackend) backends.get(i))
3161          .setStateListener(backendStateListener);
3162  }
3163
3164  /**
3165   * Set the Request Cache to use in this Request Controller.
3166   *
3167   * @param cache a Request Cache implementation
3168   */

3169  public void setResultCache(AbstractResultCache cache)
3170  {
3171    resultCache = cache;
3172    cacheParsingranularity = cache.getParsingGranularity();
3173    if (cacheParsingranularity > requiredParsingGranularity)
3174      requiredParsingGranularity = cacheParsingranularity;
3175  }
3176
3177  /**
3178   * Get the Request Scheduler (if any) used in this Request Controller.
3179   *
3180   * @return an <code>AbstractScheduler</code> value or null if no Request
3181   * Scheduler has been defined
3182   */

3183  public AbstractScheduler getScheduler()
3184  {
3185    return scheduler;
3186  }
3187
3188  /**
3189   * Set the Request Scheduler to use in this Request Controller.
3190   *
3191   * @param scheduler a Request Scheduler implementation
3192   */

3193  public void setScheduler(AbstractScheduler scheduler)
3194  {
3195    this.scheduler = scheduler;
3196    schedulerParsingranularity = scheduler.getParsingGranularity();
3197    if (schedulerParsingranularity > requiredParsingGranularity)
3198      requiredParsingGranularity = schedulerParsingranularity;
3199  }
3200
3201  /**
3202   * Sets the parsing case sensitivity. If true the request are parsed in a case
3203   * sensitive way (table/column name must match exactly the case of the names
3204   * fetched from the database or enforced by a static schema).
3205   *
3206   * @param isCaseSensitiveParsing true if parsing is case sensitive
3207   */

3208  public void setCaseSensitiveParsing(boolean isCaseSensitiveParsing)
3209  {
3210    this.isCaseSensitiveParsing = isCaseSensitiveParsing;
3211    if (parsingCache != null)
3212      parsingCache.setCaseSensitiveParsing(isCaseSensitiveParsing);
3213  }
3214
3215  //
3216
// Debug/Monitoring
3217
//
3218

3219  /**
3220   * Get xml information about this Request Manager
3221   *
3222   * @return <code>String</code> in xml formatted text
3223   */

3224  public String JavaDoc getXml()
3225  {
3226    StringBuffer JavaDoc info = new StringBuffer JavaDoc();
3227    info.append("<" + DatabasesXmlTags.ELT_RequestManager + " "
3228        + DatabasesXmlTags.ATT_caseSensitiveParsing + "=\""
3229        + isCaseSensitiveParsing + "\" " + DatabasesXmlTags.ATT_beginTimeout
3230        + "=\"" + beginTimeout / 1000 + "\" "
3231        + DatabasesXmlTags.ATT_commitTimeout + "=\"" + commitTimeout / 1000
3232        + "\" " + DatabasesXmlTags.ATT_rollbackTimeout + "=\""
3233        + rollbackTimeout / 1000 + "\">");
3234    if (scheduler != null)
3235      info.append(scheduler.getXml());
3236
3237    if (metadataCache != null || parsingCache != null || resultCache != null)
3238    {
3239      info.append("<" + DatabasesXmlTags.ELT_RequestCache + ">");
3240      if (metadataCache != null)
3241        info.append(metadataCache.getXml());
3242      if (parsingCache != null)
3243        info.append(parsingCache.getXml());
3244      if (resultCache != null)
3245        info.append(resultCache.getXml());
3246      info.append("</" + DatabasesXmlTags.ELT_RequestCache + ">");
3247    }
3248
3249    if (loadBalancer != null)
3250      info.append(loadBalancer.getXml());
3251    if (recoveryLog != null)
3252      info.append(this.recoveryLog.getXml());
3253    info.append("</" + DatabasesXmlTags.ELT_RequestManager + ">");
3254    return info.toString();
3255  }
3256
3257  /**
3258   * Returns the backendStateListener value.
3259   *
3260   * @return Returns the backendStateListener.
3261   */

3262  public BackendStateListener getBackendStateListener()
3263  {
3264    return backendStateListener;
3265  }
3266
3267  /**
3268   * Returns the beginTimeout value.
3269   *
3270   * @return Returns the beginTimeout.
3271   */

3272  public long getBeginTimeout()
3273  {
3274    return beginTimeout;
3275  }
3276
3277  /**
3278   * Returns the cacheParsingranularity value.
3279   *
3280   * @return Returns the cacheParsingranularity.
3281   */

3282  public int getCacheParsingranularity()
3283  {
3284    return cacheParsingranularity;
3285  }
3286
3287  /**
3288   * Sets the cacheParsingranularity value.
3289   *
3290   * @param cacheParsingranularity The cacheParsingranularity to set.
3291   */

3292  public void setCacheParsingranularity(int cacheParsingranularity)
3293  {
3294    this.cacheParsingranularity = cacheParsingranularity;
3295  }
3296
3297  /**
3298   * Returns the commitTimeout value.
3299   *
3300   * @return Returns the commitTimeout.
3301   */

3302  public long getCommitTimeout()
3303  {
3304    return commitTimeout;
3305  }
3306
3307  /**
3308   * Returns the number of savepoints that are defined for a given transaction
3309   *
3310   * @param tId the transaction id
3311   * @return the number of savepoints that are defined in the transaction whose
3312   * id is tId
3313   */

3314  public int getNumberOfSavepointsInTransaction(long tId)
3315  {
3316    LinkedList JavaDoc savepoints = (LinkedList JavaDoc) tidSavepoints.get(new Long JavaDoc(tId));
3317    if (savepoints == null)
3318    {
3319      return 0;
3320    }
3321    else
3322      return savepoints.size();
3323  }
3324
3325  /**
3326   * Returns the requiredParsingGranularity value.
3327   *
3328   * @return Returns the requiredParsingGranularity.
3329   */

3330  public int getRequiredParsingGranularity()
3331  {
3332    return requiredParsingGranularity;
3333  }
3334
3335  /**
3336   * Returns the rollbackTimeout value.
3337   *
3338   * @return Returns the rollbackTimeout.
3339   */

3340  public long getRollbackTimeout()
3341  {
3342    return rollbackTimeout;
3343  }
3344
3345  /**
3346   * Returns the schedulerParsingranularity value.
3347   *
3348   * @return Returns the schedulerParsingranularity.
3349   */

3350  public int getSchedulerParsingranularity()
3351  {
3352    return schedulerParsingranularity;
3353  }
3354
3355  /**
3356   * Sets the schedulerParsingranularity value.
3357   *
3358   * @param schedulerParsingranularity The schedulerParsingranularity to set.
3359   */

3360  public void setSchedulerParsingranularity(int schedulerParsingranularity)
3361  {
3362    this.schedulerParsingranularity = schedulerParsingranularity;
3363  }
3364
3365  /**
3366   * Returns the isCaseSensitiveParsing value.
3367   *
3368   * @return Returns the isCaseSensitiveParsing.
3369   */

3370  public boolean isCaseSensitiveParsing()
3371  {
3372    return isCaseSensitiveParsing;
3373  }
3374
3375  /**
3376   * @see org.continuent.sequoia.controller.jmx.AbstractStandardMBean#getAssociatedString()
3377   */

3378  public String JavaDoc getAssociatedString()
3379  {
3380    return "requestmanager";
3381  }
3382
3383  /**
3384   * Resume all transactions, writes and persistent connections.
3385   */

3386  public void resumeActivity()
3387  {
3388    scheduler.resumeWrites();
3389    scheduler.resumeNewTransactions();
3390    scheduler.resumeOpenClosePersistentConnection();
3391  }
3392
3393  /**
3394   * Suspend all transactions, writes and persistent connections.
3395   *
3396   * @throws SQLException if an error occured while waiting for writes or
3397   * transactions to complete
3398   */

3399  public void suspendActivity() throws SQLException JavaDoc
3400  {
3401    // Suspend opening and closing of persistent connections
3402
scheduler.suspendOpenClosePersistentConnection();
3403
3404    // Suspend new transactions
3405
scheduler.suspendNewTransactions();
3406
3407    // Suspend new writes
3408
scheduler.suspendNewWrites();
3409
3410    // Wait for suspended activity to complete
3411
scheduler.waitForSuspendedTransactionsToComplete();
3412    scheduler.waitForSuspendedWritesToComplete();
3413    scheduler.waitForPendingOpenClosePersistentConnection();
3414  }
3415
3416  /**
3417   * {@inheritDoc}
3418   *
3419   * @see org.continuent.sequoia.common.jmx.mbeans.RequestManagerMBean#parseSqlRequest(java.lang.String,
3420   * java.lang.String)
3421   */

3422  public String JavaDoc parseSqlRequest(String JavaDoc sqlRequest, String JavaDoc lineSeparator)
3423  {
3424    RequestFactory requestFactory = ControllerConstants.CONTROLLER_FACTORY
3425        .getRequestFactory();
3426    AbstractRequest request = requestFactory.requestFromString(sqlRequest,
3427        false, false, 0, lineSeparator);
3428    try
3429    {
3430      // Check if there is semantic information
3431
SemanticManager semanticManager = vdb.getSemanticManager();
3432      request.setSemanticManager(semanticManager);
3433      SemanticBehavior semantic = semanticManager.getRequestSemantic(request);
3434
3435      if (semantic != null)
3436        request.setSemantic(semantic);
3437      else
3438      { // No semantic, parse it
3439
request.parse(getDatabaseSchema(), ParsingGranularities.TABLE,
3440            isCaseSensitiveParsing);
3441      }
3442    }
3443    catch (SQLException JavaDoc ignored)
3444    {
3445    }
3446    return request.getParsingResultsAsString();
3447  }
3448
3449  /**
3450   * Flush metadata cache and update transaction metadata if needed.
3451   *
3452   * @param tm Transaction metadata of the transaction that is at the origin of
3453   * the cache flushing
3454   */

3455  public void flushMetadataCacheAndUpdateTransactionMetadata(
3456      TransactionMetaData tm)
3457  {
3458    if (metadataCache != null)
3459    {
3460      if (tm != null)
3461        tm.setAltersMetadataCache(true);
3462
3463      metadataCache.flushCache();
3464    }
3465  }
3466
3467  /**
3468   * Flush all caches of the request manager, ie. resultCache and metadataCache
3469   * and update transaction metadata. This is used by DatabaseSchema when no
3470   * semantic information was found for a request, which should not be possible.
3471   *
3472   * @param tm Transaction metadata of the transaction that is at the origin of
3473   * the cache flushing
3474   */

3475  public void flushAllCachesAndUpdateTransactionMetadata(TransactionMetaData tm)
3476  {
3477    if (resultCache != null)
3478    {
3479      if (tm != null)
3480        tm.setAltersQueryResultCache(true);
3481      resultCache.flushCache();
3482    }
3483
3484    if (tm != null)
3485      tm.setAltersDatabaseSchema(true);
3486
3487    flushMetadataCacheAndUpdateTransactionMetadata(tm);
3488  }
3489}
Popular Tags