00001 /** 00002 * C-JDBC: Clustered JDBC. 00003 * Copyright (C) 2002-2004 French National Institute For Research In Computer 00004 * Science And Control (INRIA). 00005 * Contact: c-jdbc@objectweb.org 00006 * 00007 * This library is free software; you can redistribute it and/or modify it 00008 * under the terms of the GNU Lesser General Public License as published by the 00009 * Free Software Foundation; either version 2.1 of the License, or any later 00010 * version. 00011 * 00012 * This library is distributed in the hope that it will be useful, but WITHOUT 00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 00015 * for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public License 00018 * along with this library; if not, write to the Free Software Foundation, 00019 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00020 * 00021 * Initial developer(s): Emmanuel Cecchet. 00022 * Contributor(s): Jaco Swart. 00023 */ 00024 00025 package org.objectweb.cjdbc.controller.loadbalancer.tasks; 00026 00027 import java.sql.ResultSet; 00028 import java.sql.SQLException; 00029 import java.util.ArrayList; 00030 00031 import org.objectweb.cjdbc.common.exceptions.SQLExceptionFactory; 00032 import org.objectweb.cjdbc.controller.loadbalancer.BackendWorkerThread; 00033 00034 /** 00035 * Defines an abstract task to be processed by a 00036 * <code>BackendWorkerThread</code>. 00037 * 00038 * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a> 00039 * @author <a href="mailto:jaco.swart@iblocks.co.uk">Jaco Swart </a> 00040 * @version 1.0 00041 */ 00042 public abstract class AbstractTask 00043 { 00044 // 00045 // How the code is organized ? 00046 // 1. Member variables 00047 // 2. Constructor(s) 00048 // 3. Task management 00049 // 4. Getter/Setter 00050 // 00051 00052 /** Total number of threads. */ 00053 private int totalNb; 00054 00055 /** Number of threads that must succeed before returning. */ 00056 private int nbToComplete; 00057 00058 /** Number of thread that have started the execution of the task */ 00059 private int executionStarted; 00060 /** Number of backendThread that have succeeded */ 00061 private int success = 0; 00062 /** Number of backendThread that have failed */ 00063 private int failed = 0; 00064 /** List of exceptions of failed nodes */ 00065 private ArrayList exceptions = null; 00066 00067 // Internal tid flag used by BackendWorkerThread 00068 private boolean hasTid = false; 00069 00070 // ResultSet for getting back autogenerated keys in an update with keys 00071 private ResultSet generatedKeysResultSet; 00072 00073 // True if the timeout has expired on this task 00074 private boolean timeoutExpired = false; 00075 00076 /* 00077 * Constructor 00078 */ 00079 00080 /** 00081 * Sets the number of threads among the total number of threads that must 00082 * successfully complete the execution of this AbstractTask before returning. 00083 * 00084 * @param nbToComplete number of threads that must succeed before returning 00085 * @param totalNb total number of threads 00086 */ 00087 public AbstractTask(int nbToComplete, int totalNb) 00088 { 00089 this.nbToComplete = nbToComplete; 00090 this.totalNb = totalNb; 00091 success = 0; 00092 failed = 0; 00093 executionStarted = 0; 00094 } 00095 00096 /* 00097 * Task management 00098 */ 00099 00100 /** 00101 * The task code executed by the backendThread. 00102 * 00103 * @param backendThread The backend thread executing this task 00104 * @throws SQLException if an error occurs 00105 */ 00106 public void execute(BackendWorkerThread backendThread) throws SQLException 00107 { 00108 synchronized (this) 00109 { 00110 // If the task has expired and nobody has executed it yet, we ignore it 00111 // else we have to play it. 00112 // Note that the exception corresponding to the timeout is set by the 00113 // caller of setExpiredTimeout. 00114 if (timeoutExpired && (executionStarted == 0)) 00115 return; 00116 this.executionStarted++; 00117 } 00118 executeTask(backendThread); 00119 // Completed executions are handled by the task internal code that calls 00120 // notifyFailure or notifySuccess. 00121 } 00122 00123 /** 00124 * The implementation specific task code to be executed by backendThread. 00125 * 00126 * @param backendThread The backend thread executing this task 00127 * @throws SQLException if an error occurs 00128 */ 00129 public abstract void executeTask(BackendWorkerThread backendThread) 00130 throws SQLException; 00131 00132 /** 00133 * Notifies the successful completion of this task. 00134 */ 00135 public synchronized void notifySuccess() 00136 00137 { 00138 success++; 00139 00140 // Notify if needed 00141 if ((success == nbToComplete) || (success + failed == totalNb)) 00142 { 00143 if (failed > 0) 00144 notifyAll(); // Notify all failed threads too 00145 else 00146 notify(); 00147 } 00148 } 00149 00150 /** 00151 * This is used to notify the completion of this task without success or 00152 * failure. This is usually used when the task has been discarded for example 00153 * by a backend that is currently disabling but still needs to execute the 00154 * remaining queries in open transactions. 00155 * <p> 00156 * Therefore, this only decrements by one the number of threads that needs to 00157 * complete. 00158 */ 00159 public synchronized void notifyCompletion() 00160 { 00161 totalNb--; 00162 // Notify if needed 00163 if (success + failed == totalNb) 00164 { 00165 notifyAll(); // Notify all failed threads 00166 } 00167 } 00168 00169 /** 00170 * Notifies that the specified backendThread failed to execute this task. If 00171 * all nodes failed, this method return <code>false</code> meaning that the 00172 * problem was due to the task and not to the thread. If the method returns 00173 * <code>true</code>, it can mean that this thread failed and is no more 00174 * coherent, therefore the backend associated to this thread should be 00175 * disabled. 00176 * 00177 * @param backendThread the backend thread that has failed 00178 * @param timeout time in milliseconds to wait for other threads to signal 00179 * success or failure 00180 * @param e the exception causing the failure 00181 * @return <code>true</code> if at least one node succeeded to execute this 00182 * task, <code>false</code> if all threads failed 00183 * @throws SQLException if an error occured in the notification process 00184 */ 00185 public synchronized boolean notifyFailure(BackendWorkerThread backendThread, 00186 long timeout, Exception e) throws SQLException 00187 { 00188 failed++; 00189 00190 // Log the exception 00191 if (exceptions == null) 00192 exceptions = new ArrayList(); 00193 if (e instanceof SQLException) 00194 { 00195 SQLException sqlEx = (SQLException) e; 00196 exceptions 00197 .add(SQLExceptionFactory.getSQLException(sqlEx,"BackendThread " 00198 + backendThread.getBackend().getName() + " failed (" 00199 + sqlEx.getMessage() + ")")); 00200 } 00201 else 00202 exceptions.add(new SQLException("BackendThread " 00203 + backendThread.getBackend().getName() + " failed (" + e + ")")); 00204 00205 // Notify if needed 00206 if (success + failed == totalNb) 00207 { 00208 notifyAll(); // Notify all failed threads 00209 } 00210 else 00211 { 00212 try 00213 { // Wait to check if all other threads failed or not 00214 wait(timeout); 00215 } 00216 catch (InterruptedException ie) 00217 { 00218 throw new SQLException("Wait interrupted() in failed task of backend " 00219 + backendThread.getBackend().getName() + " (" + e + ")"); 00220 } 00221 } 00222 return success > 0; 00223 } 00224 00225 // 00226 // Getter/Setter 00227 // 00228 00229 /** 00230 * Returns the exceptions lists. 00231 * 00232 * @return an <code>ArrayList</code> 00233 */ 00234 public ArrayList getExceptions() 00235 { 00236 return exceptions; 00237 } 00238 00239 /** 00240 * Returns the number of threads that have started the execution of the task. 00241 * 00242 * @return Returns the number of started executions. 00243 */ 00244 public synchronized int getExecutionStarted() 00245 { 00246 return executionStarted; 00247 } 00248 00249 /** 00250 * Returns the failed. 00251 * 00252 * @return an <code>int</code> value 00253 */ 00254 public int getFailed() 00255 { 00256 return failed; 00257 } 00258 00259 /** 00260 * Returns the number of threads that must succeed before returning. 00261 * 00262 * @return an <code>int</code> value 00263 */ 00264 public int getNbToComplete() 00265 { 00266 return nbToComplete; 00267 } 00268 00269 /** 00270 * Returns the success. 00271 * 00272 * @return an <code>int</code> value 00273 */ 00274 public int getSuccess() 00275 { 00276 return success; 00277 } 00278 00279 /** 00280 * Returns the total number of threads. 00281 * 00282 * @return an <code>int</code> value 00283 * @see #setTotalNb 00284 */ 00285 public int getTotalNb() 00286 { 00287 return totalNb; 00288 } 00289 00290 /** 00291 * Sets the total number of threads. 00292 * 00293 * @param totalNb the total number of threads to set 00294 * @see #getTotalNb 00295 */ 00296 public void setTotalNb(int totalNb) 00297 { 00298 this.totalNb = totalNb; 00299 } 00300 00301 /** 00302 * Returns true if this task has a tid attached to it. 00303 * <p> 00304 * Used internally by BackendWorkerThread. 00305 * 00306 * @return Returns the hasTid. 00307 */ 00308 public boolean hasTid() 00309 { 00310 return hasTid; 00311 } 00312 00313 /** 00314 * Sets the hasTid value. 00315 * <p> 00316 * Used internally by BackendWorkerThread. 00317 * 00318 * @param hasTid The hasTid to set. 00319 */ 00320 public void setHasTid(boolean hasTid) 00321 { 00322 this.hasTid = hasTid; 00323 } 00324 00325 /** 00326 * Returns the generatedKeysResultSet value. 00327 * 00328 * @return Returns the generatedKeysResultSet. 00329 */ 00330 public ResultSet getGeneratedKeysResultSet() 00331 { 00332 return generatedKeysResultSet; 00333 } 00334 00335 /** 00336 * Sets the generatedKeysResultSet value. 00337 * 00338 * @param generatedKeysResultSet The generatedKeysResultSet to set. 00339 */ 00340 public void setGeneratedKeysResultSet(ResultSet generatedKeysResultSet) 00341 { 00342 this.generatedKeysResultSet = generatedKeysResultSet; 00343 } 00344 00345 /** 00346 * Set the flag to tell that the timeout has expired on this task. If no 00347 * backend has started the task execution then the task will be canceled and 00348 * the method will return true. Otherwise, all backends will execute the 00349 * request and the method will return false. 00350 * 00351 * @return true if BackendThreads will ignore the task, false if all backends 00352 * will execute the task. 00353 */ 00354 public synchronized boolean setExpiredTimeout() 00355 { 00356 this.timeoutExpired = true; 00357 return executionStarted == 0; 00358 } 00359 00360 }