0001: /* ====================================================================
0002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
0003: *
0004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * 1. Redistributions of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * 2. Redistributions in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * 3. The end-user documentation included with the redistribution,
0019: * if any, must include the following acknowledgment:
0020: * "This product includes software developed by Jcorporate Ltd.
0021: * (http://www.jcorporate.com/)."
0022: * Alternately, this acknowledgment may appear in the software itself,
0023: * if and wherever such third-party acknowledgments normally appear.
0024: *
0025: * 4. "Jcorporate" and product names such as "Expresso" must
0026: * not be used to endorse or promote products derived from this
0027: * software without prior written permission. For written permission,
0028: * please contact info@jcorporate.com.
0029: *
0030: * 5. Products derived from this software may not be called "Expresso",
0031: * or other Jcorporate product names; nor may "Expresso" or other
0032: * Jcorporate product names appear in their name, without prior
0033: * written permission of Jcorporate Ltd.
0034: *
0035: * 6. No product derived from this software may compete in the same
0036: * market space, i.e. framework, without prior written permission
0037: * of Jcorporate Ltd. For written permission, please contact
0038: * partners@jcorporate.com.
0039: *
0040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
0044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
0046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0051: * SUCH DAMAGE.
0052: * ====================================================================
0053: *
0054: * This software consists of voluntary contributions made by many
0055: * individuals on behalf of the Jcorporate Ltd. Contributions back
0056: * to the project(s) are encouraged when you make modifications.
0057: * Please send them to support@jcorporate.com. For more information
0058: * on Jcorporate Ltd. and its products, please see
0059: * <http://www.jcorporate.com/>.
0060: *
0061: * Portions of this software are based upon other open source
0062: * products and are subject to their respective licenses.
0063: */
0064:
0065: package com.jcorporate.expresso.services.controller.dbmaint;
0066:
0067: import com.jcorporate.expresso.core.controller.Block;
0068: import com.jcorporate.expresso.core.controller.Controller;
0069: import com.jcorporate.expresso.core.controller.ControllerException;
0070: import com.jcorporate.expresso.core.controller.ControllerRequest;
0071: import com.jcorporate.expresso.core.controller.ControllerResponse;
0072: import com.jcorporate.expresso.core.controller.Input;
0073: import com.jcorporate.expresso.core.controller.NonHandleableException;
0074: import com.jcorporate.expresso.core.controller.Output;
0075: import com.jcorporate.expresso.core.controller.State;
0076: import com.jcorporate.expresso.core.controller.Transition;
0077: import com.jcorporate.expresso.core.controller.session.PersistentSession;
0078: import com.jcorporate.expresso.core.dataobjects.DataObject;
0079: import com.jcorporate.expresso.core.dataobjects.Defineable;
0080: import com.jcorporate.expresso.core.dataobjects.Securable;
0081: import com.jcorporate.expresso.core.db.DBException;
0082: import com.jcorporate.expresso.core.dbobj.DBObject;
0083: import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
0084: import com.jcorporate.expresso.core.dbobj.ValidValue;
0085: import com.jcorporate.expresso.core.misc.ConfigManager;
0086: import com.jcorporate.expresso.core.misc.ConfigurationException;
0087: import com.jcorporate.expresso.core.misc.StringUtil;
0088: import com.jcorporate.expresso.core.misc.URLUTF8Encoder;
0089: import com.jcorporate.expresso.core.security.User;
0090: import com.jcorporate.expresso.kernel.util.ClassLocator;
0091: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
0092: import com.jcorporate.expresso.services.dbobj.DBObjLimit;
0093: import com.jcorporate.expresso.services.dbobj.Setup;
0094: import org.apache.log4j.Logger;
0095:
0096: import java.util.HashMap;
0097: import java.util.Iterator;
0098: import java.util.Map;
0099: import java.util.StringTokenizer;
0100:
0101: /**
0102: * Base class for all command classes. This class provides the execute()
0103: * method which all subclasses need to override. IT does some basic functionality
0104: * that all the DBMaint states need, such as load the given DBObject as
0105: * specified.
0106: *
0107: * @author Michael Nash, contributions by Kevin King et al
0108: */
0109: public abstract class DynamicCmd extends State {
0110:
0111: /**
0112: * Flag to determine if we need to show the 'next page' icon
0113: */
0114: protected boolean showNext = false;
0115:
0116: /**
0117: * Flag to determine if we need to show the 'previous page' icon
0118: */
0119: protected boolean showPrev = false;
0120:
0121: private String searchParam = "";
0122:
0123: /**
0124: * If we are handing master/detail DB objects, then the block containing
0125: * /* the transitions to navigate the master/detail records is stored in session
0126: * /* under this key
0127: */
0128: public static String masterObjKey = "com.jcorporate.expresso.services.controller.DBMaint.MasterObj";
0129:
0130: /* fieldsParam indicates the fields for this command that have a */
0131: /* predetermined value - e.g. fields that are prompted for */
0132: /* during an add or search and are always part of the search */
0133: /* criteria during a search */
0134: private String fieldsParam = "";
0135:
0136: /**
0137: * The current dataobject used by the DBMaint subclasses.
0138: */
0139: private DataObject myDataObject = null;
0140:
0141: /**
0142: * The log4j logger for this system.
0143: */
0144: private static transient Logger log = Logger
0145: .getLogger(DynamicCmd.class);
0146:
0147: /**
0148: * A map of fields that have fixed values (ie cannot be modified by
0149: * user input)
0150: */
0151: private Map fixedFields = null;
0152:
0153: /**
0154: * Allows counting total records to be skipped so we can improve
0155: * performance on large tables.
0156: */
0157: protected boolean countTotalRecords = true;
0158:
0159: private String controllerName = null;
0160:
0161: /**
0162: * Default constructor
0163: */
0164: public DynamicCmd() {
0165:
0166: }
0167:
0168: /**
0169: * Constructor most often used
0170: *
0171: * @param code The internal name of the state
0172: * @param descrip The friendly name of the state
0173: */
0174: public DynamicCmd(String code, String descrip) {
0175: super (code, descrip);
0176: } /* DynamicCmd(String, String) */
0177:
0178: /**
0179: * @param newParam The new searching paramter String
0180: */
0181: protected void setSearchParam(String newParam) {
0182: searchParam = StringUtil.notNull(newParam);
0183: } /* setSearchParam(String) */
0184:
0185: /**
0186: * Return the "decoded" search parameter, consisting of a series
0187: * of fields and values pipe-delimited
0188: *
0189: * @return The decoded URL serach parameter
0190: * @throws ControllerException upon error
0191: */
0192: protected String getSearchParam() throws ControllerException {
0193: if (searchParam.equals("")) {
0194: searchParam = URLUTF8Encoder.decode(StringUtil
0195: .notNull(getParameter("search")));
0196: }
0197:
0198: return searchParam;
0199: } /* getSearchParam() */
0200:
0201: /**
0202: * Set a encoded 'fields' parameter,
0203: * consisting of a series of pipe-delimited fields and values
0204: *
0205: * @param newParam ??
0206: */
0207: protected void setFieldsParam(String newParam) {
0208: if (newParam != null) {
0209: fieldsParam = newParam;
0210: } else {
0211: fieldsParam = "";
0212: }
0213: } /* setFieldsParam(String) */
0214:
0215: /**
0216: * Return the "decoded" 'fields' parameter,
0217: * consisting of a series of pipe-delimited fields and values
0218: *
0219: * @return The fields Parameter as java.lang.String
0220: */
0221: protected String getFieldsParam() throws ControllerException {
0222: if (fieldsParam.equals("")) {
0223: fieldsParam = URLUTF8Encoder.decode(StringUtil
0224: .notNull(getParameter("fields")));
0225: }
0226:
0227: return fieldsParam;
0228: } /* getFieldsParam() */
0229:
0230: /**
0231: * <p>Fixed fields are a hastable of field values keyed to field names that
0232: * should not be modified. The Input of a fixed field should be rendered
0233: * read only.</p>
0234: * <p>The fields that are fixed can be defined in the 'fields' parameter
0235: * by specifying which fields should be fixed. This is useful for master/
0236: * detail relationship specifications</p>
0237: *
0238: * @return java.util.Map
0239: */
0240: protected Map getFixedFields() throws ControllerException {
0241: if (fixedFields == null) {
0242: fixedFields = new HashMap();
0243:
0244: if (!StringUtil.notNull(getFieldsParam()).equals("")) {
0245: StringTokenizer stk = new StringTokenizer(
0246: getFieldsParam(), "|");
0247: String oneFieldName = null;
0248: String oneFieldValue = null;
0249:
0250: while (stk.hasMoreTokens()) {
0251: oneFieldName = stk.nextToken();
0252:
0253: if (!stk.hasMoreTokens()) {
0254: throw new ControllerException(
0255: "Parameter 'fields' must contain "
0256: + "name/value pairs seperated with the '|' symbol");
0257: }
0258:
0259: oneFieldValue = stk.nextToken();
0260: fixedFields.put(oneFieldName, oneFieldValue);
0261: }
0262: }
0263: }
0264:
0265: return fixedFields;
0266: }
0267:
0268: /**
0269: * Remove %20 from a string replacing it with spaces
0270: *
0271: * @param orig Original string
0272: * @return String Result string with %20 replaced by spaces
0273: */
0274: protected String decode(String orig) {
0275: String newString = orig;
0276: int pos;
0277:
0278: while ((pos = newString.indexOf("%20")) > -1) {
0279: System.out.println("pos:" + pos);
0280: newString = newString.substring(0, pos) + " "
0281: + newString.substring(pos + 3);
0282: }
0283: while ((pos = newString.indexOf("%2B")) > -1) {
0284: System.out.println("pos:" + pos);
0285: newString = newString.substring(0, pos) + "+"
0286: + newString.substring(pos + 3);
0287: }
0288:
0289: return newString;
0290: } /* decode(String) */
0291:
0292: /**
0293: * All of the sub-classes of DynamicCmd extend the run method to perform their
0294: * functionality.
0295: * <p>This part of run is an important part in the it adds what is known as
0296: * the 'nav block' to the jsp pages. It also loads a dbobject into memory
0297: * corresponding to the dbobject specified by the DBMaint parameters</p>
0298: *
0299: * @param newParams The <code>ControllerRequest</code> object
0300: * @param newResponse The <code>ControllerResponse</code> object
0301: * @throws NonHandleableException upon fatal error
0302: */
0303: public void run(ControllerRequest newParams,
0304: ControllerResponse newResponse) throws ControllerException,
0305: NonHandleableException {
0306: super .run(newParams, newResponse);
0307:
0308: try {
0309: initializeDBObj();
0310: add(new Output("dbobj", URLUTF8Encoder.decode(StringUtil
0311: .notNull(getParameter("dbobj")))));
0312:
0313: if (newParams.getParameters().containsKey("nocount")) {
0314: this .countTotalRecords = false;
0315: }
0316:
0317: /* if any key values present then recover them */
0318: setFields(myDataObject, URLUTF8Encoder.decode(StringUtil
0319: .notNull(getParameter("fields"))));
0320:
0321: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0322: PersistentSession mySession = getSession();
0323: Block navBlock = (Block) mySession
0324: .getPersistentAttribute(masterObjKey);
0325:
0326: if (navBlock != null) {
0327: add(navBlock);
0328: } else {
0329: throw new ControllerException(
0330: "'detail' was specified, but no navigation block"
0331: + " was found in session");
0332: }
0333: }
0334: } catch (DBException de) {
0335: throw new ControllerException(de);
0336: }
0337: } /* run() */
0338:
0339: /**
0340: * Base class
0341: * Is this field a key in the given database object?
0342: *
0343: * @param fieldName The name of the field to check
0344: * @return boolean True if the fieldname is a key field for the
0345: * current object
0346: * @throws DBException If unable to determine if the field is a key
0347: * due to other errors
0348: */
0349: protected boolean isKeyField(String fieldName) throws DBException,
0350: ControllerException {
0351: for (Iterator e = getDataObject().getMetaData()
0352: .getKeyFieldListArray().iterator(); e.hasNext();) {
0353: if (fieldName.equalsIgnoreCase((String) e.next())) {
0354: return true;
0355: }
0356: }
0357:
0358: return false;
0359: } /* isKeyField(String) */
0360:
0361: /**
0362: * <p>Display a small table of icons giving the user the option to proceed
0363: * directly to a search, add, or list function from this form. Only
0364: * the functions this user are allowed to do are shown</p>
0365: * <p>If the record being displayed is a 'detail' record, then 'search' and
0366: * 'fields' paramters are added. This helps specify the criteria (meaning
0367: * all items here must have the same key as the master</p>
0368: * *
0369: *
0370: * @throws DBException If an error occurrs determining what functions
0371: * this user is allowed to perform
0372: */
0373: protected void showOptions() throws DBException,
0374: ControllerException {
0375: Block myTable = new Block("buttons");
0376: DataObject myDBObj = getDataObject();
0377: String myDBObjName = ((Object) myDBObj).getClass().getName();
0378: String tempParam = null;
0379: addBlock(myTable);
0380:
0381: boolean canSearch = false;
0382: boolean allowInsecure = "y".equalsIgnoreCase(Setup.getValue(
0383: this .getDBName(), "insecureDBMaint"));
0384: if (getUid() == SecuredDBObject.SYSTEM_ACCOUNT
0385: || User.getUserFromId(getUid(),
0386: this .getControllerRequest().getDataContext())
0387: .isAdmin()) {
0388: // all access ok
0389: canSearch = true;
0390: } else {
0391: if (myDBObj instanceof Securable) {
0392: try {
0393: ((Securable) myDBObj).isAllowed("S");
0394: canSearch = true;
0395: } catch (SecurityException ex) {
0396: canSearch = false;
0397: }
0398: } else {
0399: canSearch = allowInsecure;
0400: }
0401: }
0402:
0403: Integer pageLimitObj = (Integer) myDBObj
0404: .getAttribute("pageLimit");
0405: int pageLimit;
0406:
0407: if (pageLimitObj != null) {
0408: pageLimit = pageLimitObj.intValue();
0409: } else {
0410: pageLimit = 0;
0411: }
0412: if ((pageLimit > 0) && (getPageNumber() > 1) && (showPrev)) {
0413:
0414: Transition prevSet = new Transition();
0415: prevSet.setName("prevSet");
0416: prevSet
0417: .setDescription("Previous " + pageLimit
0418: + " records");
0419: prevSet.addParam("dbobj", myDBObjName);
0420: prevSet.addParam(Controller.STATE_PARAM_KEY, "List");
0421: prevSet.addParam("page", Integer
0422: .toString(getPageNumber() - 1));
0423:
0424: if (myDBObj instanceof Defineable) {
0425: prevSet.addParam("definition", ((Defineable) myDBObj)
0426: .getDefinitionName());
0427: }
0428:
0429: //Do this if then stuff to clean up the URLS for non-specified
0430: //parameters
0431: tempParam = getFieldsParam();
0432:
0433: if (tempParam.length() > 0) {
0434: prevSet.addParam("fields", URLUTF8Encoder
0435: .encode(tempParam));
0436: }
0437:
0438: tempParam = getSearchParam();
0439:
0440: if (tempParam.length() > 0) {
0441: prevSet.addParam("search", URLUTF8Encoder
0442: .encode(tempParam));
0443: }
0444:
0445: tempParam = getParameter("back");
0446:
0447: if (tempParam != null && tempParam.length() > 0) {
0448: prevSet.addParam("back", getParameter("back"));
0449: }
0450: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0451: prevSet.addParam("details", "y");
0452: }
0453: if (countTotalRecords == false) {
0454: prevSet.addParam("nocount", "");
0455: }
0456:
0457: myTable.add(prevSet);
0458: }
0459: if (canSearch) {
0460: Transition listAction = new Transition();
0461: listAction.setName("List");
0462: listAction.setLabel("List");
0463: listAction.addParam("dbobj", myDBObjName);
0464: listAction.addParam(Controller.STATE_PARAM_KEY,
0465: "SearchList");
0466: tempParam = getFieldsParam();
0467:
0468: if (tempParam.length() > 0) {
0469: listAction.addParam("fields", URLUTF8Encoder
0470: .encode(tempParam));
0471: }
0472: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0473: listAction.addParam("details", "y");
0474: tempParam = getSearchParam();
0475:
0476: if (tempParam.length() > 0) {
0477: listAction.addParam("search", URLUTF8Encoder
0478: .encode(tempParam));
0479: }
0480: }
0481: if (countTotalRecords == false) {
0482: listAction.addParam("nocount", "");
0483: }
0484:
0485: tempParam = getParameter("back");
0486:
0487: if (tempParam != null && tempParam.length() > 0) {
0488: listAction.addParam("back", tempParam);
0489: }
0490:
0491: if (myDBObj instanceof Defineable) {
0492: listAction.addParam("definition",
0493: ((Defineable) myDBObj).getDefinitionName());
0494: }
0495:
0496: listAction.setDescription("List Selected Records");
0497: myTable.add(listAction);
0498: }
0499:
0500: boolean canAdd;
0501: if (myDBObj instanceof Securable) {
0502: try {
0503: ((Securable) myDBObj).isAllowed("A");
0504: canAdd = true;
0505: } catch (SecurityException ex) {
0506: canAdd = false;
0507: }
0508: } else {
0509: canAdd = allowInsecure;
0510: }
0511:
0512: if (canAdd) {
0513: Transition addAction = new Transition();
0514: addAction.setName("Add");
0515: addAction.setLabel("Add");
0516: addAction.addParam("dbobj", myDBObjName);
0517: addAction.addParam(Controller.STATE_PARAM_KEY, "Add");
0518: if (myDBObj instanceof Defineable) {
0519: addAction.addParam("definition", ((Defineable) myDBObj)
0520: .getDefinitionName());
0521: }
0522:
0523: tempParam = getFieldsParam();
0524: if (tempParam.length() > 0) {
0525: addAction.addParam("fields", URLUTF8Encoder
0526: .encode(tempParam));
0527: }
0528:
0529: tempParam = getSearchParam();
0530:
0531: if (tempParam.length() > 0) {
0532: addAction.addParam("search", URLUTF8Encoder
0533: .encode(tempParam));
0534: }
0535: if (countTotalRecords == false) {
0536: addAction.addParam("nocount", "");
0537: }
0538: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0539: addAction.addParam("details", "y");
0540: }
0541:
0542: tempParam = getParameter("back");
0543:
0544: if (tempParam != null && tempParam.length() > 0) {
0545: addAction.addParam("back", tempParam);
0546: }
0547:
0548: addAction.setDescription("Add New Record");
0549: myTable.add(addAction);
0550: }
0551:
0552: if (canSearch) {
0553: Transition searchAction = new Transition();
0554: searchAction.setName("Search");
0555: searchAction.setLabel("Search");
0556: searchAction.addParam("dbobj", myDBObjName);
0557: searchAction.addParam(Controller.STATE_PARAM_KEY, "Search");
0558: if (myDBObj instanceof Defineable) {
0559: searchAction.addParam("definition",
0560: ((Defineable) myDBObj).getDefinitionName());
0561: }
0562:
0563: if (countTotalRecords == false) {
0564: searchAction.addParam("nocount", "");
0565: }
0566:
0567: tempParam = getFieldsParam();
0568:
0569: if (tempParam.length() > 0) {
0570: searchAction.addParam("fields", URLUTF8Encoder
0571: .encode(tempParam));
0572: }
0573:
0574: // if (StringUtil.notNull(getParameter("details")).equals("y")) {
0575: //
0576: // tempParam = getSearchParam();
0577: //
0578: // if (tempParam.length() > 0) {
0579: // searchAction.addParam("search",
0580: // URLUTF8Encoder.encode(tempParam));
0581: // }
0582: // }
0583:
0584: tempParam = getParameter("back");
0585:
0586: if (tempParam != null && tempParam.length() > 0) {
0587: searchAction.addParam("back", tempParam);
0588: }
0589: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0590: searchAction.addParam("details", "y");
0591: }
0592:
0593: searchAction.setDescription("Search for Records");
0594: myTable.add(searchAction);
0595: }
0596: if ((pageLimit > 0) && (showNext)) {
0597:
0598: /* next set */
0599: Transition nextSet = new Transition();
0600: nextSet.setName("nextSet");
0601: nextSet.addParam("dbobj", myDBObjName);
0602: if (myDBObj instanceof Defineable) {
0603: nextSet.addParam("definition", ((Defineable) myDBObj)
0604: .getDefinitionName());
0605: }
0606: nextSet.addParam(Controller.STATE_PARAM_KEY, "List");
0607: nextSet.addParam("page", Integer
0608: .toString(getPageNumber() + 1));
0609:
0610: tempParam = getFieldsParam();
0611:
0612: if (tempParam.length() > 0) {
0613: nextSet.addParam("fields", URLUTF8Encoder
0614: .encode(tempParam));
0615: }
0616:
0617: tempParam = getSearchParam();
0618:
0619: if (tempParam.length() > 0) {
0620: nextSet.addParam("search", URLUTF8Encoder
0621: .encode(tempParam));
0622: }
0623:
0624: tempParam = getParameter("back");
0625:
0626: if (tempParam != null && tempParam.length() > 0) {
0627: nextSet.addParam("back", tempParam);
0628: }
0629: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0630: nextSet.addParam("details", "y");
0631: }
0632: if (countTotalRecords == false) {
0633: nextSet.addParam("nocount", "");
0634: }
0635:
0636: nextSet.setDescription("Next " + pageLimit + " records");
0637: myTable.add(nextSet);
0638: }
0639: } /* showOptions() */
0640:
0641: /**
0642: * @param theDescrip The Page header to add as String
0643: */
0644: protected void showUserName(String theDescrip)
0645: throws ControllerException {
0646: Output unTable = new Output("untable", "");
0647: String userName = null;
0648: try {
0649: userName = User.getLoginFromId(this .getUid(), this
0650: .getDataContext());
0651: } catch (DBException ex) {
0652: log.error("Error retrieving user name", ex);
0653: throw new ControllerException("Error retrieving user name");
0654: }
0655:
0656: String db = getDBName();
0657:
0658: if (db.equals("")) {
0659: db = "default";
0660: }
0661: if (userName.equals("") || userName.equals("NONE")) {
0662: unTable
0663: .addNested(new Output("userDescrip",
0664: "Not logged in"));
0665: } else {
0666: unTable.addNested(new Output("userDescrip", userName));
0667: }
0668:
0669: unTable.addNested(new Output("pageHeader", theDescrip));
0670:
0671: String dbDescrip = "";
0672:
0673: try {
0674: dbDescrip = StringUtil.notNull(ConfigManager.getContext(db)
0675: .getDescription());
0676: } catch (ConfigurationException ce) {
0677: throw new ControllerException(ce);
0678: }
0679: if (dbDescrip.equals("")) {
0680: dbDescrip = getDBName();
0681: }
0682:
0683: unTable.addNested(new Output("dbDescrip", dbDescrip));
0684: addOutput(unTable);
0685: } /* showUserName(String) */
0686:
0687: /**
0688: * Given a class name that contains a DBObject, instantiate an
0689: * instance of the object & return it. Also associates the current user
0690: * name (from the session) with the object so
0691: * that requests to maniupluate the object can be passed through
0692: * appropriate security (If the DBObject is of type SecuredDBObject)
0693: *
0694: * @throws ControllerException if the object cannot be created or set up
0695: */
0696: protected void initializeDBObj() throws ControllerException {
0697: try {
0698: String myDBObjName = URLUTF8Encoder.decode(StringUtil
0699: .notNull(getParameter("dbobj")));
0700: if (log.isDebugEnabled()) {
0701: log
0702: .debug("Initializing dbobject '" + myDBObjName
0703: + "'");
0704: }
0705:
0706: if (myDBObjName.equals("")) {
0707: throw new DBException(
0708: "Database object name is not specified");
0709: }
0710: if (myDBObjName.equals("NONE")) {
0711: throw new DBException("Database object name is NONE");
0712: }
0713:
0714: //
0715: //Special Case Hack....
0716: //@todo check for class type and throw error if not possible.
0717: //
0718: if (com.jcorporate.expresso.core.security.User.class
0719: .getName().equals(myDBObjName)) {
0720: myDBObjName = ((Object) new User().getUserInfo())
0721: .getClass().getName();
0722: }
0723:
0724: try {
0725: myDataObject = (DataObject) ClassLocator.loadClass(
0726: myDBObjName).newInstance();
0727:
0728: if (myDataObject == null) {
0729: throw new DBException("Null object - "
0730: + "instantiate failed for database object "
0731: + myDBObjName);
0732: }
0733:
0734: if (myDataObject instanceof Defineable) {
0735: boolean initialized = false;
0736:
0737: //
0738: //Special backwards compatible case for AutoDBObjects. They can
0739: //be initialized with the 'table' paramter
0740: //
0741: if (myDBObjName
0742: .equals(com.jcorporate.expresso.core.dbobj.AutoDBObject.class
0743: .getName())) {
0744: String tableName = getParameter("table");
0745:
0746: if (tableName.equals("")) {
0747: throw new DBException(
0748: "Must specify table name for AutoDBObject");
0749: }
0750:
0751: ((com.jcorporate.expresso.core.dbobj.AutoDBObject) myDataObject)
0752: .setTargetTable(tableName);
0753: initialized = true;
0754: }
0755:
0756: //
0757: //Otherwise, we use the Defineable interface to set the definition that
0758: //we recieved and otherwise initialize the data object.
0759: //
0760: if (!initialized) {
0761: String definitionName = getParameter("definition");
0762: if (definitionName == null
0763: || definitionName.equals("")) {
0764: throw new ControllerException(
0765: "Must specify definition for Defineable data objects");
0766: }
0767:
0768: ((Defineable) myDataObject)
0769: .setDefinitionName(definitionName);
0770: }
0771: }
0772: } catch (Exception eo) {
0773: throw new DBException("Exception loading DataObject",
0774: eo);
0775: }
0776:
0777: if (myDataObject instanceof Securable) {
0778: ((Securable) myDataObject).setRequestingUid(getUid());
0779: } else {
0780: if (getUid() == SecuredDBObject.SYSTEM_ACCOUNT
0781: || User.getUserFromId(
0782: getUid(),
0783: this .getControllerRequest()
0784: .getDataContext()).isAdmin()) {
0785: // all access ok
0786: } else {
0787: String allowInsecure = Setup
0788: .getValue(
0789: this .getControllerRequest()
0790: .getDataContext(),
0791: com.jcorporate.expresso.core.ExpressoSchema.class
0792: .getName(),
0793: "insecureDBMaint");
0794: if (!(StringUtil.toBoolean(allowInsecure))) {
0795: throw new SecurityException(
0796: "Access to unsecured Objects not allowed");
0797: }
0798: }
0799: }
0800:
0801: myDataObject.setDataContext(getDBName());
0802: myDataObject.setLocale(getControllerRequest().getLocale());
0803:
0804: /* Now see if there is a "page limit" for this object */
0805: DBObjLimit dl = new DBObjLimit(
0806: SecuredDBObject.SYSTEM_ACCOUNT);
0807: dl.setDataContext(getDBName());
0808: dl.setField("DBObjectName", myDBObjName);
0809:
0810: int pageLimit = 0;
0811:
0812: try {
0813: dl.retrieve();
0814: try {
0815: pageLimit = new Integer(dl.getField("PageLimit"))
0816: .intValue();
0817: } catch (NumberFormatException ne) {
0818: throw new DBException("Can't use limit of '"
0819: + dl.getField("PageLimit") + "' for "
0820: + myDBObjName);
0821: }
0822:
0823: myDataObject
0824: .setMaxRecords((getPageNumber() * pageLimit) + 1);
0825: myDataObject.setAttribute("pageLimit", new Integer(
0826: pageLimit));
0827: if (log.isDebugEnabled()) {
0828: log.debug("Page limit for '" + myDBObjName
0829: + "' is " + pageLimit);
0830: }
0831: } catch (com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException ex) {
0832: //Set it per controller defaults then.
0833: dl
0834: .setField("DBObjectName",
0835: "com.jcorporate.expresso.services.dbobj.ControllerDefault");
0836:
0837: try {
0838: dl.retrieve();
0839: try {
0840: pageLimit = new Integer(dl
0841: .getField("PageLimit")).intValue();
0842: } catch (NumberFormatException ne) {
0843: throw new DBException("Can't use limit of '"
0844: + dl.getField("PageLimit") + "' for "
0845: + myDBObjName);
0846: }
0847:
0848: myDataObject
0849: .setMaxRecords((getPageNumber() * pageLimit) + 1);
0850: myDataObject.setAttribute("pageLimit", new Integer(
0851: pageLimit));
0852: if (log.isDebugEnabled()) {
0853: log.debug("Page limit for '" + myDBObjName
0854: + "' is " + pageLimit);
0855: }
0856: } catch (com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException ex2) {
0857: pageLimit = 0;
0858: if (log.isDebugEnabled()) {
0859: log.debug("No page limit for '" + myDBObjName
0860: + "'");
0861: }
0862: }
0863: }
0864: } catch (DBException de) {
0865: throw new ControllerException(de);
0866: }
0867: } /* getDBObj(HttpServletRequest) */
0868:
0869: /**
0870: * Return the current database object. It is an error to request this if the
0871: * current database object is not initialized
0872: *
0873: * @return the appropriate DBObject
0874: * @throws ControllerException if there is no dbobject associated with the
0875: * controller state
0876: * @deprecated Since Expresso 5.1 Use getDataObject() for compatibility with AutoDBObject,
0877: * JoinedDataObject, etc. Will be removed in Expresso 5.2
0878: */
0879: protected DBObject getCurrentDBObj() throws ControllerException {
0880: if (myDataObject == null) {
0881: throw new ControllerException("DBObject is not initialized");
0882: }
0883:
0884: return (DBObject) myDataObject;
0885: }
0886:
0887: /**
0888: * Retrieve the data object
0889: *
0890: * @return the current DataObject for this state
0891: * @throws ControllerException upon exception
0892: */
0893: protected DataObject getDataObject() throws ControllerException {
0894: if (myDataObject == null) {
0895: throw new ControllerException("DBObject is not initialized");
0896: }
0897:
0898: return myDataObject;
0899: }
0900:
0901: /**
0902: * Returns the current page number as specified in the 'page' parameter
0903: *
0904: * @return integer representing the current page number
0905: */
0906: public int getPageNumber() throws ControllerException {
0907: String page = StringUtil.notNull(getParameter("page"));
0908: int pageNumber = 1;
0909:
0910: if (!page.equals("")) {
0911: try {
0912: pageNumber = new Integer(page).intValue();
0913: } catch (NumberFormatException ne) {
0914: throw new ControllerException("Page parameter must be "
0915: + "a number if supplied at all");
0916: }
0917: } else { /* if page not blank */
0918: pageNumber = 1;
0919: }
0920:
0921: return pageNumber;
0922: }
0923:
0924: /**
0925: * Sets the fields in the production environment.
0926: *
0927: * @param myDBObj the data object to set the fields to
0928: * @param fieldPairs the piped field name|field value parameters
0929: * @throws DBException upon error
0930: */
0931: protected void setFields(DataObject myDBObj, String fieldPairs)
0932: throws DBException {
0933: if (fieldPairs.equals("")) {
0934: return;
0935: }
0936:
0937: StringTokenizer stk = new StringTokenizer(URLUTF8Encoder
0938: .decode(fieldPairs), "|");
0939: String oneFieldName = null;
0940: String oneFieldValue = null;
0941:
0942: while (stk.hasMoreTokens()) {
0943: oneFieldName = stk.nextToken();
0944:
0945: if (!stk.hasMoreTokens()) {
0946: throw new DBException(
0947: "Parameter 'fields' must contain "
0948: + "name/value pairs separated with the '|' symbol - it contained '"
0949: + URLUTF8Encoder.decode(fieldPairs)
0950: + "'");
0951: }
0952:
0953: oneFieldValue = stk.nextToken();
0954: myDBObj.set(oneFieldName, oneFieldValue);
0955: // myDBObj.setField(oneFieldName, oneFieldValue);
0956: }
0957: } /* setFields(DataObject, String) */
0958:
0959: /**
0960: * Add the fields, details, search and dbobj parameters to a given transition object
0961: *
0962: * @param t The Transition to add the appropriate parameters to.
0963: * @throws ControllerException upon error
0964: */
0965: protected void addParams(Transition t) throws ControllerException {
0966: try {
0967: DataObject myDBObj = this .getDataObject();
0968:
0969: if (myDBObj == null) {
0970: throw new DBException("Database object must be "
0971: + "initialized before calling addParams");
0972: }
0973:
0974: t
0975: .addParam("dbobj", ((Object) myDBObj).getClass()
0976: .getName());
0977:
0978: String fieldParam = URLUTF8Encoder.encode(getFieldsParam());
0979:
0980: if (!fieldParam.equals("")) {
0981: t.addParam("fields", fieldParam);
0982: }
0983:
0984: String searchParam = URLUTF8Encoder
0985: .encode(getSearchParam());
0986:
0987: if (!searchParam.equals("")) {
0988: t.addParam("search", searchParam);
0989: }
0990: if (StringUtil.notNull(getParameter("details")).equals("y")) {
0991: t.addParam("details", "y");
0992: }
0993:
0994: if (myDBObj instanceof Defineable) {
0995: t.addParam("definition", ((Defineable) myDBObj)
0996: .getDefinitionName());
0997: }
0998:
0999: } catch (DBException de) {
1000: throw new ControllerException(de);
1001: }
1002: } /* addParams(Transition) */
1003:
1004: /**
1005: * Show the edit link as often used by the List state or updateupdate state.
1006: *
1007: * @param fieldName the name of the field to render as a link
1008: * @param oneFieldValue the field's value
1009: * @param currentRecord the current data object that the field should be
1010: * retrieved from.
1011: * @param myElement the output element that is going to be decorated
1012: * @throws DBException upon data access error
1013: * @throws ControllerException upon controller error
1014: */
1015: protected synchronized void showEditLink(String fieldName,
1016: String oneFieldValue, DataObject currentRecord,
1017: Output myElement) throws DBException, ControllerException {
1018: showEditLink(fieldName, oneFieldValue, currentRecord,
1019: myElement, this .getController().getClass().getName());
1020: }
1021:
1022: /**
1023: * Create a link that is nested to myElement that is for displaying
1024: * a BLOB field.
1025: *
1026: * @param fieldName The name of the field to render the link for.
1027: * @param dbobj The DBobject that the field nests in.
1028: * @param myElement The Input object that is getting the nested Transition
1029: * @param controller The name of the controller to link to
1030: */
1031: protected void showBlobViewLink(String fieldName, DataObject dbobj,
1032: Input myElement, String controller) throws DBException,
1033: ControllerException {
1034: DataObject myDBObj = this .getDataObject();
1035: if (!myDBObj.getFieldMetaData(fieldName).isLongObjectType()) {
1036: throw new ControllerException("Field " + fieldName
1037: + " must be a long object type");
1038: }
1039:
1040: Transition t = new Transition();
1041: // Transition t = new Transition("view","View"
1042: // ,com.jcorporate.expresso.services.controller.DBMaint.class,
1043: // "ViewBlob");
1044:
1045: if (controller == null || controller.equals("")) {
1046: controller = com.jcorporate.expresso.services.controller.DBMaint.class
1047: .getName();
1048: }
1049:
1050: t.setControllerObject(controller);
1051: t.setState("ViewBlob");
1052: t.setName("view");
1053: t.setLabel("View");
1054: t.addParam("fieldName", fieldName);
1055:
1056: addParams(t);
1057: myElement.addNested(t);
1058:
1059: FastStringBuffer keyString = FastStringBuffer.getInstance();
1060:
1061: try {
1062: keyString.append("'");
1063: String oneKeyFieldName = null;
1064: boolean pipeNeeded = false;
1065:
1066: for (Iterator e = dbobj.getMetaData()
1067: .getKeyFieldListArray().iterator(); e.hasNext();) {
1068: oneKeyFieldName = (String) e.next();
1069:
1070: if (pipeNeeded) {
1071: keyString.append("|");
1072: }
1073:
1074: keyString.append(URLUTF8Encoder.encode(dbobj
1075: .getDataField(oneKeyFieldName).asString()));
1076: pipeNeeded = true;
1077: }
1078:
1079: keyString.append("'");
1080: t.addParam("key", keyString.toString());
1081:
1082: } finally {
1083: keyString.release();
1084: }
1085:
1086: }
1087:
1088: /**
1089: * Write a hyperlink command that allows edit of the current key
1090: *
1091: * @param fieldName The name of the field to have the hyperlink
1092: * @param oneFieldValue The value of the field
1093: * @param currentRecord A DBObject of the current record
1094: * @param myElement The cell in the table representing the current
1095: * field of the current record
1096: * @param controller the name of the controller to use for the edit link (may
1097: * be null)
1098: * @throws DBException If the information to build the link cannot be
1099: * retrieved from the database object
1100: */
1101: protected synchronized void showEditLink(String fieldName,
1102: String oneFieldValue, DataObject currentRecord,
1103: Output myElement, String controller) throws DBException,
1104: ControllerException {
1105: DataObject myDBObj = this .getDataObject();
1106: if (controller == null || controller.equals("")) {
1107: controller = "com.jcorporate.expresso.services.controller.DBMaint";
1108: }
1109:
1110: Transition editLink = new Transition("edit", controller);
1111: editLink.addParam(Controller.STATE_PARAM_KEY, "Update");
1112: addParams(editLink);
1113:
1114: FastStringBuffer keyString = FastStringBuffer.getInstance();
1115: FastStringBuffer fieldsString = FastStringBuffer.getInstance();
1116:
1117: try {
1118: keyString.append("'");
1119: String oneKeyFieldName = null;
1120: boolean pipeNeeded = false;
1121:
1122: for (Iterator e = currentRecord.getMetaData()
1123: .getKeyFieldListArray().iterator(); e.hasNext();) {
1124: oneKeyFieldName = (String) e.next();
1125:
1126: if (pipeNeeded) {
1127: keyString.append("|");
1128: }
1129:
1130: String keyRecord = currentRecord.getDataField(
1131: oneKeyFieldName).asString();
1132: if (keyRecord != null && keyRecord.length() > 0) {
1133: keyString.append(URLUTF8Encoder
1134: .encode(currentRecord.getDataField(
1135: oneKeyFieldName).asString()));
1136: }
1137:
1138: pipeNeeded = true;
1139: }
1140:
1141: keyString.append("'");
1142: editLink.addParam("key", keyString.toString());
1143:
1144: if (myDBObj.getFieldMetaData(fieldName).isMultiValued()) {
1145: java.util.List values = myDBObj
1146: .getValidValuesList(fieldName);
1147:
1148: if (values == null) {
1149: throw new DBException("Valid values for field "
1150: + oneKeyFieldName + " from object "
1151: + myDBObj.getMetaData().getName()
1152: + " were null");
1153: }
1154:
1155: String fieldValue = null;
1156: ValidValue oneVV = null;
1157:
1158: for (Iterator e = values.iterator(); e.hasNext();) {
1159: oneVV = (ValidValue) e.next();
1160:
1161: if (oneVV.getValue().equals(oneFieldValue)) {
1162: fieldValue = oneVV.getDescription();
1163: }
1164: }
1165: if (fieldValue == null) {
1166: fieldsString.append(oneFieldValue);
1167: myElement.setContent(oneFieldValue);
1168: } else {
1169: fieldsString.append((String) fieldValue);
1170: myElement.setContent((String) fieldValue);
1171: }
1172: } else { /* if field is multi_valued */
1173: fieldsString.append(oneFieldValue);
1174: myElement.setContent(oneFieldValue);
1175: }
1176: } catch (Throwable t) {
1177: log.error("Error building key string", t);
1178: throw new ControllerException("Error building Key String",
1179: t);
1180: } finally {
1181: keyString.release();
1182: fieldsString.release();
1183: }
1184:
1185: myElement.addNested(editLink);
1186:
1187: //editLink.addParam("fields", fieldsString.toString());
1188: } /* showEditLink(String, String, DataObject, Output) */
1189:
1190: /**
1191: * Create a key parameter for locating the DBObject. Used in generating transitions
1192: *
1193: * @param srcDBObj the DBObject to generate the key for.
1194: * @return java.lang.String, the pipe delimited URL encoded keys
1195: * @throws ControllerException upon error getting the keys for the DBObject
1196: */
1197: protected String getKeyParameter(DataObject srcDBObj)
1198: throws ControllerException {
1199: FastStringBuffer keyParam = FastStringBuffer.getInstance();
1200:
1201: try {
1202: boolean needPipe = false;
1203: String oneKeyName = null;
1204:
1205: for (Iterator keys = srcDBObj.getMetaData()
1206: .getKeyFieldListArray().iterator(); keys.hasNext();) {
1207: oneKeyName = (String) keys.next();
1208:
1209: if (needPipe) {
1210: keyParam.append("|");
1211: }
1212:
1213: needPipe = true;
1214: keyParam.append(getDataObject()
1215: .getDataField(oneKeyName).asString());
1216: }
1217:
1218: return keyParam.toString();
1219: } catch (DBException de) {
1220: throw new ControllerException(de);
1221: } finally {
1222: keyParam.release();
1223: keyParam = null;
1224: }
1225: }
1226:
1227: /**
1228: * Retrieves the DBObject as specified in the criteria key fields passed
1229: * to the DBMaint controller.
1230: *
1231: * @return DataObject requested by the request parameters
1232: * @throws DBException if unable to retrieve the object
1233: * @throws ControllerException for non-database related errors
1234: */
1235: protected DataObject retrieveMyDBObject() throws DBException,
1236: ControllerException {
1237: String allKeys = URLUTF8Encoder.decode(StringUtil
1238: .notNull(getParameter("key")));
1239:
1240: DataObject myDBObj = this .getDataObject();
1241:
1242: /* peel off the quotes if we get 'em */
1243: if (allKeys.startsWith("\'")) {
1244: allKeys = allKeys.substring(1, allKeys.length() - 1);
1245: }
1246:
1247: StringTokenizer stk = new StringTokenizer(allKeys, "|");
1248: String oneKeyFieldName = null;
1249:
1250: for (Iterator e = myDBObj.getMetaData().getKeyFieldListArray()
1251: .iterator(); e.hasNext();) {
1252: oneKeyFieldName = (String) e.next();
1253:
1254: if (!stk.hasMoreTokens()) {
1255: throw new DBException("Not enough key "
1256: + "field values for all key fields");
1257: }
1258:
1259: myDBObj.set(oneKeyFieldName, decode(stk.nextToken()));
1260: }
1261:
1262: if (!myDBObj.find()) {
1263: throw new DBException("Unable to find dataobject specified");
1264: }
1265:
1266: return myDBObj;
1267: }
1268:
1269: /**
1270: * Set the 'myDBObj' variable which is, in turn, used by the rest of the
1271: * DBMaint controller. Use this in derived classes to directly manipulate
1272: * the dbobject.
1273: *
1274: * @param myDBObj the new dataobject to set.
1275: */
1276: protected void setMyDataObject(DataObject myDBObj) {
1277: this .myDataObject = myDBObj;
1278: }
1279:
1280: /**
1281: * Retrieves an instance of my dataobject instance. Use this in derived
1282: * classes to directly access the 'myDataObject' variable.
1283: *
1284: * @return DataObject instance.
1285: */
1286: protected DataObject getMyDataObject() {
1287: return myDataObject;
1288: }
1289:
1290: /**
1291: * Retrieve the name of the controller we use. This is much faster than
1292: * getController().getClass().getName() because we precalc things once.
1293: *
1294: * @return java.lang.String
1295: * @throws ControllerException upon error
1296: */
1297: protected String getControllerName() throws ControllerException {
1298: if (controllerName == null) {
1299: //following added by pete in order to share mvc traits
1300: controllerName = (String) this .getMyDataObject()
1301: .getAttribute("mvcController");
1302:
1303: if (controllerName == null) {
1304: controllerName = this .getController().getClass()
1305: .getName();
1306: }
1307: }
1308:
1309: return controllerName;
1310: }
1311: }
1312:
1313: /* DynamicCmd */
|