001: /*
002: * Created on Jul 10, 2004
003: */
004: package net.sourceforge.orbroker;
005:
006: import java.sql.Connection;
007: import java.sql.SQLException;
008: import java.util.HashMap;
009: import java.util.Map;
010: import java.util.Properties;
011: import java.util.logging.Level;
012:
013: import net.sourceforge.orbroker.exception.ExceptionEvaluator;
014:
015: /**
016: * An abstract connection wrapper.
017: * @author Nils Kilden-Pedersen
018: */
019: abstract class BrokerConnection {
020: private final boolean autoCommit;
021:
022: private final Broker broker;
023: private Connection connection;
024: private final ConnectionContext context = new ConnectionContext();
025: private final int isolationLevel;
026: private Map localStatements = null;
027: private boolean retryStaleConnection;
028:
029: private BrokerConnection(Broker broker, Connection connection,
030: boolean retryStaleConnection) {
031: this .context.setTextReplacements(broker.getTextReplacements());
032: this .broker = broker;
033: this .connection = connection;
034: this .retryStaleConnection = retryStaleConnection;
035: try {
036: setConnectionProperties();
037: this .autoCommit = this .connection.getAutoCommit();
038: this .isolationLevel = this .connection
039: .getTransactionIsolation();
040: } catch (SQLException e) {
041: throw new BrokerException(e);
042: }
043: }
044:
045: /**
046: * Create BrokerConnection with external JDBC connection.
047: * Will not retry a stale connection.
048: * @param broker
049: * @param connection
050: */
051: protected BrokerConnection(Broker broker, Connection connection) {
052: this (broker, connection, false);
053: }
054:
055: /**
056: * Create BrokerConnection with a Broker connection.
057: * Will retry a stale connection.
058: * @param broker
059: * @param isolationLevel
060: */
061: protected BrokerConnection(Broker broker, Integer isolationLevel) {
062: this (broker, broker.getFreshConnection(isolationLevel), true);
063: }
064:
065: private final void addLocalStatement(Statement statement) {
066: if (this .localStatements == null) {
067: this .localStatements = new HashMap();
068: }
069: this .localStatements.put(statement.getId(), statement);
070: }
071:
072: /**
073: * Release connection, and close if necessary.
074: */
075: protected final void dropConnection() {
076:
077: if (this .connection == null) {
078: return;
079: }
080:
081: try {
082: if (shouldConnectionClose()) {
083: Broker.getCachedObjects(this .connection).clear();
084: if (!this .connection.isClosed()) {
085: this .connection.close();
086: }
087: }
088: } catch (SQLException e) {
089: Broker.log(Level.WARNING, e.toString());
090: } finally {
091: this .connection = null;
092: }
093: }
094:
095: /**
096: * Finalize.
097: * @see java.lang.Object#finalize()
098: */
099: protected final void finalize() {
100: if (this .connection != null) {
101: dropConnection();
102: Broker.log(Level.WARNING, getFinalizeWarning());
103: }
104: try {
105: super .finalize();
106: } catch (Throwable e) {
107: Broker.log(Level.WARNING, e.toString());
108: }
109: }
110:
111: protected final Connection getActiveConnection() {
112: if (this .connection == null) {
113: throw new IllegalStateException(
114: getClosedConnectionMessage());
115: }
116: return this .connection;
117: }
118:
119: protected abstract String getClosedConnectionMessage();
120:
121: protected final ConnectionContext getConnectionContext() {
122: return this .context;
123: }
124:
125: protected abstract String getFinalizeWarning();
126:
127: /**
128: * Return the statement. Give preference to any locally defined statements.
129: * @param statementID
130: * @return The statement with the given id
131: * @throws BrokerException
132: */
133: protected Statement getStatement(String statementID)
134: throws BrokerException {
135: if (this .localStatements != null) {
136: Statement localStatement = (Statement) this .localStatements
137: .get(statementID);
138: if (localStatement != null) {
139: return localStatement;
140: }
141: }
142: return this .broker.findStatement(statementID);
143: }
144:
145: protected final String getTransactionIsolationText() {
146: return Broker.getTransactionIsolationLevel(this .isolationLevel);
147: }
148:
149: protected final boolean isAutoCommit() {
150: return this .autoCommit;
151: }
152:
153: protected final Executable newExecutable(Transaction transaction) {
154: return new Executable(this .broker, transaction);
155: }
156:
157: /**
158: * Retry is guaranteed to return true 0 or 1 times only,
159: * so it can be used recursively without looping infinitely.
160: * @param e exception
161: * @return true if statement should be retried
162: */
163: protected boolean retryFailedStatement(SQLException e) {
164: if (this .retryStaleConnection) {
165: ExceptionEvaluator evaluator = this .broker
166: .getExceptionEvaluator();
167: if (evaluator.isStaleConnection(e)) {
168: this .connection = this .broker
169: .getFreshConnection(new Integer(
170: this .isolationLevel));
171: this .retryStaleConnection = false;
172: return true;
173: }
174: }
175: return false;
176: }
177:
178: protected abstract void setConnectionProperties()
179: throws SQLException;
180:
181: protected abstract boolean shouldConnectionClose();
182:
183: protected final boolean supportsBatch() {
184: return this .broker.supportsBatch();
185: }
186:
187: protected final boolean supportsScrollable() {
188: return this .broker.supportsScrollable();
189: }
190:
191: /**
192: * Determine exception type and throw exception.
193: * @param source SQLException
194: * @throws BrokerException
195: * @throws ConstraintException
196: * @throws DeadlockException
197: */
198: protected final void throwException(SQLException source)
199: throws BrokerException, ConstraintException,
200: DeadlockException {
201:
202: ExceptionEvaluator evaluator = this .broker
203: .getExceptionEvaluator();
204: if (evaluator.isConstraint(source)) {
205: throw new ConstraintException(source.getMessage(), source);
206: }
207:
208: if (evaluator.isDeadlock(source)) {
209: throw new DeadlockException(source.getMessage(), source);
210: }
211:
212: throw new BrokerException(source);
213: }
214:
215: /**
216: * Add context parameters.
217: * @param addContext The context to add.
218: */
219: final void addContext(ConnectionContext addContext) {
220: this .context.addContext(addContext);
221: }
222:
223: /**
224: * Add temporary statement.
225: * @param id The statement id.
226: * @param sql The SQL statement
227: * @throws BrokerException
228: * @throws ConfigurationException
229: */
230: public final void addStatement(String id, String sql)
231: throws BrokerException, ConfigurationException {
232: addStatement(id, sql, null);
233: }
234:
235: /**
236: * Add temporary statement.
237: * @param id The statement id.
238: * @param sql The SQL statement
239: * @param resultObjectId The <code>result-object</code> id as
240: * defined in the XML configuration file.
241: * @throws BrokerException
242: * @throws ConfigurationException
243: */
244: public final void addStatement(String id, String sql,
245: String resultObjectId) throws BrokerException,
246: ConfigurationException {
247: ResultObjectDefinition resultObjectDef = null;
248: if (resultObjectId != null) {
249: resultObjectDef = this .broker
250: .findResultObjectDefinition(resultObjectId);
251: }
252:
253: Statement stm = Statement.newInstance(id, sql, resultObjectDef);
254: addLocalStatement(stm);
255: }
256:
257: /**
258: * Get parameter.
259: * Used to retrieve OUT/INOUT parameters from stored procedures.
260: * Both input and output parameters are stored in the same context,
261: * so any parameter set using {@link #setParameter(String, Object)}
262: * will be available with this method. Also if a stored procedure has
263: * an output parameter that needs to be used as input in another
264: * SQL statements, it's immediately available, i.e. there's <i>no need</i>
265: * to code like this:
266: * <code style="text-decoration: line-through;">
267: * txn.setParameter("parm", txn.getParameter("parm"));</code>
268: * @param name parameter name
269: * @return parameter value
270: */
271: public final Object getParameter(String name) {
272: return this .context.getParameterValue(name);
273: }
274:
275: /**
276: * Set named parameter.
277: * @param name parameter name
278: * @param value parameter value
279: */
280: public final void setParameter(String name, Object value) {
281: this .context.setParameter(name, value);
282: }
283:
284: /**
285: * Set a text replacement value. This will replace a <code>{{key}}</code>
286: * string with the value.
287: * This will override any value set on Broker.
288: * @param key Text replacement key
289: * @param value Text replacement value
290: * @see #setTextReplacements(Properties)
291: */
292: public final void setTextReplacement(String key, String value) {
293: this .context.setTextReplacement(key, value);
294: }
295:
296: /**
297: * Set text replacement values. This will replace all <code>{{key}}</code>
298: * type properties in an sql-statement with the values.
299: * This will override any value set on Broker.
300: * @param textReplacements The replacement values to set.
301: * @see #setTextReplacement(String, String)
302: */
303: public final void setTextReplacements(Properties textReplacements) {
304: this.context.setTextReplacements(textReplacements);
305: }
306:
307: }
|