KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > loadbalancer > raidb0 > RAIDb0


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

24
25 package org.objectweb.cjdbc.controller.loadbalancer.raidb0;
26
27 import java.sql.Connection JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30
31 import org.objectweb.cjdbc.common.exceptions.BadConnectionException;
32 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
33 import org.objectweb.cjdbc.common.exceptions.NoTransactionStartWhenDisablingException;
34 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
35 import org.objectweb.cjdbc.common.i18n.Translate;
36 import org.objectweb.cjdbc.common.log.Trace;
37 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
38 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
39 import org.objectweb.cjdbc.common.sql.SelectRequest;
40 import org.objectweb.cjdbc.common.sql.StoredProcedure;
41 import org.objectweb.cjdbc.common.util.ReadPrioritaryFIFOWriteLock;
42 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
43 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
44 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
45 import org.objectweb.cjdbc.controller.connection.AbstractConnectionManager;
46 import org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer;
47 import org.objectweb.cjdbc.controller.loadbalancer.BackendWorkerThread;
48 import org.objectweb.cjdbc.controller.loadbalancer.policies.createtable.CreateTableException;
49 import org.objectweb.cjdbc.controller.loadbalancer.policies.createtable.CreateTablePolicy;
50 import org.objectweb.cjdbc.controller.loadbalancer.policies.createtable.CreateTableRule;
51 import org.objectweb.cjdbc.controller.loadbalancer.tasks.CommitTask;
52 import org.objectweb.cjdbc.controller.loadbalancer.tasks.KillThreadTask;
53 import org.objectweb.cjdbc.controller.loadbalancer.tasks.ReleaseSavepointTask;
54 import org.objectweb.cjdbc.controller.loadbalancer.tasks.RollbackTask;
55 import org.objectweb.cjdbc.controller.loadbalancer.tasks.RollbackToSavepointTask;
56 import org.objectweb.cjdbc.controller.loadbalancer.tasks.SavepointTask;
57 import org.objectweb.cjdbc.controller.requestmanager.RAIDbLevels;
58 import org.objectweb.cjdbc.controller.requestmanager.TransactionMarkerMetaData;
59 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
60 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
61
62 /**
63  * RAIDb-0: database partitioning.
64  * <p>
65  * The requests are sent to the backend nodes hosting the tables needed to
66  * execute the request. If no backend has the needed tables to perform a
67  * request, it will fail.
68  *
69  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
70  * @author <a HREF="mailto:jaco.swart@iblocks.co.uk">Jaco Swart </a>
71  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
72  * </a>
73  * @version 1.0
74  */

75 public class RAIDb0 extends AbstractLoadBalancer
76 {
77   //
78
// How the code is organized?
79
// 1. Member variables
80
// 2. Constructor(s)
81
// 3. Request handling
82
// 4. Transaction handling
83
// 5. Backend management
84
// 6. Debug/Monitoring
85
//
86

87   private ArrayList JavaDoc backendThreads;
88   private ReadPrioritaryFIFOWriteLock backendThreadsRWLock = new ReadPrioritaryFIFOWriteLock();
89   private CreateTablePolicy createTablePolicy;
90
91   protected static Trace logger = Trace
92                                                                .getLogger("org.objectweb.cjdbc.controller.loadbalancer.RAIDb0");
93
94   /*
95    * Constructors
96    */

97
98   /**
99    * Creates a new RAIDb-0 request load balancer.
100    *
101    * @param vdb the virtual database this load balancer belongs to.
102    * @param createTablePolicy the policy defining how 'create table' statements
103    * should be handled
104    * @throws Exception if an error occurs
105    */

106   public RAIDb0(VirtualDatabase vdb, CreateTablePolicy createTablePolicy)
107       throws Exception JavaDoc
108   {
109     super(vdb, RAIDbLevels.RAIDb0, ParsingGranularities.TABLE);
110     backendThreads = new ArrayList JavaDoc();
111     this.createTablePolicy = createTablePolicy;
112   }
113
114   /*
115    * Request Handling
116    */

117
118   /**
119    * Performs a read request on the backend that has the needed tables to
120    * executes the request.
121    *
122    * @param request an <code>SelectRequest</code>
123    * @param metadataCache the metadataCache if any or null
124    * @return the corresponding <code>java.sql.ResultSet</code>
125    * @exception SQLException if an error occurs
126    * @see AbstractLoadBalancer#execReadRequest(SelectRequest, MetadataCache)
127    */

128   public ControllerResultSet execReadRequest(SelectRequest request,
129       MetadataCache metadataCache) throws SQLException JavaDoc
130   {
131     try
132     {
133       vdb.acquireReadLockBackendLists(); // Acquire
134
// read
135
// lock
136
}
137     catch (InterruptedException JavaDoc e)
138     {
139       String JavaDoc msg = Translate.get(
140           "loadbalancer.backendlist.acquire.readlock.failed", e);
141       logger.error(msg);
142       throw new SQLException JavaDoc(msg);
143     }
144
145     try
146     {
147       ControllerResultSet rs = null;
148       ArrayList JavaDoc fromTables = request.getFrom();
149
150       if (fromTables == null)
151         throw new SQLException JavaDoc(Translate.get("loadbalancer.from.not.found",
152             request.getSQLShortForm(vdb.getSQLShortFormLength())));
153
154       // Find the backend that has the needed tables
155
ArrayList JavaDoc backends = vdb.getBackends();
156       int size = backends.size();
157       int enabledBackends = 0;
158
159       DatabaseBackend backend = null;
160       // The backend that will execute the query
161
for (int i = 0; i < size; i++)
162       {
163         backend = (DatabaseBackend) backends.get(i);
164         if (backend.isReadEnabled())
165           enabledBackends++;
166         if (backend.isReadEnabled() && backend.hasTables(fromTables))
167           break;
168         else
169           backend = null;
170       }
171
172       if (backend == null)
173       {
174         if (enabledBackends == 0)
175           throw new NoMoreBackendException(Translate.get(
176               "loadbalancer.execute.no.backend.enabled", request.getId()));
177         else
178           throw new SQLException JavaDoc(Translate.get(
179               "loadbalancer.backend.no.required.tables", fromTables.toString()));
180       }
181
182       if (logger.isDebugEnabled())
183       {
184         logger.debug("Backend " + backend.getName()
185             + " has all tables which are:");
186         for (int i = 0; i < fromTables.size(); i++)
187         {
188           logger.debug(fromTables.get(i));
189         }
190       }
191
192       // Execute the request on the chosen backend
193
try
194       {
195         rs = executeRequestOnBackend(request, backend, metadataCache);
196       }
197       catch (SQLException JavaDoc se)
198       {
199         String JavaDoc msg = Translate.get("loadbalancer.request.failed", new String JavaDoc[]{
200             String.valueOf(request.getId()), se.getMessage()});
201         if (logger.isInfoEnabled())
202           logger.info(msg);
203         throw new SQLException JavaDoc(msg);
204       }
205
206       return rs;
207     }
208     catch (RuntimeException JavaDoc e)
209     {
210       String JavaDoc msg = Translate
211           .get("loadbalancer.request.failed", new String JavaDoc[]{
212               request.getSQLShortForm(vdb.getSQLShortFormLength()),
213               e.getMessage()});
214       logger.fatal(msg, e);
215       throw new SQLException JavaDoc(msg);
216     }
217     finally
218     {
219       vdb.releaseReadLockBackendLists(); // Release
220
// the
221
// lock
222
}
223   }
224
225   /**
226    * Performs a write request on the backend that has the needed tables to
227    * executes the request.
228    *
229    * @param request an <code>AbstractWriteRequest</code>
230    * @return number of rows affected by the request
231    * @exception SQLException if an error occurs
232    */

233   public int execWriteRequest(AbstractWriteRequest request) throws SQLException JavaDoc
234   {
235     // Handle macros
236
handleMacros(request);
237
238     try
239     {
240       vdb.acquireReadLockBackendLists(); // Acquire
241
// read
242
// lock
243
}
244     catch (InterruptedException JavaDoc e)
245     {
246       String JavaDoc msg = Translate.get(
247           "loadbalancer.backendlist.acquire.readlock.failed", e);
248       logger.error(msg);
249       throw new SQLException JavaDoc(msg);
250     }
251
252     try
253     {
254       String JavaDoc table = request.getTableName();
255       AbstractConnectionManager cm = null;
256
257       if (table == null)
258         throw new SQLException JavaDoc(Translate.get(
259             "loadbalancer.request.target.table.not.found", request
260                 .getSQLShortForm(vdb.getSQLShortFormLength())));
261
262       // Find the backend that has the needed table
263
ArrayList JavaDoc backends = vdb.getBackends();
264       int size = backends.size();
265
266       DatabaseBackend backend = null;
267       // The backend that will execute the query
268
if (request.isCreate())
269       { // Choose the backend according to the defined policy
270
CreateTableRule rule = createTablePolicy.getTableRule(request
271             .getTableName());
272         if (rule == null)
273           rule = createTablePolicy.getDefaultRule();
274
275         // Ask the rule to pickup a backend
276
ArrayList JavaDoc choosen;
277         try
278         {
279           choosen = rule.getBackends(backends);
280         }
281         catch (CreateTableException e)
282         {
283           throw new SQLException JavaDoc(Translate.get(
284               "loadbalancer.create.table.rule.failed", e.getMessage()));
285         }
286
287         // Get the connection manager from the chosen backend
288
if (choosen != null)
289           backend = (DatabaseBackend) choosen.get(0);
290         if (backend != null)
291           cm = backend.getConnectionManager(request.getLogin());
292       }
293       else
294       { // Find the backend that has the table
295
for (int i = 0; i < size; i++)
296         {
297           backend = (DatabaseBackend) backends.get(i);
298           if ((backend.isWriteEnabled() || backend.isDisabling()) && backend.hasTable(table))
299           {
300             cm = backend.getConnectionManager(request.getLogin());
301             break;
302           }
303         }
304       }
305
306       // Sanity check
307
if (cm == null)
308         throw new SQLException JavaDoc(Translate.get(
309             "loadbalancer.backend.no.required.table", table));
310
311       // Ok, let's execute the query
312

313       if (request.isAutoCommit())
314       {
315         // We do not execute request outside the already open transactions if we
316
// are disabling the backend.
317
if (backend.isDisabling())
318           throw new SQLException JavaDoc(Translate.get(
319               "loadbalancer.backend.is.disabling", new String JavaDoc[]{
320                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
321                   backend.getName()}));
322
323         // Use a connection just for this request
324
Connection JavaDoc c = null;
325         try
326         {
327           c = cm.getConnection();
328         }
329         catch (UnreachableBackendException e1)
330         {
331           logger.error(Translate.get(
332               "loadbalancer.backend.disabling.unreachable", backend.getName()));
333           disableBackend(backend);
334           throw new SQLException JavaDoc(Translate.get(
335               "loadbalancer.backend.unreacheable", backend.getName()));
336         }
337
338         // Sanity check
339
if (c == null)
340           throw new SQLException JavaDoc(Translate.get(
341               "loadbalancer.backend.no.connection", backend.getName()));
342
343         int result;
344         try
345         {
346           result = executeUpdateRequestOnBackend(request, backend, c);
347         }
348         catch (Exception JavaDoc e)
349         {
350           throw new SQLException JavaDoc(Translate.get("loadbalancer.request.failed",
351               new String JavaDoc[]{
352                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
353                   e.getMessage()}));
354         }
355         finally
356         {
357           cm.releaseConnection(c);
358         }
359         if (logger.isDebugEnabled())
360           logger.debug(Translate.get("loadbalancer.execute.on", new String JavaDoc[]{
361               String.valueOf(request.getId()), backend.getName()}));
362         return result;
363       }
364       else
365       { // Inside a transaction
366
Connection JavaDoc c;
367         long tid = request.getTransactionId();
368         Long JavaDoc lTid = new Long JavaDoc(tid);
369
370         try
371         {
372           c = backend.getConnectionForTransactionAndLazyBeginIfNeeded(lTid, cm,
373               request.getTransactionIsolation());
374         }
375         catch (UnreachableBackendException e1)
376         {
377           logger.error(Translate.get(
378               "loadbalancer.backend.disabling.unreachable", backend.getName()));
379           disableBackend(backend);
380           throw new SQLException JavaDoc(Translate.get(
381               "loadbalancer.backend.unreacheable", backend.getName()));
382         }
383         catch (NoTransactionStartWhenDisablingException e)
384         {
385           String JavaDoc msg = Translate.get("loadbalancer.backend.is.disabling",
386               new String JavaDoc[]{
387                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
388                   backend.getName()});
389           logger.error(msg);
390           throw new SQLException JavaDoc(msg);
391         }
392
393         // Sanity check
394
if (c == null)
395           throw new SQLException JavaDoc(Translate.get(
396               "loadbalancer.unable.retrieve.connection", new String JavaDoc[]{
397                   String.valueOf(tid), backend.getName()}));
398
399         // Execute the query
400
int result;
401         try
402         {
403           result = executeUpdateRequestOnBackend(request, backend, c);
404         }
405         catch (Exception JavaDoc e)
406         {
407           throw new SQLException JavaDoc(Translate.get("loadbalancer.request.failed",
408               new String JavaDoc[]{
409                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
410                   e.getMessage()}));
411         }
412         if (logger.isDebugEnabled())
413           logger.debug(Translate.get("loadbalancer.execute.on", new String JavaDoc[]{
414               String.valueOf(request.getId()), backend.getName()}));
415         return result;
416       }
417     }
418     catch (RuntimeException JavaDoc e)
419     {
420       String JavaDoc msg = Translate
421           .get("loadbalancer.request.failed", new String JavaDoc[]{
422               request.getSQLShortForm(vdb.getSQLShortFormLength()),
423               e.getMessage()});
424       logger.fatal(msg, e);
425       throw new SQLException JavaDoc(msg);
426     }
427     finally
428     {
429       vdb.releaseReadLockBackendLists(); // Release
430
// the
431
// lock
432
}
433   }
434
435   /**
436    * @see AbstractLoadBalancer#execWriteRequestWithKeys(AbstractWriteRequest,
437    * MetadataCache)
438    */

439   public ControllerResultSet execWriteRequestWithKeys(
440       AbstractWriteRequest request, MetadataCache metadataCache)
441       throws SQLException JavaDoc
442   {
443     // Handle macros
444
handleMacros(request);
445
446     try
447     {
448       vdb.acquireReadLockBackendLists(); // Acquire
449
// read
450
// lock
451
}
452     catch (InterruptedException JavaDoc e)
453     {
454       String JavaDoc msg = Translate.get(
455           "loadbalancer.backendlist.acquire.readlock.failed", e);
456       logger.error(msg);
457       throw new SQLException JavaDoc(msg);
458     }
459
460     try
461     {
462       String JavaDoc table = request.getTableName();
463       AbstractConnectionManager cm = null;
464
465       if (table == null)
466         throw new SQLException JavaDoc(Translate.get(
467             "loadbalancer.request.target.table.not.found", request
468                 .getSQLShortForm(vdb.getSQLShortFormLength())));
469
470       // Find the backend that has the needed table
471
ArrayList JavaDoc backends = vdb.getBackends();
472       int size = backends.size();
473
474       DatabaseBackend backend = null;
475       // The backend that will execute the query
476
if (request.isCreate())
477       { // Choose the backend according to the defined policy
478
CreateTableRule rule = createTablePolicy.getTableRule(request
479             .getTableName());
480         if (rule == null)
481           rule = createTablePolicy.getDefaultRule();
482
483         // Ask the rule to pickup a backend
484
ArrayList JavaDoc choosen;
485         try
486         {
487           choosen = rule.getBackends(backends);
488         }
489         catch (CreateTableException e)
490         {
491           throw new SQLException JavaDoc(Translate.get(
492               "loadbalancer.create.table.rule.failed", e.getMessage()));
493         }
494
495         // Get the connection manager from the chosen backend
496
if (choosen != null)
497           backend = (DatabaseBackend) choosen.get(0);
498         if (backend != null)
499           cm = backend.getConnectionManager(request.getLogin());
500       }
501       else
502       { // Find the backend that has the table
503
for (int i = 0; i < size; i++)
504         {
505           backend = (DatabaseBackend) backends.get(i);
506           if ((backend.isWriteEnabled() || backend.isDisabling()) && backend.hasTable(table))
507           {
508             cm = backend.getConnectionManager(request.getLogin());
509             break;
510           }
511         }
512       }
513
514       // Sanity check
515
if (cm == null)
516         throw new SQLException JavaDoc(Translate.get(
517             "loadbalancer.backend.no.required.table", table));
518
519       if (!backend.getDriverCompliance().supportGetGeneratedKeys())
520         throw new SQLException JavaDoc(Translate.get(
521             "loadbalancer.backend.autogeneratedkeys.unsupported", backend
522                 .getName()));
523
524       // Ok, let's execute the query
525

526       if (request.isAutoCommit())
527       {
528         // We do not execute request outside the already open transactions if we
529
// are disabling the backend.
530
if (backend.isDisabling())
531           throw new SQLException JavaDoc(Translate.get(
532               "loadbalancer.backend.is.disabling", new String JavaDoc[]{
533                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
534                   backend.getName()}));
535
536         // Use a connection just for this request
537
Connection JavaDoc c = null;
538         try
539         {
540           c = cm.getConnection();
541         }
542         catch (UnreachableBackendException e1)
543         {
544           logger.error(Translate.get(
545               "loadbalancer.backend.disabling.unreachable", backend.getName()));
546           disableBackend(backend);
547           throw new SQLException JavaDoc(Translate.get(
548               "loadbalancer.backend.unreacheable", backend.getName()));
549         }
550
551         // Sanity check
552
if (c == null)
553           throw new SQLException JavaDoc(Translate.get(
554               "loadbalancer.backend.no.connection", backend.getName()));
555
556         // Execute Query
557
ControllerResultSet result;
558         try
559         {
560           result = executeUpdateRequestOnBackendWithKeys(request, backend, c,
561               metadataCache);
562         }
563         catch (Exception JavaDoc e)
564         {
565           throw new SQLException JavaDoc(Translate.get("loadbalancer.request.failed",
566               new String JavaDoc[]{
567                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
568                   e.getMessage()}));
569         }
570         finally
571         {
572           backend.removePendingRequest(request);
573           cm.releaseConnection(c);
574         }
575         if (logger.isDebugEnabled())
576           logger.debug(Translate.get("loadbalancer.execute.on", new String JavaDoc[]{
577               String.valueOf(request.getId()), backend.getName()}));
578         return result;
579       }
580       else
581       { // Inside a transaction
582
Connection JavaDoc c;
583         long tid = request.getTransactionId();
584         Long JavaDoc lTid = new Long JavaDoc(tid);
585
586         try
587         {
588           c = backend.getConnectionForTransactionAndLazyBeginIfNeeded(lTid, cm,
589               request.getTransactionIsolation());
590         }
591         catch (UnreachableBackendException e1)
592         {
593           logger.error(Translate.get(
594               "loadbalancer.backend.disabling.unreachable", backend.getName()));
595           disableBackend(backend);
596           throw new SQLException JavaDoc(Translate.get(
597               "loadbalancer.backend.unreacheable", backend.getName()));
598         }
599         catch (NoTransactionStartWhenDisablingException e)
600         {
601           String JavaDoc msg = Translate.get("loadbalancer.backend.is.disabling",
602               new String JavaDoc[]{
603                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
604                   backend.getName()});
605           logger.error(msg);
606           throw new SQLException JavaDoc(msg);
607         }
608
609         // Sanity check
610
if (c == null)
611           throw new SQLException JavaDoc(Translate.get(
612               "loadbalancer.unable.retrieve.connection", new String JavaDoc[]{
613                   String.valueOf(tid), backend.getName()}));
614
615         // Execute the query
616
ControllerResultSet result;
617         try
618         {
619           result = executeUpdateRequestOnBackendWithKeys(request, backend, c,
620               metadataCache);
621         }
622         catch (Exception JavaDoc e)
623         {
624           throw new SQLException JavaDoc(Translate.get("loadbalancer.request.failed",
625               new String JavaDoc[]{
626                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
627                   e.getMessage()}));
628         }
629         if (logger.isDebugEnabled())
630           logger.debug(Translate.get("loadbalancer.execute.on", new String JavaDoc[]{
631               String.valueOf(request.getId()), backend.getName()}));
632         return result;
633       }
634     }
635     catch (RuntimeException JavaDoc e)
636     {
637       String JavaDoc msg = Translate
638           .get("loadbalancer.request.failed", new String JavaDoc[]{
639               request.getSQLShortForm(vdb.getSQLShortFormLength()),
640               e.getMessage()});
641       logger.fatal(msg, e);
642       throw new SQLException JavaDoc(msg);
643     }
644     finally
645     {
646       vdb.releaseReadLockBackendLists(); // Release
647
// the
648
// lock
649
}
650   }
651
652   /**
653    * Execute a read request on the selected backend.
654    *
655    * @param request the request to execute
656    * @param backend the backend that will execute the request
657    * @param metadataCache the metadataCache if any or null
658    * @return the ControllerResultSet
659    * @throws SQLException if an error occurs
660    */

661   protected ControllerResultSet executeRequestOnBackend(SelectRequest request,
662       DatabaseBackend backend, MetadataCache metadataCache) throws SQLException JavaDoc
663   {
664     // Handle macros
665
handleMacros(request);
666
667     // Ok, we have a backend, let's execute the request
668
AbstractConnectionManager cm = backend.getConnectionManager(request
669         .getLogin());
670
671     // Sanity check
672
if (cm == null)
673     {
674       String JavaDoc msg = Translate.get("loadbalancer.connectionmanager.not.found",
675           new String JavaDoc[]{request.getLogin(), backend.getName()});
676       logger.error(msg);
677       throw new SQLException JavaDoc(msg);
678     }
679
680     // Execute the query
681
if (request.isAutoCommit())
682     {
683       ControllerResultSet rs = null;
684       boolean badConnection;
685       do
686       {
687         badConnection = false;
688         // Use a connection just for this request
689
Connection JavaDoc c = null;
690         try
691         {
692           c = cm.getConnection();
693         }
694         catch (UnreachableBackendException e1)
695         {
696           logger.error(Translate.get(
697               "loadbalancer.backend.disabling.unreachable", backend.getName()));
698           disableBackend(backend);
699           throw new SQLException JavaDoc(Translate.get(
700               "loadbalancer.backend.unreacheable", backend.getName()));
701         }
702
703         // Sanity check
704
if (c == null)
705           throw new SQLException JavaDoc(Translate.get(
706               "loadbalancer.backend.no.connection", backend.getName()));
707
708         // Execute Query
709
try
710         {
711           rs = executeSelectRequestOnBackend(request, backend, c, metadataCache);
712           cm.releaseConnection(c);
713         }
714         catch (SQLException JavaDoc e)
715         {
716           cm.releaseConnection(c);
717           throw new SQLException JavaDoc(Translate.get(
718               "loadbalancer.request.failed.on.backend", new String JavaDoc[]{
719                   request.getSQLShortForm(vdb.getSQLShortFormLength()),
720                   backend.getName(), e.getMessage()}));
721         }
722         catch (BadConnectionException e)
723         { // Get rid of the bad connection
724
cm.deleteConnection(c);
725           badConnection = true;
726         }
727       }
728       while (badConnection);
729       if (logger.isDebugEnabled())
730         logger.debug(Translate.get("loadbalancer.execute.on", new String JavaDoc[]{
731             String.valueOf(request.getId()), backend.getName()}));
732       return rs;
733     }
734     else
735     { // Inside a transaction
736
Connection JavaDoc c;
737       long tid = request.getTransactionId();
738       Long JavaDoc lTid = new Long JavaDoc(tid);
739
740       try
741       {
742         c = backend.getConnectionForTransactionAndLazyBeginIfNeeded(lTid, cm,
743             request.getTransactionIsolation());
744       }
745       catch (UnreachableBackendException e1)
746       {
747         logger.error(Translate.get(
748             "loadbalancer.backend.disabling.unreachable", backend.getName()));
749         disableBackend(backend);
750         throw new SQLException JavaDoc(Translate.get(
751             "loadbalancer.backend.unreacheable", backend.getName()));
752       }
753       catch (NoTransactionStartWhenDisablingException e)
754       {
755         String JavaDoc msg = Translate.get("loadbalancer.backend.is.disabling",
756             new String JavaDoc[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
757                 backend.getName()});
758         logger.error(msg);
759         throw new SQLException JavaDoc(msg);
760       }
761
762       // Sanity check
763
if (c == null)
764         throw new SQLException JavaDoc(Translate.get(
765             "loadbalancer.unable.retrieve.connection", new String JavaDoc[]{
766                 String.valueOf(tid), backend.getName()}));
767
768       // Execute Query
769
ControllerResultSet rs = null;
770       try
771       {
772         rs = executeSelectRequestOnBackend(request, backend, c, metadataCache);
773       }
774       catch (SQLException JavaDoc e)
775       {
776         throw new SQLException JavaDoc(Translate.get(
777             "loadbalancer.request.failed.on.backend", new String JavaDoc[]{
778                 request.getSQLShortForm(vdb.getSQLShortFormLength()),
779                 backend.getName(), e.getMessage()}));
780       }
781       catch (BadConnectionException e)
782       { // Get rid of the bad connection
783
cm.deleteConnection(tid);
784         throw new SQLException JavaDoc(Translate
785             .get("loadbalancer.connection.failed", new String JavaDoc[]{
786                 String.valueOf(tid), backend.getName(), e.getMessage()}));
787       }
788       if (logger.isDebugEnabled())
789         logger.debug(Translate.get("loadbalancer.execute.transaction.on",
790             new String JavaDoc[]{String.valueOf(tid), String.valueOf(request.getId()),
791                 backend.getName()}));
792       return rs;
793     }
794   }
795
796   /**
797    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
798    * MetadataCache)
799    */

800   public ControllerResultSet execReadOnlyReadStoredProcedure(
801       StoredProcedure proc, MetadataCache metadataCache) throws SQLException JavaDoc
802   {
803     throw new SQLException JavaDoc(
804         "Stored procedure calls are not supported with RAIDb-0 load balancers.");
805   }
806
807   /**
808    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadStoredProcedure(StoredProcedure,
809    * MetadataCache)
810    */

811   public ControllerResultSet execReadStoredProcedure(StoredProcedure proc,
812       MetadataCache metadataCache) throws SQLException JavaDoc
813   {
814     throw new SQLException JavaDoc(
815         "Stored procedure calls are not supported with RAIDb-0 load balancers.");
816   }
817
818   /**
819    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execWriteStoredProcedure(org.objectweb.cjdbc.common.sql.StoredProcedure)
820    */

821   public int execWriteStoredProcedure(StoredProcedure proc) throws SQLException JavaDoc
822   {
823     throw new SQLException JavaDoc(
824         "Stored procedure calls are not supported with RAIDb-0 load balancers.");
825   }
826
827   /*
828    * Transaction management
829    */

830
831   /**
832    * Begins a new transaction.
833    *
834    * @param tm the transaction marker metadata
835    * @throws SQLException if an error occurs
836    */

837   public final void begin(TransactionMarkerMetaData tm) throws SQLException JavaDoc
838   {
839   }
840
841   /**
842    * Commits a transaction.
843    *
844    * @param tm the transaction marker metadata
845    * @throws SQLException if an error occurs
846    */

847   public void commit(TransactionMarkerMetaData tm) throws SQLException JavaDoc
848   {
849     try
850     {
851       backendThreadsRWLock.acquireRead();
852     }
853     catch (InterruptedException JavaDoc e)
854     {
855       String JavaDoc msg = Translate.get(
856           "loadbalancer.backendlist.acquire.readlock.failed", e);
857       logger.error(msg);
858       throw new SQLException JavaDoc(msg);
859     }
860
861     int nbOfThreads = backendThreads.size();
862     ArrayList JavaDoc commitList = new ArrayList JavaDoc();
863     long tid = tm.getTransactionId();
864     Long JavaDoc lTid = new Long JavaDoc(tid);
865
866     // Build the list of backend that need to commit this transaction
867
for (int i = 0; i < nbOfThreads; i++)
868     {
869       BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
870       if (thread.getBackend().isStartedTransaction(lTid))
871         commitList.add(thread);
872     }
873
874     nbOfThreads = commitList.size();
875     if (nbOfThreads == 0)
876     { // Empty transaction or read-only with cache hits, then no backend
877
// actually executed any query for that transaction. Simply return.
878
// Bug reported by Ganesh (ssriganesha@rogers.com).
879
backendThreadsRWLock.releaseRead();
880       return;
881     }
882
883     // Create the task
884
CommitTask task = new CommitTask(nbOfThreads, // Wait for all to commit
885
nbOfThreads, tm.getTimeout(), tm.getLogin(), tid);
886
887     synchronized (task)
888     {
889       // Post the task in each backendThread tasklist and wakeup the threads
890
for (int i = 0; i < nbOfThreads; i++)
891       {
892         BackendWorkerThread thread = (BackendWorkerThread) commitList.get(i);
893         synchronized (thread)
894         {
895           thread.addTask(task, tid);
896           thread.notify();
897         }
898       }
899
900       backendThreadsRWLock.releaseRead();
901
902       // Wait for completion (notified by the task)
903
try
904       {
905         // Wait on task
906
long timeout = tm.getTimeout();
907         if (timeout > 0)
908         {
909           long start = System.currentTimeMillis();
910           task.wait(timeout);
911           long end = System.currentTimeMillis();
912           long remaining = timeout - (end - start);
913           if (remaining <= 0)
914           {
915             if (task.setExpiredTimeout())
916             { // Task will be ignored by all backends
917
String JavaDoc msg = Translate.get("loadbalancer.commit.timeout",
918                   new String JavaDoc[]{String.valueOf(tid),
919                       String.valueOf(task.getSuccess()),
920                       String.valueOf(task.getFailed())});
921               logger.warn(msg);
922               throw new SQLException JavaDoc(msg);
923             }
924             // else task execution already started, to late to cancel
925
}
926         }
927         else
928           task.wait();
929       }
930       catch (InterruptedException JavaDoc e)
931       {
932         if (task.setExpiredTimeout())
933         { // Task will be ignored by all backends
934
String JavaDoc msg = Translate.get("loadbalancer.commit.timeout",
935               new String JavaDoc[]{String.valueOf(tid),
936                   String.valueOf(task.getSuccess()),
937                   String.valueOf(task.getFailed())});
938           logger.warn(msg);
939           throw new SQLException JavaDoc(msg);
940         }
941         // else task execution already started, to late to cancel
942
}
943
944       if (task.getSuccess() > 0)
945         return;
946       else
947       { // All tasks failed
948
ArrayList JavaDoc exceptions = task.getExceptions();
949         if (exceptions == null)
950           throw new SQLException JavaDoc(Translate.get(
951               "loadbalancer.commit.all.failed", tid));
952         else
953         {
954           String JavaDoc errorMsg = Translate.get("loadbalancer.commit.failed.stack",
955               tid)
956               + "\n";
957           for (int i = 0; i < exceptions.size(); i++)
958             errorMsg += ((SQLException JavaDoc) exceptions.get(i)).getMessage() + "\n";
959           logger.error(errorMsg);
960           throw new SQLException JavaDoc(errorMsg);
961         }
962       }
963     }
964   }
965
966   /**
967    * Rollbacks a transaction.
968    *
969    * @param tm the transaction marker metadata
970    * @throws SQLException if an error occurs
971    */

972   public void rollback(TransactionMarkerMetaData tm) throws SQLException JavaDoc
973   {
974     try
975     {
976       backendThreadsRWLock.acquireRead();
977     }
978     catch (InterruptedException JavaDoc e)
979     {
980       String JavaDoc msg = Translate.get(
981           "loadbalancer.backendlist.acquire.readlock.failed", e);
982       logger.error(msg);
983       throw new SQLException JavaDoc(msg);
984     }
985     int nbOfThreads = backendThreads.size();
986     ArrayList JavaDoc rollbackList = new ArrayList JavaDoc();
987     long tid = tm.getTransactionId();
988     Long JavaDoc lTid = new Long JavaDoc(tid);
989
990     // Build the list of backend that need to rollback this transaction
991
for (int i = 0; i < nbOfThreads; i++)
992     {
993       BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
994       if (thread.getBackend().isStartedTransaction(lTid))
995         rollbackList.add(thread);
996     }
997
998     nbOfThreads = rollbackList.size();
999     if (nbOfThreads == 0)
1000    { // Empty transaction or read-only with cache hits, then no backend
1001
// actually executed any query for that transaction. Simply return.
1002
// Bug reported by Ganesh (ssriganesha@rogers.com).
1003
backendThreadsRWLock.releaseRead();
1004      return;
1005    }
1006
1007    // Create the task
1008
RollbackTask task = new RollbackTask(nbOfThreads, // Wait for all to
1009
// rollback
1010
nbOfThreads, tm.getTimeout(), tm.getLogin(), tid);
1011
1012    synchronized (task)
1013    {
1014      // Post the task in each backendThread tasklist and wakeup the threads
1015
for (int i = 0; i < nbOfThreads; i++)
1016      {
1017        BackendWorkerThread thread = (BackendWorkerThread) rollbackList.get(i);
1018        synchronized (thread)
1019        {
1020          thread.addTask(task, tid);
1021          thread.notify();
1022        }
1023      }
1024
1025      backendThreadsRWLock.releaseRead();
1026
1027      // Wait for completion (notified by the task)
1028
try
1029      {
1030        // Wait on task
1031
long timeout = tm.getTimeout();
1032        if (timeout > 0)
1033        {
1034          long start = System.currentTimeMillis();
1035          task.wait(timeout);
1036          long end = System.currentTimeMillis();
1037          long remaining = timeout - (end - start);
1038          if (remaining <= 0)
1039          {
1040            if (task.setExpiredTimeout())
1041            { // Task will be ignored by all backends
1042
String JavaDoc msg = Translate.get("loadbalancer.rollback.timeout",
1043                  new String JavaDoc[]{String.valueOf(tid),
1044                      String.valueOf(task.getSuccess()),
1045                      String.valueOf(task.getFailed())});
1046              logger.warn(msg);
1047              throw new SQLException JavaDoc(msg);
1048            }
1049            // else task execution already started, to late to cancel
1050
}
1051        }
1052        else
1053          task.wait();
1054      }
1055      catch (InterruptedException JavaDoc e)
1056      {
1057        if (task.setExpiredTimeout())
1058        { // Task will be ignored by all backends
1059
String JavaDoc msg = Translate.get("loadbalancer.rollback.timeout",
1060              new String JavaDoc[]{String.valueOf(tid),
1061                  String.valueOf(task.getSuccess()),
1062                  String.valueOf(task.getFailed())});
1063          logger.warn(msg);
1064          throw new SQLException JavaDoc(msg);
1065        }
1066        // else task execution already started, to late to cancel
1067
}
1068
1069      if (task.getSuccess() > 0)
1070        return;
1071      else
1072      { // All tasks failed
1073
ArrayList JavaDoc exceptions = task.getExceptions();
1074        if (exceptions == null)
1075          throw new SQLException JavaDoc(Translate.get(
1076              "loadbalancer.rollback.all.failed", tid));
1077        else
1078        {
1079          String JavaDoc errorMsg = Translate.get("loadbalancer.rollback.failed.stack",
1080              tid)
1081              + "\n";
1082          for (int i = 0; i < exceptions.size(); i++)
1083            errorMsg += ((SQLException JavaDoc) exceptions.get(i)).getMessage() + "\n";
1084          logger.error(errorMsg);
1085          throw new SQLException JavaDoc(errorMsg);
1086        }
1087      }
1088    }
1089  }
1090  
1091  /**
1092   * Rollback a transaction to a savepoint
1093   *
1094   * @param tm The transaction marker metadata
1095   * @param savepointName The name of the savepoint
1096   * @throws SQLException if an error occurs
1097   */

1098  public void rollback(TransactionMarkerMetaData tm, String JavaDoc savepointName)
1099      throws SQLException JavaDoc
1100  {
1101    try
1102    {
1103      backendThreadsRWLock.acquireRead();
1104    }
1105    catch (InterruptedException JavaDoc e)
1106    {
1107      String JavaDoc msg = Translate.get(
1108          "loadbalancer.backendlist.acquire.readlock.failed", e);
1109      logger.error(msg);
1110      throw new SQLException JavaDoc(msg);
1111    }
1112    int nbOfThreads = backendThreads.size();
1113    ArrayList JavaDoc rollbackList = new ArrayList JavaDoc();
1114    long tid = tm.getTransactionId();
1115    Long JavaDoc lTid = new Long JavaDoc(tid);
1116    
1117    // Build the list of backend that need to rollback to the savepoint
1118
for (int i = 0; i < nbOfThreads; i++)
1119    {
1120      BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
1121      if (thread.getBackend().isStartedTransaction(lTid))
1122        rollbackList.add(thread);
1123    }
1124    
1125    nbOfThreads = rollbackList.size();
1126    if (nbOfThreads == 0)
1127    { // Empty transaction or read-only with cache hits, then no backend
1128
// actually executed any query for that transaction. Simply return.
1129
// Bug reported by Ganesh (ssriganesha@rogers.com).
1130
backendThreadsRWLock.releaseRead();
1131      return;
1132    }
1133    
1134    // Create the task
1135
RollbackToSavepointTask task = new RollbackToSavepointTask(nbOfThreads,
1136        nbOfThreads, tm.getTimeout(), tm.getLogin(), tid, savepointName);
1137    
1138    synchronized (task)
1139    {
1140      // Post the task in each backendThread tasklist and wakeup the threads
1141
for (int i = 0; i < nbOfThreads; i++)
1142      {
1143        BackendWorkerThread thread = (BackendWorkerThread) rollbackList.get(i);
1144        synchronized (thread)
1145        {
1146          thread.addTask(task, tid);
1147          thread.notify();
1148        }
1149      }
1150      
1151      backendThreadsRWLock.releaseRead();
1152      // Wait for completion (notified by the task)
1153
try
1154      {
1155        // Wait on task
1156
long timeout = tm.getTimeout();
1157        if (timeout > 0)
1158        {
1159          long start = System.currentTimeMillis();
1160          task.wait(timeout);
1161          long end = System.currentTimeMillis();
1162          long remaining = timeout - (end - start);
1163          if (remaining <= 0)
1164          {
1165            if (task.setExpiredTimeout())
1166            { // Task will be ignored by all backends
1167
String JavaDoc msg = Translate.get(
1168                  "loadbalancer.rollbacksavepoint.timeout",
1169                  new String JavaDoc[]{savepointName, String.valueOf(tid),
1170                      String.valueOf(task.getSuccess()),
1171                      String.valueOf(task.getFailed())});
1172              logger.warn(msg);
1173              throw new SQLException JavaDoc(msg);
1174            }
1175            // else task execution already started, to late to cancel
1176
}
1177        }
1178        else
1179          task.wait();
1180      }
1181      catch (InterruptedException JavaDoc e)
1182      {
1183        if (task.setExpiredTimeout())
1184        { // Task will be ignored by all backends
1185
String JavaDoc msg = Translate.get(
1186              "loadbalancer.rollbacksavepoint.timeout",
1187              new String JavaDoc[]{savepointName, String.valueOf(tid),
1188                  String.valueOf(task.getSuccess()),
1189                  String.valueOf(task.getFailed())});
1190          logger.warn(msg);
1191          throw new SQLException JavaDoc(msg);
1192        }
1193        // else task execution already started, to late to cancel
1194
}
1195      
1196      if (task.getSuccess() > 0)
1197        return;
1198      else
1199      { // All tasks failed
1200
ArrayList JavaDoc exceptions = task.getExceptions();
1201        if (exceptions == null)
1202          throw new SQLException JavaDoc(Translate.get(
1203              "loadbalancer.rollbacksavepoint.all.failed",
1204              new String JavaDoc[]{savepointName, String.valueOf(tid)}));
1205        else
1206        {
1207          String JavaDoc errorMsg = Translate.get(
1208              "loadbalancer.rollbacksavepoint.failed.stack",
1209              new String JavaDoc[]{savepointName, String.valueOf(tid)})
1210              + "\n";
1211          for (int i = 0; i < exceptions.size(); i++)
1212            errorMsg += ((SQLException JavaDoc) exceptions.get(i)).getMessage() + "\n";
1213          logger.error(errorMsg);
1214          throw new SQLException JavaDoc(errorMsg);
1215        }
1216      }
1217    }
1218  }
1219  
1220  /**
1221   * Release a savepoint from a transaction
1222   *
1223   * @param tm The transaction marker metadata
1224   * @param name The name of the savepoint ro release
1225   * @throws SQLException if an error occurs
1226   */

1227  public void releaseSavepoint(TransactionMarkerMetaData tm, String JavaDoc name)
1228      throws SQLException JavaDoc
1229  {
1230    long tid = tm.getTransactionId();
1231    Long JavaDoc lTid = new Long JavaDoc(tid);
1232    
1233    // Acquire lock on backend thread list
1234
try
1235    {
1236      backendThreadsRWLock.acquireRead();
1237    }
1238    catch (InterruptedException JavaDoc e)
1239    {
1240      String JavaDoc msg = Translate.get(
1241          "loadbalancer.backendlist.acquire.readlock.failed", e);
1242      logger.error(msg);
1243      throw new SQLException JavaDoc(msg);
1244    }
1245    
1246    // Build the list of backend that need to release a savepoint
1247
// for this transaction
1248
ArrayList JavaDoc savepointList = new ArrayList JavaDoc();
1249    int nbOfThreads = backendThreads.size();
1250    for (int i = 0; i < nbOfThreads; i++)
1251    {
1252      BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
1253      if (thread.getBackend().isStartedTransaction(lTid))
1254        savepointList.add(thread);
1255    }
1256
1257    nbOfThreads = savepointList.size();
1258    if (nbOfThreads == 0)
1259    { // Empty transaction or read-only with cache hits, then no backend
1260
// actually executed any query for that transaction. Simply return.
1261
backendThreadsRWLock.releaseRead();
1262      return;
1263    }
1264
1265    // Create the task
1266
ReleaseSavepointTask task = new ReleaseSavepointTask(nbOfThreads,
1267        nbOfThreads, tm.getTimeout(), tm.getLogin(), tid, name);
1268    
1269    synchronized (task)
1270    {
1271      // Post the task in each backendThread tasklist and wakeup the threads
1272
for (int i = 0; i < nbOfThreads; i++)
1273      {
1274        BackendWorkerThread thread = (BackendWorkerThread) savepointList.get(i);
1275        synchronized (thread)
1276        {
1277          thread.addTask(task, tid);
1278          thread.notify();
1279        }
1280      }
1281
1282      backendThreadsRWLock.releaseRead();
1283
1284      // Wait for completion (notified by the task)
1285
try
1286      {
1287        // Wait on task
1288
long timeout = tm.getTimeout();
1289        if (timeout > 0)
1290        {
1291          long start = System.currentTimeMillis();
1292          task.wait(timeout);
1293          long end = System.currentTimeMillis();
1294          long remaining = timeout - (end - start);
1295          if (remaining <= 0)
1296          {
1297            if (task.setExpiredTimeout())
1298            { // Task will be ignored by all backends
1299
String JavaDoc msg = Translate.get(
1300                  "loadbalancer.releasesavepoint.timeout",
1301                  new String JavaDoc[]{name, String.valueOf(tid),
1302                      String.valueOf(task.getSuccess()),
1303                      String.valueOf(task.getFailed())});
1304              logger.warn(msg);
1305              throw new SQLException JavaDoc(msg);
1306            }
1307            // else task execution already started, to late to cancel
1308
}
1309        }
1310        else
1311          task.wait();
1312      }
1313      catch (InterruptedException JavaDoc e)
1314      {
1315        if (task.setExpiredTimeout())
1316        { // Task will be ignored by all backends
1317
String JavaDoc msg = Translate.get("loadbalancer.releasesavepoint.timeout",
1318              new String JavaDoc[]{name, String.valueOf(tid),
1319                  String.valueOf(task.getSuccess()),
1320                  String.valueOf(task.getFailed())});
1321          logger.warn(msg);
1322          throw new SQLException JavaDoc(msg);
1323        }
1324        // else task execution already started, to late to cancel
1325
}
1326
1327      if (task.getSuccess() > 0)
1328        return;
1329      else
1330      { // All tasks failed
1331
ArrayList JavaDoc exceptions = task.getExceptions();
1332        if (exceptions == null)
1333          throw new SQLException JavaDoc(Translate.get(
1334              "loadbalancer.releasesavepoint.all.failed",
1335              new String JavaDoc[]{name, String.valueOf(tid)}));
1336        else
1337        {
1338          String JavaDoc errorMsg = Translate.get(
1339              "loadbalancer.releasesavepoint.failed.stack",
1340              new String JavaDoc[]{name, String.valueOf(tid)})
1341              + "\n";
1342          for (int i = 0; i < exceptions.size(); i++)
1343            errorMsg += ((SQLException JavaDoc) exceptions.get(i)).getMessage() + "\n";
1344          logger.error(errorMsg);
1345          throw new SQLException JavaDoc(errorMsg);
1346        }
1347      }
1348    }
1349  }
1350
1351  /**
1352   * Set a savepoint to a transaction.
1353   *
1354   * @param tm The transaction marker metadata
1355   * @param name The name of the new savepoint
1356   * @throws SQLException if an error occurs
1357   */

1358  public void setSavepoint(TransactionMarkerMetaData tm, String JavaDoc name)
1359      throws SQLException JavaDoc
1360  {
1361    long tid = tm.getTransactionId();
1362    Long JavaDoc lTid = new Long JavaDoc(tid);
1363    
1364    // Acquire lock on backend thread list
1365
try
1366    {
1367      backendThreadsRWLock.acquireRead();
1368    }
1369    catch (InterruptedException JavaDoc e)
1370    {
1371      String JavaDoc msg = Translate.get(
1372          "loadbalancer.backendlist.acquire.readlock.failed", e);
1373      logger.error(msg);
1374      throw new SQLException JavaDoc(msg);
1375    }
1376    
1377    // Build the list of backend that need to set a savepoint
1378
// for this transaction
1379
ArrayList JavaDoc savepointList = new ArrayList JavaDoc();
1380    int nbOfThreads = backendThreads.size();
1381    for (int i = 0; i < nbOfThreads; i++)
1382    {
1383      BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
1384      if (thread.getBackend().isStartedTransaction(lTid))
1385        savepointList.add(thread);
1386    }
1387
1388    nbOfThreads = savepointList.size();
1389    if (nbOfThreads == 0)
1390    { // Empty transaction or read-only with cache hits, then no backend
1391
// actually executed any query for that transaction. Simply return.
1392
backendThreadsRWLock.releaseRead();
1393      return;
1394    }
1395
1396    // Create the task
1397
SavepointTask task = new SavepointTask(nbOfThreads, nbOfThreads,
1398        tm.getTimeout(), tm.getLogin(), tid, name);
1399    
1400    synchronized (task)
1401    {
1402      // Post the task in each backendThread tasklist and wakeup the threads
1403
for (int i = 0; i < nbOfThreads; i++)
1404      {
1405        BackendWorkerThread thread = (BackendWorkerThread) savepointList.get(i);
1406        synchronized (thread)
1407        {
1408          thread.addTask(task, tid);
1409          thread.notify();
1410        }
1411      }
1412
1413      backendThreadsRWLock.releaseRead();
1414
1415      // Wait for completion (notified by the task)
1416
try
1417      {
1418        // Wait on task
1419
long timeout = tm.getTimeout();
1420        if (timeout > 0)
1421        {
1422          long start = System.currentTimeMillis();
1423          task.wait(timeout);
1424          long end = System.currentTimeMillis();
1425          long remaining = timeout - (end - start);
1426          if (remaining <= 0)
1427          {
1428            if (task.setExpiredTimeout())
1429            { // Task will be ignored by all backends
1430
String JavaDoc msg = Translate.get("loadbalancer.setsavepoint.timeout",
1431                  new String JavaDoc[]{name, String.valueOf(tid),
1432                      String.valueOf(task.getSuccess()),
1433                      String.valueOf(task.getFailed())});
1434              logger.warn(msg);
1435              throw new SQLException JavaDoc(msg);
1436            }
1437            // else task execution already started, to late to cancel
1438
}
1439        }
1440        else
1441          task.wait();
1442      }
1443      catch (InterruptedException JavaDoc e)
1444      {
1445        if (task.setExpiredTimeout())
1446        { // Task will be ignored by all backends
1447
String JavaDoc msg = Translate.get("loadbalancer.setsavepoint.timeout",
1448              new String JavaDoc[]{name, String.valueOf(tid),
1449                  String.valueOf(task.getSuccess()),
1450                  String.valueOf(task.getFailed())});
1451          logger.warn(msg);
1452          throw new SQLException JavaDoc(msg);
1453        }
1454        // else task execution already started, to late to cancel
1455
}
1456
1457      if (task.getSuccess() > 0)
1458        return;
1459      else
1460      { // All tasks failed
1461
ArrayList JavaDoc exceptions = task.getExceptions();
1462        if (exceptions == null)
1463          throw new SQLException JavaDoc(Translate.get(
1464              "loadbalancer.setsavepoint.all.failed",
1465              new String JavaDoc[]{name, String.valueOf(tid)}));
1466        else
1467        {
1468          String JavaDoc errorMsg = Translate.get(
1469              "loadbalancer.setsavepoint.failed.stack",
1470              new String JavaDoc[]{name, String.valueOf(tid)})
1471              + "\n";
1472          for (int i = 0; i < exceptions.size(); i++)
1473            errorMsg += ((SQLException JavaDoc) exceptions.get(i)).getMessage() + "\n";
1474          logger.error(errorMsg);
1475          throw new SQLException JavaDoc(errorMsg);
1476        }
1477      }
1478    }
1479  }
1480
1481  /*
1482   * Backends management
1483   */

1484
1485  /**
1486   * Enables a Backend that was previously disabled.
1487   * <p>
1488   * Ask the corresponding connection manager to initialize the connections if
1489   * needed.
1490   * <p>
1491   * No sanity checks are performed by this function.
1492   *
1493   * @param db the database backend to enable
1494   * @param writeEnabled True if the backend must be enabled for writes
1495   * @throws SQLException if an error occurs
1496   */

1497  public void enableBackend(DatabaseBackend db, boolean writeEnabled)
1498      throws SQLException JavaDoc
1499  {
1500    // Create a worker thread and add it to the list
1501
BackendWorkerThread thread = new BackendWorkerThread(db, this);
1502    try
1503    {
1504      backendThreadsRWLock.acquireWrite();
1505    }
1506    catch (InterruptedException JavaDoc e)
1507    {
1508      String JavaDoc msg = Translate.get(
1509          "loadbalancer.backendlist.acquire.writelock.failed", e);
1510      logger.error(msg);
1511      throw new SQLException JavaDoc(msg);
1512    }
1513    backendThreads.add(thread);
1514    backendThreadsRWLock.releaseWrite();
1515    thread.start();
1516    logger.info(Translate.get("loadbalancer.backend.workerthread.add", db
1517        .getName()));
1518
1519    if (!db.isInitialized())
1520      db.initializeConnections();
1521    db.enableRead();
1522    if (writeEnabled)
1523      db.enableWrite();
1524  }
1525
1526  /**
1527   * Disables a backend that was previously enabled.
1528   * <p>
1529   * Ask the corresponding connection manager to finalize the connections if
1530   * needed.
1531   * <p>
1532   * No sanity checks are performed by this function.
1533   *
1534   * @param db the database backend to disable
1535   * @throws SQLException if an error occurs
1536   */

1537  public synchronized void disableBackend(DatabaseBackend db)
1538      throws SQLException JavaDoc
1539  {
1540    int nbOfThreads = backendThreads.size();
1541
1542    // Find the right thread
1543
for (int i = 0; i < nbOfThreads; i++)
1544    {
1545      BackendWorkerThread thread = (BackendWorkerThread) backendThreads.get(i);
1546      if (thread.getBackend().equals(db))
1547      {
1548        logger.info(Translate.get("loadbalancer.backend.workerthread.remove",
1549            db.getName()));
1550
1551        // Remove it from the backendThread list
1552
try
1553        {
1554          backendThreadsRWLock.acquireWrite();
1555        }
1556        catch (InterruptedException JavaDoc e)
1557        {
1558          String JavaDoc msg = Translate.get(
1559              "loadbalancer.backendlist.acquire.writelock.failed", e);
1560          logger.error(msg);
1561          throw new SQLException JavaDoc(msg);
1562        }
1563        backendThreads.remove(thread);
1564        backendThreadsRWLock.releaseWrite();
1565
1566        synchronized (thread)
1567        {
1568          // Kill the thread
1569
thread.addPriorityTask(new KillThreadTask(1, 1));
1570          thread.notify();
1571        }
1572        break;
1573      }
1574    }
1575
1576    db.disable();
1577    if (db.isInitialized())
1578      db.finalizeConnections();
1579  }
1580
1581  /**
1582   * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#setWeight(String,
1583   * int)
1584   */

1585  public void setWeight(String JavaDoc name, int w) throws SQLException JavaDoc
1586  {
1587    throw new SQLException JavaDoc("Weight is not supported with this load balancer");
1588  }
1589
1590  /**
1591   * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#getNumberOfEnabledBackends()
1592   */

1593  public int getNumberOfEnabledBackends()
1594  {
1595    return backendThreads.size();
1596  }
1597
1598  /*
1599   * Debug/Monitoring
1600   */

1601
1602  /**
1603   * Get information about the Request load balancer
1604   *
1605   * @return <code>String</code> containing information
1606   */

1607  public String JavaDoc getInformation()
1608  {
1609    return "RAIDb-0 Request load balancer\n";
1610  }
1611
1612  /**
1613   * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#getXmlImpl
1614   */

1615  public String JavaDoc getXmlImpl()
1616  {
1617    StringBuffer JavaDoc info = new StringBuffer JavaDoc();
1618    info.append("<" + DatabasesXmlTags.ELT_RAIDb_0 + ">");
1619    createTablePolicy.getXml();
1620    info.append("</" + DatabasesXmlTags.ELT_RAIDb_0 + ">");
1621    return info.toString();
1622  }
1623
1624}
Popular Tags