[cvs] expresso commit by lhamel: add isMutable() attribute and
tests for
JCorporate Ltd
jcorp at jcorporate.com
Mon Feb 21 01:14:44 UTC 2005
Log Message:
-----------
add isMutable() attribute and tests for same. moved some methods to group statics and instance vars together.
Modified Files:
--------------
expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj:
DBObject.java
Revision Data
-------------
Index: DBObject.java
===================================================================
RCS file: /home/javacorp/.cvs/expresso/expresso/expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/DBObject.java,v
retrieving revision 1.241
retrieving revision 1.242
diff -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/DBObject.java -Lexpresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/DBObject.java -u -r1.241 -r1.242
--- expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/DBObject.java
+++ expresso-web/WEB-INF/src/com/jcorporate/expresso/core/dbobj/DBObject.java
@@ -115,6 +115,7 @@
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
@@ -132,7 +133,7 @@
* <p>When making your own application, you derive your classes from DBObject or
* SecuredDBObject.</p>
*
- * @author Michael Nash
+ * @author Michael Nash, adaptations Larry Hamel
* @see com.jcorporate.expresso.core.dbobj.SecuredDBObject
* @see com.jcorporate.expresso.core.db.DBException
* @see com.jcorporate.expresso.core.db.DBConnection
@@ -168,6 +169,12 @@
public static final String ATTRIBUTE_PAGE_LIMIT = "pageLimit";
/**
+ * attribute name for whether this object is mutable;
+ * defaults to true;
+ */
+ public static final String IS_MUTABLE = "IS_MUTABLE";
+
+ /**
* Event 'Add' code
*/
public static final String EVENT_ADD = "A";
@@ -181,7 +188,14 @@
* Event 'Update' Code
*/
public static final String EVENT_UPDATE = "U";
+ public static final String WHERE_KEYWORD = " WHERE ";
+ /**
+ * setup flag name for checking relational integrity;
+ * set this to false if your DB already checks this, and you want to avoid the performance
+ * hit associated with checking this in the middleware.
+ */
+ public static final String IS_CHECK_RELATIONAL_INTEGRITY = "isCheckRelationalIntegrity";
/**
* A static zero BIG DECIMAL object
@@ -192,6 +206,25 @@
*/
transient protected static final BigDecimal BIG_DECIMAL_ZERO = new BigDecimal(0.0);
+ /**
+ * Integer Regular Expression for easy reference
+ */
+ public static final String INT_MASK = "^[+-]?[0-9]+";
+
+ /**
+ * Floating point regular expression syntax for easy reference.
+ */
+ public static final String FLOAT_MASK = "^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$";
+
+ /**
+ * Email Regular Expression Constant.
+ */
+ public static final String EMAIL_MASK =
+ "^[A-Za-z0-9\\-\\.\\_]+"
+ + "@"
+ + "[A-Za-z0-9\\-\\.\\_]+"
+ + "\\."
+ + "[a-zA-Z]{2,6}$"; // 6 chars in "museum" TLD
/* the logs
* @todo move this static variable outside DBObject so we can wrap it for
@@ -199,7 +232,6 @@
*/
transient private static Logger log = Logger.getLogger(DBObject.class);
-
/**
* 30 minute DBObjLimit cache tty
*/
@@ -207,8 +239,9 @@
/**
* Statistics for Cache entries.
+ * @todo move this to cache system
*/
- transient private static ConcurrentReaderHashMap sCacheStats = new ConcurrentReaderHashMap();
+ transient protected static ConcurrentReaderHashMap sCacheStats = new ConcurrentReaderHashMap();
/**
@@ -226,17 +259,6 @@
}
};
private static Logger slog = null;
- public static final String WHERE_KEYWORD = " WHERE ";
-
- /**
- * Retrieve a thread local instance of the Perl5 pattern matcher. Allows
- * for optimization of # of instances of pattern matcher vs synchronization.
- *
- * @return PatternMatcher
- */
- protected PatternMatcher getPatternMatcher() {
- return (PatternMatcher) patternMatcher.get();
- }
/**
@@ -266,14 +288,12 @@
*/
private Map fieldData = null;
-
/**
* Contains a map of DBObject.FieldError classes describing the errors
* set by checkField().
*/
private HashMap fieldErrors = null;
-
/* This is the locale of the DBObject. Originally this attribute
* was part of the subclass <code>SecuredDBObject</code>. However
* there is no reason that explains why all DBObjects should not
@@ -285,7 +305,6 @@
*/
private Locale myLocale = Locale.getDefault();
-
/**
* Keys that have been found in the last retrieve.
*/
@@ -296,7 +315,9 @@
*/
private HashMap attributes = null;
- /* The cache size of this particular DB object */
+ /**
+ * The cache size of this particular DB object
+ */
private int myCacheSize = -2;
@@ -308,27 +329,6 @@
/**
- * Integer Regular Expression for easy reference
- */
- public static final String INT_MASK = "^[+-]?[0-9]+";
-
- /**
- * Floating point regular expression syntax for easy reference.
- */
- public static final String FLOAT_MASK = "^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$";
-
- /**
- * Email Regular Expression Constant.
- */
- public static final String EMAIL_MASK =
- "^[A-Za-z0-9\\-\\.\\_]+"
- + "@"
- + "[A-Za-z0-9\\-\\.\\_]+"
- + "\\."
- + "[a-zA-Z]{2,6}$"; // 6 chars in "museum" TLD
- public static final String IS_CHECK_RELATIONAL_INTEGRITY = "isCheckRelationalIntegrity";
-
- /**
* Default Constructor. This allows a DB object to be dynamically
* instantiated (e.g. loaded
* with Class.forName()) and does all of the required initializations.
@@ -399,6 +399,19 @@
}
/**
+ * Initialize this DBObject and set the db/context to the specified key
+ *
+ * @param newdbKey The database Context name
+ * @throws DBException upon error.
+ */
+ public DBObject(String newdbKey)
+ throws DBException {
+ this();
+ setDataContext(newdbKey);
+ } /* DBObject(String) */
+
+
+ /**
* Get the current locale for this dbobject
*
* @return The currently set locale or null if there is no locale set.
@@ -419,18 +432,6 @@
}
/**
- * Initialize this DBObject and set the db/context to the specified key
- *
- * @param newdbKey The database Context name
- * @throws DBException upon error.
- */
- public DBObject(String newdbKey)
- throws DBException {
- this();
- setDataContext(newdbKey);
- } /* DBObject(String) */
-
- /**
* Add a new record to the target table.
* Assumes that the fields of this object are populated with data for the new
* record. All key fields at least must be supplied with values, and all fields
@@ -442,7 +443,7 @@
*/
public void add()
throws DBException {
-
+ checkMutable();
getExecutor().add(this);
/* Now log the change if we are logging */
@@ -464,8 +465,6 @@
myUpdates = null;
} /* if */
- // after add(), we know that 'current' values are now the baseline for comparison
- cacheIsChangedComparison();
setStatus(BaseDataObject.STATUS_CURRENT);
notifyListeners(EVENT_ADD);
} /* add() */
@@ -484,6 +483,8 @@
*/
public synchronized int loadFromConnection(DBConnection connection)
throws DBException {
+ checkMutable();
+
String oneFieldName = null;
Object tmpData = null;
int fieldCount = 0;
@@ -548,7 +549,7 @@
setDataContext(getDataContext());
- cacheIsChangedComparison();
+ updateIsChanged();
setStatus(BaseDataObject.STATUS_CURRENT);
return fieldCount;
@@ -560,8 +561,13 @@
* considered 'original value' for purposes of determining 'isChanged'
*
* @throws DBException upon error.
+ * @deprecated 2/05 v5.6 made protected instead of public; use updateIsChangedFlag() instead
*/
public void cacheIsChangedComparison() throws DBException {
+ updateIsChanged();
+ }
+
+ protected void updateIsChanged() throws DBException {
for (Iterator i = getMetaData().getFieldListArray().iterator(); i.hasNext();) {
String oneFieldName = (String) i.next();
DataField field = getDataField(oneFieldName);
@@ -1246,7 +1252,6 @@
myUpdates = null;
} /* if */
- cacheIsChangedComparison();
setStatus(BaseDataObject.STATUS_CURRENT);
notifyListeners(EVENT_ADD);
NextNumber.getInstance().reset(getDataContext(), this);
@@ -2271,14 +2276,14 @@
*/
public boolean find()
throws DBException {
-
+ checkMutable();
//
//If we have all the keys, then use retrieve which is much faster
//than find.
//
boolean haveAllKeys = false;
- com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData metadata = getJDBCMetaData();
+ JDBCObjectMetaData metadata = getJDBCMetaData();
ArrayList keyfields = metadata.getKeyFieldListArray();
int keysize = keyfields.size();
@@ -2500,7 +2505,7 @@
myUpdates = null;
}
- cacheIsChangedComparison();
+ updateIsChanged();
setStatus(BaseDataObject.STATUS_CURRENT);
if (isCached() && !anyFieldsToRetrieve) {
@@ -2594,8 +2599,8 @@
/**
- * Return an "attribute". Attributes are temporary (e.g. not stored in the DBMS)
- * values associated with this particular DB object instance.
+ * Return an attribute. Attributes are temporary (e.g. not stored in the DBMS)
+ * values associated with this particular object instance.
*
* @param attribName The attribute name to check
* @return the object associated with this attribute
@@ -2615,6 +2620,7 @@
* @param fieldName The field name to get the attirbutes for
* @return the Iterator for all attributes associated with this field
* @throws DBException upon error.
+ * @deprecated 2/05 v5.6; name change: use getFieldAttributesIterator instead
*/
public Iterator getAttributesIterator(String fieldName)
throws DBException {
@@ -2622,6 +2628,18 @@
}
/**
+ * Get an iterator for all of the STATIC field attributes specified for a field
+ *
+ * @param fieldName The field name to get the attirbutes for
+ * @return the Iterator for all attributes associated with this field
+ * @throws DBException upon error.
+ */
+ public Iterator getFieldAttributesIterator(String fieldName)
+ throws DBException {
+ return getDef().getAttributesIterator(fieldName);
+ }
+
+ /**
* Gets the set size of the cache for this DBOBject
*
* @return The number of cache objects available for this object.
@@ -3296,6 +3314,8 @@
*/
public void saveBinaryField(String fieldName,
byte[] incomingData) throws DBException {
+ checkMutable();
+
DBConnectionPool connectionPool = null;
DBConnection myConnection = getLocalConnection();
@@ -3678,6 +3698,10 @@
throws DBException {
try {
+ if ( getLogger().isDebugEnabled() ) {
+ getLogger().debug("For better efficiency, override method: getThisDBObj() in object: "
+ + getClass().getName());
+ }
DBObject returnObj = (DBObject) getClass().newInstance();
return returnObj;
@@ -3809,7 +3833,7 @@
}
//
// fieldData = new HashMap(dto.getTableFields());
- cacheIsChangedComparison();
+ updateIsChanged();
setStatus(BaseDataObject.STATUS_CURRENT);
}
@@ -4083,7 +4107,6 @@
}
String cacheName = myClassName + ".valueField:" + valueField + "|descrip:" + descripField + "|where:" + whereClause + "|sort:" + sortKeyString;
- CacheManager.getInstance();
CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
if (cs != null) {
if (!cs.existsCache(cacheName)) {
@@ -4295,7 +4318,6 @@
Locale oneLocale = Locale.getDefault();
String cacheName = myClassName + "." + oneLocale.toString() + ".valueField:" + valueField + "|descrip:" + descripField + "|where:" + whereClause + "|sort:" + sortKeyString;
- CacheManager.getInstance();
CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
if (cs != null && !cs.existsCache(cacheName)) {
@@ -4628,7 +4650,6 @@
* author Yves Henri Amaizo <amy_amaizo at compuserve.com>
*
* @return true if there are specific fields to input
- * @see #isFieldsToInput(String)
*/
public synchronized boolean isFieldsToInput()
throws DBException {
@@ -4826,12 +4847,8 @@
/* Tell the cache manager to clear our db object cache and */
/* valid value cache */
try {
- CacheManager.getInstance();
CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
//Caching is not enabled if cs == null
- if (cs == null) {
- return;
- }
if (EVENT_ADD.equals(eventCode)) {
//Add will automatically put it in the cache, which, in turn
@@ -4839,14 +4856,26 @@
//While it may seem odd that we're removing it when an item
//is added, right now, auto-inc fields won't necessarily
//get transferred properly into the cache.
- cs.removeItem(myClassName, this);
+ if (cs != null) {
+ cs.removeItem(myClassName, this);
+ }
+ // after update, we know that 'current' values are now the baseline for comparison
+ updateIsChanged();
+
} else if (EVENT_UPDATE.equals(eventCode)) {
//Add will automatically put it in the cache, which, in turn
//will clear out related values
- cacheUtils.addModifiedToCache(this);
+ if (cs != null) {
+ cacheUtils.addModifiedToCache(this);
+ }
+ // after update, we know that 'current' values are now the baseline for comparison
+ updateIsChanged();
+
} else if (EVENT_DELETE.equals(eventCode)) {
//Removal will clear out related values
- cs.removeItem(myClassName, this);
+ if (cs != null) {
+ cs.removeItem(myClassName, this);
+ }
} else {
throw new DBException("Unknown Notification Code: " + eventCode);
}
@@ -5014,7 +5043,6 @@
public synchronized void removeFromCache(DBObject theDBObj)
throws DBException {
try {
- CacheManager.getInstance();
CacheManager.removeItem(theDBObj.getDataContext(),
theDBObj.myClassName, theDBObj);
} catch (CacheException ce) {
@@ -5034,7 +5062,8 @@
/**
- * Get a particular record from the database into this object's fields
+ * Get a particular record from the database into this object's fields.
+ * Does NOT attempt fetch from cache first.
* Key fields for this object must be set; throws otherwise
*
* @throws DBRecordNotFoundException If the record could not be retrieved.
@@ -5043,7 +5072,7 @@
*/
public void retrieve()
throws DBException {
-
+ checkMutable();
if (!haveAllKeys()) {
throw new DBRecordNotFoundException("(" +
@@ -5063,7 +5092,7 @@
}
// after retrieve, we know that 'current' values are now the baseline for comparison
- cacheIsChangedComparison();
+ updateIsChanged();
setStatus(BaseDataObject.STATUS_CURRENT);
if (isCached() && !anyFieldsToRetrieve) {
@@ -5080,12 +5109,11 @@
}
}
-
} /* retrieve() */
/**
- * Retrieve this object from cache, if possible.
+ * Retrieve this object from cache, if possible. Makes a new copy of cached object.
*
* @return true if the cache supplied this item, false
* otherwise
@@ -5094,7 +5122,6 @@
public boolean retrieveFromCache()
throws DBException {
- CacheManager.getInstance();
CacheSystem cs = CacheManager.getCacheSystem(getDataContext());
if (cs == null) {
return false;
@@ -5111,7 +5138,7 @@
if (isCached()) {
if (myConfig.dbStats()) {
- String statName = myClassName + "|" + getDataContext();
+ String statName = getStatisticsName();
synchronized (sCacheStats) {
CacheStatEntry ce = (CacheStatEntry) sCacheStats.get(statName);
@@ -5132,11 +5159,13 @@
}
if (cs.existsCache(myClassName)) {
- DBObject cachedObject = (DBObject) cs.getItem(myClassName,
- getKey());
+ DBObject cachedObject = (DBObject) cs.getItem(myClassName, getKey());
if (cachedObject != null) {
JDBCObjectMetaData metadata = getJDBCMetaData();
+
+ // have found cache; now copy fields, with special
+ // case for when we only want certain fields
if (anyFieldsToRetrieve) {
String oneFieldName = null;
@@ -5162,25 +5191,12 @@
}
}
} else { /* for each key field */
-
- for (Iterator it = metadata.getAllFieldsMap().values().iterator();
- it.hasNext();) {
- oneField = (DBField) it.next();
- String fieldName = oneField.getName();
-
- if (!oneField.isVirtual() && !oneField.isBinaryObjectType() && !oneField.isLongBinaryType()) {
- set(fieldName, cachedObject.getDataField(fieldName)
- .asString());
- } else if (oneField.isLongBinaryType()) {
- set(fieldName, cachedObject.getFieldByteArray(fieldName));
- }
- } /* for each field */
-
+ copyAllFields(cachedObject);
} /* if anyFieldsToRetrieve */
// we know that 'current' values are now the baseline for comparison
- cacheIsChangedComparison();
+ updateIsChanged();
//
//Set it to current since the Cache contains
@@ -5196,7 +5212,7 @@
if (myConfig.dbStats()) {
synchronized (sCacheStats) {
- String statName = myClassName + "|" + getDataContext();
+ String statName = getStatisticsName();
CacheStatEntry ce = (CacheStatEntry) sCacheStats.get(statName);
@@ -5215,6 +5231,100 @@
return false;
} /* retrieveFromCache() */
+ private String getStatisticsName() {
+ return myClassName + "|" + getDataContext();
+ }
+
+ /**
+ * copy all fields from srcObject into this one. Assumes that the srcObject
+ * has same field naming and type as this one.
+ */
+ protected void copyAllFields(DBObject srcObject) throws DBException {
+ JDBCObjectMetaData metadata = getJDBCMetaData();
+ DBField oneField;
+
+ for (Iterator i = metadata.getAllFieldsMap().values().iterator(); i.hasNext();) {
+ oneField = (DBField) i.next();
+
+ if (!oneField.isVirtual() && !oneField.isBinaryObjectType()) {
+ DataField df = DefaultDataField.getInstance(oneField, this);
+ df.setValue(srcObject.getDataField(oneField.getName()).getValue());
+ setDataField(oneField.getName(), df);
+ }
+ } /* for each field */
+ }
+
+ /**
+ * Creates and returns a copy of this object. The precise meaning
+ * of "copy" may depend on the class of the object. The general
+ * intent is that, for any object <tt>x</tt>, the expression:
+ * <blockquote>
+ * <pre>
+ * x.clone() != x</pre></blockquote>
+ * will be true, and that the expression:
+ * <blockquote>
+ * <pre>
+ * x.clone().getClass() == x.getClass()</pre></blockquote>
+ * will be <tt>true</tt>, but these are not absolute requirements.
+ * While it is typically the case that:
+ * <blockquote>
+ * <pre>
+ * x.clone().equals(x)</pre></blockquote>
+ * will be <tt>true</tt>, this is not an absolute requirement.
+ * <p/>
+ * By convention, the returned object should be obtained by calling
+ * <tt>super.clone</tt>. If a class and all of its superclasses (except
+ * <tt>Object</tt>) obey this convention, it will be the case that
+ * <tt>x.clone().getClass() == x.getClass()</tt>.
+ * <p/>
+ * By convention, the object returned by this method should be independent
+ * of this object (which is being cloned). To achieve this independence,
+ * it may be necessary to modify one or more fields of the object returned
+ * by <tt>super.clone</tt> before returning it. Typically, this means
+ * copying any mutable objects that comprise the internal "deep structure"
+ * of the object being cloned and replacing the references to these
+ * objects with references to the copies. If a class contains only
+ * primitive fields or references to immutable objects, then it is usually
+ * the case that no fields in the object returned by <tt>super.clone</tt>
+ * need to be modified.
+ * <p/>
+ * The method <tt>clone</tt> for class <tt>Object</tt> performs a
+ * specific cloning operation. First, if the class of this object does
+ * not implement the interface <tt>Cloneable</tt>, then a
+ * <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
+ * are considered to implement the interface <tt>Cloneable</tt>.
+ * Otherwise, this method creates a new instance of the class of this
+ * object and initializes all its fields with exactly the contents of
+ * the corresponding fields of this object, as if by assignment; the
+ * contents of the fields are not themselves cloned. Thus, this method
+ * performs a "shallow copy" of this object, not a "deep copy" operation.
+ * <p/>
+ * The class <tt>Object</tt> does not itself implement the interface
+ * <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
+ * whose class is <tt>Object</tt> will result in throwing an
+ * exception at run time.
+ *
+ * @return a clone of this instance.
+ * @throws CloneNotSupportedException if the object's class does not
+ * support the <code>Cloneable</code> interface. Subclasses
+ * that override the <code>clone</code> method can also
+ * throw this exception to indicate that an instance cannot
+ * be cloned.
+ * @see Cloneable
+ */
+ public Object clone() throws CloneNotSupportedException {
+ DBObject result = null;
+ try {
+ result = newInstance();
+ result.setDataContext(getDataContext());
+ result.copyAllFields(this);
+ } catch (DBException e) {
+ getLogger().error("Problem cloning: ", e);
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+
+ return result;
+ }
/**
* Find a set of keys of all of the objects that match the current
@@ -5442,8 +5552,8 @@
/**
- * Set an attribute. Attributes are temporary (e.g. not stored in the DBMS) values
- * associated with this particular DB object instance.
+ * Set an attribute. Attributes are temporary (i.e., not stored in the DBMS) values
+ * associated with this particular object instance.
*
* @param attribName The name of the attribute being defined
* @param attribValue The object to store under this attribute name
@@ -5787,6 +5897,8 @@
*/
public synchronized void setField(String fieldName, byte[] fieldValue)
throws DBException {
+ checkMutable();
+
StringUtil.assertNotBlank(fieldName, "Field name may not be blank");
if (getStatus() == null) {
@@ -5945,6 +6057,7 @@
*/
public synchronized void setField(String fieldName, String fieldValue)
throws DBException {
+ checkMutable();
StringUtil.assertNotBlank(fieldName, "Field name may not be blank");
if (getStatus() == null) {
@@ -6036,7 +6149,7 @@
* @param fieldValue the Java date value to set
* @throws DBException upon error.
*/
- public void setField(String fieldName, java.util.Date fieldValue)
+ public void setField(String fieldName, Date fieldValue)
throws DBException {
DataFieldMetaData fieldMetadata = getFieldMetaData(fieldName);
if (fieldMetadata.isDateOnlyType()) {
@@ -6101,8 +6214,8 @@
/**
- * Helper function that doesn't fire all the processing... just set's the
- * field data in raw form and forgets it.
+ * Helper function that avoids all the processing associated with listeners... just set's the
+ * field data in raw form
*
* @param fieldName The name of the field to set
* @param fieldValue the value to set it to.
@@ -6170,9 +6283,9 @@
/**
* Convenience method to set the fields to be retrieved
- * within this database object.
* <b>NOTE AS OF EXPRESSO 5.0</b> setFieldsToRetrieve() could cause you
- * some problems if you're attempting to retrieve long objects since
+ * some problems if you're attempting to retrieve objects that have fields of
+ * type isBinaryObject or isLongObjectType since
* everything will be converted to a STRING. Please use caution :).
*
* @param fieldNames contain the name of the fields separate by "|"
@@ -6222,8 +6335,7 @@
} /* setFieldsToRetrieve(String) */
/**
- * Convenience method to set the fields to be input for add and update order
- * within this database object.
+ * Convenience method to set the fields to be input that become inputs during add and update
* <b>NOTE AS OF EXPRESSO 5.0</b> setFieldsToInput() could cause you
* some problems if you're attempting to input long objects since
* everything will be converted to a STRING. Please use caution :).
@@ -6285,6 +6397,7 @@
* @param keyValues The string containing one value for each key
* field in the object
* @throws DBException If the key fields cannot be set
+ * @deprecated 2/05 v5.6, use setKey() instead with standard delimiter (|) same as getKey()
*/
public synchronized void setKeys(String keyValues)
throws DBException {
@@ -6312,6 +6425,44 @@
}
} /* setKeys(String) */
+ /**
+ * Set the values for each of the key fields of this object from
+ * a |-delimited string (same delimiter as getKey()).
+ * Make sure this delimiter is impossible within a key field
+ *
+ * @see #getKey()
+ * @param keyValues The string containing one value for each key
+ * field in the object
+ * @throws DBException If the key fields cannot be set
+ */
+ public synchronized void setKey(String keyValues)
+ throws DBException {
+ checkMutable();
+
+ StringTokenizer stk = new StringTokenizer(keyValues, "|");
+ ArrayList keysToSet = new ArrayList();
+
+ while (stk.hasMoreTokens()) {
+ keysToSet.add(stk.nextToken());
+ }
+
+ ArrayList keys = getJDBCMetaData().getKeyFieldListArray();
+
+ if (keys.size() != keysToSet.size()) {
+ throw new DBException("(" + myClassName + ") There are " +
+ keys.size() +
+ " key fields in this table, but " +
+ keyValues + " only contains " +
+ keysToSet.size() + " values");
+ }
+
+ Iterator l = keysToSet.iterator();
+
+ for (Iterator i = keys.iterator(); i.hasNext();) {
+ setField((String) i.next(), (String) l.next());
+ }
+ } /* setKeys(String) */
+
/**
* Set a field's lookup object - this is the name of another database
@@ -6407,7 +6558,7 @@
/**
- * Set the name of this object - this name is used to identify the db object
+ * Set the name of this (static) class - this name is used to identify the db object
* with a more human-readable description
*
* @param theName New name for this object
@@ -6713,6 +6864,8 @@
*/
public void update(boolean updateChangedFieldsOnly)
throws DBException {
+ checkMutable();
+
getExecutor().update(this, updateChangedFieldsOnly);
/* Now log the change if we are logging */
@@ -6755,9 +6908,6 @@
myUpdates = null;
} /* if */
-
- // after update, we know that 'current' values are now the baseline for comparison
- cacheIsChangedComparison();
setStatus(BaseDataObject.STATUS_CURRENT);
notifyListeners(EVENT_UPDATE);
} /* update() */
@@ -7316,6 +7466,12 @@
*/
public void set(String fieldName, Object o) throws DataException {
try {
+ checkMutable();
+ } catch (DBException e) {
+ throw new DataException(e);
+ }
+
+ try {
//
// this is a backwards compatability hack
//
@@ -7357,6 +7513,12 @@
* @since Expresso 5.0
*/
public void setDataField(String fieldName, DataField o) throws DataException {
+ try {
+ checkMutable();
+ } catch (DBException e) {
+ throw new DataException(e);
+ }
+
if (fieldData == null) {
fieldData = new HashMap();
}
@@ -7424,6 +7586,10 @@
}
}
+ ///////////////////////////////////////////////////////////////////////////
+ // embedded objects
+ /////////////////////////////////////////////////////////////////////////
+
/**
* Private class that defines errors for fields.
*/
@@ -7515,10 +7681,6 @@
}
}
- ///////////////////////////////////////////////////////////////////////////
- // embedded object
- /////////////////////////////////////////////////////////////////////////
-
/**
* Private class used to track field updates
*/
@@ -7538,6 +7700,10 @@
} /* FieldUpdate */
+ ///////////////////////////////////////////////////////////////////////////
+ // end embedded objects
+ /////////////////////////////////////////////////////////////////////////
+
/**
* set string filters to the given filter on ALL fields that are quoted text fields
*
@@ -7657,25 +7823,160 @@
return result;
}
+ /**
+ * set the number of cache instances allowed for this object type. This setting
+ * affects all instances -- it is a class setting, typically used during
+ * setupFields(). This setting automatically persists a record to the DB; no need to
+ * call update after calling this.
+ * author Larry Hamel, CodeGuild, Inc.
+ * @param maxNumInstances maximum number of instances to cache,
+ */
+ public void setCacheLimit(int maxNumInstances) throws DBException {
+ DBObjLimit dbl = new DBObjLimit(SecuredDBObject.SYSTEM_ACCOUNT);
+ dbl.setField(DBObjLimit.DB_OBJECT_NAME, myClassName);
+ boolean found = dbl.find();
+ if ( !found ) {
+ dbl.setField(DBObjLimit.CACHE_SIZE, maxNumInstances);
+ dbl.setField(DBObjLimit.PAGE_LIMIT, "30");
+ dbl.setField(DBObjLimit.TTL, "10");
+ dbl.add();
+ } else {
+ dbl.setField(DBObjLimit.CACHE_SIZE, maxNumInstances);
+ dbl.update();
+ }
+ }
/**
* set the number of cache instances allowed for this object type. This setting
* affects all instances -- it is a class setting, typically used during
- * setupFields()
+ * setupFields(). This setting automatically persists a record to the DB; no need to
+ * call update after calling this.
* author Larry Hamel, CodeGuild, Inc.
+ * @param maxNumInstances maximum number of instances to cache,
+ * @param minutesToLive TTL for each instance in cache
*/
- public void setCacheLimit(int numInstances) throws DBException {
+ public void setCacheLimit(int maxNumInstances, int minutesToLive) throws DBException {
DBObjLimit dbl = new DBObjLimit(SecuredDBObject.SYSTEM_ACCOUNT);
- dbl.setField("DBObjectName", myClassName);
+ dbl.setField(DBObjLimit.DB_OBJECT_NAME, myClassName);
boolean found = dbl.find();
if ( !found ) {
- dbl.setField("CacheSize", numInstances);
- dbl.setField("PageLimit", "0");
- dbl.setField("TTL", "10");
+ dbl.setField(DBObjLimit.CACHE_SIZE, maxNumInstances);
+ dbl.setField(DBObjLimit.PAGE_LIMIT, "30");
+ dbl.setField(DBObjLimit.TTL, minutesToLive);
dbl.add();
} else {
- dbl.setField("CacheSize", numInstances);
+ dbl.setField(DBObjLimit.CACHE_SIZE, maxNumInstances);
+ dbl.setField(DBObjLimit.TTL, minutesToLive);
dbl.update();
}
+ }
+
+ /**
+ * a rough estimate on object size in RAM. NOT suitable for exact calculation
+ *
+ * @return a rough estimate of object size in bytes
+ */
+ public long getSizeEstimate() throws DBException {
+ long size = 0;
+ for (Iterator iterator = getDef().getAllFieldsIterator(); iterator.hasNext();) {
+ DBField f = (DBField) iterator.next();
+ if (f.isVirtual()) {
+ // no op
+ } else if (f.isLongBinaryType()) {
+ byte[] bytes = getFieldByteArray(f.getName());
+ if (bytes != null) {
+ size += bytes.length;
+ }
+ } else if (!f.isBinaryObjectType()) {
+ DataField df = getDataField(f.getName());
+ if (df != null) {
+ Object obj = df.getValue();
+ if (obj != null) {
+ size += obj.toString().length() * 2; // assumes 2 bytes per char
+ }
+ }
+ }
+ }
+
+ size += 20 * 4; // each dbobject has about 20 member variables as of 2/05; assume 4 byte references
+ return size;
+ }
+
+ /**
+ * set attribute flag whether this object can be changed--make it immutable.
+ */
+ public void isMutable(boolean shouldBeChangeable) {
+ if (shouldBeChangeable) {
+ if (isMutable()) {
+ // do nothing
+ } else {
+ // default of no setting == true
+ removeAttribute(IS_MUTABLE);
+ }
+ } else {
+ if (!isMutable()) {
+ // do nothing
+ } else {
+ setAttribute(IS_MUTABLE, "false");
+ }
+ }
+ }
+
+ /**
+ * get attribute flag whether this object can be changed--whether this object is mutable;
+ * not saved to disk. defaults to true;
+ */
+ public boolean isMutable() {
+ boolean result = true; // default to true
+ String attrib = (String) getAttribute(IS_MUTABLE);
+ if (attrib != null) {
+ result = StringUtil.toBoolean(attrib);
+ }
+ return result;
+ }
+
+ /**
+ * call this before changing data.
+ * check for whether object is mutable.
+ */
+ protected void checkMutable() throws DBException {
+ if (!isMutable()) {
+ throw new DBException(
+ "Object marked as immutable: this change not allowed for object with key: "
+ + forKey());
+ }
+ }
+
+ /**
+ * Retrieve a thread local instance of the Perl5 pattern matcher. Allows
+ * for optimization of # of instances of pattern matcher vs synchronization.
+ *
+ * @return PatternMatcher
+ */
+ protected PatternMatcher getPatternMatcher() {
+ return (PatternMatcher) patternMatcher.get();
+ }
+
+ /**
+ * get actual cached object, which is flagged to be immutable. efficient for read-only
+ * situations where you want to avoid making a new copy of the object.
+ *
+ * @see DBObject#getKey()
+ * @param dbobjClass class of desired DBObject
+ * @param keyValues String combination of all PK values (see getKey())
+ */
+ public static DBObject getImmutableCachedObject(Class dbobjClass, String keyValues) {
+ return cacheUtils.getImmutableCachedObject(dbobjClass, keyValues);
+ }
+
+ /**
+ * get actual cached object, which is flagged to be immutable. efficient for read-only
+ * situations where you want to avoid making a new copy of the object.
+ *
+ * @see DBObject#getKey()
+ * @param keyValues String combination of all PK values (see getKey())
+ */
+ public DBObject getImmutableObjectFromCache(String keyValues) {
+ return cacheUtils.getImmutableCachedObject(getClass(), keyValues);
}
} // dbobject
More information about the cvs
mailing list