001/* 002 * Copyright 2015 DuraSpace, Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fcrepo.migration; 017 018import org.fcrepo.migration.pidlist.PidListManager; 019import org.slf4j.Logger; 020import org.springframework.context.ConfigurableApplicationContext; 021import org.springframework.context.support.FileSystemXmlApplicationContext; 022import org.springframework.core.io.ClassPathResource; 023 024import javax.xml.stream.XMLStreamException; 025import java.io.BufferedReader; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.InputStreamReader; 029import java.util.List; 030 031import static org.slf4j.LoggerFactory.getLogger; 032 033/** 034 * A class that represents a command-line program to migrate a fedora 3 035 * repository to fedora 4. 036 * 037 * There are two main configuration options: the source and the handler. 038 * 039 * The source is responsible for exposing objects from a fedora repository, 040 * while the handler is responsible for processing each one. 041 * @author mdurbin 042 */ 043public class Migrator { 044 045 private static final Logger LOGGER = getLogger(Migrator.class); 046 047 /** 048 * the main method. 049 * @param args the arguments 050 * @throws IOException IO exception 051 * @throws XMLStreamException xml stream exception 052 */ 053 public static void main(final String [] args) throws IOException, XMLStreamException { 054 // Single arg with path to properties file is required 055 if (args.length != 1) { 056 printHelp(); 057 return; 058 } 059 060 final ConfigurableApplicationContext context = new FileSystemXmlApplicationContext(args[0]); 061 final Migrator m = context.getBean("migrator", Migrator.class); 062 try { 063 m.run(); 064 } finally { 065 context.close(); 066 } 067 } 068 069 private ObjectSource source; 070 071 private StreamingFedoraObjectHandler handler; 072 073 private int limit; 074 075 private List<PidListManager> pidListManagers; 076 077 private boolean continueOnError; 078 079 /** 080 * the migrator. set limit to -1. 081 */ 082 public Migrator() { 083 limit = -1; 084 } 085 086 /** 087 * set the limit. 088 * @param limit the limit 089 */ 090 public void setLimit(final int limit) { 091 this.limit = limit; 092 } 093 094 /** 095 * set the source. 096 * @param source the object source 097 */ 098 public void setSource(final ObjectSource source) { 099 this.source = source; 100 } 101 102 103 /** 104 * set the handler. 105 * @param handler the handler 106 */ 107 public void setHandler(final StreamingFedoraObjectHandler handler) { 108 this.handler = handler; 109 } 110 111 /** 112 * set the list of PidListManagers 113 * 114 * @param pidListManagers the list 115 */ 116 public void setPidListManagers(final List<PidListManager> pidListManagers) { 117 this.pidListManagers = pidListManagers; 118 } 119 120 /** 121 * set the continue on error flag 122 * 123 * @param flag flag indicating whether or not to continue on error. 124 */ 125 public void setContinueOnError(final boolean flag) { 126 this.continueOnError = flag; 127 } 128 129 /** 130 * The constructor for migrator. 131 * @param source the source 132 * @param handler the handler 133 */ 134 public Migrator(final ObjectSource source, final StreamingFedoraObjectHandler handler) { 135 this(); 136 this.source = source; 137 this.handler = handler; 138 } 139 140 /** 141 * the run method for migrator. 142 * 143 * @throws XMLStreamException xml stream exception 144 */ 145 public void run() throws XMLStreamException { 146 int index = 0; 147 148 for (final var iterator = source.iterator(); iterator.hasNext();) { 149 try (final var o = iterator.next()) { 150 final String pid = o.getObjectInfo().getPid(); 151 if (pid != null) { 152 // Process if limit is '-1', or we have not hit the non-negative 'limit'... 153 if (!(limit < 0 || index++ < limit)) { 154 LOGGER.info("Reached processing limit {}", limit); 155 break; 156 } 157 158 if (acceptPid(pid)) { 159 LOGGER.info("Processing \"" + pid + "\"..."); 160 try { 161 o.processObject(handler); 162 } catch (Exception ex) { 163 final var message = String.format("MIGRATION_FAILURE: pid=\"%s\", message=\"%s\"", 164 pid, ex.getMessage()); 165 166 if (!this.continueOnError) { 167 LOGGER.error(message, ex); 168 } else { 169 throw new RuntimeException(message, ex); 170 } 171 } 172 } 173 } 174 } catch (Exception ex) { 175 final var message = String.format("MIGRATION_FAILURE: UNREADABLE_OBJECT: message=\"%s\"", 176 ex.getMessage()); 177 178 if (this.continueOnError) { 179 LOGGER.error(message, ex); 180 } else { 181 throw new RuntimeException(message, ex); 182 } 183 } 184 } 185 } 186 187 private boolean acceptPid(final String pid) { 188 189 // If there is not manager, accept the PID 190 if (pidListManagers == null) { 191 return true; 192 } 193 194 // If any manager DOES NOT accept the PID, return false 195 for (PidListManager m : pidListManagers) { 196 if (!m.accept(pid)) { 197 return false; 198 } 199 } 200 return true; 201 } 202 203 private static void printHelp() throws IOException { 204 final StringBuilder sb = new StringBuilder(); 205 sb.append("============================\n"); 206 sb.append("Please provide the directory path to a configuration file!"); 207 sb.append("\n"); 208 sb.append("See: https://github.com/fcrepo-exts/migration-utils/blob/master/"); 209 sb.append("src/main/resources/spring/migration-bean.xml"); 210 sb.append("\n\n"); 211 sb.append("The configuration file should contain the following (with appropriate values):"); 212 sb.append("\n"); 213 sb.append("~~~~~~~~~~~~~~\n"); 214 215 final ClassPathResource resource = new ClassPathResource("spring/migration-bean.xml"); 216 try (final InputStream example = resource.getInputStream(); 217 final BufferedReader reader = new BufferedReader(new InputStreamReader(example))) { 218 String line = reader.readLine(); 219 while (null != line) { 220 sb.append(line); 221 sb.append("\n"); 222 line = reader.readLine(); 223 } 224 225 sb.append("~~~~~~~~~~~~~~\n\n"); 226 sb.append("See top of this output for details.\n"); 227 sb.append("============================\n"); 228 System.out.println(sb.toString()); 229 } 230 } 231}