001/** 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 * 006 */ 007package org.fcrepo.migration.pidlist; 008 009import org.slf4j.Logger; 010 011import java.io.BufferedReader; 012import java.io.File; 013import java.io.FileReader; 014import java.io.FileWriter; 015import java.io.IOException; 016import java.io.PrintWriter; 017 018import static org.slf4j.LoggerFactory.getLogger; 019 020/** 021 * This class "accepts" PIDs that have not already been migrated. 022 * <p> 023 * The approach taking by this implementation is to record: 024 * - the number of objects that have been migrated, and 025 * - the PID of the last migrated object 026 * <p> 027 * The assumption is that the order of processed PIDs/Objects is deterministic 028 * 029 * @author awoods 030 * @since 2019-11-08 031 */ 032public class ResumePidListManager implements PidListManager { 033 034 private static final Logger LOGGER = getLogger(ResumePidListManager.class); 035 036 private File resumeFile; 037 038 // Accept all PIDs, even if they have been processed before 039 private boolean acceptAll; 040 041 // Position of the last processed PID/Object (assuming deterministic ordering) 042 private int pidResumeIndex; 043 044 // Value of the last processed PID/Object 045 private String pidResumeValue; 046 047 // Number of times "accept" has been called 048 private int index = 0; 049 050 // Last value of current PID 051 private String value = "foo"; 052 053 054 /** 055 * Constructor 056 * 057 * @param pidDir where resume file will be read/created 058 * @param acceptAll whether to process all pids even if they've been processed before. 059 */ 060 public ResumePidListManager(final File pidDir, final boolean acceptAll) { 061 if (!pidDir.exists()) { 062 pidDir.mkdirs(); 063 } 064 065 if (!pidDir.isDirectory()) { 066 throw new IllegalArgumentException("Arg must be a directory: " + pidDir.getAbsolutePath()); 067 } 068 069 this.acceptAll = acceptAll; 070 this.resumeFile = new File(pidDir, "resume.txt"); 071 LOGGER.debug("Resume pid file: {}, accept all? {}", resumeFile.getAbsolutePath(), acceptAll); 072 073 try { 074 // Load pidResumeIndex and pidResumeValue 075 loadResumeFile(); 076 077 } catch (IOException e) { 078 throw new RuntimeException(e); 079 } 080 } 081 082 private void loadResumeFile() throws IOException { 083 084 // First run? file does not yet exist? 085 if (!resumeFile.exists() || resumeFile.length() == 0) { 086 updateResumeFile(value, index); 087 } 088 089 try (final BufferedReader reader = new BufferedReader(new FileReader(resumeFile))) { 090 091 // First line contains PID 092 pidResumeValue = reader.readLine(); 093 094 // Second line contains index 095 pidResumeIndex = Integer.parseInt(reader.readLine()); 096 } 097 } 098 099 100 /** 101 * This method 102 * - returns false if "accept" has been called less than pidResumeIndex times 103 * - returns true if "accept" has been called 104 */ 105 @Override 106 public boolean accept(final String pid) { 107 final String logMsg = "PID: " + pid + ", accept? "; 108 109 final String previousValue = value; 110 value = pid; 111 index++; 112 113 // Do not accept.. the previous run index is higher 114 if (index - 1 < pidResumeIndex) { 115 116 // Are we accepting all? 117 LOGGER.debug(logMsg + acceptAll); 118 return acceptAll; 119 } 120 121 // We are at the first PID that has not previously been processed 122 if (index - 1 == pidResumeIndex) { 123 124 // index matches, but value DOES NOT match the last state of previous run! 125 if (!previousValue.equalsIgnoreCase(pidResumeValue)) { 126 final String msg = "Number of accept requests does not align with expected PID value! " + 127 "index: " + index + ", " + 128 "pid: " + pid + ", " + 129 "expected pid: " + pidResumeValue; 130 throw new IllegalStateException(msg); 131 } 132 } 133 134 // New "accept" requests 135 updateResumeFile(value, index); 136 137 LOGGER.debug(logMsg + true); 138 return true; 139 } 140 141 /** 142 * This method resets the current index and value, and resets the resume file 143 * -- Used for test -- 144 */ 145 void reset() { 146 index = 0; 147 value = "foo"; 148 updateResumeFile(value, index); 149 } 150 151 private void updateResumeFile(final String pid, final int index) { 152 // Create writer of resumeFile (expense to do everytime... but need to overwrite file) 153 PrintWriter resumeFileWriter = null; 154 try { 155 resumeFileWriter = new PrintWriter(new FileWriter(resumeFile, false)); 156 157 } catch (IOException e) { 158 throw new RuntimeException(e); 159 } 160 161 resumeFileWriter.write(pid); 162 resumeFileWriter.write(System.getProperty("line.separator")); 163 resumeFileWriter.write(Integer.toString(index)); 164 165 resumeFileWriter.flush(); 166 resumeFileWriter.close(); 167 } 168}