0001: //** Copyright Statement ***************************************************
0002: //The Salmon Open Framework for Internet Applications (SOFIA)
0003: // Copyright (C) 1999 - 2002, Salmon LLC
0004: //
0005: // This program is free software; you can redistribute it and/or
0006: // modify it under the terms of the GNU General Public License version 2
0007: // as published by the Free Software Foundation;
0008: //
0009: // This program is distributed in the hope that it will be useful,
0010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: // GNU General Public License for more details.
0013: //
0014: // You should have received a copy of the GNU General Public License
0015: // along with this program; if not, write to the Free Software
0016: // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0017: //
0018: // For more information please visit http://www.salmonllc.com
0019: //** End Copyright Statement ***************************************************
0020: package com.salmonllc.sql;
0021:
0022: /////////////////////////
0023: //$Archive: /SOFIA/SourceCode/com/salmonllc/sql/DataStoreProxy.java $
0024: //$Author: Fred $
0025: //$Revision: 39 $
0026: //$Modtime: 9/14/04 12:37p $
0027: /////////////////////////
0028:
0029: import java.util.*;
0030: import java.io.*;
0031:
0032: /**
0033: * This class provides a storage buffer for data in SQL ResultSets for applets. The data is retrieved from a RemoteDataStore on a server via the DataServer servlet.
0034: * This class is intended to be used by applets served up by framework html pages that need to get data from a database. For each ProxyDataStore in the applet there needs to be one RemoteDataStore on the server. The following classes need to be in the applets jar file in order for the DataStoreProxy to work correctly:DataStoreBuffer, DataStoreEvaluator, DataStoreException, DataStoreInterface, DataStoreProxy, DataStoreRow, DSColumnDescriptor, DSDataRow, DSDataSourceProxy, DSDataStoreDescriptor, DSJoinDescriptor, DSQuickSort, DSTableAliasDescriptor
0035: */
0036: public class DataStoreProxy extends DataStoreBuffer implements
0037: Runnable, Serializable, DataStoreInterface {
0038:
0039: public static final int UPDATEMETHOD_UPDATES = 1;
0040: public static final int UPDATEMETHOD_DELETEINSERTS = 2;
0041:
0042: public static final int BIND_DEFAULT = 0;
0043: public static final int BIND_TRUE = 1;
0044: public static final int BIND_FALSE = 2;
0045:
0046: public static final int REMOTE_STATUS_OK = 0;
0047: public static final int REMOTE_STATUS_BAD_REQUEST = 1;
0048: public static final int REMOTE_STATUS_NOT_FOUND = 2;
0049: public static final int REMOTE_STATUS_SQL_ERROR = 3;
0050: public static final int REMOTE_STATUS_ACCESS_DENIED = 4;
0051:
0052: public static final String OPERATION_CREATE = "Create";
0053: public static final String OPERATION_RETRIEVE = "Retrieve";
0054: public static final String OPERATION_CANCEL = "Cancel";
0055: public static final String OPERATION_PING = "Ping";
0056: public static final String OPERATION_UPDATE = "Update";
0057: public static final String OPERATION_COUNT = "Count";
0058: public static final String OPERATION_DESTROY = "Destroy";
0059: public static final String OPERATION_VALIDATE = "Validate";
0060: public static final String OPERATION_GET_TABLE_COLUMNS = "GetTableColumns";
0061: public static final String USE_COMPRESSION = "SOFIA-Use-Compression";
0062: public static final String KEEP_DATA_ON_SERVER = "SOFIA-Keep-Data-On-Server";
0063:
0064: private int _updateMethod = UPDATEMETHOD_UPDATES;
0065:
0066: private boolean _checkConcurrency = false;
0067: private boolean _useBind = false;
0068: private int _maxRows = -1;
0069: private DSDataSourceProxy _dataSource;
0070: private String _remoteID = null;
0071: private String _updateResults = null;
0072: private boolean _threaded = true;
0073: private boolean _useCompression = false;
0074: private String _dbms = null;
0075: private boolean _keepDataOnServer = false;
0076:
0077: /*private class ModelChangedNotifier implements Runnable {
0078: private ModelChangedListener _mcl;
0079: private ModelChangedEvent _mce;
0080:
0081: public ModelChangedNotifier(ModelChangedListener mcl,ModelChangedEvent mce) {
0082: _mcl=mcl;
0083: _mce=mce;
0084: }
0085:
0086: public void run() {
0087: _mcl.modelChanged(_mce);
0088: }
0089: }*/
0090:
0091: /**
0092: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver. This class is intended to be use by applets served up by framework html pages that need to get data from a database. For each ProxyDataStore in the applet there needs to be one RemoteDataStore on the server. The following classes need to be in the applets jar file in order for the DataStoreProxy to work correctly:DataStoreBuffer, DataStoreEvaluator, DataStoreException, DataStoreProxy, DataStoreRow, DSColumnDescriptor, DSDataRow, DSDataSourceProxy, DSDataStoreDescriptor, DSJoinDescriptor, DSQuickSort, DSTableAliasDescriptor
0093: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/DataServerURI/RemoteDataStoreClassName
0094: * @param sessionID The server side session id to use.
0095: */
0096: public DataStoreProxy(String url, String sessionID)
0097: throws DataStoreException {
0098: this (url, sessionID, false);
0099: }
0100:
0101: /**
0102: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver. This class is intended to be use by applets served up by framework html pages that need to get data from a database. For each ProxyDataStore in the applet there needs to be one RemoteDataStore on the server. The following classes need to be in the applets jar file in order for the DataStoreProxy to work correctly:DataStoreBuffer, DataStoreEvaluator, DataStoreException, DataStoreProxy, DataStoreRow, DSColumnDescriptor, DSDataRow, DSDataSourceProxy, DSDataStoreDescriptor, DSJoinDescriptor, DSQuickSort, DSTableAliasDescriptor
0103: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/DataServerURI/RemoteDataStoreClassName
0104: * @param sessionID The server side session id to use.
0105: * @param useCompression Use zip compression to transfer data back from the server
0106: */
0107: public DataStoreProxy(String url, String sessionID,
0108: boolean useCompression) throws DataStoreException {
0109: super ();
0110: _useCompression = useCompression;
0111: _dataSource = new DSDataSourceProxy(url, sessionID, null, null,
0112: null, null, null, this );
0113: }
0114:
0115: /**
0116: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver.
0117: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/DataServerURI/RemoteDataStoreClassName
0118: * @param userID The user id to use.
0119: * @param passWord The password to use.
0120: */
0121: public DataStoreProxy(String url, String userID, String passWord)
0122: throws DataStoreException {
0123: this (url, userID, passWord, null, null, null, null);
0124: }
0125:
0126: /**
0127: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver.
0128: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/DataServerURI/RemoteDataStoreClassName
0129: * @param userID The user id to use.
0130: * @param passWord The password to use.
0131: * @param useCompression Use zip compression to transfer data back from the server
0132: */
0133: public DataStoreProxy(String url, String userID, String passWord,
0134: boolean useCompression) throws DataStoreException {
0135: this (url, userID, passWord, null, null, null, null, null,
0136: useCompression);
0137: }
0138:
0139: /**
0140: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver.
0141: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/servlet/DataServer/Application/RemoteDataStore
0142: * @param userID The user id to use.
0143: * @param passWord The password to use.
0144: * @param proxyHost The host name of a proxy server to use
0145: * @param proxyPort The port number of a proxy server to use
0146: * @param proxyUser The user id to get through the proxy server
0147: * @param proxyPassword The password to get through the proxy server
0148: */
0149: public DataStoreProxy(String url, String userID, String passWord,
0150: String proxyHost, String proxyPort, String proxyUser,
0151: String proxyPassword) throws DataStoreException {
0152: this (url, userID, passWord, proxyHost, proxyPort, proxyUser,
0153: proxyPassword, null);
0154: }
0155:
0156: /**
0157: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver.
0158: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/servlet/DataServer/Application/RemoteDataStore
0159: * @param userID The user id to use.
0160: * @param passWord The password to use.
0161: * @param proxyHost The host name of a proxy server to use
0162: * @param proxyPort The port number of a proxy server to use
0163: * @param proxyUser The user id to get through the proxy server
0164: * @param proxyPassword The password to get through the proxy server
0165: * @param criteria Additional criteria to pass to the server
0166: */
0167: public DataStoreProxy(String url, String userID, String passWord,
0168: String proxyHost, String proxyPort, String proxyUser,
0169: String proxyPassword, String criteria)
0170: throws DataStoreException {
0171: this (url, userID, passWord, proxyHost, proxyPort, proxyUser,
0172: proxyPassword, criteria, false);
0173: }
0174:
0175: /**
0176: * Creates a new empty DataStore that will retrieve it's data from a remote dataserver.
0177: * @param url The url of the RemoteDataStore. Remote DataStores urls follow the following convention http://hostname/servlet/DataServer/Application/RemoteDataStore
0178: * @param userID The user id to use.
0179: * @param passWord The password to use.
0180: * @param proxyHost The host name of a proxy server to use
0181: * @param proxyPort The port number of a proxy server to use
0182: * @param proxyUser The user id to get through the proxy server
0183: * @param proxyPassword The password to get through the proxy server
0184: * @param criteria Additional criteria to pass to the server
0185: * @param useCompression Use zip compression to transfer data back from the server
0186: */
0187: public DataStoreProxy(String url, String userID, String passWord,
0188: String proxyHost, String proxyPort, String proxyUser,
0189: String proxyPassword, String criteria,
0190: boolean useCompression) throws DataStoreException {
0191: super ();
0192: if (proxyHost != null) {
0193: Properties p = System.getProperties();
0194: p.put("proxySet", "true");
0195: p.put("proxyHost", proxyHost);
0196: p.put("proxyPort", proxyPort);
0197: }
0198:
0199: _dataSource = new DSDataSourceProxy(url, null, userID,
0200: passWord, proxyUser, proxyPassword, criteria, this );
0201: }
0202:
0203: /**
0204: * This method indicates whether all the data in the result set that is to be returned by the last retrieve statement has in fact been retrieved.
0205: * @return true if all the data has been retrieved and false if the retrieve is still in progress.
0206: */
0207: public boolean allDataRetrieved() {
0208: return (!_retrieveInProgress);
0209: }
0210:
0211: /**
0212: * If a retrieve is in progress, this method will cancel it.
0213: */
0214: public void cancelRetrieve() {
0215: if (_retrieveInProgress) {
0216: try {
0217: _dataSource.cancelRetrieve(this );
0218: } catch (Exception e) {
0219: System.out.println("DataStoreProxy.cancelRetrieve" + e);
0220: }
0221: _retrieveInProgress = false;
0222: interruptWaitingRetrieveThreads();
0223: }
0224:
0225: }
0226:
0227: /**
0228: * This method will destroy the remote data store on the data server. All resources on the server will be reclaimed.
0229: */
0230: public void destroy() throws Exception {
0231: _dataSource.destroy(this );
0232: }
0233:
0234: /**
0235: * Use this method to get the amount of rows that will be retrieved when a data store retrieve is executed.
0236: */
0237: public int estimateRowsRetrieved() throws DataStoreException {
0238: String nullSt = null;
0239: return estimateRowsRetrieved(nullSt);
0240: }
0241:
0242: /**
0243: * Use this method to get the amount of rows that will be retrieved when a data store retrieve is executed.
0244: * @param criteria The selection criteria to use for the select.
0245: */
0246: public int estimateRowsRetrieved(String criteria)
0247: throws DataStoreException {
0248:
0249: try {
0250: retrieve(criteria, true);
0251: } catch (DataStoreException e) {
0252: throw e;
0253: }
0254: _retrieveInProgress = false;
0255: interruptWaitingRetrieveThreads();
0256: return _dataSource.getCount();
0257: }
0258:
0259: /**
0260: * This method returns the name of one of the aliases used by the datastore. Use the method getAliasCount() to find out how many tables or aliases are used by the datastore.
0261: * @return The table name.
0262: */
0263: public String getAlias(int tableNo) throws DataStoreException {
0264: if (tableNo < 0 || tableNo >= _desc.getAliasCount())
0265: throw (new DataStoreException("Table Number " + tableNo
0266: + " is out of range "));
0267:
0268: return _desc.getAlias(tableNo).getAlias();
0269: }
0270:
0271: /**
0272: * This method returns the number of aliases used by the datastore.
0273: */
0274: public int getAliasCount() {
0275: return _desc.getAliasCount();
0276: }
0277:
0278: /**
0279: * Use this method to get whether or not the datastore will do a concurrency check when rows are updated and deleted.
0280: */
0281: public boolean getCheckConcurrency() {
0282: return _checkConcurrency;
0283: }
0284:
0285: /**
0286: * This method returns the database name of the column in the data store given its index.
0287: */
0288: public String getColumnDatabaseName(int col)
0289: throws DataStoreException {
0290: if (col < 0 || _desc.getColumnCount() == 0)
0291: throw new DataStoreException("Specified column (" + col
0292: + ") does not exist.");
0293: DSColumnDescriptor d = _desc.getColumn(col);
0294: String table = d.getTable();
0295: if (table == null)
0296: table = _desc.getDefaultTable();
0297:
0298: if (table == null)
0299: return d.getColumn();
0300: else
0301: return table + "." + d.getColumn();
0302: }
0303:
0304: /**
0305: * This method returns the name of the database table that the column is for.
0306: */
0307: public String getColumnTableName(int col) throws DataStoreException {
0308: if (col < 0 || _desc.getColumnCount() == 0)
0309: throw new DataStoreException("Specified column (" + col
0310: + ") does not exist.");
0311: DSColumnDescriptor d = _desc.getColumn(col);
0312: String table = d.getTable();
0313: if (table == null)
0314: table = _desc.getDefaultTable();
0315:
0316: return null;
0317: }
0318:
0319: /**
0320: * This method is used to get whether a column should be used in the update, delete concurrency check.
0321: */
0322: public boolean getConcurrencyCheckColumn(int col)
0323: throws DataStoreException {
0324: if (col < 0 || _desc.getColumnCount() == 0)
0325: throw new DataStoreException("Specified column (" + col
0326: + ") does not exist.");
0327:
0328: DSColumnDescriptor c = _desc.getColumn(col);
0329: return c.getConcurrency();
0330: }
0331:
0332: /**
0333: * This method is used to get whether a column should be used in the update, delete concurrency check.
0334: */
0335: public boolean getConcurrencyCheckColumn(String col)
0336: throws DataStoreException {
0337: int c = getColumnIndex(col);
0338: return getConcurrencyCheckColumn(c);
0339: }
0340:
0341: /**
0342: * This method is used to get selection criteria filtering for the result set of the datastore.
0343: */
0344: public String getCriteria() {
0345: return _desc.getWhereClause();
0346: }
0347:
0348: /**
0349: * This method returns the default table for the datastore
0350: */
0351: public String getDefaultTable() {
0352: return _desc.getDefaultTable();
0353: }
0354:
0355: /**
0356: * This method will return whether the distinct flag in the data store is set. The flag indicates that the distinct keyword should be placed at the beginning of a select statement.
0357: */
0358: public boolean getDistinct() {
0359: return _desc.getDistinct();
0360: }
0361:
0362: /**
0363: * This method returns the column list in the group by clause.
0364: */
0365: public String getGroupBy() {
0366: return _desc.getGroupByClause();
0367: }
0368:
0369: /**
0370: * This method returns the having clause for the datastore.
0371: */
0372: public String getHaving() {
0373: return _desc.getHavingClause();
0374: }
0375:
0376: /**
0377: * This method returns the number of columns in a particular join.
0378: */
0379: public int getJoinColumnCount(int joinNo) {
0380: return _desc.getJoin(joinNo).getLeftCount();
0381: }
0382:
0383: /**
0384: * This method returns the number of joins in the datastore.
0385: */
0386: public int getJoinCount() {
0387: return _desc.getJoinCount();
0388: }
0389:
0390: /**
0391: * This method returns a column on the left side of the join.
0392: */
0393: public String getJoinLeftColumn(int joinNo, int colNo) {
0394: return _desc.getJoin(joinNo).getLeftColumn(colNo);
0395: }
0396:
0397: /**
0398: * This method returns the true if a particular join is outer.
0399: */
0400: public boolean getJoinOuter(int joinNo) {
0401: return _desc.getJoin(joinNo).isOuter();
0402: }
0403:
0404: //fc 06/11/04: Implements the newly added method to DataStoreInterface.
0405: /**
0406: * This method returns the relation type of the join. (RELATION_ONE_TO_ONE, RELATION_ONE_TO_MANY, RELATION_MANY_TO_ONE)
0407: */
0408: public int getJoinRelationType(int joinNo) {
0409: return _desc.getJoin(joinNo).getRelationType();
0410: }
0411:
0412: /**
0413: * This method returns a column on the right side of the join.
0414: */
0415: public String getJoinRightColumn(int joinNo, int colNo) {
0416: return _desc.getJoin(joinNo).getRightColumn(colNo);
0417: }
0418:
0419: /**
0420: * This method will return the maximum number of rows that the datastore will retrieve. If the max is set to -1, the datastore will retrieve all rows in the result set. Otherwise it will stop retrieving when the max is reached.
0421: */
0422: public int getMaxRows() {
0423: return _maxRows;
0424: }
0425:
0426: /**
0427: * This method returns the order by clause for the datastore.
0428: */
0429: public String getOrderBy() {
0430: return _desc.getOrderByClause();
0431: }
0432:
0433: /**
0434: * This method creates a properties object containing the definition of the data store.
0435: */
0436: public Properties getProperties() {
0437:
0438: Properties p = super .getProperties();
0439: String desc = "base.";
0440:
0441: p.put(desc + "updateMethod", getIntProperty(_updateMethod));
0442: p.put(desc + "checkConcurrency",
0443: getBoolProperty(_checkConcurrency));
0444: p.put(desc + "useBind", getBoolProperty(_useBind));
0445: p.put(desc + "maxRows", getIntProperty(_maxRows));
0446: p.put(desc + "remoteID", getStringProperty(_remoteID));
0447:
0448: return p;
0449: }
0450:
0451: /**
0452: * Used to get the remote id for the datastore
0453: */
0454: public String getRemoteID() {
0455: return _remoteID;
0456: }
0457:
0458: /**
0459: * This method returns the name of one of the tables used by the datastore. Use the method getAliasCount() to find out how many tables or aliases are used by the datastore.
0460: * @return The table name.
0461: */
0462: public String getTable(int tableNo) throws DataStoreException {
0463: if (tableNo < 0 || tableNo >= _desc.getAliasCount())
0464: throw (new DataStoreException("Table Number " + tableNo
0465: + " is out of range "));
0466:
0467: return _desc.getAlias(tableNo).getTable();
0468: }
0469:
0470: /**
0471: * This method returns an array of all the tables referenced in the datastore.
0472: * @param updateable True if the table list should only include updateable tables and false if it should include all.
0473: */
0474: public String[] getTableList(boolean updateable) {
0475:
0476: DSColumnDescriptor col = null;
0477:
0478: Vector tables = new Vector();
0479: Vector pkey = new Vector();
0480:
0481: for (int i = 0; i < _desc.getColumnCount(); i++) {
0482: col = _desc.getColumn(i);
0483: String tableName = col.getTable();
0484: if (tableName == null)
0485: tableName = _desc.getDefaultTable();
0486:
0487: if ((!updateable) || col.isUpdateable()) {
0488: boolean found = false;
0489: for (int j = 0; j < tables.size(); j++) {
0490: if ((tables.elementAt(j)).equals(tableName)) {
0491: if (col.isPrimaryKey())
0492: pkey.setElementAt(new Boolean(true), j);
0493: found = true;
0494: break;
0495: }
0496: }
0497: if (!found && tableName != null) {
0498: tables.addElement(tableName);
0499: pkey.addElement(new Boolean(col.isPrimaryKey()));
0500: }
0501: }
0502:
0503: }
0504:
0505: if (updateable) {
0506: for (int i = pkey.size() - 1; i > -1; i--) {
0507: if (!((Boolean) pkey.elementAt(i)).booleanValue())
0508: tables.removeElementAt(i);
0509: }
0510: } else {
0511: for (int i = 0; i < getAliasCount(); i++) {
0512: try {
0513: String table = getTable(i);
0514: boolean found = false;
0515: for (int j = 0; j < tables.size(); j++) {
0516: if ((tables.elementAt(j)).equals(table))
0517: found = true;
0518: }
0519: if (!found && table != null)
0520: tables.addElement(table);
0521: } catch (Exception e) {
0522: }
0523: }
0524: }
0525:
0526: String retVal[] = new String[tables.size()];
0527: tables.copyInto(retVal);
0528:
0529: return retVal;
0530: }
0531:
0532: /**
0533: * Use this method to get whether the DataStore will trim (remove trailing spaces) from columns retrieved from the database;
0534: */
0535: public boolean getTrimStrings() {
0536: return _desc.getTrimStrings();
0537: }
0538:
0539: /**
0540: * Gets the update method for the datastore.
0541: * @see DataStore#setUpdateMethod
0542: */
0543: public int getUpdateMethod() {
0544: return _updateMethod;
0545: }
0546:
0547: /**
0548: * Returns the results of the previous update.
0549: */
0550: public String getUpdateResults() {
0551: return _updateResults;
0552: }
0553:
0554: void setUpdateResults(String updateResults) {
0555: _updateResults = updateResults;
0556: }
0557:
0558: /**
0559: * This method is used to get whether a column should use bind variables for inserts and updates. Valid values and BIND_TRUE, BIND_FALSE and BIND_DEFAULT (Use default for datastore)
0560: */
0561: public int getUseBindColumn(int col) throws DataStoreException {
0562: if (col < 0 || _desc.getColumnCount() == 0)
0563: throw new DataStoreException("Specified column (" + col
0564: + ") does not exist.");
0565:
0566: DSColumnDescriptor c = _desc.getColumn(col);
0567: return c.getUseBind();
0568: }
0569:
0570: /**
0571: * This method is used to get whether a column should use bind variables for inserts and updates. Valid values and BIND_TRUE, BIND_FALSE and BIND_DEFAULT (Use default for datastore)
0572: */
0573: public int getUseBindColumn(String col) throws DataStoreException {
0574: int c = getColumnIndex(col);
0575: return getUseBindColumn(c);
0576: }
0577:
0578: /**
0579: * Use this method to get whether or not the datastore will use bind variables as the default for updating or inserting columns.
0580: */
0581: public boolean getUseBindForUpdate() {
0582: return _useBind;
0583: }
0584:
0585: /**
0586: * This method returns whether a column is part of the primary key
0587: */
0588: public boolean isPrimaryKey(int col) throws DataStoreException {
0589: if (col < 0 || _desc.getColumnCount() == 0)
0590: throw new DataStoreException("Specified column (" + col
0591: + ") does not exist.");
0592:
0593: DSColumnDescriptor c = _desc.getColumn(col);
0594: return c.isPrimaryKey();
0595: }
0596:
0597: /**
0598: * This method returns whether a column is part of the primary key
0599: */
0600: public boolean isPrimaryKey(String col) throws DataStoreException {
0601: int c = getColumnIndex(col);
0602: return isPrimaryKey(c);
0603: }
0604:
0605: /**
0606: * This method returns whether a column is updateable
0607: */
0608: public boolean isUpdateable(int col) throws DataStoreException {
0609: if (col < 0 || _desc.getColumnCount() == 0)
0610: throw new DataStoreException("Specified column (" + col
0611: + ") does not exist.");
0612:
0613: DSColumnDescriptor c = _desc.getColumn(col);
0614: return c.isUpdateable();
0615: }
0616:
0617: /**
0618: * This method returns whether a column is updateable
0619: */
0620: public boolean isUpdateable(String col) throws DataStoreException {
0621: int c = getColumnIndex(col);
0622: return isUpdateable(c);
0623: }
0624:
0625: /**
0626: * This method will ping the server for this particular data store. Pinging from time to time will prevent the server session from expiring.
0627: * @return true if the ping succeeds and false if not.
0628: */
0629: public boolean ping() throws Exception {
0630: return _dataSource.ping(this );
0631: }
0632:
0633: /**
0634: * This method will clear all rows in the dataStore.
0635: */
0636: public synchronized void reset() {
0637: if (_retrieveInProgress)
0638: cancelRetrieve();
0639:
0640: super .reset();
0641: }
0642:
0643: /**
0644: * Executes the sql statement and retrieves to data. The data is retrieved in a new thread so the beginning of the result set can be accessed before all the data has been retrieved.
0645: * You do not need to pass a database connection to this version of retrieve, but in order to use it the DataStore must be created with a constructor that passes an application (not the no args constructor).
0646: */
0647: public void retrieve() throws DataStoreException {
0648: String criteria = null;
0649: retrieve(criteria, false);
0650: }
0651:
0652: /**
0653: * Executes the sql statement and retrieves to data. The data is retrieved in a new thread so the beginning of the result set can be accessed before all the data has been retrieved.
0654: * @param criteria Additional selection criteria to use to limit the result set
0655: */
0656: public void retrieve(String criteria) throws DataStoreException {
0657: retrieve(criteria, false);
0658: }
0659:
0660: private synchronized void retrieve(String criteria,
0661: boolean countOnly) throws DataStoreException {
0662: if (!countOnly)
0663: reset();
0664: waitForCancel(); //just in case there was already a retrieve running wait for it to be cancelled before continuing
0665: _retrieveInProgress = true;
0666:
0667: try {
0668: _dataSource.preRetrieve(this , criteria, countOnly);
0669: } catch (Exception e) {
0670: try {
0671: _dataSource.postRetrieve(this );
0672: } catch (java.sql.SQLException ex) {
0673: throw new DataStoreException(ex.toString(), ex);
0674: } catch (Exception ex) {
0675: throw new DataStoreException(ex.toString());
0676: }
0677: _retrieveInProgress = false;
0678: interruptWaitingRetrieveThreads();
0679: throw new DataStoreException(e.toString(), e);
0680: }
0681:
0682: if (!countOnly) {
0683: if (_threaded) {
0684: Thread t = new Thread(this );
0685: t.start();
0686: } else
0687: run();
0688: }
0689:
0690: }
0691:
0692: /**
0693: * The run method for the thread that retrieves the data from the database. This method should not be called directly. Instead use the retrieve method.
0694: */
0695: public void run() {
0696: try {
0697: DataStoreRow row = new DataStoreRow(this , new DSDataRow(
0698: _desc), _desc);
0699:
0700: while (_dataSource.retrieveRow(this , row)) {
0701: _rows.addElement(row.getDSDataRow());
0702: if (_threaded)
0703: notifyListeners(ModelChangedEvent.TYPE_ROW_INSERTED_OR_DELETED);
0704:
0705: if (!_retrieveInProgress
0706: || (_maxRows > -1 && _rows.size() >= _maxRows)) {
0707: _cancelInProgress = true;
0708: _retrieveInProgress = false;
0709: interruptWaitingRetrieveThreads();
0710: break;
0711: }
0712: if (_threaded)
0713: Thread.yield();
0714:
0715: row.setDSDataRow(new DSDataRow(_desc));
0716: }
0717: _dataSource.postRetrieve(this );
0718: _cancelInProgress = false;
0719: interruptWaitingCancelThreads();
0720: notifyListeners(ModelChangedEvent.TYPE_DATA_LOADED);
0721: } catch (Exception e) {
0722: System.out.println("DataStore.run:" + e);
0723: _cancelInProgress = false;
0724: interruptWaitingCancelThreads();
0725: }
0726:
0727: _retrieveInProgress = false;
0728: interruptWaitingRetrieveThreads();
0729: _cancelInProgress = false;
0730: interruptWaitingCancelThreads();
0731: }
0732:
0733: /**
0734: * This method builds the datastore from the information in the properties object.
0735: */
0736: public void setProperties(Properties p) {
0737: super .setProperties(p);
0738: String desc = "base.";
0739: _updateMethod = setIntProperty(p.getProperty(desc
0740: + "updateMethod"));
0741: _checkConcurrency = setBoolProperty(p.getProperty(desc
0742: + "checkConcurrency"));
0743: _useBind = setBoolProperty(p.getProperty(desc + "useBind"));
0744: _maxRows = setIntProperty(p.getProperty(desc + "maxRows"));
0745: _remoteID = getStringProperty(p.getProperty(desc + "remoteID"));
0746: _dbms = getStringProperty(p.getProperty(desc + "DBMS"));
0747: }
0748:
0749: /**
0750: * Used to set the remote id for the datastore
0751: */
0752: public void setRemoteID(String remoteID) {
0753: _remoteID = remoteID;
0754: }
0755:
0756: /**
0757: * Notifies all listeners that a model changed event occurred
0758: */
0759: public void notifyListeners(ModelChangedEvent e) {
0760: if (!areThereModelListeners())
0761: return;
0762:
0763: Vector modelListeners = getModelListeners();
0764: for (int i = 0; i < modelListeners.size(); i++) {
0765: ((ModelChangedListener) modelListeners.elementAt(i))
0766: .modelChanged(e);
0767: // notifyThread((ModelChangedListener) modelListeners.elementAt(i),e);
0768: }
0769: }
0770:
0771: // private void notifyThread(ModelChangedListener mcl,ModelChangedEvent e) {
0772: // Thread t=new Thread(new ModelChangedNotifier(mcl,e));
0773: // t.start();
0774: // }
0775:
0776: /**
0777: * This method will take a row from the datastores deleted buffer and move it back to the standard buffer.
0778: * @param row The number of the row to undelete. Note: this is the row number of the row in the deleted buffer not the standard buffer.
0779: * @return The number that the deleted row was moved to in the standard buffer or -1 if an error occurs.
0780: */
0781: public int unDeleteRow(int row) {
0782: if (row < 0)
0783: return -1;
0784: if (row >= getDeletedCount())
0785: return -1;
0786: DSDataRow d = (DSDataRow) _deletedRows.elementAt(row);
0787: _deletedRows.removeElementAt(row);
0788: _rows.addElement(d);
0789:
0790: return _rows.size() - 1;
0791: }
0792:
0793: /**
0794: * This method will cause the database to reflect the changes made in the data store's buffer.
0795: * @exception com.salmonllc.sql.DataStoreException If a SQLError occurs while the datastore is updating.
0796: */
0797: public void update() throws DataStoreException {
0798: waitForRetrieve();
0799: DSDataSourceProxy out = _dataSource;
0800: _updateResults = null;
0801: DataStoreProxy[] proxy = { this };
0802: try {
0803: out.preUpdate(this );
0804: out.writeDSHeader(this );
0805: updateDs(out);
0806: out.commit(this );
0807: out.postUpdate(proxy, true);
0808: resetStatus();
0809: notifyListeners(ModelChangedEvent.TYPE_DATA_LOADED);
0810: } catch (DataStoreException e) {
0811: try {
0812: out.postUpdate(proxy, false);
0813: } catch (Exception ex) {
0814: System.err
0815: .println("DataStoreProxy.update() -- postUpdate:"
0816: + ex);
0817: }
0818: throw (e);
0819: } catch (Exception e) {
0820: try {
0821: out.postUpdate(proxy, false);
0822: } catch (Exception ex) {
0823: System.err
0824: .println("DataStoreProxy.update() -- postUpdate:"
0825: + ex);
0826: }
0827:
0828: String message = e.getMessage();
0829: if (message.equals("$Update$")
0830: || message.equals("$Insert$")
0831: || message.equals("$Delete$")) {
0832: throw new DataStoreException(
0833: "DataStore updated canceled.");
0834: } else
0835: throw new DataStoreException(e.toString());
0836:
0837: }
0838: }
0839:
0840: void updateDs(DSDataSourceProxy out) throws DataStoreException,
0841: java.sql.SQLException, Exception {
0842: int rowNo = -1;
0843: DataStoreRow row = new DataStoreRow(this , null, _desc);
0844:
0845: //do deletes
0846: for (rowNo = 0; rowNo < _deletedRows.size(); rowNo++) {
0847: row.setDSDataRow((DSDataRow) _deletedRows.elementAt(rowNo));
0848: ((DSDataRow) _deletedRows.elementAt(rowNo))
0849: .setProxyRow(rowNo);
0850: if (!out.deleteRow(this , row))
0851: throw new Exception("$Delete$");
0852: }
0853:
0854: //do the updates
0855: for (rowNo = 0; rowNo < _rows.size(); rowNo++) {
0856: row.setDSDataRow((DSDataRow) _rows.elementAt(rowNo));
0857: ((DSDataRow) _rows.elementAt(rowNo)).setProxyRow(rowNo);
0858: if (row.getDSDataRow().getRowStatus() == STATUS_MODIFIED) {
0859: if (!out.updateRow(this , row))
0860: throw new Exception("$Update$");
0861: }
0862: }
0863:
0864: //do the inserts
0865: for (rowNo = 0; rowNo < _rows.size(); rowNo++) {
0866: row.setDSDataRow((DSDataRow) _rows.elementAt(rowNo));
0867: ((DSDataRow) _rows.elementAt(rowNo)).setProxyRow(rowNo);
0868: if (row.getDSDataRow().getRowStatus() == STATUS_NEW_MODIFIED) {
0869: if (!out.insertRow(this , row))
0870: throw new Exception("$Insert$");
0871: }
0872: }
0873: }
0874:
0875: DSDataSourceProxy getDataSourceProxy() {
0876: return _dataSource;
0877: }
0878:
0879: /**
0880: * @return The id of the server session this proxy is using
0881: */
0882: public String getSessionID() {
0883: if (_remoteID == null)
0884: return null;
0885: int pos = _remoteID.lastIndexOf("-");
0886: if (pos > -1)
0887: return _remoteID.substring(pos + 1);
0888: return null;
0889: }
0890:
0891: /**
0892: * Updates multiple proxy data stores in a single transaction
0893: * @param ds The Array of DataStores to update
0894: * @throws DataStoreException
0895: */
0896: public static void update(DataStoreProxy[] ds)
0897: throws DataStoreException {
0898:
0899: int i = 0;
0900: DSDataSourceProxy out = ds[0].getDataSourceProxy();
0901: try {
0902: out.preUpdate(ds[0]);
0903: for (i = 0; i < ds.length; i++) {
0904: out.writeDSHeader(ds[i]);
0905: ds[i].setRemoteUpdateReturnValue(null);
0906: ds[i].updateDs(out);
0907: }
0908: out.commit(ds[0]);
0909: out.postUpdate(ds, true);
0910: for (i = 0; i < ds.length; i++)
0911: ds[i].resetStatus();
0912:
0913: } catch (DataStoreException e) {
0914: try {
0915: out.postUpdate(ds, false);
0916: } catch (Exception ex) {
0917: System.err
0918: .println("DataStoreProxy.update() -- postUpdate:"
0919: + ex);
0920: }
0921: throw (e);
0922: } catch (Exception e) {
0923: try {
0924: out.postUpdate(ds, false);
0925: } catch (Exception ex) {
0926: System.err
0927: .println("DataStoreProxy.update() -- postUpdate:"
0928: + ex);
0929: }
0930:
0931: String message = e.getMessage();
0932: if (message.equals("$Update$")
0933: || message.equals("$Insert$")
0934: || message.equals("$Delete$")) {
0935: throw new DataStoreException(
0936: "DataStore updated canceled.");
0937: } else
0938: throw new DataStoreException(e.toString());
0939:
0940: }
0941: }
0942:
0943: protected void validateColumn(int rowNo, int colNo, Vector v,
0944: DBConnection conn) {
0945: if (rowNo < 0 || rowNo > getRowCount() || colNo < 0
0946: || colNo > getColumnCount())
0947: return;
0948: DSColumnDescriptor col = _desc.getColumn(colNo);
0949: boolean remoteRules = false;
0950: for (int i = 0; i < col.getRuleCount(); i++) {
0951: try {
0952: ValidationRule r = col.getRule(i);
0953: if (r.getRuleType() == ValidationRule.TYPE_REMOTE)
0954: remoteRules = true;
0955: else
0956: r.evaluateRule(this , rowNo, colNo, conn);
0957: } catch (DataStoreException ex) {
0958: ex.setRowNo(rowNo);
0959: try {
0960: ex.setColumn(getColumnName(colNo));
0961: } catch (DataStoreException e) {
0962: }
0963: ;
0964: v.add(ex);
0965: }
0966: }
0967: if (remoteRules) {
0968: try {
0969: DataStoreRow r = getDataStoreRow(rowNo, BUFFER_STANDARD);
0970: r.getDSDataRow().setProxyRow(rowNo);
0971: DataStoreException ex[] = getDataSourceProxy()
0972: .validateRemoteRules(this , r, rowNo, colNo);
0973: for (int i = 0; i < ex.length; i++)
0974: v.add(ex[i]);
0975: } catch (DataStoreException dex) {
0976: }
0977: }
0978: }
0979:
0980: /**
0981: * Use this method to get whether or not the datastore does a retrieve in a separate thread.
0982: */
0983: public boolean getEnableThreads() {
0984: return _threaded;
0985: }
0986:
0987: /**
0988: * Use this method to set whether or not the datastore will do retrieves in a separate thread
0989: */
0990: public void setEnableThreads(boolean truefalse) {
0991: _threaded = truefalse;
0992: }
0993:
0994: /**
0995: * @return true if the DataStore is using compression to transmit data back and forth to the server.
0996: */
0997: public boolean getUseCompression() {
0998: return _useCompression;
0999: }
1000:
1001: /**
1002: * Set to true for the DataStore to use compression to transmit data back and forth to the server.
1003: */
1004: public void setUseCompression(boolean b) {
1005: _useCompression = b;
1006: }
1007:
1008: /**
1009: * Returns a list of column definitions for a particular table in the database that the datastore is using. Note, the datastore must have an app name for this method to work.
1010: */
1011: public ColumnDefinition[] getColumnsForTable(String table) {
1012: try {
1013: return _dataSource.getColumnsForTable(this , table);
1014: } catch (Exception ex) {
1015: return null;
1016: }
1017: }
1018:
1019: /**
1020: * Returns the name of the database engine being used
1021: */
1022: public String getDBMS() {
1023: return _dbms;
1024: }
1025:
1026: /**
1027: * Returns whether the data for this Proxy Datastore is also kept on the server
1028: * Default Value is false
1029: * @return true if the DataStore is keeping the data also on the server.
1030: */
1031: public boolean getKeepDataOnServer() {
1032: return _keepDataOnServer;
1033: }
1034:
1035: /**
1036: * Sets whether the data for this Proxy Datastore is also kept on the server
1037: */
1038: public void setKeepDataOnServer(boolean keepDataOnServer) {
1039: _keepDataOnServer = keepDataOnServer;
1040: }
1041:
1042: }
|