Merging in DSpace Event Mechanism Branch

git-svn-id: http://scm.dspace.org/svn/repo/trunk@2074 9c30dcfa-912a-0410-8fc2-9e0234be79fd
This commit is contained in:
Mark Diggory
2007-07-19 19:40:11 +00:00
parent e8b21b6760
commit b835f46d7d
34 changed files with 3201 additions and 171 deletions

View File

@@ -258,6 +258,7 @@ public class ChecksumChecker
checker.setDispatcher(dispatcher); checker.setDispatcher(dispatcher);
checker.setCollector(logger); checker.setCollector(logger);
checker.process(); checker.process();
System.exit(0);
} }
/** /**

View File

@@ -170,6 +170,7 @@ public class ItemImport
String mapfile = null; String mapfile = null;
String eperson = null; // db ID or email String eperson = null; // db ID or email
String[] collections = null; // db ID or handles String[] collections = null; // db ID or handles
int status = 0;
if (line.hasOption('h')) if (line.hasOption('h'))
{ {
@@ -439,6 +440,7 @@ public class ItemImport
c.abort(); c.abort();
e.printStackTrace(); e.printStackTrace();
System.out.println(e); System.out.println(e);
status = 1;
} }
if (mapOut != null) if (mapOut != null)
@@ -450,6 +452,7 @@ public class ItemImport
{ {
System.out.println("***End of Test Run***"); System.out.println("***End of Test Run***");
} }
System.exit(status);
} }
private void addItems(Context c, Collection[] mycollections, private void addItems(Context c, Collection[] mycollections,

View File

@@ -101,6 +101,8 @@ public class MediaFilterManager
// create an options object and populate it // create an options object and populate it
CommandLineParser parser = new PosixParser(); CommandLineParser parser = new PosixParser();
int status = 0;
Options options = new Options(); Options options = new Options();
options.addOption("v", "verbose", false, options.addOption("v", "verbose", false,
@@ -217,6 +219,10 @@ public class MediaFilterManager
c.complete(); c.complete();
c = null; c = null;
} }
catch (Exception e)
{
status = 1;
}
finally finally
{ {
if (c != null) if (c != null)
@@ -224,6 +230,7 @@ public class MediaFilterManager
c.abort(); c.abort();
} }
} }
System.exit(status);
} }
public static void applyFiltersAllItems(Context c) throws Exception public static void applyFiltersAllItems(Context c) throws Exception

View File

@@ -215,6 +215,7 @@ public class METSExport
} }
context.abort(); context.abort();
System.exit(0);
} }
/** /**

View File

@@ -138,6 +138,7 @@ public class FixDefaultPolicies
} }
c.complete(); c.complete();
System.exit(0);
} }
/** /**

View File

@@ -99,6 +99,7 @@ public class PolicySet
groupID, isReplace, false); groupID, isReplace, false);
c.complete(); c.complete();
System.exit(0);
} }
/** /**

View File

@@ -0,0 +1,162 @@
/*
* BrowseConsumer.java
*
* Version: $Revision: 1.4 $
*
* Date: $Date: 2006/04/10 04:11:09 $
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.browse;
import org.apache.log4j.Logger;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import org.dspace.browse.Browse;
import org.dspace.content.Item;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.core.Constants;
import org.dspace.core.LogManager;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import org.dspace.event.EventManager;
/**
* Class for updating browse system from content events.
* Prototype: only Item events recognized.
*
* XXX FIXME NOTE: The Browse Consumer is INCOMPLETE because the
* deletion of an Item CANNOT be implemented as an event consumer:
* When an Item is deleted, the browse tables must be updated
* immediately, within the same transaction, to maintain referential
* consistency. It cannot be handled in an Event consumer since by
* definition that runs after the transaction is committed.
* Perhaps this can be addressed if the Browse system is replaced.
*
* To handle create/modify events: accumulate Sets of Items to be added
* and updated out of the event stream. Process them in endEvents()
* filter out update requests for Items that were just created.
*
* Recommended filter: Item+Create|Modify|Modify_Metadata:Collection+Add|Remove
*
* @version $Revision: 1.1 $
*/
public class BrowseConsumer implements Consumer
{
/** log4j logger */
private static Logger log = Logger.getLogger(BrowseConsumer.class);
// items to be added to browse index
private Set toAdd = null;
// items to be updated in browse index
private Set toUpdate = null;
public void initialize()
throws Exception
{
toAdd = new HashSet();
toUpdate = new HashSet();
}
public void consume(Context ctx, Event event)
throws Exception
{
DSpaceObject subj = event.getSubject(ctx);
int et = event.getEventType();
// If an Item is added or modified..
if (subj != null && subj.getType() == Constants.ITEM)
{
if (et == Event.CREATE)
toAdd.add(subj);
else
toUpdate.add(subj);
// track ADD and REMOVE from collections, that changes browse index.
} else if (subj != null && subj.getType() == Constants.COLLECTION &&
event.getObjectType() == Constants.ITEM &&
(et == Event.ADD || et == Event.REMOVE))
{
DSpaceObject obj = event.getObject(ctx);
if (obj != null)
toUpdate.add(obj);
}
else if (subj != null)
log.warn("consume() got unrecognized event: "+event.toString());
}
public void end(Context ctx)
throws Exception
{
for (Iterator ai = toAdd.iterator(); ai.hasNext();)
{
Item i = (Item)ai.next();
Browse.itemAdded(ctx, i);
toUpdate.remove(i);
if (log.isDebugEnabled())
log.debug("Added browse indices for Item id="+String.valueOf(i.getID())+", hdl="+i.getHandle());
}
// don't update an item we've just added.
for (Iterator ui = toUpdate.iterator(); ui.hasNext();)
{
Item i = (Item)ui.next();
Browse.itemChanged(ctx, i);
if (log.isDebugEnabled())
log.debug("Updated browse indices for Item id="+String.valueOf(i.getID())+", hdl="+i.getHandle());
}
// NOTE: Removed items are necessarily handled inline (ugh).
// browse updates wrote to the DB, so we have to commit.
ctx.getDBConnection().commit();
// clean out toAdd & toUpdate
toAdd.clear();
toUpdate.clear();
}
public void finish(Context ctx) {
toAdd = toUpdate = null;
return;
}
}

View File

@@ -65,6 +65,7 @@ public class InitializeBrowse
public static void main(String[] argv) public static void main(String[] argv)
{ {
Context context = null; Context context = null;
int status = 0;
try try
{ {
@@ -78,6 +79,7 @@ public class InitializeBrowse
} }
catch (SQLException sqle) catch (SQLException sqle)
{ {
status = 1;
if (context != null) if (context != null)
{ {
context.abort(); context.abort();
@@ -86,5 +88,9 @@ public class InitializeBrowse
System.err.println("Error: Browse index NOT created"); System.err.println("Error: Browse index NOT created");
sqle.printStackTrace(); sqle.printStackTrace();
} }
finally
{
System.exit(status);
}
} }
} }

View File

@@ -0,0 +1,104 @@
/*
* CheckerConsumer.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.checker;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
/**
* Class for removing Checker data for a Bitstreams based on deletion events.
*
* @version $Revision$
*/
public class CheckerConsumer implements Consumer
{
/** log4j logger */
private static Logger log = Logger.getLogger(CheckerConsumer.class);
private BitstreamInfoDAO bitstreamInfoDAO = new BitstreamInfoDAO();
/**
* Initialize - allocate any resources required to operate.
* Called at the start of ANY sequence of event consume() calls.
*/
public void initialize() throws Exception
{
// no-op
}
/**
* Consume an event
*
* @param ctx the execution context object
*
* @param event the content event
*/
public void consume(Context ctx, Event event) throws Exception
{
if (event.getEventType() == Event.DELETE)
{
log.debug("Attempting to remove Checker Info");
bitstreamInfoDAO.deleteBitstreamInfoWithHistory(event.getSubjectID());
log.debug("Completed removing Checker Info");
}
}
/**
* Signal that there are no more events queued in this
* event stream.
*/
public void end(Context ctx) throws Exception
{
// no-op
}
/**
* Finish - free any allocated resources.
* Called when consumer is being released
*/
public void finish(Context ctx) throws Exception
{
// no-op
}
}

View File

@@ -52,6 +52,7 @@ import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.event.Event;
import org.dspace.storage.bitstore.BitstreamStorageManager; import org.dspace.storage.bitstore.BitstreamStorageManager;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
@@ -81,6 +82,12 @@ public class Bitstream extends DSpaceObject
/** The bitstream format corresponding to this bitstream */ /** The bitstream format corresponding to this bitstream */
private BitstreamFormat bitstreamFormat; private BitstreamFormat bitstreamFormat;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Private constructor for creating a Bitstream object based on the contents * Private constructor for creating a Bitstream object based on the contents
* of a DB table row. * of a DB table row.
@@ -114,6 +121,9 @@ public class Bitstream extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("bitstream_id")); context.cache(this, row.getIntColumn("bitstream_id"));
modified = modifiedMetadata = false;
clearDetails();
} }
/** /**
@@ -190,6 +200,8 @@ public class Bitstream extends DSpaceObject
Bitstream bitstream = find(context, bitstreamID); Bitstream bitstream = find(context, bitstreamID);
bitstream.setFormat(null); bitstream.setFormat(null);
context.addEvent(new Event(Event.CREATE, Constants.BITSTREAM, bitstreamID, null));
return bitstream; return bitstream;
} }
@@ -223,6 +235,8 @@ public class Bitstream extends DSpaceObject
Bitstream bitstream = find(context, bitstreamID); Bitstream bitstream = find(context, bitstreamID);
bitstream.setFormat(null); bitstream.setFormat(null);
context.addEvent(new Event(Event.CREATE, Constants.BITSTREAM, bitstreamID, "REGISTER"));
return bitstream; return bitstream;
} }
@@ -261,6 +275,8 @@ public class Bitstream extends DSpaceObject
public void setSequenceID(int sid) public void setSequenceID(int sid)
{ {
bRow.setColumn("sequence_id", sid); bRow.setColumn("sequence_id", sid);
modifiedMetadata = true;
addDetails("SequenceID");
} }
/** /**
@@ -283,6 +299,8 @@ public class Bitstream extends DSpaceObject
public void setName(String n) public void setName(String n)
{ {
bRow.setColumn("name", n); bRow.setColumn("name", n);
modifiedMetadata = true;
addDetails("Name");
} }
/** /**
@@ -306,6 +324,8 @@ public class Bitstream extends DSpaceObject
public void setSource(String n) public void setSource(String n)
{ {
bRow.setColumn("source", n); bRow.setColumn("source", n);
modifiedMetadata = true;
addDetails("Source");
} }
/** /**
@@ -328,6 +348,8 @@ public class Bitstream extends DSpaceObject
public void setDescription(String n) public void setDescription(String n)
{ {
bRow.setColumn("description", n); bRow.setColumn("description", n);
modifiedMetadata = true;
addDetails("Description");
} }
/** /**
@@ -374,6 +396,8 @@ public class Bitstream extends DSpaceObject
// but we need to find the unknown format! // but we need to find the unknown format!
setFormat(null); setFormat(null);
bRow.setColumn("user_format_description", desc); bRow.setColumn("user_format_description", desc);
modifiedMetadata = true;
addDetails("UserFormatDescription");
} }
/** /**
@@ -451,6 +475,7 @@ public class Bitstream extends DSpaceObject
// Update the ID in the table row // Update the ID in the table row
bRow.setColumn("bitstream_format_id", bitstreamFormat.getID()); bRow.setColumn("bitstream_format_id", bitstreamFormat.getID());
modified = true;
} }
/** /**
@@ -468,6 +493,18 @@ public class Bitstream extends DSpaceObject
log.info(LogManager.getHeader(bContext, "update_bitstream", log.info(LogManager.getHeader(bContext, "update_bitstream",
"bitstream_id=" + getID())); "bitstream_id=" + getID()));
if (modified)
{
bContext.addEvent(new Event(Event.MODIFY, Constants.BITSTREAM, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
bContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.BITSTREAM, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
DatabaseManager.update(bContext, bRow); DatabaseManager.update(bContext, bRow);
} }
@@ -490,6 +527,8 @@ public class Bitstream extends DSpaceObject
log.info(LogManager.getHeader(bContext, "delete_bitstream", log.info(LogManager.getHeader(bContext, "delete_bitstream",
"bitstream_id=" + getID())); "bitstream_id=" + getID()));
bContext.addEvent(new Event(Event.DELETE, Constants.BITSTREAM, getID(), String.valueOf(getSequenceID())));
// Remove from cache // Remove from cache
bContext.removeCached(this, getID()); bContext.removeCached(this, getID());
@@ -541,7 +580,7 @@ public class Bitstream extends DSpaceObject
bRow.getIntColumn("bitstream_id")); bRow.getIntColumn("bitstream_id"));
// Build a list of Bundle objects // Build a list of Bundle objects
List bundles = new ArrayList(); List<Bundle> bundles = new ArrayList<Bundle>();
while (tri.hasNext()) while (tri.hasNext())
{ {

View File

@@ -53,6 +53,7 @@ import org.dspace.authorize.AuthorizeManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.event.Event;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -80,7 +81,13 @@ public class Bundle extends DSpaceObject
private TableRow bundleRow; private TableRow bundleRow;
/** The bitstreams in this bundle */ /** The bitstreams in this bundle */
private List bitstreams; private List<Bitstream> bitstreams;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Construct a bundle object with the given table row * Construct a bundle object with the given table row
@@ -94,7 +101,7 @@ public class Bundle extends DSpaceObject
{ {
ourContext = context; ourContext = context;
bundleRow = row; bundleRow = row;
bitstreams = new ArrayList(); bitstreams = new ArrayList<Bitstream>();
// Get bitstreams // Get bitstreams
TableRowIterator tri = DatabaseManager.queryTable( TableRowIterator tri = DatabaseManager.queryTable(
@@ -126,6 +133,8 @@ public class Bundle extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("bundle_id")); context.cache(this, row.getIntColumn("bundle_id"));
modified = modifiedMetadata = false;
} }
/** /**
@@ -192,6 +201,8 @@ public class Bundle extends DSpaceObject
log.info(LogManager.getHeader(context, "create_bundle", "bundle_id=" log.info(LogManager.getHeader(context, "create_bundle", "bundle_id="
+ row.getIntColumn("bundle_id"))); + row.getIntColumn("bundle_id")));
context.addEvent(new Event(Event.CREATE, Constants.BUNDLE, row.getIntColumn("bundle_id"), null));
return new Bundle(context, row); return new Bundle(context, row);
} }
@@ -225,6 +236,7 @@ public class Bundle extends DSpaceObject
public void setName(String name) public void setName(String name)
{ {
bundleRow.setColumn("name", name); bundleRow.setColumn("name", name);
modifiedMetadata = true;
} }
/** /**
@@ -246,6 +258,7 @@ public class Bundle extends DSpaceObject
public void setPrimaryBitstreamID(int bitstreamID) public void setPrimaryBitstreamID(int bitstreamID)
{ {
bundleRow.setColumn("primary_bitstream_id", bitstreamID); bundleRow.setColumn("primary_bitstream_id", bitstreamID);
modified = true;
} }
/** /**
@@ -309,7 +322,7 @@ public class Bundle extends DSpaceObject
*/ */
public Item[] getItems() throws SQLException public Item[] getItems() throws SQLException
{ {
List items = new ArrayList(); List<Item> items = new ArrayList<Item>();
// Get items // Get items
TableRowIterator tri = DatabaseManager.queryTable( TableRowIterator tri = DatabaseManager.queryTable(
@@ -421,6 +434,8 @@ public class Bundle extends DSpaceObject
// Add the bitstream object // Add the bitstream object
bitstreams.add(b); bitstreams.add(b);
ourContext.addEvent(new Event(Event.ADD, Constants.BUNDLE, getID(), Constants.BITSTREAM, b.getID(), String.valueOf(b.getSequenceID())));
// copy authorization policies from bundle to bitstream // copy authorization policies from bundle to bitstream
// FIXME: multiple inclusion is affected by this... // FIXME: multiple inclusion is affected by this...
AuthorizeManager.inheritPolicies(ourContext, this, b); AuthorizeManager.inheritPolicies(ourContext, this, b);
@@ -475,6 +490,8 @@ public class Bundle extends DSpaceObject
} }
} }
ourContext.addEvent(new Event(Event.REMOVE, Constants.BUNDLE, getID(), Constants.BITSTREAM, b.getID(), String.valueOf(b.getSequenceID())));
// Delete the mapping row // Delete the mapping row
DatabaseManager.updateQuery(ourContext, DatabaseManager.updateQuery(ourContext,
"DELETE FROM bundle2bitstream WHERE bundle_id= ? "+ "DELETE FROM bundle2bitstream WHERE bundle_id= ? "+
@@ -505,6 +522,17 @@ public class Bundle extends DSpaceObject
log.info(LogManager.getHeader(ourContext, "update_bundle", "bundle_id=" log.info(LogManager.getHeader(ourContext, "update_bundle", "bundle_id="
+ getID())); + getID()));
if (modified)
{
ourContext.addEvent(new Event(Event.MODIFY, Constants.BUNDLE, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
ourContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.BUNDLE, getID(), null));
modifiedMetadata = false;
}
DatabaseManager.update(ourContext, bundleRow); DatabaseManager.update(ourContext, bundleRow);
} }
@@ -518,6 +546,8 @@ public class Bundle extends DSpaceObject
log.info(LogManager.getHeader(ourContext, "delete_bundle", "bundle_id=" log.info(LogManager.getHeader(ourContext, "delete_bundle", "bundle_id="
+ getID())); + getID()));
ourContext.addEvent(new Event(Event.DELETE, Constants.BUNDLE, getID(), getName()));
// Remove from cache // Remove from cache
ourContext.removeCached(this, getID()); ourContext.removeCached(this, getID());

View File

@@ -41,7 +41,9 @@ package org.dspace.content;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.*; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.MissingResourceException; import java.util.MissingResourceException;
@@ -57,9 +59,8 @@ import org.dspace.core.Context;
import org.dspace.core.I18nUtil; import org.dspace.core.I18nUtil;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.history.HistoryManager;
import org.dspace.search.DSIndexer;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -99,6 +100,12 @@ public class Collection extends DSpaceObject
/** Our Handle */ /** Our Handle */
private String handle; private String handle;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Groups corresponding to workflow steps - NOTE these start from one, so * Groups corresponding to workflow steps - NOTE these start from one, so
* workflowGroups[0] corresponds to workflow_step_1. * workflowGroups[0] corresponds to workflow_step_1.
@@ -162,6 +169,9 @@ public class Collection extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("collection_id")); context.cache(this, row.getIntColumn("collection_id"));
modified = modifiedMetadata = false;
clearDetails();
} }
/** /**
@@ -250,8 +260,7 @@ public class Collection extends DSpaceObject
myPolicy.setGroup(anonymousGroup); myPolicy.setGroup(anonymousGroup);
myPolicy.update(); myPolicy.update();
HistoryManager.saveHistory(context, c, HistoryManager.CREATE, context context.addEvent(new Event(Event.CREATE, Constants.COLLECTION, c.getID(), c.handle));
.getCurrentUser(), context.getExtraLogInfo());
log.info(LogManager.getHeader(context, "create_collection", log.info(LogManager.getHeader(context, "create_collection",
"collection_id=" + row.getIntColumn("collection_id")) "collection_id=" + row.getIntColumn("collection_id"))
@@ -275,7 +284,7 @@ public class Collection extends DSpaceObject
TableRowIterator tri = DatabaseManager.queryTable(context, "collection", TableRowIterator tri = DatabaseManager.queryTable(context, "collection",
"SELECT * FROM collection ORDER BY name"); "SELECT * FROM collection ORDER BY name");
List collections = new ArrayList(); List<Collection> collections = new ArrayList<Collection>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -350,8 +359,19 @@ public class Collection extends DSpaceObject
return collectionRow.getIntColumn("collection_id"); return collectionRow.getIntColumn("collection_id");
} }
/**
* @see org.dspace.content.DSpaceObject#getHandle()
*/
public String getHandle() public String getHandle()
{ {
if(handle == null) {
try {
handle = HandleManager.findHandle(this.ourContext, this);
} catch (SQLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
return handle; return handle;
} }
@@ -397,6 +417,13 @@ public class Collection extends DSpaceObject
} }
} }
collectionRow.setColumn(field, value); collectionRow.setColumn(field, value);
modifiedMetadata = true;
addDetails(field);
}
public String getName()
{
return getMetadata("name");
} }
/** /**
@@ -469,6 +496,7 @@ public class Collection extends DSpaceObject
+ newLogo.getID())); + newLogo.getID()));
} }
modified = true;
return logo; return logo;
} }
@@ -527,6 +555,7 @@ public class Collection extends DSpaceObject
{ {
collectionRow.setColumn("workflow_step_" + step, g.getID()); collectionRow.setColumn("workflow_step_" + step, g.getID());
} }
modified = true;
} }
/** /**
@@ -571,6 +600,7 @@ public class Collection extends DSpaceObject
AuthorizeManager.addPolicy(ourContext, this, Constants.ADD, submitters); AuthorizeManager.addPolicy(ourContext, this, Constants.ADD, submitters);
modified = true;
return submitters; return submitters;
} }
@@ -624,6 +654,7 @@ public class Collection extends DSpaceObject
admins); admins);
} }
modified = true;
return admins; return admins;
} }
@@ -704,6 +735,7 @@ public class Collection extends DSpaceObject
{ {
collectionRow.setColumn("license", license); collectionRow.setColumn("license", license);
} }
modified = true;
} }
/** /**
@@ -742,6 +774,7 @@ public class Collection extends DSpaceObject
"collection_id=" + getID() + ",template_item_id=" "collection_id=" + getID() + ",template_item_id="
+ template.getID())); + template.getID()));
} }
modified = true;
} }
/** /**
@@ -773,6 +806,7 @@ public class Collection extends DSpaceObject
template.delete(); template.delete();
template = null; template = null;
} }
ourContext.addEvent(new Event(Event.MODIFY, Constants.COLLECTION, getID(), "remove_template_item"));
} }
/** /**
@@ -801,6 +835,8 @@ public class Collection extends DSpaceObject
row.setColumn("item_id", item.getID()); row.setColumn("item_id", item.getID());
DatabaseManager.update(ourContext, row); DatabaseManager.update(ourContext, row);
ourContext.addEvent(new Event(Event.ADD, Constants.COLLECTION, getID(), Constants.ITEM, item.getID(), item.getHandle()));
} }
/** /**
@@ -826,6 +862,8 @@ public class Collection extends DSpaceObject
"AND item_id= ? ", "AND item_id= ? ",
getID(), item.getID()); getID(), item.getID());
ourContext.addEvent(new Event(Event.REMOVE, Constants.COLLECTION, getID(), Constants.ITEM, item.getID(), item.getHandle()));
// Is the item an orphan? // Is the item an orphan?
TableRowIterator tri = DatabaseManager.query(ourContext, TableRowIterator tri = DatabaseManager.query(ourContext,
"SELECT * FROM collection2item WHERE item_id= ? ", "SELECT * FROM collection2item WHERE item_id= ? ",
@@ -864,17 +902,22 @@ public class Collection extends DSpaceObject
// Check authorisation // Check authorisation
canEdit(); canEdit();
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "update_collection", log.info(LogManager.getHeader(ourContext, "update_collection",
"collection_id=" + getID())); "collection_id=" + getID()));
DatabaseManager.update(ourContext, collectionRow); DatabaseManager.update(ourContext, collectionRow);
// reindex this collection (could be smarter, to only do when name if (modified)
// changes) {
DSIndexer.reIndexContent(ourContext, this); ourContext.addEvent(new Event(Event.MODIFY, Constants.COLLECTION, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
ourContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.COLLECTION, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
} }
public boolean canEditBoolean() throws java.sql.SQLException public boolean canEditBoolean() throws java.sql.SQLException
@@ -928,15 +971,11 @@ public class Collection extends DSpaceObject
log.info(LogManager.getHeader(ourContext, "delete_collection", log.info(LogManager.getHeader(ourContext, "delete_collection",
"collection_id=" + getID())); "collection_id=" + getID()));
// remove from index ourContext.addEvent(new Event(Event.DELETE, Constants.COLLECTION, getID(), getHandle()));
DSIndexer.unIndexContent(ourContext, this);
// Remove from cache // Remove from cache
ourContext.removeCached(this, getID()); ourContext.removeCached(this, getID());
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
// remove subscriptions - hmm, should this be in Subscription.java? // remove subscriptions - hmm, should this be in Subscription.java?
DatabaseManager.updateQuery(ourContext, DatabaseManager.updateQuery(ourContext,
"DELETE FROM subscription WHERE collection_id= ? ", "DELETE FROM subscription WHERE collection_id= ? ",
@@ -1062,7 +1101,7 @@ public class Collection extends DSpaceObject
getID()); getID());
// Build a list of Community objects // Build a list of Community objects
List communities = new ArrayList(); List<Community> communities = new ArrayList<Community>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -1162,7 +1201,7 @@ public class Collection extends DSpaceObject
public static Collection[] findAuthorized(Context context, Community comm, public static Collection[] findAuthorized(Context context, Community comm,
int actionID) throws java.sql.SQLException int actionID) throws java.sql.SQLException
{ {
List myResults = new ArrayList(); List<Collection> myResults = new ArrayList<Collection>();
Collection[] myCollections = null; Collection[] myCollections = null;

View File

@@ -55,9 +55,8 @@ import org.dspace.core.Context;
import org.dspace.core.I18nUtil; import org.dspace.core.I18nUtil;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.history.HistoryManager;
import org.dspace.search.DSIndexer;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -89,6 +88,12 @@ public class Community extends DSpaceObject
/** Handle, if any */ /** Handle, if any */
private String handle; private String handle;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Construct a community object from a database row. * Construct a community object from a database row.
* *
@@ -118,6 +123,9 @@ public class Community extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("community_id")); context.cache(this, row.getIntColumn("community_id"));
modified = modifiedMetadata = false;
clearDetails();
} }
/** /**
@@ -198,8 +206,11 @@ public class Community extends DSpaceObject
myPolicy.setGroup(anonymousGroup); myPolicy.setGroup(anonymousGroup);
myPolicy.update(); myPolicy.update();
HistoryManager.saveHistory(context, c, HistoryManager.CREATE, context context.addEvent(new Event(Event.CREATE, Constants.COMMUNITY, c.getID(), c.handle));
.getCurrentUser(), context.getExtraLogInfo());
// if creating a top-level Community, simulate an ADD event at the Site.
if (parent == null)
context.addEvent(new Event(Event.ADD, Constants.SITE, Site.SITE_ID, Constants.COMMUNITY, c.getID(), c.handle));
log.info(LogManager.getHeader(context, "create_community", log.info(LogManager.getHeader(context, "create_community",
"community_id=" + row.getIntColumn("community_id")) "community_id=" + row.getIntColumn("community_id"))
@@ -222,7 +233,7 @@ public class Community extends DSpaceObject
TableRowIterator tri = DatabaseManager.queryTable(context, "community", TableRowIterator tri = DatabaseManager.queryTable(context, "community",
"SELECT * FROM community ORDER BY name"); "SELECT * FROM community ORDER BY name");
List communities = new ArrayList(); List<Community> communities = new ArrayList<Community>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -268,7 +279,7 @@ public class Community extends DSpaceObject
+ "(SELECT child_comm_id FROM community2community) " + "(SELECT child_comm_id FROM community2community) "
+ "ORDER BY name"); + "ORDER BY name");
List topCommunities = new ArrayList(); List<Community> topCommunities = new ArrayList<Community>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -306,8 +317,19 @@ public class Community extends DSpaceObject
return communityRow.getIntColumn("community_id"); return communityRow.getIntColumn("community_id");
} }
/**
* @see org.dspace.content.DSpaceObject#getHandle()
*/
public String getHandle() public String getHandle()
{ {
if(handle == null) {
try {
handle = HandleManager.findHandle(this.ourContext, this);
} catch (SQLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
return handle; return handle;
} }
@@ -353,6 +375,13 @@ public class Community extends DSpaceObject
} }
} }
communityRow.setColumn(field, value); communityRow.setColumn(field, value);
modifiedMetadata = true;
addDetails(field);
}
public String getName()
{
return getMetadata("name");
} }
/** /**
@@ -418,6 +447,7 @@ public class Community extends DSpaceObject
+ newLogo.getID())); + newLogo.getID()));
} }
modified = true;
return logo; return logo;
} }
@@ -429,16 +459,22 @@ public class Community extends DSpaceObject
// Check authorisation // Check authorisation
canEdit(); canEdit();
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "update_community", log.info(LogManager.getHeader(ourContext, "update_community",
"community_id=" + getID())); "community_id=" + getID()));
DatabaseManager.update(ourContext, communityRow); DatabaseManager.update(ourContext, communityRow);
// now re-index this Community if (modified)
DSIndexer.reIndexContent(ourContext, this); {
ourContext.addEvent(new Event(Event.MODIFY, Constants.COMMUNITY, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
ourContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.COMMUNITY, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
} }
/** /**
@@ -449,7 +485,7 @@ public class Community extends DSpaceObject
*/ */
public Collection[] getCollections() throws SQLException public Collection[] getCollections() throws SQLException
{ {
List collections = new ArrayList(); List<Collection> collections = new ArrayList<Collection>();
// Get the table rows // Get the table rows
TableRowIterator tri = DatabaseManager.queryTable( TableRowIterator tri = DatabaseManager.queryTable(
@@ -496,7 +532,7 @@ public class Community extends DSpaceObject
*/ */
public Community[] getSubcommunities() throws SQLException public Community[] getSubcommunities() throws SQLException
{ {
List subcommunities = new ArrayList(); List<Community> subcommunities = new ArrayList<Community>();
// Get the table rows // Get the table rows
TableRowIterator tri = DatabaseManager.queryTable( TableRowIterator tri = DatabaseManager.queryTable(
@@ -585,7 +621,7 @@ public class Community extends DSpaceObject
*/ */
public Community[] getAllParents() throws SQLException public Community[] getAllParents() throws SQLException
{ {
List parentList = new ArrayList(); List<Community> parentList = new ArrayList<Community>();
Community parent = getParentCommunity(); Community parent = getParentCommunity();
while (parent != null) while (parent != null)
@@ -649,6 +685,8 @@ public class Community extends DSpaceObject
mappingRow.setColumn("community_id", getID()); mappingRow.setColumn("community_id", getID());
mappingRow.setColumn("collection_id", c.getID()); mappingRow.setColumn("collection_id", c.getID());
ourContext.addEvent(new Event(Event.ADD, Constants.COMMUNITY, getID(), Constants.COLLECTION, c.getID(), c.getHandle()));
DatabaseManager.update(ourContext, mappingRow); DatabaseManager.update(ourContext, mappingRow);
} }
// close the TableRowIterator to free up resources // close the TableRowIterator to free up resources
@@ -702,6 +740,8 @@ public class Community extends DSpaceObject
mappingRow.setColumn("parent_comm_id", getID()); mappingRow.setColumn("parent_comm_id", getID());
mappingRow.setColumn("child_comm_id", c.getID()); mappingRow.setColumn("child_comm_id", c.getID());
ourContext.addEvent(new Event(Event.ADD, Constants.COMMUNITY, getID(), Constants.COMMUNITY, c.getID(), c.getHandle()));
DatabaseManager.update(ourContext, mappingRow); DatabaseManager.update(ourContext, mappingRow);
} }
// close the TableRowIterator to free up resources // close the TableRowIterator to free up resources
@@ -728,6 +768,8 @@ public class Community extends DSpaceObject
"DELETE FROM community2collection WHERE community_id= ? "+ "DELETE FROM community2collection WHERE community_id= ? "+
"AND collection_id= ? ", getID(), c.getID()); "AND collection_id= ? ", getID(), c.getID());
ourContext.addEvent(new Event(Event.REMOVE, Constants.COMMUNITY, getID(), Constants.COLLECTION, c.getID(), c.getHandle()));
// Is the community an orphan? // Is the community an orphan?
TableRowIterator tri = DatabaseManager.query(ourContext, TableRowIterator tri = DatabaseManager.query(ourContext,
"SELECT * FROM community2collection WHERE collection_id= ? ", "SELECT * FROM community2collection WHERE collection_id= ? ",
@@ -774,6 +816,8 @@ public class Community extends DSpaceObject
"DELETE FROM community2community WHERE parent_comm_id= ? " + "DELETE FROM community2community WHERE parent_comm_id= ? " +
" AND child_comm_id= ? ", getID(),c.getID()); " AND child_comm_id= ? ", getID(),c.getID());
ourContext.addEvent(new Event(Event.REMOVE, Constants.COMMUNITY, getID(), Constants.COMMUNITY, c.getID(), c.getHandle()));
// Is the subcommunity an orphan? // Is the subcommunity an orphan?
TableRowIterator tri = DatabaseManager.query(ourContext, TableRowIterator tri = DatabaseManager.query(ourContext,
"SELECT * FROM community2community WHERE child_comm_id= ? ", "SELECT * FROM community2community WHERE child_comm_id= ? ",
@@ -834,11 +878,7 @@ public class Community extends DSpaceObject
log.info(LogManager.getHeader(ourContext, "delete_community", log.info(LogManager.getHeader(ourContext, "delete_community",
"community_id=" + getID())); "community_id=" + getID()));
// remove from the search index ourContext.addEvent(new Event(Event.DELETE, Constants.COMMUNITY, getID(), getHandle()));
DSIndexer.unIndexContent(ourContext, this);
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
// Remove from cache // Remove from cache
ourContext.removeCached(this, getID()); ourContext.removeCached(this, getID());

View File

@@ -39,11 +39,53 @@
*/ */
package org.dspace.content; package org.dspace.content;
import java.sql.SQLException;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
/** /**
* Abstract base class for DSpace objects * Abstract base class for DSpace objects
*/ */
public abstract class DSpaceObject public abstract class DSpaceObject
{ {
// accumulate information to add to "detail" element of content Event,
// e.g. to document metadata fields touched, etc.
private StringBuffer eventDetails = null;
/**
* Reset the cache of event details.
*/
protected void clearDetails()
{
eventDetails = null;
}
/**
* Add a string to the cache of event details. Automatically
* separates entries with a comma.
* Subclass can just start calling addDetails, since it creates
* the cache if it needs to.
* @param detail detail string to add.
*/
protected void addDetails(String d)
{
if (eventDetails == null)
eventDetails = new StringBuffer(d);
else
eventDetails.append(", ").append(d);
}
/**
* @returns summary of event details, or null if there are none.
*/
protected String getDetails()
{
return (eventDetails == null ? null : eventDetails.toString());
}
/** /**
* Get the type of this object, found in Constants * Get the type of this object, found in Constants
* *
@@ -65,4 +107,40 @@ public abstract class DSpaceObject
* one * one
*/ */
public abstract String getHandle(); public abstract String getHandle();
/**
* Get a proper name for the object. This may return <code>null</code>.
* Name should be suitable for display in a user interface.
*
* @return Name for the object, or <code>null</code> if it doesn't have
* one
*/
abstract public String getName();
/**
* Generic find for when the precise type of a DSO is not known, just the
* a pair of type number and database ID.
*
* @param context - the context
* @param type - type number
* @param id - id within table of type'd objects
* @return the object found, or null if it does not exist.
* @throws SQLException only upon failure accessing the database.
*/
public static DSpaceObject find(Context context, int type, int id)
throws SQLException
{
switch (type)
{
case Constants.BITSTREAM : return Bitstream.find(context, id);
case Constants.BUNDLE : return Bundle.find(context, id);
case Constants.ITEM : return Item.find(context, id);
case Constants.COLLECTION: return Collection.find(context, id);
case Constants.COMMUNITY : return Community.find(context, id);
case Constants.GROUP : return Group.find(context, id);
case Constants.EPERSON : return EPerson.find(context, id);
case Constants.SITE : return Site.find(context, id);
}
return null;
}
} }

View File

@@ -46,7 +46,6 @@ import org.dspace.authorize.AuthorizeException;
import org.dspace.core.ConfigurationManager; import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.search.DSIndexer;
/** /**
* Support to install item in the archive * Support to install item in the archive
@@ -145,9 +144,6 @@ public class InstallItem
// save changes ;-) // save changes ;-)
item.update(); item.update();
// add item to search and browse indices
DSIndexer.indexContent(c, item);
// remove in-progress submission // remove in-progress submission
is.deleteWrapper(); is.deleteWrapper();

View File

@@ -61,11 +61,10 @@ import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.event.Event;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.history.HistoryManager;
import org.dspace.search.DSIndexer;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -104,10 +103,10 @@ public class Item extends DSpaceObject
private EPerson submitter; private EPerson submitter;
/** The bundles in this item - kept in sync with DB */ /** The bundles in this item - kept in sync with DB */
private List bundles; private List<Bundle> bundles;
/** The Dublin Core metadata - a list of DCValue objects. */ /** The Dublin Core metadata - a list of DCValue objects. */
private List dublinCore; private List<DCValue> dublinCore;
/** Handle, if any */ /** Handle, if any */
private String handle; private String handle;
@@ -118,6 +117,12 @@ public class Item extends DSpaceObject
*/ */
private boolean dublinCoreChanged; private boolean dublinCoreChanged;
/**
* True if anything else was changed since last update()
* (to drive event mechanism)
*/
private boolean modified;
/** /**
* Construct an item with the given table row * Construct an item with the given table row
* *
@@ -132,7 +137,9 @@ public class Item extends DSpaceObject
ourContext = context; ourContext = context;
itemRow = row; itemRow = row;
dublinCoreChanged = false; dublinCoreChanged = false;
dublinCore = new ArrayList(); modified = false;
dublinCore = new ArrayList<DCValue>();
clearDetails();
// Get Dublin Core metadata // Get Dublin Core metadata
TableRowIterator tri = DatabaseManager.queryTable(ourContext, "MetadataValue", TableRowIterator tri = DatabaseManager.queryTable(ourContext, "MetadataValue",
@@ -247,8 +254,7 @@ public class Item extends DSpaceObject
i.update(); i.update();
context.setIgnoreAuthorization(false); context.setIgnoreAuthorization(false);
HistoryManager.saveHistory(context, i, HistoryManager.CREATE, context context.addEvent(new Event(Event.CREATE, Constants.ITEM, i.getID(), null));
.getCurrentUser(), context.getExtraLogInfo());
log.info(LogManager.getHeader(context, "create_item", "item_id=" log.info(LogManager.getHeader(context, "create_item", "item_id="
+ row.getIntColumn("item_id"))); + row.getIntColumn("item_id")));
@@ -312,6 +318,14 @@ public class Item extends DSpaceObject
*/ */
public String getHandle() public String getHandle()
{ {
if(handle == null) {
try {
handle = HandleManager.findHandle(this.ourContext, this);
} catch (SQLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
return handle; return handle;
} }
@@ -364,6 +378,7 @@ public class Item extends DSpaceObject
public void setArchived(boolean isArchived) public void setArchived(boolean isArchived)
{ {
itemRow.setColumn("in_archive", isArchived); itemRow.setColumn("in_archive", isArchived);
modified = true;
} }
/** /**
@@ -375,6 +390,7 @@ public class Item extends DSpaceObject
public void setOwningCollection(Collection c) public void setOwningCollection(Collection c)
{ {
itemRow.setColumn("owning_collection", c.getID()); itemRow.setColumn("owning_collection", c.getID());
modified = true;
} }
/** /**
@@ -493,13 +509,9 @@ public class Item extends DSpaceObject
String lang) String lang)
{ {
// Build up list of matching values // Build up list of matching values
List values = new ArrayList(); List<DCValue> values = new ArrayList<DCValue>();
Iterator i = dublinCore.iterator(); for (DCValue dcv : dublinCore)
while (i.hasNext())
{ {
DCValue dcv = (DCValue) i.next();
if (match(schema, element, qualifier, lang, dcv)) if (match(schema, element, qualifier, lang, dcv))
{ {
// We will return a copy of the object in case it is altered // We will return a copy of the object in case it is altered
@@ -661,6 +673,8 @@ public class Item extends DSpaceObject
dcv.value = null; dcv.value = null;
} }
dublinCore.add(dcv); dublinCore.add(dcv);
addDetails(schema+"."+element+((qualifier==null)? "": "."+qualifier));
} }
if (values.length > 0) if (values.length > 0)
@@ -752,13 +766,9 @@ public class Item extends DSpaceObject
String lang) String lang)
{ {
// We will build a list of values NOT matching the values to clear // We will build a list of values NOT matching the values to clear
List values = new ArrayList(); List<DCValue> values = new ArrayList<DCValue>();
Iterator i = dublinCore.iterator(); for (DCValue dcv : dublinCore)
while (i.hasNext())
{ {
DCValue dcv = (DCValue) i.next();
if (!match(schema, element, qualifier, lang, dcv)) if (!match(schema, element, qualifier, lang, dcv))
{ {
values.add(dcv); values.add(dcv);
@@ -885,6 +895,7 @@ public class Item extends DSpaceObject
{ {
itemRow.setColumnNull("submitter_id"); itemRow.setColumnNull("submitter_id");
} }
modified = true;
} }
/** /**
@@ -895,7 +906,7 @@ public class Item extends DSpaceObject
*/ */
public Collection[] getCollections() throws SQLException public Collection[] getCollections() throws SQLException
{ {
List collections = new ArrayList(); List<Collection> collections = new ArrayList<Collection>();
// Get collection table rows // Get collection table rows
TableRowIterator tri = DatabaseManager.queryTable(ourContext,"collection", TableRowIterator tri = DatabaseManager.queryTable(ourContext,"collection",
@@ -940,7 +951,7 @@ public class Item extends DSpaceObject
*/ */
public Community[] getCommunities() throws SQLException public Community[] getCommunities() throws SQLException
{ {
List communities = new ArrayList(); List<Community> communities = new ArrayList<Community>();
// Get community table rows // Get community table rows
TableRowIterator tri = DatabaseManager.queryTable(ourContext,"community", TableRowIterator tri = DatabaseManager.queryTable(ourContext,"community",
@@ -990,7 +1001,7 @@ public class Item extends DSpaceObject
{ {
if (bundles == null) if (bundles == null)
{ {
bundles = new ArrayList(); bundles = new ArrayList<Bundle>();
// Get bundles // Get bundles
TableRowIterator tri = DatabaseManager.queryTable(ourContext, "bundle", TableRowIterator tri = DatabaseManager.queryTable(ourContext, "bundle",
"SELECT bundle.* FROM bundle, item2bundle WHERE " + "SELECT bundle.* FROM bundle, item2bundle WHERE " +
@@ -1035,7 +1046,7 @@ public class Item extends DSpaceObject
*/ */
public Bundle[] getBundles(String name) throws SQLException public Bundle[] getBundles(String name) throws SQLException
{ {
List matchingBundles = new ArrayList(); List<Bundle> matchingBundles = new ArrayList<Bundle>();
// now only keep bundles with matching names // now only keep bundles with matching names
Bundle[] bunds = getBundles(); Bundle[] bunds = getBundles();
@@ -1121,6 +1132,8 @@ public class Item extends DSpaceObject
mappingRow.setColumn("item_id", getID()); mappingRow.setColumn("item_id", getID());
mappingRow.setColumn("bundle_id", b.getID()); mappingRow.setColumn("bundle_id", b.getID());
DatabaseManager.update(ourContext, mappingRow); DatabaseManager.update(ourContext, mappingRow);
ourContext.addEvent(new Event(Event.ADD, Constants.ITEM, getID(), Constants.BUNDLE, b.getID(), b.getName()));
} }
/** /**
@@ -1161,6 +1174,8 @@ public class Item extends DSpaceObject
"AND bundle_id= ? ", "AND bundle_id= ? ",
getID(), b.getID()); getID(), b.getID());
ourContext.addEvent(new Event(Event.REMOVE, Constants.ITEM, getID(), Constants.BUNDLE, b.getID(), b.getName()));
// If the bundle is orphaned, it's removed // If the bundle is orphaned, it's removed
TableRowIterator tri = DatabaseManager.query(ourContext, TableRowIterator tri = DatabaseManager.query(ourContext,
"SELECT * FROM item2bundle WHERE bundle_id= ? ", "SELECT * FROM item2bundle WHERE bundle_id= ? ",
@@ -1238,7 +1253,7 @@ public class Item extends DSpaceObject
*/ */
public Bitstream[] getNonInternalBitstreams() throws SQLException public Bitstream[] getNonInternalBitstreams() throws SQLException
{ {
List bitstreamList = new ArrayList(); List<Bitstream> bitstreamList = new ArrayList<Bitstream>();
// Go through the bundles and bitstreams picking out ones which aren't // Go through the bundles and bitstreams picking out ones which aren't
// of internal formats // of internal formats
@@ -1359,9 +1374,6 @@ public class Item extends DSpaceObject
AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE); AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE);
} }
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "update_item", "item_id=" log.info(LogManager.getHeader(ourContext, "update_item", "item_id="
+ getID())); + getID()));
@@ -1419,7 +1431,7 @@ public class Item extends DSpaceObject
// Keys are Strings: "element" or "element.qualifier" // Keys are Strings: "element" or "element.qualifier"
// Values are Integers indicating number of values written for a // Values are Integers indicating number of values written for a
// element/qualifier // element/qualifier
Map elementCount = new HashMap(); Map<String,Integer> elementCount = new HashMap<String,Integer>();
DatabaseManager.update(ourContext, itemRow); DatabaseManager.update(ourContext, itemRow);
@@ -1430,12 +1442,8 @@ public class Item extends DSpaceObject
removeMetadataFromDatabase(); removeMetadataFromDatabase();
// Add in-memory DC // Add in-memory DC
Iterator i = dublinCore.iterator(); for (DCValue dcv : dublinCore)
while (i.hasNext())
{ {
DCValue dcv = (DCValue) i.next();
// Get the DC Type // Get the DC Type
int schemaID; int schemaID;
MetadataSchema schema = MetadataSchema.find(ourContext,dcv.schema); MetadataSchema schema = MetadataSchema.find(ourContext,dcv.schema);
@@ -1497,11 +1505,16 @@ public class Item extends DSpaceObject
metadata.create(ourContext); metadata.create(ourContext);
} }
ourContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.ITEM, getID(), getDetails()));
dublinCoreChanged = false; dublinCoreChanged = false;
clearDetails();
} }
// Update browse indices if (modified)
Browse.itemChanged(ourContext, this); {
ourContext.addEvent(new Event(Event.MODIFY, Constants.ITEM, getID(), null));
modified = false;
}
} }
/** /**
@@ -1560,13 +1573,7 @@ public class Item extends DSpaceObject
// Update item in DB // Update item in DB
update(); update();
// Invoke History system ourContext.addEvent(new Event(Event.MODIFY, Constants.ITEM, getID(), "WITHDRAW"));
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY, e,
ourContext.getExtraLogInfo());
// Remove from indicies
Browse.itemRemoved(ourContext, getID());
DSIndexer.unIndexContent(ourContext, this);
// and all of our authorization policies // and all of our authorization policies
// FIXME: not very "multiple-inclusion" friendly // FIXME: not very "multiple-inclusion" friendly
@@ -1621,14 +1628,7 @@ public class Item extends DSpaceObject
// Update item in DB // Update item in DB
update(); update();
// Invoke History system ourContext.addEvent(new Event(Event.MODIFY, Constants.ITEM, getID(), "REINSTATE"));
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY, e,
ourContext.getExtraLogInfo());
// Add to indicies
// Remove - update() already performs this
// Browse.itemAdded(ourContext, this);
DSIndexer.indexContent(ourContext, this);
// authorization policies // authorization policies
if (colls.length > 0) if (colls.length > 0)
@@ -1656,8 +1656,7 @@ public class Item extends DSpaceObject
*/ */
void delete() throws SQLException, AuthorizeException, IOException void delete() throws SQLException, AuthorizeException, IOException
{ {
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE, ourContext.addEvent(new Event(Event.DELETE, Constants.ITEM, getID(), getHandle()));
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "delete_item", "item_id=" log.info(LogManager.getHeader(ourContext, "delete_item", "item_id="
+ getID())); + getID()));
@@ -1665,12 +1664,19 @@ public class Item extends DSpaceObject
// Remove from cache // Remove from cache
ourContext.removeCached(this, getID()); ourContext.removeCached(this, getID());
// Remove from indices, if appropriate // Remove from browse indices, if appropriate
/** XXX FIXME
** Although all other Browse index updates are managed through
** Event consumers, removing an Item *must* be done *here* (inline)
** because otherwise, tables are left in an inconsistent state
** and the DB transaction will fail.
** Any fix would involve too much work on Browse code that
** is likely to be replaced soon anyway. --lcs, Aug 2006
**/
if (isArchived()) if (isArchived())
{ {
// Remove from Browse indices // Remove from Browse indices
Browse.itemRemoved(ourContext, getID()); Browse.itemRemoved(ourContext, getID());
DSIndexer.unIndexContent(ourContext, this);
} }
// Delete the Dublin Core // Delete the Dublin Core
@@ -1687,15 +1693,6 @@ public class Item extends DSpaceObject
// remove all of our authorization policies // remove all of our authorization policies
AuthorizeManager.removeAllPolicies(ourContext, this); AuthorizeManager.removeAllPolicies(ourContext, this);
// Remove any Handle
// FIXME: This is sort of a "tentacle" - HandleManager should provide
// a way of doing this. Plus, deleting a Handle may have ramifications
// that need considering.
DatabaseManager.updateQuery(ourContext,
"DELETE FROM handle WHERE resource_type_id= ? " +
"AND resource_id= ? ",
Constants.ITEM,getID());
// Finally remove item row // Finally remove item row
DatabaseManager.delete(ourContext, itemRow); DatabaseManager.delete(ourContext, itemRow);
} }
@@ -1982,4 +1979,9 @@ public class Item extends DSpaceObject
return false; return false;
} }
public String getName()
{
DCValue t[] = getMetadata("dc", "title", null, Item.ANY);
return (t.length >= 1) ? t[0].value : null;
}
} }

View File

@@ -0,0 +1,141 @@
/*
* Site.java
*
* Version: $Revision: 1.8 $
*
* Date: $Date: 2005/04/20 14:22:34 $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.content;
import java.sql.SQLException;
import java.net.URI;
import java.io.IOException;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.authorize.AuthorizeException;
/**
* Represents the root of the DSpace Archive.
* By default, the handle suffix "0" represents the Site, e.g. "1721.1/0"
*/
public class Site extends DSpaceObject
{
/** "database" identifier of the site */
public static final int SITE_ID = 0;
// cache for Handle that is persistent ID for entire site.
private static String handle = null;
private static Site theSite = null;
/**
* Get the type of this object, found in Constants
*
* @return type of the object
*/
public int getType()
{
return Constants.SITE;
}
/**
* Get the internal ID (database primary key) of this object
*
* @return internal ID of object
*/
public int getID()
{
return SITE_ID;
}
/**
* Get the Handle of the object. This may return <code>null</code>
*
* @return Handle of the object, or <code>null</code> if it doesn't have
* one
*/
public String getHandle()
{
return getSiteHandle();
}
/**
* Static method to return site Handle without creating a Site.
* @returns handle of the Site.
*/
public static String getSiteHandle()
{
if (handle == null)
handle = ConfigurationManager.getProperty("handle.prefix")+"/"+
String.valueOf(SITE_ID);
return handle;
}
/**
* Get Site object corresponding to db id (which is ignroed).
* @param context the context.
* @param id integer database id, ignored.
* @returns Site object.
*/
public static DSpaceObject find(Context context, int id)
throws SQLException
{
if (theSite == null)
theSite = new Site();
return theSite;
}
void delete()
throws SQLException, AuthorizeException, IOException
{
}
public void update()
throws SQLException, AuthorizeException, IOException
{
}
public String getName()
{
return ConfigurationManager.getProperty("dspace.name");
}
}

View File

@@ -52,7 +52,6 @@ import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.history.HistoryManager;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -286,9 +285,6 @@ public class WorkspaceItem implements InProgressSubmission
WorkspaceItem wi = new WorkspaceItem(c, row); WorkspaceItem wi = new WorkspaceItem(c, row);
HistoryManager.saveHistory(c, wi, HistoryManager.CREATE, c
.getCurrentUser(), c.getExtraLogInfo());
return wi; return wi;
} }
@@ -463,8 +459,6 @@ public class WorkspaceItem implements InProgressSubmission
public void update() throws SQLException, AuthorizeException, IOException public void update() throws SQLException, AuthorizeException, IOException
{ {
// Authorisation is checked by the item.update() method below // Authorisation is checked by the item.update() method below
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "update_workspace_item", log.info(LogManager.getHeader(ourContext, "update_workspace_item",
"workspace_item_id=" + getID())); "workspace_item_id=" + getID()));
@@ -499,9 +493,6 @@ public class WorkspaceItem implements InProgressSubmission
+ "original submitter to delete a workspace item"); + "original submitter to delete a workspace item");
} }
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "delete_workspace_item", log.info(LogManager.getHeader(ourContext, "delete_workspace_item",
"workspace_item_id=" + getID() + "item_id=" + item.getID() "workspace_item_id=" + getID() + "item_id=" + item.getID()
+ "collection_id=" + collection.getID())); + "collection_id=" + collection.getID()));
@@ -536,9 +527,6 @@ public class WorkspaceItem implements InProgressSubmission
// Check authorisation. We check permissions on the enclosed item. // Check authorisation. We check permissions on the enclosed item.
AuthorizeManager.authorizeAction(ourContext, item, Constants.WRITE); AuthorizeManager.authorizeAction(ourContext, item, Constants.WRITE);
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
log.info(LogManager.getHeader(ourContext, "delete_workspace_item", log.info(LogManager.getHeader(ourContext, "delete_workspace_item",
"workspace_item_id=" + getID() + "item_id=" + item.getID() "workspace_item_id=" + getID() + "item_id=" + item.getID()
+ "collection_id=" + collection.getID())); + "collection_id=" + collection.getID()));

View File

@@ -0,0 +1,95 @@
/*
* StreamDisseminationCrosswalk
*
* Version: $Revision: 1.4 $
*
* Date: $Date: 2006/04/10 04:11:09 $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.content.crosswalk;
import java.io.OutputStream;
import java.io.IOException;
import java.sql.SQLException;
import org.dspace.core.Constants;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.crosswalk.CrosswalkException;
/**
* A class implementing this interface crosswalk metadata directly
* from a DSpace Object to an output stream, in a specific format.
* <p>
* Stream-oriented crosswalks are intended to be used for metadata
* formats which are either (a) not XML-based, or (b) too bulky for the
* DOM-ish in-memory model developed for the METS and IMSCP packagers.
* The METS packagers (all subclasses of AbstractMETSDisseminator / AbstractMETSIngester
* are equipped to call these crosswalks as well as the XML-based ones,
* just refer to the desired crosswalk by its plugin name.
*
* @author Larry Stone
* @version $Revision: 1.0 $
*/
public interface StreamDisseminationCrosswalk
{
/**
* Predicate: Can this disseminator crosswalk the given object.
*
* @param dso dspace object, e.g. an <code>Item</code>.
* @return true when disseminator is capable of producing metadata.
*/
public boolean canDisseminate(Context context, DSpaceObject dso);
/**
* Execute crosswalk on the given object, sending output to the stream.
*
* @param context the DSpace context
* @param dso the DSpace Object whose metadata to export.
* @param out output stream to write to
*
* @throws CrosswalkInternalException (<code>CrosswalkException</code>) failure of the crosswalk itself.
* @throws CrosswalkObjectNotSupported (<code>CrosswalkException</code>) Cannot crosswalk this kind of DSpace object.
* @throws IOException I/O failure in services this calls
* @throws SQLException Database failure in services this calls
* @throws AuthorizeException current user not authorized for this operation.
*/
public void disseminate(Context context, DSpaceObject dso, OutputStream out)
throws CrosswalkException, IOException, SQLException, AuthorizeException;
public String getMIMEType();
}

View File

@@ -0,0 +1,85 @@
/*
* StreamIngestionCrosswalk
*
* Version: $Revision: 1.4 $
*
* Date: $Date: 2006/04/10 04:11:09 $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.content.crosswalk;
import java.io.InputStream;
import java.io.IOException;
import java.sql.SQLException;
import org.dspace.core.Constants;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.crosswalk.CrosswalkException;
/**
* A class implementing this interface can crosswalk metadata directly
* from a stream (assumed to be in a specific format) to the object.
* <p>
* Stream-oriented crosswalks are intended to be used for metadata
* formats which are either (a) not XML-based, or (b) too bulky for the
* DOM-ish in-memory model developed for the METS and IMSCP packagers.
* The METS packagers (all subclasses of AbstractMETSDisseminator / AbstractMETSIngester
* are equipped to call these crosswalks as well as the XML-based ones,
* just refer to the desired crosswalk by its plugin name.
*
* @author Larry Stone
* @version $Revision: 1.0 $
*/
public interface StreamIngestionCrosswalk
{
/**
* Execute crosswalk on the given object, taking input from the stream.
*
* @param context the DSpace context
* @param dso the DSpace Object whose metadata is being ingested.
* @param out input stream containing the metadata.
*
* @throws CrosswalkInternalException (<code>CrosswalkException</code>) failure of the crosswalk itself.
* @throws CrosswalkObjectNotSupported (<code>CrosswalkException</code>) Cannot crosswalk this kind of DSpace object.
* @throws IOException I/O failure in services this calls
* @throws SQLException Database failure in services this calls
* @throws AuthorizeException current user not authorized for this operation.
*/
public void ingest(Context context, DSpaceObject dso, InputStream in, String MIMEType)
throws CrosswalkException, IOException, SQLException, AuthorizeException;
}

View File

@@ -51,6 +51,9 @@ import java.util.Map;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.event.EventManager;
import org.dspace.event.Dispatcher;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
/** /**
@@ -68,7 +71,6 @@ import org.dspace.storage.rdbms.DatabaseManager;
* The context object is also used as a cache for CM API objects. * The context object is also used as a cache for CM API objects.
* *
* *
* @author Robert Tansley
* @version $Revision$ * @version $Revision$
*/ */
public class Context public class Context
@@ -96,6 +98,12 @@ public class Context
/** Group IDs of special groups user is a member of */ /** Group IDs of special groups user is a member of */
private List specialGroups; private List specialGroups;
/** Content events */
private List<Event> events = null;
/** Event dispatcher name */
private String dispName = null;
/** /**
* Construct a new context object. A database connection is opened. No user * Construct a new context object. A database connection is opened. No user
* is authenticated. * is authenticated.
@@ -241,7 +249,7 @@ public class Context
try try
{ {
// Commit any changes made as part of the transaction // Commit any changes made as part of the transaction
connection.commit(); commit();
} }
finally finally
{ {
@@ -259,12 +267,79 @@ public class Context
* if there was an error completing the database transaction * if there was an error completing the database transaction
* or closing the connection * or closing the connection
*/ */
public void commit() throws SQLException public void commit() throws SQLException {
{
// Commit any changes made as part of the transaction // Commit any changes made as part of the transaction
Dispatcher dispatcher = null;
try {
if (events != null) {
if (dispName == null) {
dispName = EventManager.DEFAULT_DISPATCHER;
}
dispatcher = EventManager.getDispatcher(dispName);
connection.commit();
dispatcher.dispatch(this);
} else {
connection.commit(); connection.commit();
} }
} finally {
events = null;
if(dispatcher != null)
{
/*
* TODO return dispatcher via internal method dispatcher.close();
* and remove the returnDispatcher method from EventManager.
*/
EventManager.returnDispatcher(dispName, dispatcher);
}
}
}
/**
* Select an event dispatcher, <code>null</code> selects the default
*
*/
public void setDispatcher(String dispatcher)
{
if (log.isDebugEnabled())
{
log.debug(this.toString() + ": setDispatcher(\"" + dispatcher + "\")");
}
dispName = dispatcher;
}
/**
* Add an event to be dispatched when this context is committed.
*
* @param event
*/
public void addEvent(Event event)
{
if (events == null)
{
events = new ArrayList<Event>();
}
events.add(event);
}
/**
* Get the current event list. If there is a separate list of events from
* already-committed operations combine that with current list.
*
* @return List of all available events.
*/
public List<Event> getEvents()
{
return events;
}
/** /**
* Close the context, without committing any of the changes performed using * Close the context, without committing any of the changes performed using
* this context. The database connection is freed. No exception is thrown if * this context. The database connection is freed. No exception is thrown if
@@ -287,10 +362,12 @@ public class Context
{ {
DatabaseManager.freeConnection(connection); DatabaseManager.freeConnection(connection);
connection = null; connection = null;
events = null;
} }
} }
/** /**
*
* Find out if this context is valid. Returns <code>false</code> if this * Find out if this context is valid. Returns <code>false</code> if this
* context has been aborted or completed. * context has been aborted or completed.
* *

View File

@@ -52,6 +52,13 @@ import java.text.ParseException;
import java.util.Random; import java.util.Random;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import org.apache.log4j.Logger;
/** /**
* Utility functions for DSpace. * Utility functions for DSpace.
@@ -61,6 +68,9 @@ import java.util.regex.Pattern;
*/ */
public class Utils public class Utils
{ {
/** log4j logger */
private static Logger log = Logger.getLogger(Utils.class);
private static final Pattern DURATION_PATTERN = Pattern private static final Pattern DURATION_PATTERN = Pattern
.compile("(\\d+)([smhdwy])"); .compile("(\\d+)([smhdwy])");
@@ -82,6 +92,31 @@ public class Utils
private static VMID vmid = new VMID(); private static VMID vmid = new VMID();
// for parseISO8601Date
private static SimpleDateFormat parseFmt[] =
{
// first try at parsing, has milliseconds (note General time zone)
new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSz"),
// second try at parsing, no milliseconds (note General time zone)
new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ssz"),
// finally, try without any timezone (defaults to current TZ)
new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS"),
new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss")
};
// for formatISO8601Date
// output canonical format (note RFC22 time zone, easier to hack)
private static SimpleDateFormat outFmtSecond = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ");
// output format with millsecond precision
private static SimpleDateFormat outFmtMillisec = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSZ");
private static Calendar outCal = GregorianCalendar.getInstance();
/** Private Constructor */ /** Private Constructor */
private Utils() private Utils()
{ {
@@ -350,4 +385,64 @@ public class Utils
return qint * multiplier; return qint * multiplier;
} }
/**
* Translates timestamp from an ISO 8601-standard format, which
* is commonly used in XML and RDF documents.
* This method is synchronized because it depends on a non-reentrant
* static DateFormat (more efficient than creating a new one each call).
*
* @param s the input string
* @return Date object, or null if there is a problem translating.
*/
public static synchronized Date parseISO8601Date(String s)
{
// attempt to normalize the timezone to something we can parse;
// SimpleDateFormat can't handle "Z"
char tzSign = s.charAt(s.length()-6);
if (s.endsWith("Z"))
s = s.substring(0, s.length()-1) + "GMT+00:00";
// check for trailing timezone
else if (tzSign == '-' || tzSign == '+')
s = s.substring(0, s.length()-6) + "GMT" + s.substring(s.length()-6);
// try to parse without millseconds
ParseException lastError = null;
for (int i = 0; i < parseFmt.length; ++i)
{
try
{
return parseFmt[i].parse(s);
}
catch (ParseException e)
{
lastError = e;
}
}
if (lastError != null)
log.error("Error parsing date:", lastError);
return null;
}
/**
* Convert a Date to String in the ISO 8601 standard format.
* The RFC822 timezone is almost right, still need to insert ":".
* This method is synchronized because it depends on a non-reentrant
* static DateFormat (more efficient than creating a new one each call).
*
* @param d the input Date
* @return String containing formatted date.
*/
public static synchronized String formatISO8601Date(Date d)
{
String result;
outCal.setTime(d);
if (outCal.get(Calendar.MILLISECOND) == 0)
result = outFmtSecond.format(d);
else
result = outFmtMillisec.format(d);
int rl = result.length();
return result.substring(0, rl-2) + ":" + result.substring(rl-2);
}
} }

View File

@@ -52,7 +52,7 @@ import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.core.Utils; import org.dspace.core.Utils;
import org.dspace.history.HistoryManager; import org.dspace.event.Event;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -89,6 +89,12 @@ public class EPerson extends DSpaceObject
/** The row in the table representing this eperson */ /** The row in the table representing this eperson */
private TableRow myRow; private TableRow myRow;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Construct an EPerson * Construct an EPerson
* *
@@ -104,6 +110,8 @@ public class EPerson extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("eperson_id")); context.cache(this, row.getIntColumn("eperson_id"));
modified = modifiedMetadata = false;
clearDetails();
} }
/** /**
@@ -426,8 +434,7 @@ public class EPerson extends DSpaceObject
log.info(LogManager.getHeader(context, "create_eperson", "eperson_id=" log.info(LogManager.getHeader(context, "create_eperson", "eperson_id="
+ e.getID())); + e.getID()));
HistoryManager.saveHistory(context, e, HistoryManager.REMOVE, context context.addEvent(new Event(Event.CREATE, Constants.EPERSON, e.getID(), null));
.getCurrentUser(), context.getExtraLogInfo());
return e; return e;
} }
@@ -446,9 +453,6 @@ public class EPerson extends DSpaceObject
"You must be an admin to delete an EPerson"); "You must be an admin to delete an EPerson");
} }
HistoryManager.saveHistory(myContext, this, HistoryManager.REMOVE,
myContext.getCurrentUser(), myContext.getExtraLogInfo());
// check for presence of eperson in tables that // check for presence of eperson in tables that
// have constraints on eperson_id // have constraints on eperson_id
Vector constraintList = getDeleteConstraints(); Vector constraintList = getDeleteConstraints();
@@ -460,9 +464,14 @@ public class EPerson extends DSpaceObject
throw new EPersonDeletionException(constraintList); throw new EPersonDeletionException(constraintList);
} }
myContext.addEvent(new Event(Event.DELETE, Constants.EPERSON, getID(), getEmail()));
// Remove from cache // Remove from cache
myContext.removeCached(this, getID()); myContext.removeCached(this, getID());
// XXX FIXME: This sidesteps the object model code so it won't
// generate REMOVE events on the affected Groups.
// Remove any group memberships first // Remove any group memberships first
DatabaseManager.updateQuery(myContext, DatabaseManager.updateQuery(myContext,
"DELETE FROM EPersonGroup2EPerson WHERE eperson_id= ? ", "DELETE FROM EPersonGroup2EPerson WHERE eperson_id= ? ",
@@ -549,6 +558,7 @@ public class EPerson extends DSpaceObject
} }
myRow.setColumn("email", s); myRow.setColumn("email", s);
modified = true;
} }
/** /**
@@ -575,6 +585,7 @@ public class EPerson extends DSpaceObject
} }
myRow.setColumn("netid", s); myRow.setColumn("netid", s);
modified = true;
} }
/** /**
@@ -621,6 +632,7 @@ public class EPerson extends DSpaceObject
public void setFirstName(String firstname) public void setFirstName(String firstname)
{ {
myRow.setColumn("firstname", firstname); myRow.setColumn("firstname", firstname);
modified = true;
} }
/** /**
@@ -642,6 +654,7 @@ public class EPerson extends DSpaceObject
public void setLastName(String lastname) public void setLastName(String lastname)
{ {
myRow.setColumn("lastname", lastname); myRow.setColumn("lastname", lastname);
modified = true;
} }
/** /**
@@ -653,6 +666,7 @@ public class EPerson extends DSpaceObject
public void setCanLogIn(boolean login) public void setCanLogIn(boolean login)
{ {
myRow.setColumn("can_log_in", login); myRow.setColumn("can_log_in", login);
modified = true;
} }
/** /**
@@ -674,6 +688,7 @@ public class EPerson extends DSpaceObject
public void setRequireCertificate(boolean isrequired) public void setRequireCertificate(boolean isrequired)
{ {
myRow.setColumn("require_certificate", isrequired); myRow.setColumn("require_certificate", isrequired);
modified = true;
} }
/** /**
@@ -695,6 +710,7 @@ public class EPerson extends DSpaceObject
public void setSelfRegistered(boolean sr) public void setSelfRegistered(boolean sr)
{ {
myRow.setColumn("self_registered", sr); myRow.setColumn("self_registered", sr);
modified = true;
} }
/** /**
@@ -737,6 +753,8 @@ public class EPerson extends DSpaceObject
public void setMetadata(String field, String value) public void setMetadata(String field, String value)
{ {
myRow.setColumn(field, value); myRow.setColumn(field, value);
modifiedMetadata = true;
addDetails(field);
} }
/** /**
@@ -751,6 +769,7 @@ public class EPerson extends DSpaceObject
String encoded = Utils.getMD5(s); String encoded = Utils.getMD5(s);
myRow.setColumn("password", encoded); myRow.setColumn("password", encoded);
modified = true;
} }
/** /**
@@ -786,8 +805,17 @@ public class EPerson extends DSpaceObject
log.info(LogManager.getHeader(myContext, "update_eperson", log.info(LogManager.getHeader(myContext, "update_eperson",
"eperson_id=" + getID())); "eperson_id=" + getID()));
HistoryManager.saveHistory(myContext, this, HistoryManager.MODIFY, if (modified)
myContext.getCurrentUser(), myContext.getExtraLogInfo()); {
myContext.addEvent(new Event(Event.MODIFY, Constants.EPERSON, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
myContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.EPERSON, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
} }
/** /**
@@ -830,7 +858,7 @@ public class EPerson extends DSpaceObject
*/ */
public Vector getDeleteConstraints() throws SQLException public Vector getDeleteConstraints() throws SQLException
{ {
Vector tableList = new Vector(); Vector<String> tableList = new Vector<String>();
// check for eperson in item table // check for eperson in item table
TableRowIterator tri = DatabaseManager.query(myContext, TableRowIterator tri = DatabaseManager.query(myContext,
@@ -872,4 +900,10 @@ public class EPerson extends DSpaceObject
// explaining to the user why the eperson cannot be deleted. // explaining to the user why the eperson cannot be deleted.
return tableList; return tableList;
} }
public String getName()
{
return getEmail();
}
} }

View File

@@ -47,6 +47,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.io.IOException;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
@@ -56,6 +57,7 @@ import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.event.Event;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -83,9 +85,9 @@ public class Group extends DSpaceObject
private TableRow myRow; private TableRow myRow;
/** lists of epeople and groups in the group */ /** lists of epeople and groups in the group */
private List epeople = new ArrayList(); private List<EPerson> epeople = new ArrayList<EPerson>();
private List groups = new ArrayList(); private List<Group> groups = new ArrayList<Group>();
/** lists that need to be written out again */ /** lists that need to be written out again */
private boolean epeopleChanged = false; private boolean epeopleChanged = false;
@@ -95,6 +97,9 @@ public class Group extends DSpaceObject
/** is this just a stub, or is all data loaded? */ /** is this just a stub, or is all data loaded? */
private boolean isDataLoaded = false; private boolean isDataLoaded = false;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/** /**
* Construct a Group from a given context and tablerow * Construct a Group from a given context and tablerow
* *
@@ -108,6 +113,9 @@ public class Group extends DSpaceObject
// Cache ourselves // Cache ourselves
context.cache(this, row.getIntColumn("eperson_group_id")); context.cache(this, row.getIntColumn("eperson_group_id"));
modifiedMetadata = false;
clearDetails();
} }
/** /**
@@ -213,6 +221,8 @@ public class Group extends DSpaceObject
log.info(LogManager.getHeader(context, "create_group", "group_id=" log.info(LogManager.getHeader(context, "create_group", "group_id="
+ g.getID())); + g.getID()));
context.addEvent(new Event(Event.CREATE, Constants.GROUP, g.getID(), null));
return g; return g;
} }
@@ -245,6 +255,8 @@ public class Group extends DSpaceObject
public void setName(String name) public void setName(String name)
{ {
myRow.setColumn("name", name); myRow.setColumn("name", name);
modifiedMetadata = true;
addDetails("name");
} }
/** /**
@@ -264,6 +276,8 @@ public class Group extends DSpaceObject
epeople.add(e); epeople.add(e);
epeopleChanged = true; epeopleChanged = true;
myContext.addEvent(new Event(Event.ADD, Constants.GROUP, getID(), Constants.EPERSON, e.getID(), e.getEmail()));
} }
/** /**
@@ -283,6 +297,8 @@ public class Group extends DSpaceObject
groups.add(g); groups.add(g);
groupsChanged = true; groupsChanged = true;
myContext.addEvent(new Event(Event.ADD, Constants.GROUP, getID(), Constants.GROUP, g.getID(), g.getName()));
} }
/** /**
@@ -298,6 +314,7 @@ public class Group extends DSpaceObject
if (epeople.remove(e)) if (epeople.remove(e))
{ {
epeopleChanged = true; epeopleChanged = true;
myContext.addEvent(new Event(Event.REMOVE, Constants.GROUP, getID(), Constants.EPERSON, e.getID(), e.getEmail()));
} }
} }
@@ -313,6 +330,7 @@ public class Group extends DSpaceObject
if (groups.remove(g)) if (groups.remove(g))
{ {
groupsChanged = true; groupsChanged = true;
myContext.addEvent(new Event(Event.REMOVE, Constants.GROUP, getID(), Constants.GROUP, g.getID(), g.getName()));
} }
} }
@@ -397,9 +415,9 @@ public class Group extends DSpaceObject
public static Group[] allMemberGroups(Context c, EPerson e) public static Group[] allMemberGroups(Context c, EPerson e)
throws SQLException throws SQLException
{ {
List groupList = new ArrayList(); List<Group> groupList = new ArrayList<Group>();
Set myGroups = allMemberGroupIDs(c, e); Set<Integer> myGroups = allMemberGroupIDs(c, e);
// now convert those Integers to Groups // now convert those Integers to Groups
Iterator i = myGroups.iterator(); Iterator i = myGroups.iterator();
@@ -419,7 +437,7 @@ public class Group extends DSpaceObject
* @return Set of Integer groupIDs * @return Set of Integer groupIDs
* @throws SQLException * @throws SQLException
*/ */
public static Set allMemberGroupIDs(Context c, EPerson e) public static Set<Integer> allMemberGroupIDs(Context c, EPerson e)
throws SQLException throws SQLException
{ {
// two queries - first to get groups eperson is a member of // two queries - first to get groups eperson is a member of
@@ -429,7 +447,7 @@ public class Group extends DSpaceObject
"SELECT * FROM epersongroup2eperson WHERE eperson_id= ?", "SELECT * FROM epersongroup2eperson WHERE eperson_id= ?",
e.getID()); e.getID());
Set groupIDs = new HashSet(); Set<Integer> groupIDs = new HashSet<Integer>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -513,9 +531,9 @@ public class Group extends DSpaceObject
public static EPerson[] allMembers(Context c, Group g) public static EPerson[] allMembers(Context c, Group g)
throws SQLException throws SQLException
{ {
List epersonList = new ArrayList(); List<EPerson> epersonList = new ArrayList<EPerson>();
Set myEpeople = allMemberIDs(c, g); Set<Integer> myEpeople = allMemberIDs(c, g);
// now convert those Integers to EPerson objects // now convert those Integers to EPerson objects
Iterator i = myEpeople.iterator(); Iterator i = myEpeople.iterator();
@@ -538,19 +556,19 @@ public class Group extends DSpaceObject
* @return Set of Integer epersonIDs * @return Set of Integer epersonIDs
* @throws SQLException * @throws SQLException
*/ */
public static Set allMemberIDs(Context c, Group g) public static Set<Integer> allMemberIDs(Context c, Group g)
throws SQLException throws SQLException
{ {
// two queries - first to get all groups which are a member of this group // two queries - first to get all groups which are a member of this group
// second query gets all members of each group in the first query // second query gets all members of each group in the first query
Set epeopleIDs = new HashSet(); Set<Integer> epeopleIDs = new HashSet<Integer>();
// Get all groups which are a member of this group // Get all groups which are a member of this group
TableRowIterator tri = DatabaseManager.queryTable(c, "group2groupcache", TableRowIterator tri = DatabaseManager.queryTable(c, "group2groupcache",
"SELECT * FROM group2groupcache WHERE parent_id= ? ", "SELECT * FROM group2groupcache WHERE parent_id= ? ",
g.getID()); g.getID());
Set groupIDs = new HashSet(); Set<Integer> groupIDs = new HashSet<Integer>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -611,7 +629,7 @@ public class Group extends DSpaceObject
private static boolean epersonInGroup(Context c, int groupID, EPerson e) private static boolean epersonInGroup(Context c, int groupID, EPerson e)
throws SQLException throws SQLException
{ {
Set groupIDs = Group.allMemberGroupIDs(c, e); Set<Integer> groupIDs = Group.allMemberGroupIDs(c, e);
return groupIDs.contains(new Integer(groupID)); return groupIDs.contains(new Integer(groupID));
} }
@@ -868,6 +886,9 @@ public class Group extends DSpaceObject
public void delete() throws SQLException public void delete() throws SQLException
{ {
// FIXME: authorizations // FIXME: authorizations
myContext.addEvent(new Event(Event.DELETE, Constants.GROUP, getID(), getName()));
// Remove from cache // Remove from cache
myContext.removeCached(this, getID()); myContext.removeCached(this, getID());
@@ -964,6 +985,13 @@ public class Group extends DSpaceObject
// FIXME: Check authorisation // FIXME: Check authorisation
DatabaseManager.update(myContext, myRow); DatabaseManager.update(myContext, myRow);
if (modifiedMetadata)
{
myContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.GROUP, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
// Redo eperson mappings if they've changed // Redo eperson mappings if they've changed
if (epeopleChanged) if (epeopleChanged)
{ {
@@ -1062,7 +1090,7 @@ public class Group extends DSpaceObject
TableRowIterator tri = DatabaseManager.queryTable(myContext, "group2group", TableRowIterator tri = DatabaseManager.queryTable(myContext, "group2group",
"SELECT * FROM group2group"); "SELECT * FROM group2group");
Map parents = new HashMap(); Map<Integer,Set<Integer>> parents = new HashMap<Integer,Set<Integer>>();
while (tri.hasNext()) while (tri.hasNext())
{ {
@@ -1074,7 +1102,7 @@ public class Group extends DSpaceObject
// if parent doesn't have an entry, create one // if parent doesn't have an entry, create one
if (!parents.containsKey(parentID)) if (!parents.containsKey(parentID))
{ {
Set children = new HashSet(); Set<Integer> children = new HashSet<Integer>();
// add child id to the list // add child id to the list
children.add(childID); children.add(childID);
@@ -1084,7 +1112,7 @@ public class Group extends DSpaceObject
{ {
// parent has an entry, now add the child to the parent's record // parent has an entry, now add the child to the parent's record
// of children // of children
Set children = (Set) parents.get(parentID); Set<Integer> children = parents.get(parentID);
children.add(childID); children.add(childID);
} }
} }
@@ -1103,7 +1131,7 @@ public class Group extends DSpaceObject
{ {
Integer parentID = (Integer) i.next(); Integer parentID = (Integer) i.next();
Set myChildren = getChildren(parents, parentID); Set<Integer> myChildren = getChildren(parents, parentID);
Iterator j = myChildren.iterator(); Iterator j = myChildren.iterator();
@@ -1112,7 +1140,7 @@ public class Group extends DSpaceObject
// child of a parent // child of a parent
Integer childID = (Integer) j.next(); Integer childID = (Integer) j.next();
((Set) parents.get(parentID)).add(childID); ((Set<Integer>) parents.get(parentID)).add(childID);
} }
} }
@@ -1127,7 +1155,7 @@ public class Group extends DSpaceObject
{ {
Integer parent = (Integer) pi.next(); Integer parent = (Integer) pi.next();
Set children = (Set) parents.get(parent); Set<Integer> children = parents.get(parent);
Iterator ci = children.iterator(); // child iterator Iterator ci = children.iterator(); // child iterator
while (ci.hasNext()) while (ci.hasNext())
@@ -1158,16 +1186,16 @@ public class Group extends DSpaceObject
* the parent you're interested in * the parent you're interested in
* @return Map whose keys are all of the children of a parent * @return Map whose keys are all of the children of a parent
*/ */
private Set getChildren(Map parents, Integer parent) private Set<Integer> getChildren(Map<Integer,Set<Integer>> parents, Integer parent)
{ {
Set myChildren = new HashSet(); Set<Integer> myChildren = new HashSet<Integer>();
// degenerate case, this parent has no children // degenerate case, this parent has no children
if (!parents.containsKey(parent)) if (!parents.containsKey(parent))
return myChildren; return myChildren;
// got this far, so we must have children // got this far, so we must have children
Set children = (Set) parents.get(parent); Set<Integer> children = parents.get(parent);
// now iterate over all of the children // now iterate over all of the children
Iterator i = children.iterator(); Iterator i = children.iterator();

View File

@@ -0,0 +1,183 @@
/*
* BasicDispatcher.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.core.Utils;
/**
* BasicDispatcher implements the primary task of a Dispatcher: it delivers a
* filtered list of events, synchronously, to a configured list of consumers. It
* may be extended for more elaborate behavior.
*
* @version $Revision$
*/
public class BasicDispatcher extends Dispatcher
{
public BasicDispatcher(String name)
{
super(name);
}
/** log4j category */
private static Logger log = Logger.getLogger(BasicDispatcher.class);
public void addConsumerProfile(ConsumerProfile cp)
throws IllegalArgumentException
{
if (consumers.containsKey(cp.getName()))
throw new IllegalArgumentException(
"This dispatcher already has a consumer named \""
+ cp.getName() + "\"");
consumers.put(cp.getName(), cp);
if (log.isDebugEnabled())
{
int n = 0;
for (Iterator i = cp.getFilters().iterator(); i.hasNext(); ++n)
{
int f[] = (int[]) i.next();
log.debug("Adding Consumer=\"" + cp.getName() + "\", instance="
+ cp.getConsumer().toString() + ", filter["
+ String.valueOf(n) + "]=(ObjMask="
+ String.valueOf(f[Event.SUBJECT_MASK])
+ ", EventMask=" + String.valueOf(f[Event.EVENT_MASK])
+ ")");
}
}
}
/**
* Dispatch all events added to this Context according to configured
* consumers.
*
* @param ctx
* the execution context
*/
public void dispatch(Context ctx)
{
if (!consumers.isEmpty())
{
List events = ctx.getEvents();
if (events == null)
{
return;
}
if (log.isDebugEnabled())
log.debug("Processing queue of "
+ String.valueOf(events.size()) + " events.");
// transaction identifier applies to all events created in
// this context for the current transaction. Prefix it with
// some letters so RDF readers don't mistake it for an integer.
String tid = "TX" + Utils.generateKey();
for (Iterator ei = events.iterator(); ei.hasNext();)
{
Event event = (Event) ei.next();
event.setDispatcher(getIdentifier());
event.setTransactionID(tid);
if (log.isDebugEnabled())
log.debug("Iterating over "
+ String.valueOf(consumers.values().size())
+ " consumers...");
for (Iterator ci = consumers.values().iterator(); ci.hasNext();)
{
ConsumerProfile cp = (ConsumerProfile) ci.next();
if (event.pass(cp.getFilters()))
{
if (log.isDebugEnabled())
log.debug("Sending event to \"" + cp.getName()
+ "\": " + event.toString());
try
{
cp.getConsumer().consume(ctx, event);
// Record that the event has been consumed by this
// consumer
event.setBitSet(cp.getName());
}
catch (Exception e)
{
log.error("Consumer(\"" + cp.getName()
+ "\").consume threw: " + e.toString(), e);
}
}
}
}
// Call end on the consumers that got synchronous events.
for (Iterator ci = consumers.values().iterator(); ci.hasNext();)
{
ConsumerProfile cp = (ConsumerProfile) ci.next();
if (cp != null)
{
if (log.isDebugEnabled())
log.debug("Calling end for consumer \"" + cp.getName()
+ "\"");
try
{
cp.getConsumer().end(ctx);
}
catch (Exception e)
{
log.error("Error in Consumer(\"" + cp.getName()
+ "\").end: " + e.toString(), e);
}
}
}
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Consumer.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import org.dspace.core.Context;
/**
* Interface for content event consumers. Note that the consumer cannot tell if
* it is invoked synchronously or asynchronously; the consumer interface and
* sequence of calls is the same for both. Asynchronous consumers may see more
* consume() calls between the start and end of the event stream, if they are
* invoked asynchronously, once in a long time period, rather than synchronously
* after every Context.commit().
*
* @version $Revision$
*/
public interface Consumer
{
/**
* Initialize - allocate any resources required to operate. This may include
* initializing any pooled JMS resources. Called ONCE when created by the
* dispatcher pool. This should be used to set up expensive resources that
* will remain for the lifetime of the consumer.
*/
public void initialize() throws Exception;
/**
* Consume an event; events may get filtered at the dispatcher level, hiding
* it from the consumer. This behavior is based on the dispatcher/consumer
* configuration. Should include logic to initialize any resources required
* for a batch of events.
*
* @param ctx
* the execution context object
*
* @param event
* the content event
*/
public void consume(Context ctx, Event event) throws Exception;
/**
* Signal that there are no more events queued in this event stream and
* event processing for the preceding consume calls should be finished up.
*/
public void end(Context ctx) throws Exception;
/**
* Finish - free any allocated resources. Called when consumer (via it's
* parent dispatcher) is going to be destroyed by the dispatcher pool.
*/
public void finish(Context ctx) throws Exception;
}

View File

@@ -0,0 +1,179 @@
/*
* ConsumerProfile.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
/**
* An instance of this class contains the configuration profile of a specific,
* named Consumer, <em>in the context of a specific
* Dispatcher</em>. This
* includes the name, the class to instantiate and event filters. Note that all
* characteristics are "global" and the same for all dispatchers.
*
* @version $Revision$
*/
public class ConsumerProfile
{
/** log4j category */
private static Logger log = Logger.getLogger(EventManager.class);
/** Name matching the key in DSpace Configuration */
private String name;
/** Instance of configured consumer class */
private Consumer consumer;
/** Filters - each is an array of 2 bitmasks, action mask and subject mask */
private List<int[]> filters;
// Prefix of keys in DSpace Configuration.
private final String CONSUMER_PREFIX = "event.consumer.";
/**
* Constructor.
*/
private ConsumerProfile(String name)
{
this.name = name;
}
/**
* Factory method, create new profile from configuration.
*
* @param name
* configuration name of the consumer profile
* @returns a new ConsumerProfile; never null.
*/
public static ConsumerProfile makeConsumerProfile(String name)
throws IllegalArgumentException, ClassNotFoundException,
InstantiationException, IllegalAccessException
{
ConsumerProfile result = new ConsumerProfile(name);
result.readConfiguration();
return result;
}
// Get class and filters from DSpace Configuration.
private void readConfiguration() throws IllegalArgumentException,
ClassNotFoundException, InstantiationException,
IllegalAccessException
{
String className = ConfigurationManager.getProperty(CONSUMER_PREFIX
+ name + ".class");
String filterString = ConfigurationManager.getProperty(CONSUMER_PREFIX
+ name + ".filters");
if (className == null)
throw new IllegalArgumentException(
"No class configured for consumer named: " + name);
if (filterString == null)
throw new IllegalArgumentException(
"No filters configured for consumer named: " + name);
consumer = (Consumer) Class.forName(className.trim()).newInstance();
// Each "filter" is <objectTypes> + <eventTypes> : ...
filters = new ArrayList<int[]>();
String part[] = filterString.trim().split(":");
for (int j = 0; j < part.length; ++j)
{
String fpart[] = part[j].split("\\+");
if (fpart.length != 2)
log
.error("Bad Filter clause in consumer stanza in Configuration entry for "
+ CONSUMER_PREFIX
+ name
+ ".consumers: "
+ part[j]);
else
{
int filter[] = new int[2];
filter[0] = filter[1] = 0;
String objectNames[] = fpart[0].split("\\|");
for (int k = 0; k < objectNames.length; ++k)
{
int ot = Event.parseObjectType(objectNames[k]);
if (ot == 0)
log
.error("Bad ObjectType in Consumer Stanza in Configuration entry for "
+ CONSUMER_PREFIX
+ name
+ ".consumers: " + objectNames[k]);
else
filter[Event.SUBJECT_MASK] |= ot;
}
String eventNames[] = fpart[1].split("\\|");
for (int k = 0; k < eventNames.length; ++k)
{
int et = Event.parseEventType(eventNames[k]);
if (et == 0)
log
.error("Bad EventType in Consumer Stanza in Configuration entry for "
+ CONSUMER_PREFIX
+ name
+ ".consumers: " + eventNames[k]);
else
filter[Event.EVENT_MASK] |= et;
}
filters.add(filter);
}
}
}
public Consumer getConsumer()
{
return consumer;
}
public List<int[]> getFilters()
{
return filters;
}
public String getName()
{
return name;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Dispatcher.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dspace.core.Context;
/**
* Interface for event dispatchers. The primary role of a dispatcher is to
* deliver a set of events to a configured list of consumers. It may also
* transform, consolidate, and otherwise optimize the event stream prior to
* delivering events to its consumers.
*
* @version $Revision$
*/
public abstract class Dispatcher
{
protected String name;
/** unique identifer of this dispatcher - cached hash of its text Name */
protected int identifier;
/**
* Map of consumers by their configured name.
*/
protected Map<String, ConsumerProfile> consumers = new HashMap<String, ConsumerProfile>();
protected Dispatcher(String name)
{
super();
this.name = name;
this.identifier = name.hashCode();
}
public Collection getConsumers()
{
return consumers.values();
}
/**
* @returns unique integer that identifies this Dispatcher configuration.
*/
public int getIdentifier()
{
return identifier;
}
/**
* Add a consumer to the end of the list.
*
* @param consumer
* the event consumer to add
* @param filter
* the event filter to apply
*/
public abstract void addConsumerProfile(ConsumerProfile cp)
throws IllegalArgumentException;
/**
* Dispatch all events added to this Context according to configured
* consumers.
*
* @param ctx
* the execution context object
*/
public abstract void dispatch(Context ctx);
}

View File

@@ -0,0 +1,614 @@
/*
* Event.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
/**
* An Event object represents a single action that changed one object in the
* DSpace data model. An "atomic" action at the application or business-logic
* API level may spawn many of these events.
* <p>
* This class includes tools to help set and use the contents of the event. Note
* that it describes DSpace data object types in two ways: by the type
* identifiers in the Constants class, and also by an Event-specific bitmask
* (used by its internal filters). All public API calls use the Constants
* version of the data model types.
* <p>
* Note that the type of the event itself is actually descriptive of the
* <em>action</em> it performs: ADD, MODIFY, etc. The most significant
* elements of the event are:
* <p>
* <br> - (Action) Type <br> - Subject -- DSpace object to which the action
* applies, e.g. the Collection to which an ADD adds a member. <br> - Object --
* optional, when present it is the other object effected by an action, e.g. the
* Item ADDed to a Collection by an ADD. <br> - detail -- a textual summary of
* what changed, content and its significance varies by the combination of
* action and subject type. <br> - timestamp -- exact millisecond timestamp at
* which event was logged.
*
* @version $Revision$
*/
public class Event implements Serializable
{
/** ---------- Constants ------------- * */
/** Event (Action) types */
public static final int CREATE = 1 << 0; // create new object
public static final int MODIFY = 1 << 1; // modify object
public static final int MODIFY_METADATA = 1 << 2; // modify object
public static final int ADD = 1 << 3; // add content to container
public static final int REMOVE = 1 << 4; // remove content from container
public static final int DELETE = 1 << 5; // destroy object
/** Index of filter parts in their array: */
public static final int SUBJECT_MASK = 0; // mask of subject types
public static final int EVENT_MASK = 1; // mask of event type
// XXX NOTE: keep this up to date with any changes to event (action) types.
private static final String eventTypeText[] = { "CREATE", "MODIFY",
"MODIFY_METADATA", "ADD", "REMOVE", "DELETE" };
/** XXX NOTE: These constants must be kept synchronized * */
/** XXX NOTE: with ALL_OBJECTS_MASK *AND* objTypeToMask hash * */
private static final int NONE = 0;
private static final int BITSTREAM = 1 << Constants.BITSTREAM; // 0
private static final int BUNDLE = 1 << Constants.BUNDLE; // 1
private static final int ITEM = 1 << Constants.ITEM; // 2
private static final int COLLECTION = 1 << Constants.COLLECTION; // 3
private static final int COMMUNITY = 1 << Constants.COMMUNITY; // 4
private static final int SITE = 1 << Constants.SITE; // 5
private static final int GROUP = 1 << Constants.GROUP; // 6
private static final int EPERSON = 1 << Constants.EPERSON; // 7
private static final int ALL_OBJECTS_MASK = BITSTREAM | BUNDLE | ITEM
| COLLECTION | COMMUNITY | SITE | GROUP | EPERSON;
private static Map<Integer, Integer> objTypeToMask = new HashMap<Integer, Integer>();
private static Map<Integer, Integer> objMaskToType = new HashMap<Integer, Integer>();
static
{
objTypeToMask.put(new Integer(Constants.BITSTREAM), new Integer(
BITSTREAM));
objMaskToType.put(new Integer(BITSTREAM), new Integer(
Constants.BITSTREAM));
objTypeToMask.put(new Integer(Constants.BUNDLE), new Integer(BUNDLE));
objMaskToType.put(new Integer(BUNDLE), new Integer(Constants.BUNDLE));
objTypeToMask.put(new Integer(Constants.ITEM), new Integer(ITEM));
objMaskToType.put(new Integer(ITEM), new Integer(Constants.ITEM));
objTypeToMask.put(new Integer(Constants.COLLECTION), new Integer(
COLLECTION));
objMaskToType.put(new Integer(COLLECTION), new Integer(
Constants.COLLECTION));
objTypeToMask.put(new Integer(Constants.COMMUNITY), new Integer(
COMMUNITY));
objMaskToType.put(new Integer(COMMUNITY), new Integer(
Constants.COMMUNITY));
objTypeToMask.put(new Integer(Constants.SITE), new Integer(SITE));
objMaskToType.put(new Integer(SITE), new Integer(Constants.SITE));
objTypeToMask.put(new Integer(Constants.GROUP), new Integer(GROUP));
objMaskToType.put(new Integer(GROUP), new Integer(Constants.GROUP));
objTypeToMask.put(new Integer(Constants.EPERSON), new Integer(EPERSON));
objMaskToType.put(new Integer(EPERSON), new Integer(Constants.EPERSON));
}
/** ---------- Event Fields ------------- * */
/** identifier of Dispatcher that created this event (hash of its name) */
private int dispatcher;
/** event (action) type - above enumeration */
private int eventType;
/** object-type of SUBJECT - see above enumeration */
private int subjectType;
/** content model identifier */
private int subjectID;
/** object-type of SUBJECT - see above enumeration */
private int objectType = NONE;
/** content model identifier */
private int objectID = -1;
/** timestamp */
private long timeStamp;
/** "detail" - arbitrary field for relevant detail, */
/** e.g. former handle for DELETE event since obj is no longer available. */
/**
* FIXME This field is not a complete view of the DSpaceObject that was
* modified. Providing these objects to the consumer (e.g. by storing
* lifecycle versions of the changed objects in the context) would provide
* for more complex consumer abilities that are beyond our purview.
*/
private String detail;
/** unique key to bind together events from one context's transaction */
private String transactionID;
/** identity of authenticated user, i.e. context.getCurrentUser() */
/** only needed in the event for marshalling for asynch event messages */
private int currentUser = -1;
/** copy of context's "extraLogInfo" filed, used only for */
/** marshalling for asynch event messages */
private String extraLogInfo = null;
private BitSet consumedBy = new BitSet();
/** log4j category */
private static Logger log = Logger.getLogger(Event.class);
/**
* Constructor.
*
* @param eventType
* action type, e.g. Event.ADD
* @param subjectType
* DSpace Object Type of subject e.g. Constants.ITEM.
* @param subjectID
* database ID of subject instance.
* @param detail
* detail information that depends on context.
*/
public Event(int eventType, int subjectType, int subjectID, String detail)
{
this.eventType = eventType;
this.subjectType = coreTypeToMask(subjectType);
this.subjectID = subjectID;
timeStamp = System.currentTimeMillis();
this.detail = detail;
}
/**
* Constructor.
*
* @param eventType
* action type, e.g. Event.ADD
* @param subjectType
* DSpace Object Type of subject e.g. Constants.ITEM.
* @param subjectID
* database ID of subject instance.
* @param objectType
* DSpace Object Type of object e.g. Constants.BUNDLE.
* @param objectID
* database ID of object instance.
* @param detail
* detail information that depends on context.
* @param
*/
public Event(int eventType, int subjectType, int subjectID, int objectType,
int objectID, String detail)
{
this.eventType = eventType;
this.subjectType = coreTypeToMask(subjectType);
this.subjectID = subjectID;
this.objectType = coreTypeToMask(objectType);
this.objectID = objectID;
timeStamp = System.currentTimeMillis();
this.detail = detail;
}
/**
* Compare two events. Ignore any difference in the timestamps. Also ignore
* transactionID since that is not always set initially.
*
* @param other
* the event to compare this one to
* @returns true if events are "equal", false otherwise.
*/
public boolean equals(Event other)
{
return (this.detail == null ? other.detail == null : this.detail
.equals(other.detail))
&& this.eventType == other.eventType
&& this.subjectType == other.subjectType
&& this.subjectID == other.subjectID
&& this.objectType == other.objectType
&& this.objectID == other.objectID;
}
/**
* Set the identifier of the dispatcher that first processed this event.
*
* @param id
* the unique (hash code) value characteristic of the dispatcher.
*/
public void setDispatcher(int id)
{
dispatcher = id;
}
// translate a "core.Constants" object type value to local bitmask value.
private static int coreTypeToMask(int core)
{
Integer mask = (Integer) objTypeToMask.get(new Integer(core));
if (mask == null)
return -1;
else
return mask.intValue();
}
// translate bitmask object-type to "core.Constants" object type.
private static int maskTypeToCore(int mask)
{
Integer core = (Integer) objMaskToType.get(new Integer(mask));
if (core == null)
return -1;
else
return core.intValue();
}
/**
* Get the DSpace object which is the "object" of an event.
*
* @returns DSpaceObject or null if none can be found or no object was set.
*/
public DSpaceObject getObject(Context context) throws SQLException
{
int type = getObjectType();
int id = getObjectID();
if (type < 0 || id < 0)
return null;
else
return DSpaceObject.find(context, type, id);
}
/**
* Syntactic sugar to get the DSpace object which is the "subject" of an
* event.
*
* @returns DSpaceObject or null if none can be found.
*/
public DSpaceObject getSubject(Context context) throws SQLException
{
return DSpaceObject.find(context, getSubjectType(), getSubjectID());
}
/**
* @returns database ID of subject of this event.
*/
public int getSubjectID()
{
return subjectID;
}
/**
* @returns database ID of object of this event, or -1 if none was set.
*/
public int getObjectID()
{
return objectID;
}
/**
* @returns type number (e.g. Constants.ITEM) of subject of this event.
*/
public int getSubjectType()
{
return maskTypeToCore(subjectType);
}
/**
* @returns type number (e.g. Constants.ITEM) of object of this event, or -1
* if none was set.
*/
public int getObjectType()
{
return maskTypeToCore(objectType);
}
/**
* @returns type of subject of this event as a String, e.g. for logging.
*/
public String getSubjectTypeAsString()
{
int i = log2(subjectType);
if (i >= 0 && i < Constants.typeText.length)
return Constants.typeText[i];
else
return "(Unknown)";
}
/**
* @returns type of object of this event as a String, e.g. for logging.
*/
public String getObjectTypeAsString()
{
int i = log2(objectType);
if (i >= 0 && i < Constants.typeText.length)
return Constants.typeText[i];
else
return "(Unknown)";
}
/**
* Translate a textual DSpace Object type name into an event subject-type
* mask. NOTE: This returns a BIT-MASK, not a numeric type value; the mask
* is only used within the event system.
*
* @param s
* text name of object type.
* @returns numeric value of object type or 0 for error.
*/
public static int parseObjectType(String s)
{
if (s.equals("*") | s.equalsIgnoreCase("all"))
return ALL_OBJECTS_MASK;
else
{
int id = Constants.getTypeID(s.toUpperCase());
if (id >= 0)
return 1 << id;
}
return 0;
}
/**
* @returns event-type (i.e. action) this event, one of the masks like
* Event.ADD defined above.
*/
public int getEventType()
{
return eventType;
}
/**
* Get the text name of event (action) type.
*
* @returns event-type (i.e. action) this event as a String, e.g. for
* logging.
*/
public String getEventTypeAsString()
{
int i = log2(eventType);
if (i >= 0 && i < eventTypeText.length)
return eventTypeText[i];
else
return "(Unknown)";
}
/**
* Interpret named event type.
*
* @param text
* name of event type.
* @returns numeric value of event type or 0 for error.
*/
public static int parseEventType(String s)
{
if (s.equals("*") | s.equalsIgnoreCase("all"))
{
int result = 0;
for (int i = 0; i < eventTypeText.length; ++i)
result |= (1 << i);
return result;
}
for (int i = 0; i < eventTypeText.length; ++i)
if (eventTypeText[i].equalsIgnoreCase(s))
return 1 << i;
return 0;
}
/**
* @returns timestamp at which event occurred, as a count of milliseconds
* since the epoch (standard Java format).
*/
public long getTimeStamp()
{
return timeStamp;
}
/**
* @returns hashcode identifier of name of Dispatcher which first dispatched
* this event. (Needed by asynch dispatch code.)
*/
public int getDispatcher()
{
return dispatcher;
}
/**
* @returns value of detail element of the event.
*/
public String getDetail()
{
return detail;
}
/**
* @returns value of transactionID element of the event.
*/
public String getTransactionID()
{
return transactionID;
}
/**
* Sets value of transactionID element of the event.
*
* @param tid
* new value of transactionID.
*/
public void setTransactionID(String tid)
{
transactionID = tid;
}
public void setCurrentUser(int uid)
{
currentUser = uid;
}
public int getCurrentUser()
{
return currentUser;
}
public void setExtraLogInfo(String info)
{
extraLogInfo = info;
}
public String getExtraLogInfo()
{
return extraLogInfo;
}
/**
* @param filters
* list of filter masks; each one is an Array of two ints.
* @returns true if this event would be passed through the given filter
* list.
*/
public boolean pass(List filters)
{
boolean result = false;
for (Iterator fi = filters.iterator(); fi.hasNext();)
{
int filter[] = (int[]) fi.next();
if ((subjectType & filter[SUBJECT_MASK]) != 0
&& (eventType & filter[EVENT_MASK]) != 0)
result = true;
}
if (log.isDebugEnabled())
log.debug("Filtering event: " + "eventType="
+ String.valueOf(eventType) + ", subjectType="
+ String.valueOf(subjectType) + ", result="
+ String.valueOf(result));
return result;
}
// dumb integer "log base 2", returns -1 if there are no 1's in number.
private static int log2(int n)
{
for (int i = 0; i < 32; ++i)
if (n == 1)
return i;
else
n = n >> 1;
return -1;
}
/**
* Keeps track of which consumers the event has been consumed by. Should be
* called by a dispatcher when calling consume(Context ctx, String name,
* Event event) on an event.
*
* @param consumerName
*/
public void setBitSet(String consumerName)
{
consumedBy.set(EventManager.getConsumerIndex(consumerName));
}
public BitSet getBitSet()
{
return consumedBy;
}
/**
* @returns Detailed string representation of contents of this event, to
* help in logging and debugging.
*/
public String toString()
{
return "org.dspace.event.Event(eventType="
+ this.getEventTypeAsString()
+ ", SubjectType="
+ this.getSubjectTypeAsString()
+ ", SubjectID="
+ String.valueOf(subjectID)
+ ", ObjectType="
+ this.getObjectTypeAsString()
+ ", ObjectID="
+ String.valueOf(objectID)
+ ", TimeStamp="
+ String.valueOf(timeStamp)
+ ", dispatcher="
+ String.valueOf(dispatcher)
+ ", detail="
+ (detail == null ? "[null]" : "\"" + detail + "\"")
+ ", transactionID="
+ (transactionID == null ? "[null]" : "\"" + transactionID
+ "\"") + ")";
}
}

View File

@@ -0,0 +1,389 @@
/*
* EventManager.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.PoolUtils;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
/**
* Class for managing the content event environment. The EventManager mainly
* acts as a factory for Dispatchers, which are used by the Context to send
* events to consumers. It also contains generally useful utility methods.
*
* Version: $Revision$
*/
public class EventManager
{
/** log4j category */
private static Logger log = Logger.getLogger(EventManager.class);
// The name of the default dispatcher assigned to every new context unless
// overridden
public static final String DEFAULT_DISPATCHER = "default";
private static DispatcherPoolFactory dispatcherFactory = null;
private static GenericKeyedObjectPool.Config poolConfig = null;
// Keyed FIFO Pool of event dispatchers
private static KeyedObjectPool dispatcherPool = null;
private static HashMap<String, Integer> consumerIndicies = null;
private static final String CONSUMER_PFX = "event.consumer.";
public EventManager()
{
initPool();
log.info("Event Dispatcher Pool Initialized");
}
private static void initPool()
{
if (dispatcherPool == null)
{
// TODO EVENT Some of these pool configuration
// parameters can live in dspace.cfg or a
// separate configuration file
// TODO EVENT Eviction parameters should be set
poolConfig = new GenericKeyedObjectPool.Config();
poolConfig.maxActive = 100;
poolConfig.maxIdle = 5;
poolConfig.maxTotal = 100;
try
{
dispatcherFactory = new DispatcherPoolFactory();
dispatcherPool = PoolUtils
.synchronizedPool(new GenericKeyedObjectPool(
dispatcherFactory, poolConfig));
enumerateConsumers();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* Get dispatcher for configuration named by "name". Returns cached instance
* if one exists.
*/
public static Dispatcher getDispatcher(String name)
{
if (dispatcherPool == null)
{
initPool();
}
if (name == null)
name = DEFAULT_DISPATCHER;
try
{
return (Dispatcher) dispatcherPool.borrowObject(name);
}
catch (Exception e)
{
throw new RuntimeException("Unable to aquire dispatcher named "
+ name, e);
}
}
public static void returnDispatcher(String key, Dispatcher disp)
{
try
{
dispatcherPool.returnObject(key, disp);
}
catch (Exception e)
{
log.error(e.getMessage(), e);
}
}
protected static int getConsumerIndex(String consumerClass)
{
Integer index = (Integer) consumerIndicies.get(consumerClass);
return index != null ? index.intValue() : -1;
}
private static void enumerateConsumers()
{
Enumeration propertyNames = ConfigurationManager.propertyNames();
int bitSetIndex = 0;
if (consumerIndicies == null)
{
consumerIndicies = new HashMap<String, Integer>();
}
while (propertyNames.hasMoreElements())
{
String ckey = ((String) propertyNames.nextElement()).trim();
if (ckey.startsWith(CONSUMER_PFX) && ckey.endsWith(".class"))
{
String consumerName = ckey.substring(CONSUMER_PFX.length(),
ckey.length() - 6);
consumerIndicies.put(consumerName, (Integer) bitSetIndex);
bitSetIndex++;
}
}
}
static class DispatcherPoolFactory implements KeyedPoolableObjectFactory
{
// Prefix of keys in DSpace Configuration
private static final String PROP_PFX = "event.dispatcher.";
// Cache of event dispatchers, keyed by name, for re-use.
private static Map<String, String> dispatchers = new HashMap<String, String>();
public DispatcherPoolFactory()
{
parseEventConfig();
log.info("");
}
public Object makeObject(Object dispatcherName) throws Exception
{
Dispatcher dispatcher = null;
String dispClass = dispatchers.get(dispatcherName);
if (dispClass != null)
{
try
{
// all this to call a constructor with an argument
final Class argTypes[] = { String.class };
Constructor dc = Class.forName(dispClass).getConstructor(
argTypes);
Object args[] = new Object[1];
args[0] = dispatcherName;
dispatcher = (Dispatcher) dc.newInstance(args);
// OK, now get its list of consumers/filters
String consumerKey = PROP_PFX + dispatcherName
+ ".consumers";
String consumerList = ConfigurationManager
.getProperty(consumerKey);
if (consumerList == null)
{
throw new RuntimeException(
"No Configuration entry found for consumer list of event Dispatcher: \""
+ consumerKey + "\"");
}
// Consumer list format:
// <consumer-name>:<mode>, ...
String[] consumerStanza = consumerList.trim().split(
"\\s*,\\s*");
// I think this should be a fatal error.. --lcs
if (consumerStanza.length < 1)
{
throw new RuntimeException(
"Cannot initialize Dispatcher, malformed Configuration value for "
+ consumerKey);
}
ConsumerProfile consumerProfile = null;
// parts: 0 is name, part 1 is mode.
for (int i = 0; i < consumerStanza.length; i++)
{
consumerProfile = ConsumerProfile
.makeConsumerProfile(consumerStanza[i]);
consumerProfile.getConsumer().initialize();
dispatcher.addConsumerProfile(consumerProfile);
}
}
catch (NoSuchMethodException e)
{
throw new RuntimeException(
"Constructor not found for event dispatcher="
+ dispatcherName, e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException(
"Error creating event dispatcher=" + dispatcherName,
e);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(
"Dispatcher/Consumer class not found for event dispatcher="
+ dispatcherName, e);
}
catch (InstantiationException e)
{
throw new RuntimeException(
"Dispatcher/Consumer instantiation failure for event dispatcher="
+ dispatcherName, e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(
"Dispatcher/Consumer access failure for event dispatcher="
+ dispatcherName, e);
}
}
else
{
throw new RuntimeException(
"Requested Dispatcher Does Not Exist In DSpace Configuration!");
}
return dispatcher;
}
public void activateObject(Object arg0, Object arg1) throws Exception
{
// No-op
return;
}
public void destroyObject(Object key, Object dispatcher)
throws Exception
{
Context ctx = new Context();
for (Iterator ci = ((Dispatcher) dispatcher).getConsumers()
.iterator(); ci.hasNext();)
{
ConsumerProfile cp = (ConsumerProfile) ci.next();
if (cp != null)
cp.getConsumer().finish(ctx);
}
return;
}
public void passivateObject(Object arg0, Object arg1) throws Exception
{
// No-op
return;
}
public boolean validateObject(Object arg0, Object arg1)
{
// No-op
return false;
}
/**
* Looks through the configuration for dispatcher configurations and
* loads one of each into a HashMap. This Map will be used to clone new
* objects when the pool needs them.
*
* Looks for configuration properties like:
*
* <pre>
* # class of dispatcher &quot;default&quot;
* event.dispatcher.default = org.dspace.event.BasicDispatcher
* # list of consumers followed by filters for each, format is
* # &lt;consumerClass&gt;:&lt;filter&gt;[:&lt;anotherFilter&gt;..] , ...
* # and each filter is expressed as:
* # &lt;objectType&gt;[|&lt;objectType&gt; ...] + &lt;eventType&gt;[|&lt;eventType&gt; ..]
* org.dspace.event.TestConsumer:all+all, \
* org.dspace.eperson.SubscribeConsumer:Item+CREATE|DELETE:Collection+ADD, ...
* </pre>
*
*/
private void parseEventConfig()
{
Enumeration propertyNames = ConfigurationManager.propertyNames();
while (propertyNames.hasMoreElements())
{
String ckey = ((String) propertyNames.nextElement()).trim();
if (ckey.startsWith(PROP_PFX) && ckey.endsWith(".class"))
{
String name = ckey.substring(PROP_PFX.length(), ckey
.length() - 6);
String dispatcherClass = ConfigurationManager
.getProperty(ckey);
// Can we grab all of the consumers configured for this
// dispatcher
// and store them also? Then there is no
// ConfigurationManager call
// upon other makeObject(key) requests resulting in a faster
// pool
// get.
dispatchers.put(name, dispatcherClass);
}
}
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* TestConsumer.java
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.event;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
/**
* Demonstration and test consumer for the event system. This consumer only
* makes an entry in the log, and on an output stream, for each event it
* receives. It also logs when consume() and end() get called. It is intended
* for testing, exploring, and debugging the event system.
*
* @version $Revision$
*/
public class TestConsumer implements Consumer
{
// Log4j logger
private static Logger log = Logger.getLogger(TestConsumer.class);
// Send diagnostic output here - set to null to turn it off.
private static PrintStream out = ConfigurationManager
.getBooleanProperty("testConsumer.verbose") ? System.out : null;
static final DateFormat df = new SimpleDateFormat(
"dd-MMM-yyyy HH:mm:ss.SSS Z");
public void initialize() throws Exception
{
log.info("EVENT: called TestConsumer.initialize();");
if (out != null)
out.println("TestConsumer.initialize();");
}
/**
* Consume a content event - display it in detail.
*
* @param ctx
* DSpace context
* @param event
* Content event
*/
public void consume(Context ctx, Event event) throws Exception
{
EPerson ep = ctx.getCurrentUser();
String user = (ep == null) ? "(none)" : ep.getEmail();
String detail = event.getDetail();
String msg = "EVENT: called TestConsumer.consume(): EventType="
+ event.getEventTypeAsString()
+ ", SubjectType="
+ event.getSubjectTypeAsString()
+ ", SubjectID="
+ String.valueOf(event.getSubjectID())
+ ", ObjectType="
+ event.getObjectTypeAsString()
+ ", ObjectID="
+ String.valueOf(event.getObjectID())
+ ", TimeStamp="
+ df.format(new Date(event.getTimeStamp()))
+ ", user=\""
+ user
+ "\""
+ ", extraLog=\""
+ ctx.getExtraLogInfo()
+ "\""
+ ", dispatcher="
+ String.valueOf(event.getDispatcher())
+ ", detail="
+ (detail == null ? "[null]" : "\"" + detail + "\"")
+ ", transactionID="
+ (event.getTransactionID() == null ? "[null]" : "\""
+ event.getTransactionID() + "\"") + ", context="
+ ctx.toString();
log.info(msg);
if (out != null)
out.println("TestConsumer.consume(): " + msg);
}
public void end(Context ctx) throws Exception
{
log.info("EVENT: called TestConsumer.end();");
if (out != null)
out.println("TestConsumer.end();");
}
public void finish(Context ctx) throws Exception
{
log.info("EVENT: called TestConsumer.finish();");
if (out != null)
out.println("TestConsumer.finish();");
}
}

View File

@@ -0,0 +1,282 @@
/*
* SearchConsumer.java
*
* Location: $URL$
*
* Version: $Revision$
*
* Date: $Date$
*
* Copyright (c) 2002-2007, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.search;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.log4j.Logger;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
/**
* Class for updating search indices from content events.
*
* @version $Revision$
*/
public class SearchConsumer implements Consumer
{
/** log4j logger */
private static Logger log = Logger.getLogger(SearchConsumer.class);
// collect Items, Collections, Communities newly created.
private Set objectsCreated = null;
// collect Items, Collections, Communities that need reindexing
private Set objectsToUpdate = null;
// handles to delete since IDs are not useful by now.
private Set handlesToDelete = null;
public void initialize() throws Exception
{
// No-op
}
/**
* Consume a content event -- just build the sets of objects to add (new) to
* the index, update, and delete.
*
* @param ctx
* DSpace context
* @param event
* Content event
*/
public void consume(Context ctx, Event event) throws Exception
{
if (objectsCreated == null)
{
objectsCreated = new HashSet();
objectsToUpdate = new HashSet();
handlesToDelete = new HashSet();
}
int st = event.getSubjectType();
if (!(st == Constants.ITEM || st == Constants.BUNDLE
|| st == Constants.COLLECTION || st == Constants.COMMUNITY))
{
log
.warn("SearchConsumer should not have been given this kind of Subject in an event, skipping: "
+ event.toString());
return;
}
DSpaceObject dso = null;
// EVENT FIXME This call to getSubjectOfEvent is catching a SQLException
// but other Consumers don't
try
{
dso = event.getSubject(ctx);
}
catch (SQLException se)
{
}
// If event subject is a Bundle and event was Add or Remove,
// transform the event to be a Modify on the owning Item.
// It could be a new bitstream in the TEXT bundle which
// would change the index.
int et = event.getEventType();
if (st == Constants.BUNDLE)
{
if ((et == Event.ADD || et == Event.REMOVE) && dso != null
&& ((Bundle) dso).getName().equals("TEXT"))
{
st = Constants.ITEM;
et = Event.MODIFY;
dso = ((Bundle) dso).getItems()[0];
if (log.isDebugEnabled())
log.debug("Transforming Bundle event into MODIFY of Item "
+ dso.getHandle());
}
else
return;
}
switch (et)
{
case Event.CREATE:
if (dso == null)
log.warn("CREATE event, could not get object for "
+ event.getSubjectTypeAsString() + " id="
+ String.valueOf(event.getSubjectID())
+ ", perhaps it has been deleted.");
else
objectsCreated.add(dso);
break;
case Event.MODIFY:
case Event.MODIFY_METADATA:
if (dso == null)
log.warn("MODIFY event, could not get object for "
+ event.getSubjectTypeAsString() + " id="
+ String.valueOf(event.getSubjectID())
+ ", perhaps it has been deleted.");
else
objectsToUpdate.add(dso);
break;
case Event.DELETE:
String detail = event.getDetail();
if (detail == null)
log.warn("got null detail on DELETE event, skipping it.");
else
handlesToDelete.add(detail);
break;
default:
log
.warn("SearchConsumer should not have been given a event of type="
+ event.getEventTypeAsString()
+ " on subject="
+ event.getSubjectTypeAsString());
break;
}
}
/**
* Process sets of objects to add, update, and delete in index. Correct for
* interactions between the sets -- e.g. objects which were deleted do not
* need to be added or updated, new objects don't also need an update, etc.
*/
public void end(Context ctx) throws Exception
{
// add new created items to index, unless they were deleted.
for (Iterator ii = objectsCreated.iterator(); ii.hasNext();)
{
DSpaceObject ic = (DSpaceObject) ii.next();
if (ic.getType() != Constants.ITEM || ((Item) ic).isArchived())
{
// if handle is NOT in list of deleted objects, index it:
String hdl = ic.getHandle();
if (hdl != null && !handlesToDelete.contains(hdl))
{
try
{
DSIndexer.indexContent(ctx, ic);
if (log.isDebugEnabled())
log.debug("Indexed NEW "
+ Constants.typeText[ic.getType()]
+ ", id=" + String.valueOf(ic.getID())
+ ", handle=" + hdl);
}
catch (Exception e)
{
log.error("Failed while indexing new object: ", e);
objectsCreated = null;
objectsToUpdate = null;
handlesToDelete = null;
}
}
}
// remove it from modified list since we just indexed it.
objectsToUpdate.remove(ic);
}
// update the changed Items not deleted because they were on create list
for (Iterator ii = objectsToUpdate.iterator(); ii.hasNext();)
{
DSpaceObject iu = (DSpaceObject) ii.next();
if (iu.getType() != Constants.ITEM || ((Item) iu).isArchived())
{
// if handle is NOT in list of deleted objects, index it:
String hdl = iu.getHandle();
if (hdl != null && !handlesToDelete.contains(hdl))
{
try
{
DSIndexer.reIndexContent(ctx, iu);
if (log.isDebugEnabled())
log.debug("RE-Indexed "
+ Constants.typeText[iu.getType()]
+ ", id=" + String.valueOf(iu.getID())
+ ", handle=" + hdl);
}
catch (Exception e)
{
log.error("Failed while RE-indexing object: ", e);
objectsCreated = null;
objectsToUpdate = null;
handlesToDelete = null;
}
}
}
}
for (Iterator ii = handlesToDelete.iterator(); ii.hasNext();)
{
String hdl = (String) ii.next();
try
{
DSIndexer.unIndexContent(ctx, hdl);
if (log.isDebugEnabled())
log.debug("UN-Indexed Item, handle=" + hdl);
}
catch (Exception e)
{
log.error("Failed while UN-indexing object: " + hdl, e);
objectsCreated = new HashSet();
objectsToUpdate = new HashSet();
handlesToDelete = new HashSet();
}
}
// "free" the resources
objectsCreated = null;
objectsToUpdate = null;
handlesToDelete = null;
}
public void finish(Context ctx) throws Exception
{
// No-op
}
}

View File

@@ -52,7 +52,6 @@ import org.dspace.content.Item;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.history.HistoryManager;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -356,9 +355,6 @@ public class WorkflowItem implements InProgressSubmission
// Update ourselves // Update ourselves
DatabaseManager.update(ourContext, wfRow); DatabaseManager.update(ourContext, wfRow);
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
} }
/** /**
@@ -370,9 +366,6 @@ public class WorkflowItem implements InProgressSubmission
// Remove from cache // Remove from cache
ourContext.removeCached(this, getID()); ourContext.removeCached(this, getID());
HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
// delete any pending tasks // delete any pending tasks
WorkflowManager.deleteTasks(ourContext, this); WorkflowManager.deleteTasks(ourContext, this);

View File

@@ -68,7 +68,6 @@ import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.history.HistoryManager;
import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator; import org.dspace.storage.rdbms.TableRowIterator;
@@ -195,10 +194,6 @@ public class WorkflowManager
wfi.setMultipleTitles(wsi.hasMultipleTitles()); wfi.setMultipleTitles(wsi.hasMultipleTitles());
wfi.setPublishedBefore(wsi.isPublishedBefore()); wfi.setPublishedBefore(wsi.isPublishedBefore());
// Write history creation event
HistoryManager.saveHistory(c, wfi, HistoryManager.CREATE, c
.getCurrentUser(), c.getExtraLogInfo());
// remove the WorkspaceItem // remove the WorkspaceItem
wsi.deleteWrapper(); wsi.deleteWrapper();