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

Reply via email to