/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package fedora.server.management; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import fedora.server.Context; import fedora.server.Module; import fedora.server.Server; import fedora.server.errors.ModuleInitializationException; import fedora.server.errors.ModuleShutdownException; import fedora.server.errors.ServerException; import fedora.server.proxy.AbstractInvocationHandler; import fedora.server.proxy.ProxyFactory; import fedora.server.security.Authorization; import fedora.server.storage.DOManager; import fedora.server.storage.ExternalContentManager; import fedora.server.storage.types.Datastream; import fedora.server.storage.types.RelationshipTuple; /** * @author Edwin Shin * @version $Id$ */ public class ManagementModule extends Module implements Management, ManagementDelegate { /** Logger for this class. */ private static Logger LOG = Logger.getLogger(ManagementModule.class.getName()); private Authorization m_fedoraXACMLModule; private DOManager m_manager; private ExternalContentManager m_contentManager; private int m_uploadStorageMinutes; private int m_lastId; private File m_tempDir; private Hashtable<String, Long> m_uploadStartTime; private Management mgmt; private AbstractInvocationHandler[] invocationHandlers; /** Delay between purge of two uploaded files. */ private long m_purgeDelayInMillis; public ManagementModule(Map<String, String> moduleParameters, Server server, String role) throws ModuleInitializationException { super(moduleParameters, server, role); } @Override public void initModule() throws ModuleInitializationException { // how many minutes should we hold on to uploaded files? default=5 String min = getParameter("uploadStorageMinutes"); if (min == null) { min = "5"; } try { m_uploadStorageMinutes = Integer.parseInt(min); if (m_uploadStorageMinutes < 1) { throw new ModuleInitializationException("uploadStorageMinutes " + "must be 1 or more, if specified.", getRole()); } } catch (NumberFormatException nfe) { throw new ModuleInitializationException("uploadStorageMinutes must " + "be an integer, if specified.", getRole()); } // initialize storage area by 1) ensuring the directory is there // and 2) reading in the existing files, if any, and setting their // startTime to the current time. try { m_tempDir = new File(getServer().getHomeDir(), "management/upload"); if (!m_tempDir.isDirectory()) { m_tempDir.mkdirs(); } // put leftovers in hash, while saving highest id as m_lastId m_uploadStartTime = new Hashtable<String, Long>(); String[] fNames = m_tempDir.list(); Long leftoverStartTime = new Long(System.currentTimeMillis()); m_lastId = 0; for (String element : fNames) { try { int id = Integer.parseInt(element); if (id > m_lastId) { m_lastId = id; } m_uploadStartTime.put(element, leftoverStartTime); } catch (NumberFormatException nfe) { // skip files that aren't named numerically } } } catch (Exception e) { throw new ModuleInitializationException("Error while initializing " + "temporary storage area: " + e.getClass().getName() + ": " + e.getMessage(), getRole(), e); } // initialize variables pertaining to checksumming datastreams. String auto = getParameter("autoChecksum"); LOG.debug("Got Parameter: autoChecksum = " + auto); if (auto.equalsIgnoreCase("true")) { Datastream.autoChecksum = true; Datastream.defaultChecksumType = getParameter("checksumAlgorithm"); } LOG.debug("autoChecksum is " + auto); LOG.debug("defaultChecksumType is " + Datastream.defaultChecksumType); // get delay between purge of two uploaded files (default 1 minute) String purgeDelayInMillis = getParameter("purgeDelayInMillis"); if (purgeDelayInMillis == null) { purgeDelayInMillis = "60000"; } try { this.m_purgeDelayInMillis = Integer.parseInt(purgeDelayInMillis); } catch (NumberFormatException nfe) { throw new ModuleInitializationException( "purgeDelayInMillis must be an integer, if specified.", getRole()); } } @Override public void postInitModule() throws ModuleInitializationException { // Verify required modules have been loaded m_manager = (DOManager) getServer() .getModule("fedora.server.storage.DOManager"); if (m_manager == null) { throw new ModuleInitializationException("Can't get a DOManager " + "from Server.getModule", getRole()); } m_contentManager = (ExternalContentManager) getServer() .getModule("fedora.server.storage.ExternalContentManager"); if (m_contentManager == null) { throw new ModuleInitializationException("Can't get an ExternalContentManager " + "from Server.getModule", getRole()); } m_fedoraXACMLModule = (Authorization) getServer() .getModule("fedora.server.security.Authorization"); if (m_fedoraXACMLModule == null) { throw new ModuleInitializationException("Can't get Authorization module (in default management) from Server.getModule", getRole()); } Management m = new DefaultManagement(m_fedoraXACMLModule, m_manager, m_contentManager, m_uploadStorageMinutes, m_lastId, m_tempDir, m_uploadStartTime, m_purgeDelayInMillis); mgmt = getProxyChain(m); } @Override public void shutdownModule() throws ModuleShutdownException { if (invocationHandlers != null) { for (AbstractInvocationHandler h : invocationHandlers) { h.close(); } } } /** * {@inheritDoc} */ public String addDatastream(Context context, String pid, String dsID, String[] altIDs, String dsLabel, boolean versionable, String MIMEType, String formatURI, String location, String controlGroup, String dsState, String checksumType, String checksum, String logMessage) throws ServerException { return mgmt.addDatastream(context, pid, dsID, altIDs, dsLabel, versionable, MIMEType, formatURI, location, controlGroup, dsState, checksumType, checksum, logMessage); } /** * {@inheritDoc} */ public boolean addRelationship(Context context, String pid, String relationship, String object, boolean isLiteral, String datatype) throws ServerException { return mgmt.addRelationship(context, pid, relationship, object, isLiteral, datatype); } /** * {@inheritDoc} */ public String compareDatastreamChecksum(Context context, String pid, String dsID, Date asOfDateTime) throws ServerException { return mgmt.compareDatastreamChecksum(context, pid, dsID, asOfDateTime); } /** * {@inheritDoc} */ public InputStream export(Context context, String pid, String format, String exportContext, String encoding) throws ServerException { return mgmt.export(context, pid, format, exportContext, encoding); } /** * {@inheritDoc} */ public Datastream getDatastream(Context context, String pid, String datastreamID, Date asOfDateTime) throws ServerException { return mgmt.getDatastream(context, pid, datastreamID, asOfDateTime); } /** * {@inheritDoc} */ public Datastream[] getDatastreamHistory(Context context, String pid, String datastreamID) throws ServerException { return mgmt.getDatastreamHistory(context, pid, datastreamID); } /** * {@inheritDoc} */ public Datastream[] getDatastreams(Context context, String pid, Date asOfDateTime, String dsState) throws ServerException { return mgmt.getDatastreams(context, pid, asOfDateTime, dsState); } /** * {@inheritDoc} */ public String[] getNextPID(Context context, int numPIDs, String namespace) throws ServerException { return mgmt.getNextPID(context, numPIDs, namespace); } /** * {@inheritDoc} */ public InputStream getObjectXML(Context context, String pid, String encoding) throws ServerException { return mgmt.getObjectXML(context, pid, encoding); } /** * {@inheritDoc} */ public RelationshipTuple[] getRelationships(Context context, String pid, String relationship) throws ServerException { return mgmt.getRelationships(context, pid, relationship); } /** * {@inheritDoc} */ public InputStream getTempStream(String id) throws ServerException { return mgmt.getTempStream(id); } /** * {@inheritDoc} */ public String ingest(Context context, InputStream serialization, String logMessage, String format, String encoding, boolean newPid) throws ServerException { return mgmt.ingest(context, serialization, logMessage, format, encoding, newPid); } /** * {@inheritDoc} */ public Date modifyDatastreamByReference(Context context, String pid, String datastreamID, String[] altIDs, String dsLabel, String mimeType, String formatURI, String dsLocation, String checksumType, String checksum, String logMessage, boolean force) throws ServerException { return mgmt.modifyDatastreamByReference(context, pid, datastreamID, altIDs, dsLabel, mimeType, formatURI, dsLocation, checksumType, checksum, logMessage, force); } /** * {@inheritDoc} */ public Date modifyDatastreamByValue(Context context, String pid, String datastreamID, String[] altIDs, String dsLabel, String mimeType, String formatURI, InputStream dsContent, String checksumType, String checksum, String logMessage, boolean force) throws ServerException { return mgmt.modifyDatastreamByValue(context, pid, datastreamID, altIDs, dsLabel, mimeType, formatURI, dsContent, checksumType, checksum, logMessage, force); } /** * {@inheritDoc} */ public Date modifyObject(Context context, String pid, String state, String label, String ownerId, String logMessage) throws ServerException { return mgmt.modifyObject(context, pid, state, label, ownerId, logMessage); } /** * {@inheritDoc} */ public Date[] purgeDatastream(Context context, String pid, String datastreamID, Date startDT, Date endDT, String logMessage, boolean force) throws ServerException { return mgmt.purgeDatastream(context, pid, datastreamID, startDT, endDT, logMessage, force); } /** * {@inheritDoc} */ public Date purgeObject(Context context, String pid, String logMessage, boolean force) throws ServerException { return mgmt.purgeObject(context, pid, logMessage, force); } /** * {@inheritDoc} */ public boolean purgeRelationship(Context context, String pid, String relationship, String object, boolean isLiteral, String datatype) throws ServerException { return mgmt.purgeRelationship(context, pid, relationship, object, isLiteral, datatype); } /** * {@inheritDoc} */ public String putTempStream(Context context, InputStream in) throws ServerException { return mgmt.putTempStream(context, in); } /** * {@inheritDoc} */ public Date setDatastreamState(Context context, String pid, String dsID, String dsState, String logMessage) throws ServerException { return mgmt.setDatastreamState(context, pid, dsID, dsState, logMessage); } /** * {@inheritDoce} */ public Date setDatastreamVersionable(Context context, String pid, String dsID, boolean versionable, String logMessage) throws ServerException { return mgmt.setDatastreamVersionable(context, pid, dsID, versionable, logMessage); } /** * Build a proxy chain as configured by the module parameters. * * @param m * The concrete Management implementation to wrap. * @return A proxy chain for Management * @throws ModuleInitializationException */ private Management getProxyChain(Management m) throws ModuleInitializationException { try { invocationHandlers = getInvocationHandlers(); return (Management) ProxyFactory.getProxy(m, invocationHandlers); } catch (Exception e) { throw new ModuleInitializationException(e.getMessage(), getRole()); } } /** * Get an <code>Array</code> of <code>AbstractInvocationHandler</code>s. The * ordering is ascending alphabetical, determined by the module parameter * names that begin with the string "decorator", e.g. "decorator1", * "decorator2". * * @return An array InvocationHandlers */ private AbstractInvocationHandler[] getInvocationHandlers() throws Exception { List<String> pNames = new ArrayList<String>(); Iterator<String> it = parameterNames(); String param; while (it.hasNext()) { param = it.next(); if (param.startsWith("decorator")) { pNames.add(param); } } Collections.sort(pNames); AbstractInvocationHandler[] invocationHandlers = new AbstractInvocationHandler[pNames.size()]; for (int i = 0; i < pNames.size(); i++) { invocationHandlers[i] = getInvocationHandler(getParameter(pNames.get(i))); } return invocationHandlers; } private static AbstractInvocationHandler getInvocationHandler(String invocationHandler) throws Exception { if (invocationHandler == null) return null; Class<?> c = Class.forName(invocationHandler); return (AbstractInvocationHandler)c.newInstance(); } }