/** * Copyright 2010 The University of North Carolina at Chapel Hill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package unc.lib.cdr.workbench.stage; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import staging.plugin.StagingPlugin; import staging.plugin.StagingUtils; import staging.plugin.StagingUtils.StagingResult; import unc.lib.cdr.workbench.originals.OriginalFileStore; import unc.lib.cdr.workbench.project.MetsProjectNature; import unc.lib.cdr.workbench.rcp.Activator; import edu.unc.lib.staging.SharedStagingArea; import edu.unc.lib.staging.Stages; import gov.loc.mets.CHECKSUMTYPEType; import gov.loc.mets.DivType; import gov.loc.mets.FLocatType; import gov.loc.mets.FileType; import gov.loc.mets.FptrType; import gov.loc.mets.LOCTYPEType; import gov.loc.mets.MetsPackage; import gov.loc.mets.util.METSUtils; /** * @author Gregory Jansen * */ public class StagingJob extends Job { private final static String markerID = "workbench_plugin.stagingProblem"; private static final Logger log = LoggerFactory.getLogger(StagingJob.class); IProject project = null; public static MutexRule mySchedulingRule = new MutexRule(); SharedStagingArea stage = null; URL destinationRepo = null; public static class MutexRule implements ISchedulingRule { public boolean isConflicting(ISchedulingRule rule) { return rule == this; } public boolean contains(ISchedulingRule rule) { return rule == this; } } @Override public boolean belongsTo(Object family) { if (stagingJobFamilyObject == family) { return true; } return super.belongsTo(family); } public static final String stagingJobFamilyObject = "stagingFamily"; /** * @param name */ // public StagingJob(String name, List<OriginalFileStore> toStage2) { // super(name); // this.toStage = toStage2; // this.setRule(mySchedulingRule); // } /** * @param name */ public StagingJob(String name, IProject project) { super(name); this.project = project; this.setRule(mySchedulingRule); } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime. * IProgressMonitor) */ @Override protected IStatus run(IProgressMonitor monitor) { clearMarkers(); final MetsProjectNature mpn = MetsProjectNature.get(project); DivType bag = METSUtils.findBagDiv(mpn.getMets()); getEffectiveStagingArea(mpn); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } // get things we can stage, in place, migrate, etc.. List<OriginalFileStore> toStage = new ArrayList<OriginalFileStore>(); List<OriginalFileStore> toStageInPlace = new ArrayList<OriginalFileStore>(); Map<OriginalFileStore, IFileStore> toMigrate = new HashMap<OriginalFileStore, IFileStore>(); Set<OriginalFileStore> skipped = new HashSet<OriginalFileStore>(); for (TreeIterator<EObject> iter = bag.eAllContents(); iter.hasNext();) { EObject next = iter.next(); log.debug(next.toString()); if (!(next instanceof FptrType)) { continue; } FptrType fptr = (FptrType) next; OriginalFileStore original = MetsProjectNature .getOriginalFileStore((DivType) fptr.eContainer()); log.error("original file store: {}", original); if (original == null) { log.error("Cannot get original file store for fptr: {}", fptr); continue; } FLocatType loc = original.getStagingLocatorType(); boolean originalInStagingArea = isWithinRepoStagingArea(original .getWrapped().toURI()); log.error("original staging locator: {}", loc); if (loc == null) { // not staged yet if (originalInStagingArea) { toStageInPlace.add(original); toStage.add(original); } else { toStage.add(original); } } else { URI stagedLoc = URI.create(loc.getHref()); if (!isWithinRepoStagingArea(stagedLoc)) { log.debug("found migration: {} {}", original.toURI(), stagedLoc); SharedStagingArea s = StagingPlugin.getDefault() .getStages().findMatchingArea(stagedLoc); if (!s.isConnected()) StagingPlugin.getDefault().getStages() .connect(s.getURI()); try { URI stagedStorageURI = StagingPlugin.getDefault() .getStages().getStorageURI(stagedLoc); stagedStorageURI = mpn .resolveProjectRelativeURI(stagedStorageURI); IFileStore source = EFS.getStore(stagedStorageURI); toMigrate.put(original, source); toStage.add(original); } catch (Throwable e) { addProblemMarker(e.getLocalizedMessage()); monitor.worked(100); skipped.add(original); continue; } } } } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } String taskName = "Staging " + toStage.size() + " files"; monitor.beginTask(taskName, toStage.size() * 100); int stageCount = 0; for (OriginalFileStore original : toStage) { if (monitor.isCanceled()) return Status.CANCEL_STATUS; monitor.setTaskName("Staging " + stageCount++ + " of " + toStage.size()); // check if original still captured String fileID = original.getFileID(); FileType fileRec = (FileType) mpn.getMets().eResource() .getEObject(fileID); String divID = original.getDivID(); DivType div = (DivType) mpn.getMets().eResource().getEObject(divID); if (div == null) { monitor.worked(100); continue; // no longer captured } StagingResult result = null; try { if (toStageInPlace.contains(original)) { result = StagingUtils.stageInPlace(original.getWrapped(), project, original.getDistinctStagingPath(), fileRec.getCHECKSUM(), stage, destinationRepo, monitor); } else { // find the source file for staging IFileStore stagingSource = null; if (toMigrate.containsKey(original)) { stagingSource = toMigrate.get(original); } else if (original.isAttached()) { stagingSource = original; } if (stagingSource == null) { addProblemMarker("Cannot locate a staging source for " + original.toString()); monitor.worked(100); skipped.add(original); continue; } result = StagingUtils .stage(stagingSource, this.project, original.getDistinctStagingPath(), fileRec.getCHECKSUM(), mpn.getStagingManifestURI(), stage, destinationRepo, new SubProgressMonitor( monitor, 100, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); } } catch (CoreException e) { mpn.setAutomaticStaging(false); addProblemMarker("Staging stopped due to an error, autostaging disabled: " + e.getMessage()); log.error("Staging encountered an error and stopped, autostaging disabled.", e); return new Status( IStatus.ERROR, Activator.PLUGIN_ID, "Staging encountered an error and stopped, autostaging disabled.", e); } // set checksum if (fileRec.getCHECKSUM() == null) { // use newly calculated fileRec.setCHECKSUMTYPE(CHECKSUMTYPEType.MD5); fileRec.setCHECKSUM(result.md5Sum); } else { if (!fileRec.getCHECKSUM().equals(result.md5Sum)) { addProblemMarker("A file changed since the last checksum was calculated: " + original.toString()); fileRec.setCHECKSUM(result.md5Sum); fileRec.setCHECKSUMTYPE(CHECKSUMTYPEType.MD5); } } // record staged File in METS // checksum, size, location type, other loc type, URI FLocatType flocat = METSUtils.makeStagedFileLocator(mpn.getMets(), fileID, original.getWrapped().toURI(), result.manifestURI, LOCTYPEType.OTHER, result.manifestURIScheme); CompoundCommand addremove = new CompoundCommand( "Add new stage and remove old."); Command add = AddCommand.create(mpn.getEditingDomain(), fileRec, MetsPackage.eINSTANCE.getFileType_FLocat(), flocat); addremove.append(add); if (toMigrate.containsKey(original)) { Command remove = RemoveCommand.create(mpn.getEditingDomain(), original.getStagingLocatorType()); addremove.append(remove); } if (addremove.canExecute()) { mpn.getCommandStack().execute(addremove); mpn.getEMFSession().save(); if (toMigrate.containsKey(original)) { IFileStore sourceStore = toMigrate.get(original); if (!original.getWrapped().toURI() .equals(sourceStore.toURI())) { // if source was not also the original.. try { log.debug("deleting old staged file: " + sourceStore.toURI()); sourceStore.delete(EFS.NONE, new NullProgressMonitor()); } catch (CoreException e) { addProblemMarker("Cannot delete file from old staging area: " + sourceStore); } } } } else { log.error("Cannot save results of staging, command not executable"); return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot finish staging operation, command not executable"); } } monitor.done(); if (skipped.size() > 0) { return new Status( IStatus.INFO, Activator.PLUGIN_ID, skipped.size() + " original files cannot stage because they are not accessible."); } else { return Status.OK_STATUS; // add a result dialog message for user reward!! } } private boolean isWithinRepoStagingArea(URI file) { SharedStagingArea inPlaceArea = StagingPlugin.getDefault().getStages() .findMatchingArea(file); return (inPlaceArea != null && inPlaceArea.getURI().isAbsolute()); } private void getEffectiveStagingArea(MetsProjectNature mpn) { URI stageURI = mpn.getStagingManifestURI(); Stages stages = StagingPlugin.getDefault().getStages(); SharedStagingArea projectStage = (SharedStagingArea) stages.findMatchingArea(stageURI); if (projectStage != null) { this.destinationRepo = projectStage.getConfigURL(); } if (projectStage != null && !projectStage.isConnected()) { StagingPlugin.getDefault().getStages() .connect(projectStage.getURI()); } if (projectStage == null || !projectStage.isConnected()) { final boolean unrecognized = projectStage == null; final SharedStagingArea bagIt = StagingPlugin.getDefault() .getStages().findMatchingArea(URI.create("data/")); final StagingJob myJob = this; final MetsProjectNature finalmpn = mpn; Display.getDefault().syncExec(new Runnable() { public void run() { if (bagIt != null && MessageDialog .openConfirm( Display.getCurrent() .getActiveShell(), "Project Staging Area " + (unrecognized ? "Unrecognized" : "Disconnected"), "Press OK to stage files in the project data folder or cancel to turn off staging until connected.")) { // stage to project data folder (BagIt) stage = bagIt; } else { // turn off project automatic staging finalmpn.setAutomaticStaging(false); // cancel staging operation myJob.cancel(); addProblemMarker("Staging disabled. Could not connect to preferred staging area."); } } }); } else { stage = projectStage; } } private void clearMarkers() { try { IMarker[] marks = this.project.findMarkers(markerID, true, IResource.DEPTH_ZERO); for (IMarker mark : marks) mark.delete(); } catch (CoreException e) { log.error("Cannot clear markers", e); } } private void addProblemMarker(String message) { try { IMarker m = project.createMarker(StagingJob.markerID); m.setAttribute(IMarker.MESSAGE, message); } catch (CoreException e1) { log.error("there was a problem setting the problem marker:" + message); } } }