001: package com.salmonllc.sql;
002:
003: //** Copyright Statement ***************************************************
004: //The Salmon Open Framework for Internet Applications (SOFIA)
005: // Copyright (C) 1999 - 2002, Salmon LLC
006: //
007: // This program is free software; you can redistribute it and/or
008: // modify it under the terms of the GNU General Public License version 2
009: // as published by the Free Software Foundation;
010: //
011: // This program is distributed in the hope that it will be useful,
012: // but WITHOUT ANY WARRANTY; without even the implied warranty of
013: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: // GNU General Public License for more details.
015: //
016: // You should have received a copy of the GNU General Public License
017: // along with this program; if not, write to the Free Software
018: // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: //
020: // For more information please visit http://www.salmonllc.com
021: //** End Copyright Statement ***************************************************
022: import com.salmonllc.sql.DataStore;
023: import com.salmonllc.sql.DataDictionary;
024: import com.salmonllc.sql.DataStoreException;
025: import com.salmonllc.sql.DBConnection;
026: import com.salmonllc.util.MessageLog;
027:
028: import java.util.Vector;
029: import java.sql.PreparedStatement;
030: import java.sql.ResultSet;
031: import java.sql.SQLException;
032:
033: /**
034: * This class performs referential integrity checking functions even if the underlying database doesn't.
035: * Each IntegrityChecker object should correspond to a single DataStore. The object may contain rules
036: * that are checked for inserts/updates as well as rules to be checked before a row is to be deleted.
037: * Note: The column within the datastore and the column in the external table we are checking must be
038: * of the same data type.
039: * @author Tyler Williams http://www.dataterrace.com
040: */
041: public class IntegrityChecker {
042: private Vector _refCheckDel = new Vector();
043: private Vector _refCheckInsUpd = new Vector();
044: private String _appName;
045: private DataStore _ds;
046: private DataDictionary _dd;
047: private static final int CHECK_DELETE = 0;
048: private static final int CHECK_INSERTUPDATE = 1;
049:
050: /**
051: * Constructs a new integrity checker object and initializes variables.
052: * @param appName The application to get the default database connection for.
053: * @param ds The data store containing the rows we will be checking for integrity
054: */
055: public IntegrityChecker(String appName, DataStore ds) {
056: _appName = appName;
057: _ds = ds;
058: _dd = new DataDictionary(_appName);
059: }
060:
061: /**
062: * This is an inner class that represents a specific referential integrity rule.
063: */
064: private class RefRule {
065: private String _localColumnName;
066: private String _tableName;
067: private String _columnName;
068: private String _query;
069:
070: /**
071: * Constructs a new referential integrity rule object and initializes variables.
072: * @param lc The local column.
073: * @param t The external table.
074: * @param c The external column.
075: * @param q The query being used.
076: */
077: RefRule(String lc, String t, String c, String q) {
078: _localColumnName = lc;
079: _tableName = t;
080: _columnName = c;
081: _query = q;
082: }
083:
084: String getLocalColumnName() {
085: return _localColumnName;
086: }
087:
088: String getTableName() {
089: return _tableName;
090: }
091:
092: String getColumnName() {
093: return _columnName;
094: }
095:
096: String getQuery() {
097: return _query;
098: }
099: }
100:
101: /**
102: * Adds a referential integrity rule that will be used on deletion attempts.
103: * Note: The localColumn and the externalColumn must be of the same data type.
104: * @param localColumn The column within this IntegrityChecker object's DataStore _ds.
105: * @param externalTable The external table we will use to check against the internal column.
106: * @param externalColumn The external column we will use to check against the internal column.
107: * @throws DataStoreException when the local or external column or external table are not found.
108: */
109: public void addDeleteRefCheck(String localColumn,
110: String externalTable, String externalColumn)
111: throws DataStoreException {
112: addRefCheck(CHECK_DELETE, localColumn, externalTable,
113: externalColumn);
114: }
115:
116: /**
117: * Adds a referential integrity rule that will be used on insert/update attempts.
118: * Note: The localColumn and the externalColumn must be of the same data type.
119: * @param localColumn The column within this IntegrityChecker object's DataStore _ds.
120: * @param externalTable The external table we will use to check against the internal column.
121: * @param externalColumn The external column we will use to check against the internal column.
122: * @throws DataStoreException when the local or external column or external table are not found.
123: */
124: public void addInsertUpdateRefCheck(String localColumn,
125: String externalTable, String externalColumn)
126: throws DataStoreException {
127: addRefCheck(CHECK_INSERTUPDATE, localColumn, externalTable,
128: externalColumn);
129: }
130:
131: /**
132: * An internal method that actually adds the rules.
133: * @param checkType The type of rule we are adding (CHECK_DELETE or CHECK_INSERTUPDATE).
134: * @param localColumn The column within this IntegrityChecker object's DataStore _ds.
135: * @param localColumn The column within this IntegrityChecker object's DataStore _ds.
136: * @param externalTable The external table we will use to check against the internal column.
137: * @param externalColumn The external column we will use to check against the internal column.
138: * @throws DataStoreException when the local or external column or external table are not found.
139: */
140: private void addRefCheck(int checkType, String localColumn,
141: String externalTable, String externalColumn)
142: throws DataStoreException {
143: Vector _localVector;
144: int localColumnIndex = _ds.getColumnIndex(localColumn);
145: if (localColumnIndex == -1)
146: throw new DataStoreException("Specified local column ("
147: + localColumn + ") does not exist.");
148: if (!_dd.getTableNames().contains(externalTable))
149: throw new DataStoreException("Specified external table ("
150: + externalTable + ") does not exist.");
151: // if (!_dd.getColumns(externalTable).contains(externalColumn))
152: // throw new DataStoreException("Specified external column (" + externalColumn + ") does not exist in table " + externalTable + ".");
153: StringBuffer sb = new StringBuffer("select (1) from ");
154: if (checkType == CHECK_DELETE)
155: _localVector = _refCheckDel;
156: else
157: _localVector = _refCheckInsUpd;
158: sb.append(externalTable + " where " + externalColumn + " = ?");
159: _localVector.add(new RefRule(localColumn, externalTable,
160: externalColumn, sb.toString()));
161: }
162:
163: /**
164: * A method that checks whether a row passes the rules for allowing deletion.
165: * @param row The row within DataStore _ds that we are checking the integrity of.
166: * @throws DataStoreException when the local column doesn't contain data.
167: */
168: public IntegrityMessage isRowOKToDelete(int row)
169: throws DataStoreException {
170: return checkIntegrity(CHECK_DELETE, row);
171: }
172:
173: /**
174: * A method that checks whether a row passes the rules for allowing updating/insertion.
175: * @param row The row within DataStore _ds that we are checking the integrity of.
176: * @throws DataStoreException when the local column doesn't contain data.
177: */
178: public IntegrityMessage isRowOKToInsertOrUpdate(int row)
179: throws DataStoreException {
180: return checkIntegrity(CHECK_INSERTUPDATE, row);
181: }
182:
183: /**
184: * An internal method that actually checks the rules.
185: * @param checkType The type of rule we are checking (CHECK_DELETE or CHECK_INSERTUPDATE).
186: * @param row The row within DataStore _ds that we are checking the integrity of.
187: * @throws DataStoreException when the local column doesn't contain data.
188: */
189: private IntegrityMessage checkIntegrity(int checkType, int row)
190: throws DataStoreException {
191: DBConnection _conn = null;
192: PreparedStatement _st = null;
193: ResultSet _rs = null;
194: String _query = "";
195: RefRule _rr;
196: Vector _localVector;
197: IntegrityMessage _im = new IntegrityMessage();
198: // this should give a pointer to the original vector
199: if (checkType == CHECK_DELETE)
200: _localVector = _refCheckDel;
201: else
202: _localVector = _refCheckInsUpd;
203:
204: try {
205: for (int i = 0; i < _localVector.size(); i++) {
206: _rr = (RefRule) _localVector.elementAt(i);
207: _conn = DBConnection.getConnection(_appName);
208: _query = _rr.getQuery();
209: _st = _conn.getJDBCConnection().prepareStatement(
210: _query, ResultSet.TYPE_SCROLL_SENSITIVE,
211: ResultSet.CONCUR_UPDATABLE);
212: Object _colVal = _ds.getAny(row, _rr
213: .getLocalColumnName());
214: if (_colVal == null)
215: throw new DataStoreException("Specified column ("
216: + _rr.getLocalColumnName()
217: + ") does not contain a value.");
218: DSDataSourceJDBC
219: .prepareForType(_ds, _st, _colVal, _ds
220: .getColumnDataType(_rr
221: .getLocalColumnName()), 1);
222: _rs = _st.executeQuery();
223: if (_rs.first()) {
224: // If we are looking at deletes we have a problem when any rows exist
225: if (checkType == CHECK_DELETE) {
226: _im.addViolation(_rr.getTableName(), _rr
227: .getColumnName());
228: }
229: } else {
230: // If we are looking at inserts or updates we have a problem when NO rows exist
231: if (checkType == CHECK_INSERTUPDATE) {
232: _im.addViolation(_rr.getTableName(), _rr
233: .getColumnName());
234: }
235: }
236: }
237: } catch (SQLException el) {
238: throw new DataStoreException(
239: "Error reading from database, row = " + row
240: + el.getErrorCode() + el.getMessage());
241: } finally {
242: try {
243: // A little db cleanup
244: if (_rs != null)
245: _rs.close();
246: if (_st != null)
247: _st.close();
248: if (_conn != null)
249: _conn.freeConnection();
250: } catch (SQLException se) {
251: MessageLog.writeErrorMessage(
252: "Error cleaning up database connection", se,
253: this);
254: }
255: }
256: return _im;
257: }
258:
259: }
|