import java.io.*; import java.util.*; import java.net.*; /** * * Persistent Object Class * (derived from BackendUtil.java) * * This is a class which other objects that need network * access extends. * There are several abstract methods that the application developer * must overload in order to use this class. * * @version 1.2 Oct 1996 * @author Francis Chan */ abstract public class PersistentObject{ public static final int LOAD = 1; public static final int SAVE = 2; public static final int QUERY = 3; public static final int IF_EXISTS = 4; // modes for the network operations // may include more in the future public static final int PUT = 2; public static final int GETSTR = 1; public static final int MODIFY = 0; public static final int GETID = 3; public static final int GETATT = 4; public static final int DELETE = 5; public static final int CONNECT = 6; public static final int CONNECTNEW = 7; public static final int DISCONNECT = 8; public static final int GETATTINFO = 9; // types for type-casting public static final int STRING = 1; public static final int INT = 2; public static final int FLOAT = 3; public static final int DOUBLE = 4; public static final int BOOLEAN = 5; public static final int CHAR = 6; public static final int OBJECT = 7; public static final int VECTOR = 8; public static final int PARENTS = 9; public static final int CHILDREN = 10; public static final int UNIQUE_ID = 11; public static final int ARRAY = 12; public static final int CONTAINERS = 9; // matches that of PersistentObject's public static final int CONTENTS = 10; // PARENTS and CHILDREN // indicators for the declaration of functions with same name // by different input type // initial value does not matter protected static final String STRING_INDICATOR = "str"; protected static final int INT_INDICATOR = 0; protected static final float FLOAT_INDICATOR = 1; protected static final double DOUBLE_INDICATOR = 2; protected static final boolean BOOLEAN_INDICATOR = true; protected static final char CHAR_INDICATOR = 'c'; protected static final Object OBJECT_INDICATOR = null; protected double[] ARRAY_INDICATOR; // responses from backend DB server public static final int OK_VAL = 200; public static final int DUPLICATE_VAL = 300; public static final int BAD_REQUEST_VAL = 400; public static final int NOT_FOUND_VAL = 404; public static final int NOT_FOUND_A_VAL = 405; public static final int NOT_FOUND_B_VAL = 406; public static final int EMPTY_REQUEST_VAL = 407; public static final int NO_ATTACH_VAL = 408; public static final int SELF_ATTACH_VAL = 409; public static final int PERM_ERROR_VAL = 410; public static final int CONT_ERROR_VAL = 411; public static final int INTERNAL_ERROR_VAL = 500; public static final int ATTACH_NOT_FOUND_VAL = 501; // modes for parsing server response // there should be more (maybe one for each event, // (e.g. update, delete, etc..) public static final int LOAD_OK = 0; public static final int SAVE_OK = 1; // save responses public static final int OK = 0; public static final int ERROR = 1; public static final int DO_NOT_PROCEED = 4; // new private static int OPEN_BRACKET = '<'; private static int CLOSE_BRACKET = '>'; private Vector objFieldVector; private Vector inputVector = new Vector(); private String paramStr = new String(); protected Vector loadVector = new Vector(); // for the type-casting of arguments from Input Stream private int numField = 0; // used for the allocation of the array /** * The number of object fields the object has */ protected int numObjField = 0; /** * The array whose contents indicate the type of the field in the * corresponding field position of the object */ protected int[] fieldTypeArray; // internal data structures private OutputStream outStream = null; private String outStreamString = new String(); private String server = "yoyodyne.eecs.berkeley.edu"; private Socket netSocket; /** * Port number for the data server */ public int port; private boolean netObjResponse = false; // flag to validate useful message // have been received for further processing /** * The class name of the object which inherits the Persistent Object * class. */ public String objectClassName; // for db backend identification purposes /** * Unique identifier returned by the data server after a Save() */ protected int dbId; /** * The number of parents (objects that it attaches to) that the object has */ protected int numParents; /** * The number of children (objects that attaches to it) that the object has */ protected int numChildren; /** * Indicates whether the object's containers are in memory */ public boolean containersInMem = false; /** * Indicates whether the object's contents are in memory */ public boolean contentsInMem = false; /** * The name of the current class returned during parsing */ protected String currentRelativeClass; /** * The Id of the object that is being loaded from the data server */ protected int attachObjId; /** * Variable indicating whether the object has been saved before */ public boolean isPersistent = false; /** * User Id of the Persistent Object (for network transactions) */ protected String userId; // for testing purposes /** * Variable indicating whether a network operation has been successful */ protected boolean succeedTransmit = false; /** * Variable indicating whether a parse (network operation) * has been successful */ protected boolean succeedParse = false; /** * Flag to print out debugging statements */ public boolean debugFlag = false; //------------------------------------------- // important variable for testing with and without the network // protected boolean testNoNet = false; // with network // protected boolean testNoNet = true; // NO network // //------------------------------------------ /** * Constructor of PersistentObject * Should never be called explicitly */ public PersistentObject(){ } /** * * Method that opens a socket connection to the designated server * which is set to be yoyodyne.eecs.berkeley.edu, * port 701x right now. * Returns true if connection is completed, * false if not. * * Server and port number should/could be passed in in the future */ private boolean OpenConnection() { // for testing locally without the networt stuff if (testNoNet == true) return true; // DEBUG("connecting to port: " + port + " ..."); try { netSocket = new Socket(server, port); DEBUG("Opened Port!!"); return true; } catch (Exception e) { System.out.println("Port can't be opened"); e.printStackTrace(); return false; } } private void CloseSocket() { if (testNoNet == false) { try { netSocket.close(); } catch (Exception e) { System.out.println("Socket can't be closed!"); } } } /** * * Load (from Persistent Storage over the network). * * Needs to be overloaded by the object developer. * Return OCT.OK if Load is successful, OCT.ERROR otherwise. * * @param strId String type of id * @param intId Integer type of id */ abstract public boolean Load(String strId); abstract public boolean Load(int intId); /** * Sets up the object's fields into internal data structure. * * Needs to be overloaded by the object developer */ abstract protected void SetObjFields(); /** * * Network Object Setup * method that needs to be run before the object can operate * as a persistent object over the network * * @param port Port number of the server * @param numFields Number of fields the object has * @param objectClassName Name of the class */ protected void NetObjSetup(int port, int numFields, String objectClassName) { // number of fields must be pre-determined by object developer SetNumField(numFields); SetPort(port); SetObjClassName(objectClassName); SetObjFields(); } /** * * Initializes fieldTypeArray. * Set the number of fields and the size of the * field_type_array and object_field_vector of the object. * * Needs to be done by the object developer * * @param fieldsSize Number of fields of the object */ protected void SetNumField(int fieldsSize) { this.numField = fieldsSize; fieldTypeArray = new int[fieldsSize]; objFieldVector = new Vector(fieldsSize); } /** * Sets the number of Object fields in the object * For generation of contents/children purposes */ protected void SetNumObjField(int numObjs) { this.numObjField = numObjs; } /** * * Sets the type of each field. * This is a method the Object designer has to overload. * * @param nthField The position of the field * @param type The type of the field */ protected void SetType(int nthField, int type) { fieldTypeArray[nthField] = type; } /** * * Sets the port number for network transaction. * * Needs to be done by the object developer. * @param port The port number */ protected void SetPort(int port) { this.port = port; } /** * * Sets the Object Class Name for network transaction. * * Needs to be done by the object developer. * * @param name Name of the Class */ protected void SetObjClassName(String name) { this.objectClassName = name; } /** * * Sets the fields in the Vector holding the fields of the * object * * Non-object types are stored in object that matches mostly closely * its type. * * @param nthField The position of the field * @param Second_Argument Different data types may be used * @param type The type of the field */ protected void SetField(int nthField, String stringField, int type) { objFieldVector.insertElementAt(stringField, nthField); SetType(nthField, PersistentObject.STRING); } protected void SetField(int nthField, int intField, int type) { Integer intObj = new Integer(intField); objFieldVector.insertElementAt(intObj, nthField); if (type != UNIQUE_ID) { SetType(nthField, PersistentObject.INT); } else { SetType(nthField, PersistentObject.UNIQUE_ID); } } protected void SetField(int nthField, double doubleField, int type) { /* LocalHelperObject obj = new LocalHelperObject(); obj.SetDouble(doubleField); */ Double doubleObj = new Double(doubleField); objFieldVector.insertElementAt(doubleObj, nthField); SetType(nthField, PersistentObject.DOUBLE); } protected void SetField(int nthField, char charField, int type) { String charString = new String(); charString = charString.valueOf(charField); objFieldVector.insertElementAt(charString, nthField); SetType(nthField, PersistentObject.CHAR); } protected void SetField(int nthField, boolean boolField, int type) { if (boolField == true) { objFieldVector.insertElementAt("T", nthField); } else objFieldVector.insertElementAt("F", nthField); SetType(nthField, PersistentObject.BOOLEAN); } protected void SetField(int nthField, float floatField, int type) { Float floatObj = new Float(floatField); objFieldVector.insertElementAt(floatObj, nthField); SetType(nthField, PersistentObject.FLOAT); } protected void SetField(int nthField, Vector vectorField, int type) { objFieldVector.insertElementAt(vectorField, nthField); // SetType(nthField, PersistentObject.VECTOR); SetType(nthField, type); } protected void SetField(int nthField, Object objField, int type) { objFieldVector.insertElementAt(objField, nthField); SetType(nthField, type); } protected void SetField(int nthField, double[] arrayField, int type) { // need some thought and work!!... objFieldVector.insertElementAt(arrayField, nthField); SetType(nthField, type); } /** * The actual method call thats being made when a Load() is called. * * @param objectClassName (String) Name of the class * @param strId String type of Id of the object * @param intId Integer type of Id of the object * */ protected boolean LoadObject(String objectClassName, String strId, int intId) { // dummy vector to pass to PrepareStream and return if something's wrong Vector vector = new Vector(); int loadType; if (OpenConnection() == false) { DEBUG("Can't open connection, abort, I repeat, ABORT NOW!"); return false; } // at this point, connected is true or OpenConnection was successful if (strId != null) { outStreamString = PrepareString(objectClassName, vector, strId, intId, GETSTR); loadType = GETSTR; } else { outStreamString = PrepareCommandString(GETID, userId, intId, 0, 0); loadType = GETID; } SendString(outStreamString); loadVector = ParseString(loadType); if (succeedTransmit != true) return false; PostLoadPolicy(this); SetPersistent(true); return true; } /** * Loads and instantiates a "copy" of the (non-OctCell) object from * the data back-end. */ protected PersistentObject MakeInstance() { Load(dbId); contentsInMem = false; containersInMem = false; // isPersistent flag is set to false so the object can be saved // and not "modified" SetPersistent(false); InstancePolicy(this); return this; } /** * Policy that must be followed after instantiating an object */ abstract public void InstancePolicy(PersistentObject pObj); /** * * Save (Object) * * Encapsulation of the actual call to SaveObject * (returns either OK, ERROR or the Object's UniqueID) * */ public int Save() { DEBUG("PerObj: Saving... " + this.objectClassName); return SaveObject(this.objectClassName); } /** * * Save Object * * returns OK if save is completed * ERROR if not * * @param objectClassName Name of the class */ protected int SaveObject(String objectClassName) { // immediately don't save if SavePolicy indicates the // object has not been modified Vector objVector; if (SavePolicy(this) == OK) { DEBUG(objectClassName + " SavePolicy passed! "); if (OpenConnection() == false) { DEBUG("Can't open connection, abort, I repeat, ABORT NOW!"); return ERROR; } // clear obj field vector for new fields objFieldVector = new Vector(); SetObjFields(); if (isPersistent == false) { outStreamString = PrepareString(objectClassName, objFieldVector, null, 0, PUT); } else { outStreamString = PrepareString(objectClassName, objFieldVector, null, dbId, MODIFY); } SendString(outStreamString); // parse and verify the OK message if (isPersistent == false) { objVector = ParseString(PUT); } else { objVector = ParseString(MODIFY); } if (succeedTransmit == false) { // some went wrong should return System.out.println("Saving of " + objectClassName + " cannot be completed"); return OCT.ERROR; } // dbId should be set // Saving Vector fields // Objects should be somewhat similar; // traverse all fields and look for vector fields DEBUG("DbID after save: " + this.dbId); for (int i=0; i 1!"); return inputVector; } inputVector.addElement(strToke.sval); DEBUG("Para " + (i-2) + " is " + strToke.sval); break; /* if (token == CLOSE_BRACKET) System.out.println("4th Para > "); */ // need to deal with strings that contain // "whitespace" case ARRAY: if (strToke.ttype != strToke.TT_NUMBER) { System.out.println("Array size not num!"); return inputVector; } else { int numEle = (int) strToke.nval; double[] dArray = new double[numEle+1]; DEBUG("Size of Array: " + numEle); dArray[0] = (double) numEle; for (int j=1; j<=numEle; j++) { token = strToke.nextToken(); if (strToke.ttype != strToke.TT_NUMBER) { System.out.println("Array element not num!"); return inputVector; } else { dArray[j] = (double) strToke.nval; DEBUG("Array element no. " + j + " is " + dArray[j]); } } inputVector.addElement(dArray); break; } case OBJECT: // needs to get and parse string // find out what the object type is and look it up // correspondingly // need to add a dummy object so that ordering // in the vector is preserved strToke.pushBack(); // push token back since objects are handled // in a different way String dummyStr = new String("dummy String"); inputVector.addElement(dummyStr); break; case UNIQUE_ID: if (strToke.ttype != strToke.TT_NUMBER) { System.out.println("ID field not num!"); return inputVector; } else { dbId = (int) strToke.nval; DEBUG(objectClassName + "'s Id value is: " + dbId); break; } case PARENTS: if (strToke.ttype != strToke.TT_NUMBER) { System.out.println("Parent field not num!"); return inputVector; } else { numParents = (int) strToke.nval; DEBUG(objectClassName + " has " + numParents + " parents"); break; } case CHILDREN: if (strToke.ttype != strToke.TT_NUMBER) { System.out.println("Children field not num!"); return inputVector; } else { numChildren = (int) strToke.nval; DEBUG(objectClassName + " has " + numChildren + " children"); break; } default: break; } } } } catch (Exception e) { System.out.println("Can't Tokenize Stream"); e.printStackTrace(); } } } catch (Exception e) { System.out.println("Input file/stream error!"); } DEBUG("Message code: " + status + " length: " + length); succeedTransmit = true; return inputVector; } /** * * Get the corresponding contents from an internal vector * that parsed and stored the values of a network Load * * @param nthField Position of the field * @param Indictor Different types according to the type expected * */ protected String GetField(int nthField, String indicator) { return (String) loadVector.elementAt(nthField); } protected int GetField(int nthField, int indicator) { return ((Integer) loadVector.elementAt(nthField)).intValue(); } protected double GetField(int nthField, double indicator) { return ((Double) loadVector.elementAt(nthField)).doubleValue(); } protected boolean GetField(int nthField, boolean indicator) { if (((String) loadVector.elementAt(nthField)).compareTo("T") == 0) return true; return false; } protected char GetField(int nthField, char indicator) { return ((String) loadVector.elementAt(nthField)).charAt(0); } protected float GetField(int nthField, float indicator) { return ((Float) loadVector.elementAt(nthField)).floatValue(); } abstract protected Object GetField(int nthField, Object indicator, int nthObject); abstract protected int GetObjFields(); protected double[] GetField(int nthField, double[] indicator) { return (double[]) loadVector.elementAt(nthField); } /** * * Sets the isPersistent flag which indicates that this object * has persistence (has a copy at the data backend) * * Future operations will then make use of the object's unique id. * */ protected void SetPersistent(boolean flag) { this.isPersistent = flag; } /** * * Debug * Prints debugging statements if the debug flag is set to true * * @param debugStmt The debugging statement supplied */ protected void DEBUG(String debugStmt){ if (debugFlag == true) { System.out.println("PersistentObject: " + debugStmt); } } }