001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.core.dbobj;
066:
067: import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCObjectMetaData;
068: import com.jcorporate.expresso.core.db.DBException;
069: import com.jcorporate.expresso.core.misc.ConfigManager;
070: import com.jcorporate.expresso.core.misc.StringUtil;
071: import com.jcorporate.expresso.kernel.util.ClassLocator;
072: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
073: import org.apache.log4j.Logger;
074:
075: /**
076: * Base class for pluggable next number autoincrementing system.
077: *
078: * @author Original by Michael Nash, rewritten by Michael Rimov
079: * <p/>
080: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
081: * @see com.jcorporate.expresso.core.dbobj.NextNumberImpl for more information
082: * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
083: */
084: abstract public class NextNumber {
085: public static final String DEFAULT_CLASS_HANDLER = NextNumberImpl.class
086: .getName();
087:
088: /**
089: * Set this to false and recompile to cause NextNumber to not check it's input
090: * parameters. May result in NullPointerExceptions if there's bugs in the
091: * code. But will result in a speed increase.
092: */
093: protected static final boolean CHECK_PARAMETERS = true;
094:
095: /**
096: * The lo4j log category.
097: */
098: protected static Logger log = Logger.getLogger(NextNumber.class);
099:
100: /**
101: * The actual static instance of the Next Number implementation
102: */
103: protected static NextNumber theInstance = null;
104:
105: /**
106: * Constructor - Do not call directly. Use getInstance() instead.
107: */
108: protected NextNumber() {
109: } /* NextNumber() */
110:
111: /**
112: * Factory Method returns a constructed instance of the NextNumber Manager.
113: *
114: * @return instantiated NextNumber object
115: */
116: public static synchronized NextNumber getInstance()
117: throws DBException {
118: if (theInstance == null) {
119: // retest after getting sync
120: if (theInstance == null) {
121: String classHandler = null;
122: classHandler = ConfigManager
123: .getClassHandler("nextNumber");
124:
125: if (classHandler == null) {
126: classHandler = DEFAULT_CLASS_HANDLER;
127: }
128: try {
129: theInstance = (NextNumber) ClassLocator.loadClass(
130: classHandler).newInstance();
131: } catch (ClassNotFoundException cnfe) {
132: log.error("Class Not Found: " + classHandler, cnfe);
133: throw new DBException(cnfe);
134: } catch (IllegalAccessException iae) {
135: log.error("Class Not Found: " + classHandler, iae);
136: throw new DBException(iae);
137: } catch (InstantiationException ine) {
138: log.error("Class Not Found: " + classHandler, ine);
139: throw new DBException(ine);
140: }
141: }
142: }
143: return theInstance;
144: }
145:
146: /**
147: * Gets the maximum value for a particular object and field Used if there's
148: * no value loaded in memory or state.
149: *
150: * @param db The dataContext to get for
151: * @param callingObject the calling DBObject
152: * @param oneField the field name to query
153: * @return long value covering the max used for the field so far
154: */
155: protected long getMax(String db, DBObject callingObject,
156: String oneField) throws DBException {
157: callingObject.setDataContext(db);
158:
159: String value = callingObject.getMax(oneField);
160: long lo;
161:
162: if (StringUtil.notNull(value).length() == 0) {
163: return 0;
164: }
165: try {
166: lo = Long.parseLong(value);
167: } catch (NumberFormatException nfe) {
168: throw new DBException(
169: "Error converting max values for field: "
170: + oneField, nfe);
171: }
172:
173: return (lo); //Nextnumber will be max + 1.
174: }
175:
176: /**
177: * Register a field for next number information. This may happen even if it isn't an
178: * auto-inc field.
179: *
180: * @param db The database context to work with.
181: * @param callingDBObject The calling database object to register the field for
182: * @param fieldName The field name to register into the NextNumber engine
183: */
184: abstract public void registerField(String db,
185: DBObject callingDBObject, String fieldName)
186: throws DBException;
187:
188: /**
189: * Get the nextnumber for this dbobject. Increments the internal value.
190: *
191: * @param db The datacontext. MAKE SURE THIS IS CORRECT or else you will get duplicate running counts for each incorrect DB name, and thus Duplicate key errors when trying to write rows.
192: * @param callingDBObject The calling DBOBject
193: * @param fieldName the name of the field to get the next number value for
194: * @return long integer representing the next number to use for that field
195: */
196: abstract public long getNext(String db, DBObject callingDBObject,
197: String fieldName) throws DBException;
198:
199: /**
200: * Reset the counts for the paritcular db each subsequent operation will
201: * require a new getMax(). There is questionable threadsafety about the
202: * reset methods. Please only do it on a "non-live" server. Used after a
203: * DBCreate or DeleteSchema has been called.
204: *
205: * @param db the db context to clear all the next number values for.
206: */
207: abstract public void reset(String db);
208:
209: /**
210: * Clears the table on a particular dbobject. Similar to reset db, but
211: *
212: * @param db The db context that the next number resides in.
213: * @param callingObject the object that links to the various nextnumber
214: * objects
215: */
216: abstract public void reset(String db, DBObject callingObject);
217:
218: /**
219: * <p>Fill in for potential resource removal Should be called by ConfigInit.
220: * destroy() Or by unit tests to reset everything to a pristine state.</p>
221: * <i>Note:</i> Do not call getInstance() to call destroy. Simply use
222: * NextNumber.destroy();
223: */
224: public static synchronized void destroy() {
225: if (theInstance == null) {
226: return;
227: } else {
228:
229: //Clear static variables.
230: theInstance = null;
231: }
232: }
233:
234: /**
235: * Builds the key string for lookup within the dbobject hashmap.
236: *
237: * @param callingDBObject the calling DBObject
238: * @param fieldName the fieldName to get the key for
239: * @return a string formatted in [callingDBObject].[fieldName]
240: * @throws DBException upon error
241: * <p/>
242: * Modify by Yves Henri AMAIZO <amy_amaizo@compuserve.com>
243: * @since $DatabaseSchema $Date: 2004/11/17 20:48:11 $
244: */
245: protected String getKey(DBObject callingDBObject, String fieldName)
246: throws DBException {
247: FastStringBuffer result = FastStringBuffer.getInstance();
248: String returnValue = null;
249: try {
250: result.append(((JDBCObjectMetaData) callingDBObject
251: .getMetaData()).getTargetTable());
252: result.append(".");
253: result.append(fieldName);
254: returnValue = result.toString();
255: } finally {
256: result.release();
257: result = null;
258: }
259:
260: return returnValue;
261: }
262:
263: } /* NextNumber */
|