/* * EmbargoManager.java * * Version: $Revision: 1.10 $ * * Date: $Date: 2009/09/08 20:03:45 $ * * Copyright (c) 2002-2009, The DSpace Foundation. 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.embargo; import java.sql.SQLException; import java.util.Date; import java.io.IOException; import org.apache.log4j.Logger; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.PosixParser; import org.apache.commons.cli.ParseException; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Item; import org.dspace.content.ItemIterator; import org.dspace.content.DCDate; import org.dspace.content.DCValue; import org.dspace.content.DSpaceObject; import org.dspace.content.MetadataSchema; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; import org.dspace.core.PluginManager; import org.dspace.handle.HandleManager; /** * Public interface to the embargo subsystem. * * Configuration properties: (with examples) * # DC metadata field to hold the user-supplied embargo terms * embargo.field.terms = dc.embargo.terms * # DC metadata field to hold computed "lift date" of embargo * embargo.field.lift = dc.date.available * # String to indicate indefinite (forever) embargo in terms * embargo.terms.open = Indefinite * # implementation of embargo setter plugin * plugin.single.org.dspace.embargo.EmbargoSetter = edu.my.Setter * # implementation of embargo lifter plugin * plugin.single.org.dspace.embargo.EmbargoLifter = edu.my.Lifter * * @author Larry Stone * @author Richard Rodgers */ public class EmbargoManager { /** Special date signalling an Item is to be embargoed forever. ** The actual date is the first day of the year 10,000 UTC. **/ public static final DCDate FOREVER = new DCDate("10000-01-01"); /** log4j category */ private static Logger log = Logger.getLogger(EmbargoManager.class); // Metadata field components for user-supplied embargo terms // set from the DSpace configuration by init() private static String terms_schema = null; private static String terms_element = null; private static String terms_qualifier = null; // Metadata field components for lift date, encoded as a DCDate // set from the DSpace configuration by init() private static String lift_schema = null; private static String lift_element = null; private static String lift_qualifier = null; // plugin implementations // set from the DSpace configuration by init() private static EmbargoSetter setter = null; private static EmbargoLifter lifter = null; /** * Put an Item under embargo until the specified lift date. * Calls EmbargoSetter plugin to adjust Item access control policies. * * @param context the DSpace context * @param item the item to embargo * @param lift date on which the embargo is to be lifted. */ public static void setEmbargo(Context context, Item item, DCDate lift) throws SQLException, AuthorizeException, IOException { init(); String slift = lift.toString(); boolean ignoreAuth = context.ignoreAuthorization(); try { context.setIgnoreAuthorization(true); item.clearMetadata(lift_schema, lift_element, lift_qualifier, Item.ANY); item.addMetadata(lift_schema, lift_element, lift_qualifier, null, slift); log.info("Set embargo on Item "+item.getHandle()+", expires on: "+slift); setter.setEmbargo(context, item); item.update(); } finally { context.setIgnoreAuthorization(ignoreAuth); } } /** * Get the embargo lift date for an Item, if any. This looks for the * metadata field configured to hold embargo terms, and gives it * to the EmbargoSetter plugin's method to interpret it into * an absolute timestamp. This is intended to be called at the time * the Item is installed into the archive. * <p> * Note that the plugin is *always* called, in case it gets its cue for * the embargo date from sources other than, or in addition to, the * specified field. * * @param context the DSpace context * @param item the item to embargo * @return lift date on which the embargo is to be lifted, or null if none */ public static DCDate getEmbargoDate(Context context, Item item) throws SQLException, AuthorizeException, IOException { init(); DCValue terms[] = item.getMetadata(terms_schema, terms_element, terms_qualifier, Item.ANY); DCDate result = setter.parseTerms(context, item, terms.length > 0 ? terms[0].value : null); // sanity check: do not allow an embargo lift date in the past. if (result != null && result.toDate().before(new Date())) { throw new IllegalArgumentException("Embargo lift date must be in the future, but this is in the past: "+result.toString()); } return result; } /** * Lift the embargo on an item which is assumed to be under embargo. * Call the plugin to manage permissions in its own way, then delete * the administrative metadata fields that dictated embargo date. * * @param context the DSpace context * @param item the item on which to lift the embargo */ public static void liftEmbargo(Context context, Item item) throws SQLException, AuthorizeException, IOException { init(); lifter.liftEmbargo(context, item); item.clearMetadata(lift_schema, lift_element, lift_qualifier, Item.ANY); // set the dc.date.available value to right now item.clearMetadata(MetadataSchema.DC_SCHEMA, "date", "available", Item.ANY); item.addMetadata(MetadataSchema.DC_SCHEMA, "date", "available", null, DCDate.getCurrent().toString()); log.info("Lifting embargo on Item "+item.getHandle()); item.update(); } /** * Command-line service to scan for every Items with an expired embargo, * and then lift that embargo. * * Options: * -c,--check Function: ONLY check the state of embargoed Items, do * NOT lift any embargoes. * -h,--help help * -i,--identifier Process ONLY this Handle identifier(s), which must be * an Item. Can be repeated. * -l,--lift Function: ONLY lift embargoes, do NOT check the state * of any embargoed Items. * -n,--dryrun Do not change anything in the data model, print * message instead. * -v,--verbose Print a line describing action taken for each * embargoed Item found. * -q,--quiet No output except upon error */ public static void main(String argv[]) { init(); int status = 0; Options options = new Options(); options.addOption("v", "verbose", false, "Print a line describing action taken for each embargoed Item found."); options.addOption("q", "quiet", false, "Do not print anything except for errors."); options.addOption("n", "dryrun", false, "Do not change anything in the data model, print message instead."); options.addOption("i", "identifier", true, "Process ONLY this Handle identifier(s), which must be an Item. Can be repeated."); options.addOption("c", "check", false, "Function: ONLY check the state of embargoed Items, do NOT lift any embargoes."); options.addOption("l", "lift", false, "Function: ONLY lift embargoes, do NOT check the state of any embargoed Items."); options.addOption("h", "help", false, "help"); CommandLine line = null; try { line = new PosixParser().parse(options, argv); } catch(ParseException e) { System.err.println("Command error: " + e.getMessage()); new HelpFormatter().printHelp(EmbargoManager.class.getName(), options); System.exit(1); } if (line.hasOption('h')) { new HelpFormatter().printHelp(EmbargoManager.class.getName(), options); System.exit(0); } // sanity check, --lift and --check are mutually exclusive: if (line.hasOption('l') && line.hasOption('c')) { System.err.println("Command error: --lift and --check are mutually exclusive, try --help for assistance."); System.exit(1); } Context context = null; try { context = new Context(); context.setIgnoreAuthorization(true); Date now = new Date(); // scan items under embargo if (line.hasOption('i')) { for (String handle : line.getOptionValues('i')) { DSpaceObject dso = HandleManager.resolveToObject(context, handle); if (dso == null) { System.err.println("Error, cannot resolve handle="+handle+" to a DSpace Item."); status = 1; } else if (dso.getType() != Constants.ITEM) { System.err.println("Error, the handle="+handle+" is not a DSpace Item."); status = 1; } else { if (processOneItem(context, (Item)dso, line, now)) status = 1; } } } else { ItemIterator ii = Item.findByMetadataField(context, lift_schema, lift_element, lift_qualifier, Item.ANY); while (ii.hasNext()) { if (processOneItem(context, ii.next(), line, now)) status = 1; } } log.debug("Cache size at end = "+context.getCacheSize()); context.complete(); context = null; } catch (Exception e) { System.err.println("ERROR, got exception: "+e); e.printStackTrace(); status = 1; } finally { if (context != null) { try { context.abort(); } catch (Exception e) { } } } System.exit(status); } // lift or check embargo on one Item, handle exceptions // return flase on success, true if there was fatal exception. private static boolean processOneItem(Context context, Item item, CommandLine line, Date now) throws Exception { boolean status = false; DCValue lift[] = item.getMetadata(lift_schema, lift_element, lift_qualifier, Item.ANY); if (lift.length > 0) { // need to survive any failure on a single item, go on to process the rest. try { DCDate liftDate = new DCDate(lift[0].value); log.debug("Testing embargo on item="+item.getHandle()+", date="+liftDate.toString()); if (liftDate.toDate().before(now)) { if (line.hasOption('v')) System.err.println("Lifting embargo from Item handle="+item.getHandle()+", lift date="+lift[0].value); if (line.hasOption('n')) { if (!line.hasOption('q')) System.err.println("DRY RUN: would have lifted embargo from Item handle="+item.getHandle()+", lift date="+lift[0].value); } else if (!line.hasOption('c')) liftEmbargo(context, item); } else if (!line.hasOption('l')) { if (line.hasOption('v')) System.err.println("Checking current embargo on Item handle="+item.getHandle()+", lift date="+lift[0].value); setter.checkEmbargo(context, item); } } catch (Exception e) { log.error("Failed attempting to lift embargo, item="+item.getHandle()+": ", e); System.err.println("Failed attempting to lift embargo, item="+item.getHandle()+": "+ e); status = true; } } context.removeCached(item, item.getID()); return status; } // initialize - get plugins and MD field settings from config private static void init() { if (terms_schema == null) { String terms = ConfigurationManager.getProperty("embargo.field.terms"); String lift = ConfigurationManager.getProperty("embargo.field.lift"); if (terms == null || lift == null) throw new IllegalStateException("Missing one or more of the required DSpace configuration properties for EmbargoManager, check your configuration file."); terms_schema = getSchemaOf(terms); terms_element = getElementOf(terms); terms_qualifier = getQualifierOf(terms); lift_schema = getSchemaOf(lift); lift_element = getElementOf(lift); lift_qualifier = getQualifierOf(lift); setter = (EmbargoSetter)PluginManager.getSinglePlugin(EmbargoSetter.class); if (setter == null) throw new IllegalStateException("The EmbargoSetter plugin was not defined in DSpace configuration."); lifter = (EmbargoLifter)PluginManager.getSinglePlugin(EmbargoLifter.class); if (lifter == null) throw new IllegalStateException("The EmbargoLifter plugin was not defined in DSpace configuration."); } } // return the schema part of "schema.element.qualifier" metadata field spec private static String getSchemaOf(String field) { String sa[] = field.split("\\.", 3); return sa[0]; } // return the element part of "schema.element.qualifier" metadata field spec, if any private static String getElementOf(String field) { String sa[] = field.split("\\.", 3); return sa.length > 1 ? sa[1] : null; } // return the qualifier part of "schema.element.qualifier" metadata field spec, if any private static String getQualifierOf(String field) { String sa[] = field.split("\\.", 3); return sa.length > 2 ? sa[2] : null; } }