1 | /* |
2 | * Copyright 2007 brunella ltd |
3 | * |
4 | * Licensed under the LGPL Version 3 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * |
7 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
8 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
9 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
10 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
11 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
12 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
13 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
14 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
15 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
16 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
17 | * THE POSSIBILITY OF SUCH DAMAGE. |
18 | */ |
19 | package sf.qof.session; |
20 | |
21 | import java.sql.Connection; |
22 | import java.sql.SQLException; |
23 | |
24 | /** |
25 | * <code>RetrySessionRunner</code> is an implementation of <code>SessionRunner</code> |
26 | * that runs a <code>TransactionRunnable</code> in a session context and retries |
27 | * if an error occured. |
28 | * |
29 | * Calling <code>execute</code> starts a new session call the <code>run</code> |
30 | * method of the <code>TransactionRunnable</code> and commits or rolls back |
31 | * the transaction on success or failure. If an error occured it retries up to |
32 | * <code>numberOfRetries</code> times. <code>delayInMilliSeconds</code> can be |
33 | * used to wait a specified amount of time between the retries. |
34 | * |
35 | * <p>Typical usage is like this:</p> |
36 | * |
37 | * <p><blockquote><pre> |
38 | * List<Person> personList = ... |
39 | * PersonUpdaterRunnable runnable = new PersonUpdaterRunnable(); |
40 | * Integer numberOfUpdates = |
41 | * new RetrySessionRunner<Integer>(runnable, "MY_CONTEXT_NAME").execute(personList, 3); |
42 | * </pre></blockquote></p> |
43 | * |
44 | * @param <T> the type of the result of the <code>TransactionRunnable</code>. If |
45 | * no result is returned this type should be <code>Void</code> |
46 | * |
47 | * @see SessionRunner |
48 | */ |
49 | public class RetrySessionRunner<T> extends BaseSessionRunner<T> { |
50 | |
51 | private int numberOfRetries; |
52 | private long delayInMilliSeconds; |
53 | private TransactionRunnable<T> runnable; |
54 | |
55 | /** |
56 | * Creates a <code>RetrySessionRunner</code> that creates a session |
57 | * from the default session context. |
58 | * |
59 | * @param runnable a <code>TransactionRunnable</code> |
60 | * @param numberOfRetries max number of retries |
61 | */ |
62 | public RetrySessionRunner(TransactionRunnable<T> runnable, int numberOfRetries) { |
63 | this(runnable, numberOfRetries, 0); |
64 | } |
65 | |
66 | /** |
67 | * Creates a <code>RetrySessionRunner</code> that creates a session |
68 | * from the default session context. |
69 | * |
70 | * @param runnable a <code>TransactionRunnable</code> |
71 | * @param numberOfRetries max number of retries |
72 | * @param delayInMilliSeconds delay inbetween retries in milli seconds |
73 | */ |
74 | public RetrySessionRunner(TransactionRunnable<T> runnable, int numberOfRetries, long delayInMilliSeconds) { |
75 | this(runnable, SessionContext.DEFAULT_CONTEXT_NAME, numberOfRetries, delayInMilliSeconds); |
76 | } |
77 | |
78 | /** |
79 | * Creates a <code>RetrySessionRunner</code> that creates a session |
80 | * from the session context with the given name. |
81 | * |
82 | * @param runnable a <code>TransactionRunnable</code> |
83 | * @param contextName the context name |
84 | * @param numberOfRetries max number of retries |
85 | */ |
86 | public RetrySessionRunner(TransactionRunnable<T> runnable, String contextName, int numberOfRetries) { |
87 | this(runnable, contextName, numberOfRetries, 0); |
88 | } |
89 | |
90 | /** |
91 | * Creates a <code>RetrySessionRunner</code> that creates a session |
92 | * from the session context with the given name. |
93 | * |
94 | * @param runnable a <code>TransactionRunnable</code> |
95 | * @param contextName the context name |
96 | * @param numberOfRetries max number of retries |
97 | * @param delayInMilliSeconds delay inbetween retries in milli seconds |
98 | */ |
99 | public RetrySessionRunner(TransactionRunnable<T> runnable, String contextName, int numberOfRetries, long delayInMilliSeconds) { |
100 | super(contextName); |
101 | this.runnable = runnable; |
102 | this.numberOfRetries = numberOfRetries; |
103 | this.delayInMilliSeconds = delayInMilliSeconds; |
104 | } |
105 | |
106 | /** |
107 | * @see SessionRunner#execute(Object[]) |
108 | */ |
109 | @Override |
110 | public T execute(Object... arguments) throws SystemException { |
111 | T result = null; |
112 | int tries = 0; |
113 | boolean success = false; |
114 | while (!success && tries++ <= numberOfRetries) { |
115 | try { |
116 | result = super.execute(arguments); |
117 | success = true; |
118 | } catch (SystemException e) { |
119 | if (tries > numberOfRetries) { |
120 | throw e; |
121 | } |
122 | } |
123 | if (!success && delayInMilliSeconds > 0) { |
124 | try { |
125 | Thread.sleep(delayInMilliSeconds); |
126 | } catch (InterruptedException e) {} |
127 | } |
128 | } |
129 | return result; |
130 | } |
131 | |
132 | @Override |
133 | protected T run(Connection connection, Object... arguments) throws SQLException { |
134 | return runnable.run(connection, arguments); |
135 | } |
136 | } |