001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Emmanuel Cecchet.
019: * Contributor(s): ______________________.
020: */package org.continuent.sequoia.controller.requests;
021:
022: import java.sql.Connection;
023:
024: /**
025: * This class defines a RequestFactory.<br>
026: * Given a SQL query, it creates a full request of the correct type. These
027: * request types are defined by the implementing classes, as well as the regular
028: * expressions to be used for the SQL query parsing
029: *
030: * @author <a href="mailto:emmanuel.cecchet@emicnetworks.com">Emmanuel Cecchet</a>
031: * @author <a href="mailto:gilles.rayrat@continuent.com">Gilles Rayrat</a>
032: * @version 2.0
033: */
034: public abstract class RequestFactory {
035: /** Reqular expressions to be used for request parsing */
036: private RequestRegExp driverRegExp = null;
037:
038: /**
039: * Sets the custom regular expressions
040: *
041: * @param regexp regular expression to be used for request parsing
042: */
043: protected RequestFactory(RequestRegExp regexp) {
044: driverRegExp = regexp;
045: }
046:
047: /**
048: * Parses the given string:
049: * <ul>
050: * <li>if a read query is identified, creates and returns the appropriate
051: * read request
052: * <li>else if a write query is identified, creates and returns an unknown
053: * write request
054: * <li>else returns null
055: * </ul>
056: *
057: * @param sqlQuery sql statement to parse
058: * @param escapeProcessing query parameter to set
059: * @param timeout query parameter to set
060: * @param lineSeparator query parameter to set
061: * @return a <code>SelectRequest</code>, an <code>UnknownReadRequest</code>,
062: * an <code>UnknownWriteRequest</code> or <code>null</code>
063: */
064: protected AbstractRequest decodeReadRequestFromString(
065: String sqlQuery, boolean escapeProcessing, int timeout,
066: String lineSeparator) {
067: AbstractRequest decodedRequest = null;
068: if (driverRegExp.getSelectRequestPattern().matcher(sqlQuery)
069: .matches()) {
070: decodedRequest = getSelectRequest(sqlQuery,
071: escapeProcessing, timeout, lineSeparator);
072: if (driverRegExp.getSelectForUpdatePattern().matcher(
073: sqlQuery).matches())
074: ((SelectRequest) decodedRequest).setMustBroadcast(true);
075: }
076: if (driverRegExp.getUnknownWriteRequestPattern().matcher(
077: sqlQuery).matches()) {
078: decodedRequest = getUnknownWriteRequest(sqlQuery,
079: escapeProcessing, timeout, lineSeparator);
080: } else if (driverRegExp.getUnknownReadRequestPattern().matcher(
081: sqlQuery).matches()) {
082: decodedRequest = getUnknownReadRequest(sqlQuery,
083: escapeProcessing, timeout, lineSeparator);
084: if (driverRegExp.getSelectForUpdatePattern().matcher(
085: sqlQuery).matches())
086: ((SelectRequest) decodedRequest).setMustBroadcast(true);
087: }
088: return decodedRequest;
089: }
090:
091: /**
092: * Parses the given string and creates/returns the appropriate write request,
093: * stored procedure or <code>null</code> if no write query was identified
094: *
095: * @param sqlQuery sql statement to parse
096: * @param escapeProcessing query parameter to set
097: * @param timeout query parameter to set
098: * @param lineSeparator query parameter to set
099: * @return an <code>InsertRequest</code>, an <code>UpdateRequest</code>,
100: * a <code>DeleteRequest</code>, a <code>CreateRequest</code>, a
101: * <code>DropRequest</code>, an <code>AlterRequest</code>, a
102: * <code>StoredProcedure</code> or <code>null</code>
103: */
104: protected AbstractRequest decodeWriteRequestFromString(
105: String sqlQuery, boolean escapeProcessing, int timeout,
106: String lineSeparator) {
107: AbstractRequest decodedRequest = null;
108: if (driverRegExp.getInsertQueryPattern().matcher(sqlQuery)
109: .matches()) {
110: decodedRequest = getInsertRequest(sqlQuery,
111: escapeProcessing, timeout, lineSeparator);
112: } else if (driverRegExp.getUpdateRequestPattern().matcher(
113: sqlQuery).matches()) {
114: decodedRequest = getUpdateRequest(sqlQuery,
115: escapeProcessing, timeout, lineSeparator);
116: } else if (driverRegExp.getDeleteRequestPattern().matcher(
117: sqlQuery).matches()) {
118: decodedRequest = getDeleteRequest(sqlQuery,
119: escapeProcessing, timeout, lineSeparator);
120: } else if (driverRegExp.getCreateRequestPattern().matcher(
121: sqlQuery).matches()) {
122: decodedRequest = getCreateRequest(sqlQuery,
123: escapeProcessing, timeout, lineSeparator);
124: } else if (driverRegExp.getDropRequestPattern().matcher(
125: sqlQuery).matches()) {
126: decodedRequest = getDropRequest(sqlQuery, escapeProcessing,
127: timeout, lineSeparator);
128: } else if (driverRegExp.getAlterRequestPattern().matcher(
129: sqlQuery).matches()) {
130: decodedRequest = getAlterRequest(sqlQuery,
131: escapeProcessing, timeout, lineSeparator);
132: } else if (driverRegExp.getStoredProcedurePattern().matcher(
133: sqlQuery).matches()) {
134: decodedRequest = getStoredProcedure(sqlQuery,
135: escapeProcessing, timeout, lineSeparator);
136: }
137: return decodedRequest;
138: }
139:
140: /**
141: * Given a sql statement string, creates the appropriate read or write
142: * request, or <code>null</code> if no sql query could be identified.
143: * Priority of the parsing and the parameters of the request to be created are
144: * configurable tries to identify read or write first)
145: *
146: * @param sqlQuery sql statement to parse
147: * @param isProbablyAReadRequest set to true if the given sql query is
148: * probably a read statement. This will give priority to the parsing
149: * of read requests
150: * @param escapeProcessing query parameter to set
151: * @param timeout query parameter to set
152: * @param lineSeparator query parameter to set
153: * @return an <code>InsertRequest</code>, an <code>UpdateRequest</code>,
154: * a <code>DeleteRequest</code>, a <code>CreateRequest</code>, a
155: * <code>DropRequest</code>, an <code>AlterRequest</code>, a
156: * <code>StoredProcedure</code>, a <code>SelectRequest</code>,
157: * an <code>UnknownReadRequest</code>, an
158: * <code>UnknownWriteRequest</code> or <code>null</code>
159: */
160: public AbstractRequest requestFromString(String sqlQuery,
161: boolean isProbablyAReadRequest, boolean escapeProcessing,
162: int timeout, String lineSeparator) {
163: AbstractRequest decodedRequest = null;
164: if (isProbablyAReadRequest) {
165: // more probably a read, let's try it first
166: decodedRequest = decodeReadRequestFromString(sqlQuery,
167: escapeProcessing, timeout, lineSeparator);
168: }
169: if (decodedRequest == null
170: || decodedRequest instanceof UnknownWriteRequest) {
171: // either isProbablyAReadRequest == false or decodeRead found an unknown
172: // write
173: decodedRequest = decodeWriteRequestFromString(sqlQuery,
174: escapeProcessing, timeout, lineSeparator);
175: }
176: if (decodedRequest == null && !isProbablyAReadRequest) {
177: // decodeWrite didn't find anything, let's try decodeRead
178: decodedRequest = decodeReadRequestFromString(sqlQuery,
179: escapeProcessing, timeout, lineSeparator);
180: } else if (decodedRequest == null) {
181: // nothing found, safer to say that it is an unknown read
182: decodedRequest = getUnknownReadRequest(sqlQuery,
183: escapeProcessing, timeout, lineSeparator);
184: }
185: return decodedRequest;
186: }
187:
188: /**
189: * Returns true if the query has to be executed with statement.execute() or if
190: * it contains an inline batch such as "SELECT xxx ; INSERT xxx ; DROP xxx ;
191: * ..."
192: *
193: * @param request the request to check
194: * @return true if the request has to be executed with statement.execute() or
195: * contains an inline batch
196: */
197: public boolean requestNeedsExecute(AbstractRequest request) {
198: // Check if the request match the StatementExecuteRequest pattern
199: if (driverRegExp.getStatementExecuteRequestPattern().matcher(
200: request.getSqlOrTemplate()).matches())
201: return true;
202:
203: // Otherwise, check if the query contains an inline batch such as "SELECT
204: // xxx ; INSERT xxx ; DROP xxx ; ..."
205: String sql = request.getSqlOrTemplate();
206: int semiColonIdx = sql.indexOf(';');
207:
208: // Test for no semicolon
209: if (semiColonIdx == -1)
210: return false;
211:
212: // Test for multiple semicolons
213: // TODO: Check that semicolon does not belong to a parameter
214: if (sql.indexOf(';', semiColonIdx + 1) != -1)
215: return true;
216:
217: // Single semicolon, check that this was not just appended at the end of a
218: // single query
219: if (sql.trim().endsWith(";"))
220: return false; // query ends by semicolon
221: else
222: return true;
223: }
224:
225: /**
226: * Check whether the given sql is authorized to execute on the cluster or not.
227: *
228: * @param sql the SQL statement to check
229: * @return false if the statement is not authorized and must be blocked, true
230: * otherwise
231: */
232: public boolean isAuthorizedRequest(String sql) {
233: return !driverRegExp.getUnauthorizedRequestsPattern().matcher(
234: sql).matches();
235: }
236:
237: /**
238: *
239: * Determines whether or not a select statement should be broadcast
240: * based on the setting of the transaction isolation level. By
241: * default request at transaction isolation level serializable are
242: * broadcast.
243: *
244: * @param transactionIsolationLevel the transaction isolation level
245: * for the current request.
246: *
247: * @return true if the select should be broadcast.
248: */
249: public boolean isBroadcastRequired(int transactionIsolationLevel) {
250: return transactionIsolationLevel == Connection.TRANSACTION_SERIALIZABLE;
251: }
252:
253: /**
254: * @see AlterRequest#AlterRequest(String, boolean, int, String)
255: */
256: protected abstract AlterRequest getAlterRequest(String sqlQuery,
257: boolean escapeProcessing, int timeout, String lineSeparator);
258:
259: /**
260: * @see CreateRequest#CreateRequest(String, boolean, int, String)
261: */
262: protected abstract CreateRequest getCreateRequest(String sqlQuery,
263: boolean escapeProcessing, int timeout, String lineSeparator);
264:
265: /**
266: * @see DeleteRequest#DeleteRequest(String, boolean, int, String)
267: */
268: protected abstract DeleteRequest getDeleteRequest(String sqlQuery,
269: boolean escapeProcessing, int timeout, String lineSeparator);
270:
271: /**
272: * @see DropRequest#DropRequest(String, boolean, int, String)
273: */
274: protected abstract DropRequest getDropRequest(String sqlQuery,
275: boolean escapeProcessing, int timeout, String lineSeparator);
276:
277: /**
278: * @see InsertRequest#InsertRequest(String, boolean, int, String)
279: */
280: protected abstract InsertRequest getInsertRequest(String sqlQuery,
281: boolean escapeProcessing, int timeout, String lineSeparator);
282:
283: /**
284: * @see SelectRequest#SelectRequest(String, boolean, int, String)
285: */
286: protected abstract SelectRequest getSelectRequest(String sqlQuery,
287: boolean escapeProcessing, int timeout, String lineSeparator);
288:
289: /**
290: * @see StoredProcedure#StoredProcedure(String, boolean, int, String)
291: */
292: protected abstract StoredProcedure getStoredProcedure(
293: String sqlQuery, boolean escapeProcessing, int timeout,
294: String lineSeparator);
295:
296: /**
297: * @see UnknownReadRequest#UnknownReadRequest(String, boolean, int, String)
298: */
299: protected abstract UnknownReadRequest getUnknownReadRequest(
300: String sqlQuery, boolean escapeProcessing, int timeout,
301: String lineSeparator);
302:
303: /**
304: * @see UnknownWriteRequest#UnknownWriteRequest(String, boolean, int, String)
305: */
306: protected abstract UnknownWriteRequest getUnknownWriteRequest(
307: String sqlQuery, boolean escapeProcessing, int timeout,
308: String lineSeparator);
309:
310: /**
311: * @see UpdateRequest#UpdateRequest(String, boolean, int, String)
312: */
313: protected abstract UpdateRequest getUpdateRequest(String sqlQuery,
314: boolean escapeProcessing, int timeout, String lineSeparator);
315: }
|