/* dhcpNetworkCustom.java This file is a management class for DHCP Network objects in Ganymede. Created: 10 October 2007 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.gasharl; import java.util.Vector; import arlut.csd.JDialog.JDialogBuff; import arlut.csd.ganymede.common.GanyPermissionsException; import arlut.csd.ganymede.common.Invid; import arlut.csd.ganymede.common.NotLoggedInException; import arlut.csd.ganymede.common.ReturnVal; import arlut.csd.ganymede.common.SchemaConstants; import arlut.csd.ganymede.server.DBEditObject; import arlut.csd.ganymede.server.DBEditSet; import arlut.csd.ganymede.server.DBField; import arlut.csd.ganymede.server.DBObject; import arlut.csd.ganymede.server.DBObjectBase; import arlut.csd.ganymede.server.DBSession; import arlut.csd.ganymede.server.Ganymede; import arlut.csd.ganymede.server.GanymedeSession; import arlut.csd.ganymede.server.InvidDBField; import arlut.csd.ganymede.server.StringDBField; /*------------------------------------------------------------------------------ class dhcpNetworkCustom ------------------------------------------------------------------------------*/ public class dhcpNetworkCustom extends DBEditObject implements SchemaConstants, dhcpNetworkSchema { /** * <p>Customization Constructor</p> */ public dhcpNetworkCustom(DBObjectBase objectBase) { super(objectBase); } /** * <p>Create new object constructor</p> */ public dhcpNetworkCustom(DBObjectBase objectBase, Invid invid, DBEditSet editset) { super(objectBase, invid, editset); } /** * <p>Check-out constructor, used by DBObject.createShadow() to pull * out an object for editing.</p> */ public dhcpNetworkCustom(DBObject original, DBEditSet editset) { super(original, editset); } /** * <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.getID() == SUBNETS && "_GLOBAL_".equals(field.getOwner().getFieldValueLocal(NAME))) { return false; } return super.canSeeField(session, 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) { switch (fieldid) { case NAME: return true; case OPTIONS: if ("_GLOBAL_".equals(object.getFieldValueLocal(NAME))) { return true; } break; case SUBNETS: if (!"_GLOBAL_".equals(object.getFieldValueLocal(NAME))) { return true; } break; } return false; } /** * <p>Customization method to verify overall consistency of a * DBObject. This method is intended to be overridden in * DBEditObject subclasses, and will be called by {@link * arlut.csd.ganymede.server.DBEditObject#commitPhase1() * commitPhase1()} to verify the readiness of this object for * commit. The DBObject passed to this method will be a * DBEditObject, complete with that object's GanymedeSession * reference if this method is called during transaction commit, and * that session reference may be used by the verifying code if the * code needs to access the database.</p> * * <p>This method is for custom checks specific to custom * DBEditObject subclasses. Standard checking for missing fields * for which fieldRequired() returns true is done by {@link * arlut.csd.ganymede.server.DBEditSet#commit_checkObjectMissingFields(arlut.csd.ganymede.server.DBEditObject)} * during {@link * arlut.csd.ganymede.server.DBEditSet#commit_handlePhase1()}.</p> * * <p>To be overridden on necessity in DBEditObject subclasses.</p> * * <p><b>*PSEUDOSTATIC*</b></p> * * @return A ReturnVal indicating success or failure. May * be simply 'null' to indicate success if no feedback need * be provided. */ @Override public ReturnVal consistencyCheck(DBObject object) { if ("_GLOBAL_".equals(object.getFieldValueLocal(NAME))) { if (object.isDefined(SUBNETS)) { return Ganymede.createErrorDialog("DHCP Network Problem", "The _GLOBAL_ DHCP Network definition cannot have any subnets."); } } return null; } /** * <p>Customization method to verify whether a specific field * in object should be cloned using the basic field-clone * logic.</p> * * <p>To be overridden on necessity in DBEditObject subclasses.</p> * * <p><b>*PSEUDOSTATIC*</b></p> */ @Override public boolean canCloneField(DBSession session, DBObject object, DBField field) { return super.canCloneField(session, object, field); } /** * <p>Hook to allow the cloning of an object. If this object type * supports cloning (which should be very much customized for this * object type.. creation of the ancillary objects, which fields to * clone, etc.), this customization method will actually do the * work.</p> * * <p>This method is called on a newly created object, in order to * clone the state of origObj into it. This method does not * actually create a new object.. that is handled by {@link * arlut.csd.ganymede.server.GanymedeSession#clone_db_object(arlut.csd.ganymede.common.Invid) * clone_db_object()} before this method is called on the newly * created object.</p> * * <p>The default (DBEditObject) implementation of this method will * only clone fields for which {@link * arlut.csd.ganymede.server.DBEditObject#canCloneField(arlut.csd.ganymede.server.DBSession, * arlut.csd.ganymede.server.DBObject, * arlut.csd.ganymede.server.DBField) canCloneField()} returns true, * and which are not connected to a namespace (and thus could not * possibly be cloned, because the values are constrained to be * unique and non-duplicated).</p> * * <p>If one or more fields in the original object are unreadable by * the cloning session, we will provide a list of fields that could * not be cloned due to a lack of read permissions in a dialog in * the ReturnVal. Such a problem will not result in a failure code * being returned, however.. the clone will succeed, but an * informative dialog will be provided to the user.</p> * * <p>To be overridden on necessity in DBEditObject subclasses, but * this method's default logic will probably do what you need it to * do. If you need to make changes, try to chain your subclassed * method to this one via super.cloneFromObject().</p> * * @param session The DBSession that the new object is to be created in * @param origObj The object we are cloning * @param local If true, fields that have choice lists will not be checked against * those choice lists and read permissions for each field will not be consulted. * The canCloneField() method will still be consulted, however. * * @return A standard ReturnVal status object. May be null on success, or * else may carry a dialog with information on problems and a success flag. */ @Override public ReturnVal cloneFromObject(DBSession session, DBObject origObj, boolean local) { boolean parentCloneProblem = false; ReturnVal retVal = super.cloneFromObject(session, origObj, local); if (!ReturnVal.didSucceed(retVal)) { return retVal; } retVal = ReturnVal.merge(retVal, copyObjects(session, origObj, OPTIONS, local)); if (!ReturnVal.didSucceed(retVal)) { return retVal; } retVal = ReturnVal.merge(retVal, copyObjects(session, origObj, SUBNETS, local)); return retVal; } private ReturnVal copyObjects(DBSession session, DBObject origObject, short copyFieldID, boolean local) { if (!origObject.isDefined(copyFieldID)) { return null; } InvidDBField origField = origObject.getInvidField(copyFieldID); InvidDBField newField = getInvidField(copyFieldID); StringBuilder resultBuf = new StringBuilder(); try { for (Invid oldInvid: (Vector<Invid>) origField.getValuesLocal()) { DBObject origSubObject = session.viewDBObject(oldInvid); ReturnVal tmpVal; try { tmpVal = newField.createNewEmbedded(local); } catch (GanyPermissionsException ex) { tmpVal = Ganymede.createErrorDialog(session.getGSession(), "permissions", "permissions failure creating embedded object " + ex); } if (!ReturnVal.didSucceed(tmpVal)) { if (tmpVal.getDialog() != null) { resultBuf.append("\n\n"); resultBuf.append(tmpVal.getDialog().getText()); } continue; } DBEditObject newSubObject = session.editDBObject(tmpVal.getInvid()); tmpVal = newSubObject.cloneFromObject(session, origSubObject, local); if (!ReturnVal.didSucceed(tmpVal)) { if (tmpVal.getDialog() != null) { resultBuf.append("\n\n"); resultBuf.append(tmpVal.getDialog().getText()); } } } } catch (NotLoggedInException ex) { return Ganymede.loginError(ex); } if (resultBuf.length() != 0) { ReturnVal retVal = new ReturnVal(true, false); retVal.setDialog(new JDialogBuff("Possible Clone Problems", resultBuf.toString(), "Ok", null, "ok.gif")); } return null; } /** * <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 (field.getID() == NAME) { String currentValue = (String) field.getValueLocal(); if ("_GLOBAL_".equals(currentValue)) { return Ganymede.createErrorDialog(this.getGSession(), "Can't change name of _GLOBAL_ DHCP Network", "The _GLOBAL_ DHCP Network is required in order to provide the default configuration for DHCP. It may not be renamed or deleted."); } if ("_GLOBAL_".equals(value)) { if (isDefined(SUBNETS)) { return Ganymede.createErrorDialog(this.getGSession(), "Can't set DHCP Network name to _GLOBAL_", "The subnets field must be empty in the _GLOBAL_ DHCP network."); } return ReturnVal.success().addRescanField(getInvid(), SUBNETS); } } return null; } }