Index: HandleManager.java =================================================================== --- HandleManager.java (.../dspace/trunk/dspace-api/src/main/java/org/dspace/handle) (revision 5257) +++ HandleManager.java (.../sandbox/aip-external-1_6-prototype/dspace-api/src/main/java/org/dspace/handle) (revision 5257) @@ -46,6 +46,7 @@ import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.Site; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -174,7 +175,7 @@ if (log.isDebugEnabled()) { log.debug("Created new handle for " - + Constants.typeText[dso.getType()] + " " + handleId); + + Constants.typeText[dso.getType()] + " (ID=" + dso.getID() + ") " + handleId ); } return handleId; @@ -195,10 +196,37 @@ public static String createHandle(Context context, DSpaceObject dso, String suppliedHandle) throws SQLException { - TableRow handle = DatabaseManager.create(context, "Handle"); String handleId = suppliedHandle; - handle.setColumn("handle", handleId); + //Check if the supplied handle is already in use -- cannot use the same handle twice + TableRow handle = findHandleInternal(context, handleId); + if(handle!=null && !handle.isColumnNull("resource_id")) + { + //handle found in DB table & already in use by an existing resource + throw new IllegalStateException("Attempted to create a handle which is already in use: " + suppliedHandle); + } + else if(handle!=null && !handle.isColumnNull("resource_type_id")) + { + //If there is a 'resource_type_id' (but 'resource_id' is empty), then the object using + // this handle was previously unbound (see unbindHandle() method) -- likely because object was deleted + int previousType = handle.getIntColumn("resource_type_id"); + + //Since we are restoring an object to a pre-existing handle, double check we are restoring the same *type* of object + // (e.g. we will not allow an Item to be restored to a handle previously used by a Collection) + if(previousType != dso.getType()) + { + throw new IllegalStateException("Attempted to reuse a handle previously used by a " + + Constants.typeText[previousType] + " for a new " + + Constants.typeText[dso.getType()]); + } + } + else if(handle==null) //if handle not found, create it + { + //handle not found in DB table -- create a new table entry + handle = DatabaseManager.create(context, "Handle"); + handle.setColumn("handle", handleId); + } + handle.setColumn("resource_type_id", dso.getType()); handle.setColumn("resource_id", dso.getID()); DatabaseManager.update(context, handle); @@ -206,13 +234,42 @@ if (log.isDebugEnabled()) { log.debug("Created new handle for " - + Constants.typeText[dso.getType()] + " " + handleId); + + Constants.typeText[dso.getType()] + " (ID=" + dso.getID() + ") " + handleId ); } return handleId; } /** + * Removes binding of Handle to a DSpace object, while leaving the + * Handle in the table so it doesn't get reallocated. The AIP + * implementation also needs it there for foreign key references. + * + * @param context DSpace context + * @param dso DSpaceObject whose Handle to unbind. + */ + public static void unbindHandle(Context context, DSpaceObject dso) + throws SQLException + { + TableRow row = getHandleInternal(context, dso.getType(), dso.getID()); + if (row != null) + { + //Only set the "resouce_id" column to null when unbinding a handle. + // We want to keep around the "resource_type_id" value, so that we + // can verify during a restore whether the same *type* of resource + // is reusing this handle! + row.setColumnNull("resource_id"); + DatabaseManager.update(context, row); + + if(log.isDebugEnabled()) + log.debug("Unbound Handle " + row.getStringColumn("handle") + " from object " + Constants.typeText[dso.getType()] + " id=" + dso.getID()); + + } + else + log.warn("Cannot find Handle entry to unbind for object " + Constants.typeText[dso.getType()] + " id=" + dso.getID()); + } + + /** * Return the object which handle maps to, or null. This is the object * itself, not a URL which points to it. * @@ -222,23 +279,33 @@ * The handle to resolve * @return The object which handle maps to, or null if handle is not mapped * to any object. + * @exception IllegalStateException + * If handle was found but is not bound to an object * @exception SQLException * If a database error occurs */ public static DSpaceObject resolveToObject(Context context, String handle) - throws SQLException + throws IllegalStateException, SQLException { TableRow dbhandle = findHandleInternal(context, handle); if (dbhandle == null) { + //If this is the Site-wide Handle, return Site object + if (handle.equals(Site.getSiteHandle())) + return Site.find(context, 0); + //Otherwise, return null (i.e. handle not found in DB) return null; } + // check if handle was allocated previously, but is currently not + // associated with a DSpaceObject + // (this may occur when 'unbindHandle()' is called for an obj that was removed) if ((dbhandle.isColumnNull("resource_type_id")) || (dbhandle.isColumnNull("resource_id"))) { - throw new IllegalStateException("No associated resource type"); + //if handle has been unbound, just return null (as this will result in a PageNotFound) + return null; } // What are we looking at here? @@ -300,10 +367,16 @@ public static String findHandle(Context context, DSpaceObject dso) throws SQLException { - // if (!(obj instanceof Item)) - // return null; - // Item item = (Item) obj; - return getHandleInternal(context, dso.getType(), dso.getID()); + TableRow row = getHandleInternal(context, dso.getType(), dso.getID()); + if (row == null) + { + if (dso.getType() == Constants.SITE) + return Site.getSiteHandle(); + else + return null; + } + else + return row.getStringColumn("handle"); } /** @@ -360,15 +433,13 @@ * @exception SQLException * If a database error occurs */ - private static String getHandleInternal(Context context, int type, int id) + private static TableRow getHandleInternal(Context context, int type, int id) throws SQLException { - String sql = "SELECT handle FROM Handle WHERE resource_type_id = ? " + + String sql = "SELECT * FROM Handle WHERE resource_type_id = ? " + "AND resource_id = ?"; - - TableRow row = DatabaseManager.querySingle(context, sql,type,id); - return (row == null) ? null : row.getStringColumn("handle"); + return DatabaseManager.querySingleTable(context, "Handle", sql, type, id); } /** @@ -397,7 +468,7 @@ /** * Create a new handle id. The implementation uses the PK of the RDBMS * Handle table. - * + * * @return A new handle id * @exception SQLException * If a database error occurs