MarkW, Did you look over the Quartz Job Scheduler Addon, this type of asynchronous thread spawning activity is what I intended it to alleviate us from doing.
MarkD On Jun 9, 2010, at 9:49 AM, nore...@scm.dspace.org wrote: > Author: mwoodiupui > Date: Wed Jun 9 16:49:36 2010 > New Revision: 5076 > > Log: > Add an event consumer to drive the packager, producing AIPs for affected > objects > > Added: > > sandbox/aip-external-1_6-prototype/dspace-api/src/main/java/org/dspace/content/packager/PackageConsumer.java > > Added: > sandbox/aip-external-1_6-prototype/dspace-api/src/main/java/org/dspace/content/packager/PackageConsumer.java > ============================================================================== > --- (empty file) > +++ > sandbox/aip-external-1_6-prototype/dspace-api/src/main/java/org/dspace/content/packager/PackageConsumer.java > Wed Jun 9 16:49:36 2010 > @@ -0,0 +1,219 @@ > +/** > + * PackageConsumer.java > + * > + * Version: $Revision$ > + * > + * Date: $Date$ > + * > + * Copyright (c) 2010, 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 DSpace Foundation nor the names of its > + * 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.packager; > + > +import java.io.BufferedReader; > +import java.io.File; > +import java.io.IOException; > +import java.io.InputStreamReader; > +import java.util.ArrayList; > +import java.util.HashMap; > +import java.util.List; > +import java.util.Map; > +import java.util.Map.Entry; > + > +import org.dspace.core.ConfigurationManager; > +import org.dspace.core.Constants; > +import org.dspace.core.Context; > +import org.dspace.event.Consumer; > +import org.dspace.event.Event; > +import org.slf4j.Logger; > +import org.slf4j.LoggerFactory; > + > +/** > + * An Event Consumer which creates AIPs as the DSpace model changes. This is > + * done by invoking the packager utility in subprocesses, one per affected > + * object. Any "console" output from the packager is logged. > + * <p> > + * If a single operation (from the user's point of view) causes multiple > + * update()s of a given object, then some objects may be exported more than > once > + * per user operation, but no object should be exported more than once per > + * update(). See the object implementations to understand how this happens. > In > + * other words, over time the number of exports is not minimal, but I think > it > + * is as close as we can get. > + * <p> > + * It is mandatory to configure {...@code packageConsumer.workingDirectory} > when > + * employing this class; otherwise {...@code initialize} will throw a > + * NullPointerException. > + * > + * @author Mark Wood > + */ > +public class PackageConsumer implements Consumer > +{ > + /** Log file access */ > + private static final Logger log = LoggerFactory > + .getLogger(PackageConsumer.class); > + > + /** Configuration property: subprocess' working directory. REQUIRED. */ > + private static final String WORKING_DIRECTORY = > "packageConsumer.workingDirectory"; > + > + /** > + * Creates pre-configured subprocesses to execute external packaging > + * commands. > + */ > + private ProcessBuilder processFactory; > + > + /** Accumulator for unique objects to be exported */ > + private Map<String, Integer> objects; > + > + /** Path to the external packager utility */ > + private String verb; > + > + public void initialize() throws Exception > + { > + verb = ConfigurationManager.getProperty("dspace.home") + > "/bin/dspace"; > + objects = new HashMap<String, Integer>(); > + > + processFactory = new ProcessBuilder(); > + > + String workingDirectory = ConfigurationManager > + .getProperty(WORKING_DIRECTORY); > + if (null == workingDirectory) > + throw new NullPointerException(WORKING_DIRECTORY > + + " not configured"); > + processFactory.directory(new File(workingDirectory)); > + > + processFactory.redirectErrorStream(true); > + } > + > + public void consume(Context ctx, Event event) throws Exception > + { > + // TODO what event.getEventType() values are interesting? > +/* > + if (event.getEventType() & (Event.)) > + return; > +*/ > + > + int type = event.getObjectType(); > + switch(type) > + { > + case Constants.COMMUNITY: > + case Constants.COLLECTION: > + case Constants.ITEM: > + String handle = event.getObject(ctx).getHandle(); > + objects.put(handle, new Integer(type)); > + break; > + } > + } > + > + public void end(Context ctx) throws Exception > + { > + for (Entry<String, Integer> object : objects.entrySet()) > + { > + String handle = object.getKey(); > + String type = Constants.typeText[object.getValue()]; > + String filePath = type + "@" + handle.replace('/', '-') + ".zip"; > + > + // Build the command > + List<String> command = new ArrayList<String>(); > + command.add(verb); > + command.add("packager"); > + command.add("-d"); > + command.add("-t"); > + command.add("AIP"); > + command.add("-e"); > + command.add(ctx.getCurrentUser().getNetid()); > + command.add("-i"); > + command.add(handle); > + command.add(filePath); > + > + // Spawn a child process to execute the command > + processFactory.command(command); > + Process child = processFactory.start(); > + > + // Monitor child until it exits > + new Thread(new nanny(child)).start(); > + } > + objects.clear(); > + } > + > + public void finish(Context ctx) throws Exception > + { > + // Nothing to do here > + } > + > + /** Monitor a child process */ > + private class nanny implements Runnable > + { > + /** The process to watch */ > + private Process child; > + > + /** > + * Monitor a single child. > + * > + * @param child > + * the process to be watched > + */ > + private nanny(Process child) > + { > + this.child = child; > + } > + > + public void run() > + { > + BufferedReader childStdout = new BufferedReader( > + new InputStreamReader(child.getInputStream())); > + boolean incomplete = true; > + String stdoutLine = null; > + while (incomplete) > + { > + try > + { > + stdoutLine = childStdout.readLine(); > + incomplete = (null != stdoutLine); > + if (incomplete) > + log.debug("Child says:", stdoutLine); > + } > + catch (IOException e) > + { > + log.warn("Error reading child stdout", e); > + } > + } > + log.debug("Child exited with value {}", child.exitValue()); > + try > + { > + childStdout.close(); > + } > + catch (IOException e) > + { > + log.warn("Error closing child stdout", e); > + } > + } > + } > +} > > ------------------------------------------------------------------------------ > ThinkGeek and WIRED's GeekDad team up for the Ultimate > GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the > lucky parental unit. See the prize list and enter to win: > http://p.sf.net/sfu/thinkgeek-promo > _______________________________________________ > DSpace-changelog mailing list > dspace-change...@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/dspace-changelog ------------------------------------------------------------------------------ ThinkGeek and WIRED's GeekDad team up for the Ultimate GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the lucky parental unit. See the prize list and enter to win: http://p.sf.net/sfu/thinkgeek-promo _______________________________________________ Dspace-devel mailing list Dspace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dspace-devel