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.services.dbobj;
066:
067: import com.jcorporate.expresso.core.ExpressoSchema;
068: import com.jcorporate.expresso.core.cache.CacheException;
069: import com.jcorporate.expresso.core.cache.CacheManager;
070: import com.jcorporate.expresso.core.cache.CacheSystem;
071: import com.jcorporate.expresso.core.controller.ControllerRequest;
072: import com.jcorporate.expresso.core.db.DBConnection;
073: import com.jcorporate.expresso.core.db.DBConnectionPool;
074: import com.jcorporate.expresso.core.db.DBException;
075: import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
076: import com.jcorporate.expresso.core.dbobj.ValidValue;
077: import com.jcorporate.expresso.core.misc.ConfigManager;
078: import com.jcorporate.expresso.core.misc.ConfigurationException;
079: import com.jcorporate.expresso.core.misc.StringUtil;
080: import org.apache.log4j.Logger;
081:
082: import java.util.Enumeration;
083: import java.util.HashSet;
084: import java.util.Iterator;
085:
086: /**
087: * <p>Copyright 1999, 2000, 2001 Jcorporate Ltd.</p>
088: * <p>Setup is a generic configuration/setup value table. Each schema can
089: * define a number of default setup values that are created when the
090: * schema is initialized. The user can then customize these values.</p>
091: *
092: * @author Michael Nash
093: */
094: public class Setup extends SecuredDBObject {
095:
096: /**
097: * Is the setup cache initialized?
098: */
099: private static boolean cacheInitialized = false;
100:
101: /**
102: * Name of the setup cache file.
103: */
104: public static final String CACHE_NAME = Setup.class.getName()
105: + ".setupCache";
106:
107: /**
108: * The Log4J Logger Instance
109: */
110: private static Logger log = Logger.getLogger(Setup.class);
111:
112: /**
113: * Contains a list of tableExists objects
114: */
115: private static HashSet tableNotExists = new HashSet();
116: public static final String SETUP_CODE = "SetupCode";
117: public static final String SETUP_DESCRIP = "Descrip";
118: public static final String SETUP_VALUE = "SetupValue";
119: public static final String SCHEMA_CLASS = "SchemaClass";
120:
121: /**
122: * Constructor
123: *
124: * @throws DBException upon error
125: */
126: public Setup() throws DBException {
127: super ();
128: } /* Setup() */
129:
130: /**
131: * Initializes Setup with proper user context
132: *
133: * @param uid the Owning user's uid
134: * @throws DBException upon error
135: */
136: public Setup(int uid) throws DBException {
137: super (uid);
138: }
139:
140: /**
141: * For using DBObjects within Controllers. Initializes based upon the
142: * current user and the requested db. [Of course this can be modified later]
143: *
144: * @param request - The controller request handed to you by the framework.
145: * @throws DBException upon error
146: */
147: public Setup(ControllerRequest request) throws DBException {
148: super (request);
149: }
150:
151: /**
152: * Extend add to call updatepool
153: *
154: * @throws DBException upon error
155: */
156: public synchronized void add() throws DBException {
157: super .add();
158: updateCache();
159: } /* add() */
160:
161: /**
162: * Extend update to call updatepool
163: *
164: * @throws DBException upon error
165: */
166: public synchronized void delete() throws DBException {
167: super .delete();
168: updateCache();
169: } /* delete() */
170:
171: /**
172: * @return boolean true if something was indeed found.
173: * @throws DBException upon error
174: */
175: public boolean find() throws DBException {
176: if (getField(SCHEMA_CLASS).equals("")) {
177: setField(SCHEMA_CLASS, ExpressoSchema.class.getName());
178: }
179:
180: return super .find();
181: } /* find() */
182:
183: /**
184: * Get a config value for the default schema
185: * Config values are like INI file variables or registry entries - except
186: * stored in a database. There is one database connection held open for
187: * accessing these values from the connection pool. This function simply
188: * references the Expresso Schema's setup values. For your own application
189: * you will want to call the getValue that names your Schema
190: *
191: * @param dbName The data context to get the value for
192: * @param setupCode The code string of the setup code required
193: * @return String value stored in the given config value
194: * @throws DBException If the value cannot be retrieved
195: */
196: public static String getValue(String dbName, String setupCode)
197: throws DBException {
198:
199: if (tableNotExists.contains(dbName)) {
200: if (log.isDebugEnabled()) {
201: log.debug("No setup info yet for db " + dbName);
202: }
203:
204: return "";
205: }
206:
207: return getValue(dbName,
208: "com.jcorporate.expresso.core.ExpressoSchema",
209: setupCode);
210: } /* getValue(String, String) */
211:
212: /**
213: * Get a config value for the default schema
214: * Config values are like INI file variables or registry entries - except
215: * stored in a database. There is one database connection held open for
216: * accessing these values from the connection pool
217: *
218: * @param dbName The data context name
219: * @param schemaName The name of the schema
220: * @param setupCode The code string of the setup code required
221: * @return String value stored in the given config value
222: * @throws DBException If the value cannot be retrieved
223: */
224: public static String getValue(String dbName, String schemaName,
225: String setupCode) throws DBException {
226: if (StringUtil.notNull(dbName).equals("")) {
227: dbName = DBConnection.DEFAULT_DB_CONTEXT_NAME;
228: }
229:
230: // handle case where caching is turned off
231: boolean isCaching = false;
232: try {
233: isCaching = ConfigManager.getJdbc(dbName).cache();
234: } catch (ConfigurationException e) {
235: throw new DBException(e);
236: }
237:
238: if (!isCaching) {
239: Setup sl = new Setup(SecuredDBObject.SYSTEM_ACCOUNT);
240: sl.setDataContext(dbName);
241: sl.setField(SETUP_CODE, setupCode);
242: sl.setField(SCHEMA_CLASS, schemaName);
243: sl.retrieve();
244:
245: return ConfigManager.expandValue(StringUtil.notNull(sl
246: .getField(SETUP_VALUE)));
247: }
248:
249: // handle case where caching is on
250: CacheManager.getInstance(); // must precede access
251:
252: if (tableNotExists.contains(dbName)) {
253: if (log.isDebugEnabled()) {
254: log.debug("No setup info yet for db " + dbName);
255: }
256:
257: return ("");
258: }
259: if (StringUtil.notNull(schemaName).equals("")) {
260: throw new DBException(
261: "Schema name may not be null or blank");
262: }
263: if (StringUtil.notNull(setupCode).equals("")) {
264: throw new DBException("Setup code may not be null or blank");
265: }
266:
267: if (!CacheManager.existsCache(dbName, CACHE_NAME) && isCaching) {
268: log
269: .info("Setup cache did not exist - reading setup values");
270: readSetups();
271: }
272:
273: /**
274: * If there are no items in the cache, re-read them
275: * (as it probably got cleared by the cache manager)
276: */
277: int itemCount = CacheManager.getItemCount(dbName, CACHE_NAME);
278:
279: if (itemCount == 0) {
280:
281: if (isCaching) {
282: log
283: .info("Setup cache did not exist - reading setup values");
284: readSetups();
285: }
286:
287: itemCount = CacheManager.getItemCount(dbName, CACHE_NAME);
288:
289: if (itemCount == 0) {
290: log
291: .error("For context: "
292: + dbName
293: + ", Setup cache still has zero items after re-reading");
294: }
295: }
296:
297: ValidValue oneItem = (ValidValue) CacheManager.getItem(dbName,
298: CACHE_NAME, schemaName + "|" + setupCode);
299:
300: if (oneItem == null) {
301: return null;
302: }
303:
304: return ConfigManager.expandValue(StringUtil.notNull(oneItem
305: .getDescription()));
306: } /* getValue(String, String, String) */
307:
308: /**
309: * Get a value from the configuration values. The value must be
310: * valid (not null) or the method throws an exception
311: *
312: * @param dbName The datacontext name
313: * @param setupCode Code of the required setup value
314: * @return java.lang.String
315: * @throws DBException if the value does not exist or is null
316: */
317: public static String getValueRequired(String dbName,
318: String setupCode) throws DBException {
319: String setupValue = null;
320:
321: if (tableNotExists.contains(dbName)) {
322: throw new DBException("No Setup Value '" + setupCode
323: + "' for default schema, db '" + dbName + "'"
324: + " Table does not yet exist");
325: }
326:
327: setupValue = getValue(dbName, setupCode);
328:
329: if (setupValue == null) {
330: throw new DBException("No value supplied in setup for '"
331: + setupCode + "' for default schema, db '" + dbName
332: + "'");
333: } else {
334: return setupValue;
335: }
336: } /* getValueRequired(String, String) */
337:
338: /**
339: * Get a value from the configuration values. Return null (no exception) if value doesn't exist
340: *
341: * @param dbName The datacontext name
342: * @param setupCode Code of the required setup value
343: * @return java.lang.String of value, or null if not found
344: */
345: public static String getValueUnrequired(String dbName,
346: String setupCode) {
347: String setupValue = null;
348:
349: try {
350: setupValue = getValue(dbName, setupCode);
351: // getvalue can return "" for some error cases
352: if ("".equals(setupValue)) {
353: setupValue = null;
354: }
355: } catch (DBException e) {
356: // no problem
357: }
358:
359: return setupValue;
360: } /* getValueRequired(String, String) */
361:
362: /**
363: * Get a value from the configuration values. Return null (no exception) if value doesn't exist
364: *
365: * @param dbName The datacontext name
366: * @param setupCode Code of the required setup value
367: * @param schema name of schema
368: * @return java.lang.String of value, or null if not found
369: */
370: public static String getValueUnrequired(String dbName,
371: String schema, String setupCode) {
372: String setupValue = null;
373:
374: try {
375: setupValue = getValue(dbName, schema, setupCode);
376: // getvalue can return "" for some error cases
377: if ("".equals(setupValue)) {
378: setupValue = null;
379: }
380: } catch (DBException e) {
381: // no problem
382: }
383:
384: return setupValue;
385: } /* getValueRequired(String, String) */
386:
387: /**
388: * Get a value from the configuration values. The value must be
389: * valid or the method throws an exception
390: *
391: * @param dbName The data context for the value
392: * @param schema The schema class for this setup code
393: * @param setupCode Code of the required setup value
394: * @return String
395: * @throws DBException if the value does not exist or is null
396: */
397: public static String getValueRequired(String dbName, String schema,
398: String setupCode) throws DBException {
399:
400: if (tableNotExists.contains(dbName)) {
401: throw new DBException("No Setup Value '" + setupCode
402: + "' for default schema, db '" + dbName + "'"
403: + " Table does not yet exist");
404: }
405: if (schema.equals("")) {
406: return getValueRequired(dbName, setupCode);
407: }
408: if (StringUtil.notNull(dbName).equals("")) {
409: dbName = "default";
410: }
411:
412: String setupValue = getValue(dbName, schema, setupCode);
413:
414: if (setupValue == null) {
415: throw new DBException("No value supplied in setup for '"
416: + setupCode + "' for schema '" + schema + "', db '"
417: + dbName + "'");
418: } else {
419: return setupValue;
420: }
421: } /* getValueRequired(String, String, String) */
422:
423: /**
424: * Check if this setup value is a directory
425: *
426: * @throws DBException upon error
427: */
428: private void isDirectory() throws DBException {
429:
430: /* It's OK for the value to be blank */
431: if (ConfigManager.expandValue(getField(SETUP_VALUE)).equals("")) {
432: return;
433: }
434: /* if it's not blank, it must be a directory name */
435: if (!ConfigManager.expandValue(getField(SETUP_VALUE)).endsWith(
436: "/")) {
437: throw new DBException(
438: "Setup value '"
439: + getField(SETUP_VALUE)
440: + "' for code '"
441: + getField(SETUP_CODE)
442: + "' ("
443: + getField(SETUP_DESCRIP)
444: + ") is not a valid directory name. Must use '/' as "
445: + "path seperator and end with '/'");
446: }
447: } /* isDirectory() */
448:
449: /**
450: * @param forceRead - Tries to reread the setup values even if the db tables
451: * don't exist yet.
452: * @throws DBException if there's an error reading the setups
453: */
454: public static synchronized void readSetups(boolean forceRead)
455: throws DBException {
456:
457: if (!ConfigManager.isInitialized()) {
458: throw new DBException("ConfigManager was not initialized",
459: ConfigManager.getConfigurationFailureException());
460: }
461:
462: log.info("Reading setup values for all databases/contexts");
463:
464: CacheManager.getInstance(); // must precede accesses
465: int keyCount = 0;
466: String oneConfigKey = null;
467: for (Enumeration e = ConfigManager.getAllConfigKeys(); e
468: .hasMoreElements();) {
469: keyCount++;
470:
471: /* We do an inner try here so that if we can't read
472: setup values for one */
473: /* database, we continue & try to */
474: /* read values for the rest of the databases */
475: boolean skipThisContext = false;
476:
477: try {
478: oneConfigKey = (String) e.nextElement();
479:
480: /* If the table doesn't exist, unless we're told to force
481: read, don't do it */
482: skipThisContext = false;
483:
484: if (tableNotExists.contains(oneConfigKey)) {
485: skipThisContext = true;
486: }
487: if (forceRead) {
488: skipThisContext = false;
489: }
490: /* If we're specifically told in the expresso-config.xml file
491: * that a particular context is NOT an "expresso"
492: * context - e.g. doesn't contain Expresso tables, then don't
493: * try to read setup values from it.
494: */
495: try {
496: if (!ConfigManager.getContext(oneConfigKey)
497: .hasSetupTables()) {
498: skipThisContext = true;
499: }
500: } catch (ConfigurationException ce) {
501: throw new DBException(ce);
502: }
503: if (!skipThisContext) {
504: CacheSystem cs = CacheManager
505: .getCacheSystem(oneConfigKey);
506: if (log.isInfoEnabled()) {
507: log.info("Reading setup values for '"
508: + oneConfigKey + "'");
509: }
510:
511: if (!cs.existsCache(CACHE_NAME)) {
512: cs.createCache(CACHE_NAME, false);
513: }
514:
515: Setup sl = new Setup(SecuredDBObject.SYSTEM_ACCOUNT);
516: sl.setDataContext(oneConfigKey);
517:
518: Setup oneSetup = null;
519: int setupCount = 0;
520:
521: for (Iterator ek = sl.searchAndRetrieveList()
522: .iterator(); ek.hasNext();) {
523: setupCount++;
524: oneSetup = (Setup) ek.next();
525: CacheManager
526: .addItem(
527: oneConfigKey,
528: CACHE_NAME,
529: new ValidValue(
530: oneSetup
531: .getField(SCHEMA_CLASS)
532: + "|"
533: + oneSetup
534: .getField(SETUP_CODE),
535: oneSetup
536: .getField(SETUP_VALUE)));
537: }
538: if (setupCount == 0) {
539: throw new DBException(
540: "No setup values were read"
541: + "in db '"
542: + oneConfigKey
543: + "'. Run DBCreate to setup default "
544: + "setup values.");
545: } else {
546: log.info(setupCount
547: + " setup entries read in db '"
548: + oneConfigKey + "'");
549: }
550:
551: DBConnectionPool onePool = DBConnectionPool
552: .getInstance(oneConfigKey);
553:
554: String maxConnect = StringUtil.notNull(getValue(
555: oneConfigKey, "MaxConnections"));
556:
557: if (!maxConnect.equals("")) {
558: try {
559: int newMax = new Integer(maxConnect)
560: .intValue();
561: onePool.setMaxConnections(newMax);
562: } catch (NumberFormatException ne) {
563: throw new DBException(
564: "Value for MaxConnections"
565: + "must be a number");
566: }
567: }
568: try {
569: String timeOut = StringUtil.notNull(getValue(
570: oneConfigKey, "ConnTimeOut"));
571: int interval = 30;
572:
573: if (!timeOut.equals("")) {
574: interval = new Integer(getValue(
575: oneConfigKey, "ConnTimeOut"))
576: .intValue();
577: }
578:
579: onePool.setTimeOutInterval(interval);
580: } catch (NumberFormatException ne) {
581: throw new DBException(
582: "Value for ConnTimeOut is "
583: + "not a number. Check setup values");
584: }
585: } /* End if Force Read */
586:
587: } catch (DBException de) {
588: log.error(
589: "WARNING: Unable to read setup values for db '"
590: + oneConfigKey + "'", de);
591:
592: /* Assume that the table is unreadable right now,
593: try after a DBCreate */
594: tableNotExists.add(oneConfigKey);
595: } catch (CacheException ce) {
596: log.error(
597: "WARNING: Cache error reading setup values for db '"
598: + oneConfigKey + "'", ce);
599: }
600: }
601: /* for each config key */
602: if (keyCount == 0) {
603: throw new DBException("No config keys to read setup values"
604: + "for - there must be at least the "
605: + "default config key/db");
606: }
607:
608: cacheInitialized = true;
609: }
610:
611: /**
612: * Read the setup/config values into a hashtable for cached access
613: *
614: * @throws DBException upon error
615: */
616: public static synchronized void readSetups() throws DBException {
617: readSetups(false);
618: } /* readSetups() */
619:
620: /**
621: * Extend retrieve to supply a defeult for the schemaclass field
622: *
623: * @throws DBException If the retrieve fails
624: */
625: public synchronized void retrieve() throws DBException {
626: if (getField(SCHEMA_CLASS).equals("")) {
627: setField(SCHEMA_CLASS, ExpressoSchema.class.getName());
628: }
629:
630: super .retrieve();
631: } /* retrieve() */
632:
633: /**
634: * DB Tool calls this when the Expresso Setup Values are finally able to be
635: * read
636: *
637: * @param dbName the data context that setup tables now exist for
638: */
639: public static synchronized void setTableExists(String dbName) {
640: if (tableNotExists.contains(dbName)) {
641: tableNotExists.remove(dbName);
642: }
643: }
644:
645: /**
646: * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject#setupFields
647: */
648: protected synchronized void setupFields() throws DBException {
649: setTargetTable("SETUP");
650: setDescription("DBsetup");
651: setCharset("ISO-8859-1");
652: addField(SCHEMA_CLASS, "char", 128, false, "schema");
653: addField(SETUP_CODE, "char", 30, false, SETUP_CODE);
654: addField(SETUP_DESCRIP, "char", 80, false, "description");
655: addField(SETUP_VALUE, "varchar", 255, true, SETUP_VALUE);
656: setStringFilter(SCHEMA_CLASS, "stripFilter");
657: setStringFilter(SETUP_CODE, "stripFilter");
658: setStringFilter(SETUP_DESCRIP, "stripFilter");
659: setStringFilter(SETUP_VALUE, "rawFilter");
660: addKey(SCHEMA_CLASS);
661: addKey(SETUP_CODE);
662: setMultiValued(SCHEMA_CLASS);
663: setLookupObject(SCHEMA_CLASS, SchemaList.class.getName());
664: } /* setupFields() */
665:
666: /**
667: * Extend update to validate some setup values
668: *
669: * @throws DBException upon error
670: */
671: public synchronized void update() throws DBException {
672:
673: /* Verify the field has a valid value */
674: if (getField(SETUP_CODE).equals("BaseDir")) {
675: isDirectory();
676: } else if (getField(SETUP_CODE).equals("QueueDirectory")) {
677: isDirectory();
678: } else if (getField(SETUP_CODE).equals("ReportRoot")) {
679: isDirectory();
680: } else if (getField(SETUP_CODE).equals("TempDir")) {
681: isDirectory();
682: }
683:
684: super .update();
685: updateCache();
686: } /* update() */
687:
688: /**
689: * updateCache is called when we've updated setup values,
690: * so they must be read into the cache again
691: *
692: * @throws DBException upon error
693: */
694: private void updateCache() throws DBException {
695:
696: /* If we've already read a cache, read it again */
697: if (cacheInitialized) {
698: readSetups();
699: }
700: } /* updateCache() */
701:
702: /**
703: * convenience setter for code
704: *
705: * @param code new code to set
706: */
707: public void setCode(String code) throws DBException {
708: setField(SETUP_CODE, code);
709: }
710:
711: /**
712: * convenience setter for value
713: *
714: * @param value new value to set
715: */
716: public void setValue(String value) throws DBException {
717: setField(SETUP_VALUE, value);
718: }
719:
720: /**
721: * convenience setter for description of Setup item
722: *
723: * @param descrip new description to set
724: */
725: public void setDescrip(String descrip) throws DBException {
726: setField(SETUP_DESCRIP, descrip);
727: }
728:
729: /**
730: * convenience setter for schema class; schema is optional; if not provided, find() will default to Expresso
731: *
732: * @param schemaclassname new name to set
733: */
734: public void setSchemaClass(String schemaclassname)
735: throws DBException {
736: setField(SCHEMA_CLASS, schemaclassname);
737: }
738:
739: } /* Setup */
|