/* * Copyright (C) 2014 Jan Pokorsky, Robert Simonovsky * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package cz.cas.lib.proarc.common.export; import com.yourmediashelf.fedora.generated.foxml.DigitalObject; import cz.cas.lib.proarc.common.export.ExportResultLog.ResultError; import cz.cas.lib.proarc.common.export.ExportResultLog.ResultStatus; import cz.cas.lib.proarc.common.export.mets.MetsContext; import cz.cas.lib.proarc.common.export.mets.MetsExportException; import cz.cas.lib.proarc.common.export.mets.MetsExportException.MetsExportExceptionElement; import cz.cas.lib.proarc.common.export.mets.MetsUtils; import cz.cas.lib.proarc.common.export.mets.structure.MetsElement; import cz.cas.lib.proarc.common.export.mets.structure.MetsElementVisitor; import cz.cas.lib.proarc.common.fedora.DigitalObjectException; import cz.cas.lib.proarc.common.fedora.FoxmlUtils; import cz.cas.lib.proarc.common.fedora.RemoteStorage; import cz.cas.lib.proarc.common.fedora.RemoteStorage.RemoteObject; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; /** * Exports digital object and transforms its data streams to NDK format. * * @author Jan Pokorsky * @see <a href='http://ndk.cz/digitalizace/nove-standardy-digitalizace-od-roku-2011'>NDK</a> */ public final class NdkExport { private static final Logger LOG = Logger.getLogger(NdkExport.class.getName()); private final RemoteStorage rstorage; public NdkExport(RemoteStorage rstorage) { this.rstorage = rstorage; } // /** // * Runs export to validate inputs. It cleans outputs on exit. // * @param exportsFolder folder with user exports // * @param pid PID to validate // * @param hierarchy export PID ant its children // * @return validation report // * @throws ExportException unexpected failure // */ // public List<MetsExportExceptionElement> validate(File exportsFolder, String pid, // boolean hierarchy) throws ExportException { // // Result export = export(exportsFolder, pid, "ValPKGID", hierarchy, false, null); // if (export.getValidationError() != null) { // return export.getValidationError().getExceptions(); // } else { // return null; // } // } /** * Prepares export package of a single PID without children for later download. * @param exportsFolder folder with user exports * @param pid PID to export * @return the result with token or validation errors * @throws ExportException unexpected failure */ public Result exportDownload(File exportsFolder, String pid) throws ExportException { return export(exportsFolder, pid, null, true, false, null); } /** * Exports PIDs in Mets format * * @param exportsFolder * folder with user exports * @param pids * PID to export * @param hierarchy * export PID and its children * @param keepResult * delete or not export folder on exit * @param log * message for storage logging * @return the result * @throws ExportException * unexpected failure */ public List<Result> export(File exportsFolder, List<String> pids, boolean hierarchy, boolean keepResult, String log ) throws ExportException { ExportResultLog reslog = new ExportResultLog(); File target = ExportUtils.createFolder(exportsFolder, FoxmlUtils.pidAsUuid(pids.get(0))); ArrayList<Result> results = new ArrayList<Result>(pids.size()); for (String pid : pids) { ExportResultLog.ExportResult logItem = new ExportResultLog.ExportResult(); logItem.setInputPid(pid); reslog.getExports().add(logItem); try { Result r = export(target, pid, null, hierarchy, keepResult, log); results.add(r); logResult(r, logItem); } catch (ExportException ex) { logItem.setStatus(ResultStatus.FAILED); logItem.getError().add(new ResultError(null, ex)); ExportUtils.writeExportResult(target, reslog); throw ex; } finally { logItem.setEnd(); } } ExportUtils.writeExportResult(target, reslog); return results; } Result export(File target, String pid, String packageId, boolean hierarchy, boolean keepResult, String log ) throws ExportException { Result result = new Result(); try { if (keepResult) { result.setTargetFolder(target); } RemoteObject fo = rstorage.find(pid); MetsContext dc = buildContext(fo, packageId, target); try { List<String> PSPs = MetsUtils.findPSPPIDs(fo.getPid(), dc, hierarchy); for (String pspPid : PSPs) { dc.resetContext(); DigitalObject dobj = MetsUtils.readFoXML(pspPid, fo.getClient()); MetsElement mElm = MetsElement.getElement(dobj, null, dc, hierarchy); mElm.accept(new MetsElementVisitor()); // XXX use relative path to users folder? } storeExportResult(dc, target.toURI().toASCIIString(), log); return result; } catch (MetsExportException ex) { keepResult = false; // do not clean folder as i it is possilbe to write status.log if (ex.getExceptions().isEmpty()) { throw new ExportException(pid, ex); } return result.setValidationError(ex); } catch (Throwable ex) { keepResult = false; // do not clean folder as i it is possilbe to write status.log throw new ExportException(pid, ex); } } finally { if (!keepResult) { // // run asynchronously not to block client request? // boolean deleted = FileUtils.deleteQuietly(target); // if (!deleted) { // LOG.warning("Cannot delete: " + target.toString()); // } } } } private MetsContext buildContext(RemoteObject fo, String packageId, File targetFolder) { MetsContext mc = new MetsContext(); mc.setFedoraClient(fo.getClient()); mc.setRemoteStorage(rstorage); mc.setPackageID(packageId); mc.setOutputPath(targetFolder.getAbsolutePath()); mc.setAllowNonCompleteStreams(false); mc.setAllowMissingURNNBN(false); return mc; } /** * Stores logs to the digital object hierarchy. * * @param mElm * exported elements * @throws MetsExportException * write failure */ void storeExportResult(MetsContext metsContext, String target, String log) throws MetsExportException { for (String pid : metsContext.getPidElements().keySet()) { try { ExportUtils.storeObjectExportResult(pid, target, log); } catch (DigitalObjectException ex) { throw new MetsExportException(pid, "Cannot store logs!", false, ex); } } } private void logResult(Result r, ExportResultLog.ExportResult logItem) { if (r.getValidationError() != null) { logItem.setStatus(ResultStatus.FAILED); List<MetsExportExceptionElement> exceptions = r.getValidationError().getExceptions(); for (MetsExportExceptionElement mex : exceptions) { List<String> validations = mex.getValidationErrors(); String pid = mex.getPid(); if (validations != null && !validations.isEmpty()) { logItem.getError().add(new ResultError(pid, mex.getMessage(), validations)); } else { logItem.getError().add(new ResultError(pid, mex.getMessage(), mex.getEx())); } } } else { logItem.setStatus(ResultStatus.OK); } } /** * The export result. */ public static class Result { private File targetFolder; private MetsExportException validationError; public MetsExportException getValidationError() { return validationError; } Result setValidationError(MetsExportException validationError) { this.validationError = validationError; return this; } /** * Gets the folder with exported packages. * @return {@code null} if the result should not be kept */ public File getTargetFolder() { return targetFolder; } Result setTargetFolder(File targetFolder) { this.targetFolder = targetFolder; return this; } /** * Gets the token for future requests. * @return the token */ public String getDownloadToken() { return targetFolder == null ? null: targetFolder.getName(); } } }