/* taskCustom.java This file is a management class for task records in Ganymede. Created: 5 February 1999 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2014 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.server; import java.rmi.RemoteException; import arlut.csd.ganymede.common.Invid; import arlut.csd.ganymede.common.NotLoggedInException; import arlut.csd.ganymede.common.QueryResult; import arlut.csd.ganymede.common.ReturnVal; import arlut.csd.ganymede.common.SchemaConstants; /*------------------------------------------------------------------------------ class taskCustom ------------------------------------------------------------------------------*/ /** * * This class customizes DBEditObject for handling fields in the Ganymede * server's task object type. * */ public class taskCustom extends DBEditObject implements SchemaConstants { static final QueryResult choiceList = new QueryResult(); static { choiceList.addRow(null, GanymedeScheduler.minutes_str, false); choiceList.addRow(null, GanymedeScheduler.hours_str, false); choiceList.addRow(null, GanymedeScheduler.days_str, false); choiceList.addRow(null, GanymedeScheduler.weeks_str, false); } /* -- */ /** * * Customization Constructor * */ public taskCustom(DBObjectBase objectBase) throws RemoteException { super(objectBase); } /** * * Create new object constructor * */ public taskCustom(DBObjectBase objectBase, Invid invid, DBEditSet editset) throws RemoteException { super(objectBase, invid, editset); } /** * * Check-out constructor, used by DBObject.createShadow() * to pull out an object for editing. * */ public taskCustom(DBObject original, DBEditSet editset) throws RemoteException { super(original, editset); } /** * <p>This method provides a hook that a DBEditObject subclass * can use to indicate whether a given field can only * choose from a choice provided by obtainChoiceList()</p> * * <p>To be overridden on necessity in DBEditObject subclasses, * particularly if you have a StringDBField that you want to force * to pick from the list of choices provided by your DBEditObject * subclass' obtainChoiceList() method.</p> */ @Override public boolean mustChoose(DBField field) { if (field.getID() == SchemaConstants.TaskPeriodUnit) { return true; } return super.mustChoose(field); } /** * <p>This method provides a hook that can be used to generate * choice lists for invid and string fields that provide * such. String and Invid DBFields will call their owner's * obtainChoiceList() method to get a list of valid choices.</p> * * <p>This method will provide a reasonable default for targetted * invid fields, filtered by the GanymedeSession's * visibilityFilterInvids list.</p> * * <p>NOTE: This method does not need to be synchronized. Making this * synchronized can lead to DBEditObject/DBSession nested monitor * deadlocks.</p> */ @Override public QueryResult obtainChoiceList(DBField field) throws NotLoggedInException { if (field.getID() == SchemaConstants.TaskPeriodUnit) { return choiceList; } return super.obtainChoiceList(field); } /** * <p>Customization method to control whether a specified field * is required to be defined at commit time for a given object.</p> * * <p>To be overridden on necessity in DBEditObject subclasses.</p> * * <p>Note that this method will not be called if the controlling * GanymedeSession's enableOversight is turned off, as in * bulk loading.</p> * * <p>Note as well that the designated label field for objects are * always required, whatever this method returns, and that this * requirement holds without regard to the GanymedeSession's * enableOversight value.</p> * * <p><b>*PSEUDOSTATIC*</b></p> */ @Override public boolean fieldRequired(DBObject object, short fieldid) { if (fieldid == SchemaConstants.TaskName || fieldid == SchemaConstants.TaskClass) { return true; } if (fieldid == SchemaConstants.TaskPeriodUnit) { return object.isSet(SchemaConstants.TaskRunPeriodically); } return false; } /** * This method provides a hook that a DBEditObject subclass * can use to indicate that a given Numeric field has a restricted * range of possibilities. */ @Override public boolean isIntLimited(DBField field) { if (field.getID() == SchemaConstants.TaskPeriodCount) { return true; } return super.isIntLimited(field); } /** * This method is used to specify the minimum acceptable value * for the specified field. */ @Override public int minInt(DBField field) { if (field.getID() == SchemaConstants.TaskPeriodCount) { return 0; } return super.minInt(field); } /** * <p>This method allows the DBEditObject to have executive approval * of any scalar set operation, and to take any special actions in * reaction to the set. When a scalar field has its value set, it * will call its owners finalizeSetValue() method, passing itself as * the <field> parameter, and passing the new value to be * approved as the <value> parameter. A Ganymede customizer * who creates custom subclasses of the DBEditObject class can * override the finalizeSetValue() method and write his own logic * to examine any change and either approve or reject the change.</p> * * <p>A custom finalizeSetValue() method will typically need to * examine the field parameter to see which field is being changed, * and then do the appropriate checking based on the value * parameter. The finalizeSetValue() method can call the normal * this.getFieldValueLocal() type calls to examine the current state * of the object, if such information is necessary to make * appropriate decisions.</p> * * <p>If finalizeSetValue() returns null or a ReturnVal object with * a positive success value, the DBField that called us is * guaranteed to proceed to make the change to its value. If this * method returns a non-success code in its ReturnVal, as with the * result of a call to Ganymede.createErrorDialog(), the DBField * that called us will not make the change, and the field will be * left unchanged. Any error dialog returned from finalizeSetValue() * will be passed to the user.</p> * * <p>The DBField that called us will take care of all standard * checks on the operation (including a call to our own * verifyNewValue() method before calling this method. Under normal * circumstances, we won't need to do anything here. * finalizeSetValue() is useful when you need to do unusually * involved checks, and for when you want a chance to trigger other * changes in response to a particular field's value being * changed.</p> * * @return A ReturnVal indicating success or failure. May * be simply 'null' to indicate success if no feedback need * be provided. */ @Override public ReturnVal finalizeSetValue(DBField field, Object value) { // if we have either of the check boxes toggled, we're going // to toggle the other. We'll also signal the client to // refresh all the fields in this object so as to show the // status change for the other check box and to hide or reveal // the fields for periodic execution if (field.getID() == SchemaConstants.TaskRunOnCommit) { boolean boolVal = ((Boolean) value).booleanValue(); ReturnVal result = new ReturnVal(true); result.setRescanAll(field.getOwner().getInvid()); if (boolVal) { setFieldValueLocal(SchemaConstants.TaskRunPeriodically, Boolean.FALSE); } return result; } if (field.getID() == SchemaConstants.TaskRunPeriodically) { boolean boolVal = ((Boolean) value).booleanValue(); ReturnVal result = new ReturnVal(true); result.setRescanAll(field.getOwner().getInvid()); if (boolVal) { setFieldValueLocal(SchemaConstants.TaskRunOnCommit, Boolean.FALSE); } return result; } return null; } /** * <p>Customization method to verify whether the user should be able to * see a specific field in a given object. Instances of * {@link arlut.csd.ganymede.server.DBField DBField} will * wind up calling up to here to let us override the normal visibility * process.</p> * * <p>Note that it is permissible for session to be null, in which case * this method will always return the default visiblity for the field * in question.</p> * * <p>If field is not from an object of the same base as this DBEditObject, * an exception will be thrown.</p> * * <p>To be overridden on necessity in DBEditObject subclasses.</p> * * <p><b>*PSEUDOSTATIC*</b></p> */ @Override public boolean canSeeField(DBSession session, DBField field) { if (field.getFieldDef().base() != this.objectBase) { throw new IllegalArgumentException("field/object mismatch"); } if (field.getOwner() instanceof DBEditObject) { DBEditObject myObj = (DBEditObject) field.getOwner(); switch (field.getID()) { case SchemaConstants.TaskPeriodUnit: case SchemaConstants.TaskPeriodCount: case SchemaConstants.TaskPeriodAnchor: return myObj.isSet(SchemaConstants.TaskRunPeriodically); } } // by default, return the field definition's visibility return field.getFieldDef().isVisible(); } /** * <p>This method is a hook for subclasses to override to * pass the phase-two commit command to external processes.</p> * * <p>For normal usage this method would not be overridden. For * cases in which change to an object would result in an external * process being initiated whose <b>success or failure would not * affect the successful commit of this DBEditObject in the * Ganymede server</b>, the process invocation should be placed here, * rather than in * {@link arlut.csd.ganymede.server.DBEditObject#commitPhase1() commitPhase1()}.</p> * * <p>commitPhase2() is generally the last method called on a * DBEditObject before it is discarded by the server in the * {@link arlut.csd.ganymede.server.DBEditSet DBEditSet} * {@link arlut.csd.ganymede.server.DBEditSet#commit(java.lang.String) commit()} method.</p> * * <p>Subclasses that override this method may wish to make this method * synchronized.</p> * * <p><b>WARNING!</b> this method is called at a time when portions * of the database are locked for the transaction's integration into * the database. You must not call methods that seek to gain a lock * on the Ganymede database. At this point, this means no composite * queries on embedded object types, where you seek an object based * on a field in an embedded object and in the object itself, using * the GanymedeSession query calls, or else you will lock the server.</p> * * <p>This method should NEVER try to edit or change any DBEditObject * in the server.. at this point in the game, the server has fixed the * transaction working set and is depending on commitPhase2() not trying * to make changes internal to the server.</p> * * <p>To be overridden on necessity in DBEditObject subclasses.</p> */ @Override public void commitPhase2() { String origName = null, taskName; /* -- */ if (original != null) { origName = (String) original.getFieldValueLocal(SchemaConstants.TaskName); } taskName = (String) getFieldValueLocal(SchemaConstants.TaskName); switch (getStatus()) { case DROPPING: return; case DELETING: Ganymede.scheduler.unregisterTask(origName); break; case EDITING: if (!origName.equals(taskName)) { // we changed our task name.. ditch the old record Ganymede.scheduler.unregisterTask(origName); // and re-register ourselves appropriately Ganymede.scheduler.registerTaskObject(this); } else { // no name change, go ahead and reschedule ourselves Ganymede.scheduler.registerTaskObject(this); } break; case CREATING: Ganymede.scheduler.registerTaskObject(this); } } }