/*
syncChannelCustom.java
This file is a management class for sync channel definitions in Ganymede.
Created: 2 February 2005
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;
import arlut.csd.ganymede.server.SyncRunner.SyncType;
import arlut.csd.Util.TranslationService;
/*------------------------------------------------------------------------------
class
syncChannelCustom
------------------------------------------------------------------------------*/
/**
* <p>This class is a {@link arlut.csd.ganymede.server.DBEditObject}
* subclass for handling fields in the Ganymede server's sync channel
* object type. It contains special logic for handling the set up and
* configuration of {@link arlut.csd.ganymede.server.SyncRunner} in
* the Ganymede server.</p>
*/
public class syncChannelCustom extends DBEditObject implements SchemaConstants {
/**
* <p>TranslationService object for handling string localization in
* the Ganymede server.</p>
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.server.syncChannelCustom");
// ---
/**
* Customization Constructor
*/
public syncChannelCustom(DBObjectBase objectBase) throws RemoteException
{
super(objectBase);
}
/**
* Create new object constructor
*/
public syncChannelCustom(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 syncChannelCustom(DBObject original, DBEditSet editset) throws RemoteException
{
super(original, editset);
}
@Override public ReturnVal initializeNewObject()
{
// we don't want to do any of this initialization during
// bulk-loading.
if (!getGSession().enableOversight)
{
return null;
}
// a newly created Sync Channel object have its type set to manual
StringDBField typeField = getStringField(SchemaConstants.SyncChannelTypeString);
typeField.setValueLocal(ts.l("global.manual"));
return null;
}
/**
* This method provides a hook that a DBEditObject subclass
* can use to indicate whether a given string field can only
* choose from a choice provided by obtainChoiceList()
*/
@Override public boolean mustChoose(DBField field)
{
if (field.getID() == SchemaConstants.SyncChannelTypeString)
{
// we want to force type choosing
return true;
}
return super.mustChoose(field);
}
/**
* 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.
*/
@Override public QueryResult obtainChoiceList(DBField field) throws NotLoggedInException
{
if (field.getID() == SchemaConstants.SyncChannelTypeString)
{
QueryResult syncTypes = new QueryResult();
syncTypes.addRow(ts.l("global.manual")); // "Manual"
syncTypes.addRow(ts.l("global.incremental")); // "Automatic Incremental"
syncTypes.addRow(ts.l("global.fullstate")); // "Automatic Full State"
return syncTypes;
}
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)
{
switch (fieldid)
{
case SchemaConstants.SyncChannelName:
case SchemaConstants.SyncChannelTypeString:
case SchemaConstants.SyncChannelTypeNum:
return true;
}
SyncType type = SyncType.get(((Integer) object.getFieldValueLocal(SchemaConstants.SyncChannelTypeNum)).intValue());
switch (fieldid)
{
case SchemaConstants.SyncChannelDirectory:
return type == SyncType.INCREMENTAL;
case SchemaConstants.SyncChannelFullStateFile:
return type == SyncType.FULLSTATE;
case SchemaConstants.SyncChannelServicer:
return type != SyncType.MANUAL;
}
// We'll allow SyncChannelFields and SyncChannelPlaintextOK to be
// false or undefined
return false;
}
/**
* <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 change the SyncChannelTypeString field, we'll need to
// hide or reveal the appropriate fields, by telling the client to
// refresh its display of this object
if (field.getID() == SchemaConstants.SyncChannelTypeString)
{
NumericDBField indexField = getNumericField(SchemaConstants.SyncChannelTypeNum);
String type = (String) value;
if (type == null)
{
if (this.isDeleting())
{
indexField.setValueLocal(Integer.valueOf(-1));
}
else
{
// "Error, couldn''t set Sync Channel type string to null when not deleting the object."
return Ganymede.createErrorDialog(ts.l("finalizeSetValue.null_token"));
}
}
else
{
if (type.equals(ts.l("global.manual")))
{
indexField.setValueLocal(SyncType.MANUAL.Val());
}
else if (type.equals(ts.l("global.incremental")))
{
indexField.setValueLocal(SyncType.INCREMENTAL.Val());
}
else if (type.equals(ts.l("global.fullstate")))
{
indexField.setValueLocal(SyncType.FULLSTATE.Val());
}
else
{
/* this shouldn't happen if mustChoose is set, but just in case.. */
// Error, couldn''t recognize Sync Channel type string "{0}".
return Ganymede.createErrorDialog(ts.l("finalizeSetValue.unrecognized_type_token", type));
}
}
ReturnVal result = new ReturnVal(true);
result.setRescanAll(field.getOwner().getInvid());
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");
}
try
{
DBObject myObj = field.getOwner();
int typeVal = -1;
// this may throw a NullPointerException if there is no
// numeric type index set
typeVal = ((Integer) myObj.getFieldValueLocal(SchemaConstants.SyncChannelTypeNum)).intValue();
SyncType type = SyncType.get(typeVal);
switch (field.getID())
{
case SchemaConstants.SyncChannelClassName:
case SchemaConstants.SyncChannelDirectory:
return type == SyncType.INCREMENTAL;
case SchemaConstants.SyncChannelFullStateFile:
return type == SyncType.FULLSTATE;
case SchemaConstants.SyncChannelServicer:
return type != SyncType.MANUAL;
}
}
catch (Throwable ex)
{
Ganymede.logError(ex);
}
// never show the hidden index field
if (field.getID() == SchemaConstants.SyncChannelTypeNum)
{
return false;
}
// by default, return the field definition's visibility
return field.getFieldDef().isVisible();
}
/**
* This method provides a hook that a DBEditObject subclass
* can use to indicate that a given
* {@link arlut.csd.ganymede.server.NumericDBField NumericDBField}
* has a restricted range of possibilities.
*/
@Override public boolean isIntLimited(DBField field)
{
if (field.getID() == SchemaConstants.SyncChannelTypeNum)
{
return true;
}
return super.isIntLimited(field);
}
/**
* This method is used to specify the minimum acceptable value
* for the specified
* {@link arlut.csd.ganymede.server.NumericDBField NumericDBField}.
*/
@Override public int minInt(DBField field)
{
if (field.getID() == SchemaConstants.SyncChannelTypeNum)
{
return 0;
}
return super.minInt(field);
}
/**
* This method is used to specify the maximum acceptable value
* for the specified
* {@link arlut.csd.ganymede.server.NumericDBField NumericDBField}.
*/
@Override public int maxInt(DBField field)
{
if (field.getID() == SchemaConstants.SyncChannelTypeNum)
{
return 2;
}
return super.maxInt(field);
}
/**
* <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;
/* -- */
if (original != null)
{
origName = (String) original.getFieldValueLocal(SchemaConstants.SyncChannelName);
}
switch (getStatus())
{
case DROPPING:
return;
case DELETING:
Ganymede.unregisterSyncChannel(origName);
break;
case EDITING:
Ganymede.unregisterSyncChannel(origName);
Ganymede.registerSyncChannel(new SyncRunner(this));
break;
case CREATING:
Ganymede.registerSyncChannel(new SyncRunner(this));
}
}
}