/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.cache.persistence;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Inspects a completed backup and parses the operation log file data from the restore script
* produced by a previous backup.
*/
public abstract class BackupInspector {
/**
* Maps operation log file names to script lines that copy previously backed up operation log
* files. These lines will be added to future restore scripts if the operation logs are still
* relevant to the member.
*/
protected Map<String, String> oplogLineMap = new HashMap<String, String>();
/**
* Contains the unique set of operation log file names contained in the restore script.
*/
protected Set<String> oplogFileNames = new HashSet<String>();
/**
* Root directory for a member's backup.
*/
protected File backupDir = null;
/**
* Returns a BackupInspector for a member's backup directory.
*
* @param backupDir a member's backup directory.
* @return a new BackupInspector.
* @throws IOException the backup directory was malformed.
*/
public static BackupInspector createInspector(File backupDir) throws IOException {
if (isWindows()) {
return new WindowsBackupInspector(backupDir);
}
return new UnixBackupInspector(backupDir);
}
/**
* Creates a new BackupInspector.
*
* @param backupDir a a previous backup for a member.
* @throws IOException an error occurred while parsing the restore script.
*/
public BackupInspector(File backupDir) throws IOException {
this.backupDir = backupDir;
if (!backupDir.exists()) {
throw new IOException("Backup directory " + backupDir.getAbsolutePath() + " does not exist.");
}
File restoreFile = getRestoreFile(backupDir);
if (!restoreFile.exists()) {
throw new IOException("Restore file " + restoreFile.getName() + " does not exist.");
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(restoreFile));
parseRestoreFile(reader);
} finally {
if (null != reader) {
reader.close();
}
}
}
/**
* Searches for the incremental backup marker.
*
* @param reader restore file reader.
* @throws IOException
*/
private void parseRestoreFile(BufferedReader reader) throws IOException {
boolean markerFound = false;
String line = null;
String incrementalMarker = getIncrementalMarker();
while (!markerFound && (null != (line = reader.readLine()))) {
markerFound = line.startsWith(incrementalMarker);
}
if (markerFound) {
parseOplogLines(reader);
}
}
/**
* @return true if the host operating system is windows.
*/
public static boolean isWindows() {
return (System.getProperty("os.name").indexOf("Windows") != -1);
}
/**
* Returns true if the restore script is incremental.
*/
public boolean isIncremental() {
return !this.oplogFileNames.isEmpty();
}
/**
* @return the backup directory being inspected.
*/
public File getBackupDir() {
return this.backupDir;
}
/**
* Returns the restore script line that restores a particular operation log file.
*
* @param oplogFileName an operation log file.
*/
public String getScriptLineForOplogFile(String oplogFileName) {
return this.oplogLineMap.get(oplogFileName);
}
/**
* Returns the set of operation log files copied in the incremental backup section of the restore
* script.
*/
public Set<String> getIncrementalOplogFileNames() {
return Collections.unmodifiableSet(this.oplogFileNames);
}
/**
* @return the incremental marke contained in the backup restore script.
*/
protected abstract String getIncrementalMarker();
/**
* Returns the restore script for the backup.
*
* @param backupDir a member's backup directory.
*/
protected abstract File getRestoreFile(final File backupDir);
/**
* Returns the copyTo operation log file path for an operation log file name.
*
* @param oplogFileName an operation log file.
*/
public abstract String getCopyToForOplogFile(String oplogFileName);
/**
* Returns the copy from operation log file path for an operation log file name.
*
* @param oplogFileName an operation log file.
*/
public abstract String getCopyFromForOplogFile(String oplogFileName);
/**
* Parses out operation log data from the incremental backup portion of the restore script.
*
* @param reader restore file reader.
* @throws IOException
*/
protected abstract void parseOplogLines(BufferedReader reader) throws IOException;
}
/**
* A BackupInspector for the Windows platform(s).
*
*/
class WindowsBackupInspector extends BackupInspector {
/**
* When found indicates that the restore script was produced from an incremental backup.
*/
private static final String INCREMENTAL_MARKER = "rem Incremental backup";
/**
* Restore file for windows platform.
*/
static final String RESTORE_FILE = "restore.bat";
WindowsBackupInspector(File backupDir) throws IOException {
super(backupDir);
}
@Override
public String getCopyFromForOplogFile(String oplogFileName) {
String copyFrom = null;
String line = this.oplogLineMap.get(oplogFileName);
if (null != line) {
String[] parts = line.split("\\s");
copyFrom = parts[1].substring(1, parts[1].length() - 1) + File.separator + parts[3];
}
return copyFrom;
}
@Override
public String getCopyToForOplogFile(String oplogFileName) {
String copyTo = null;
String line = this.oplogLineMap.get(oplogFileName);
if (null != line) {
String[] parts = line.split("\\s");
copyTo = parts[2].substring(1, parts[2].length() - 1) + File.separator + parts[3];
}
return copyTo;
}
@Override
/**
* Parses out operation log data from the incremental backup portion of the restore script.
*
* @param reader restore file reader.
* @throws IOException
*/
protected void parseOplogLines(BufferedReader reader) throws IOException {
String line = null;
int beginIndex, endIndex;
String oplogName = "";
while (null != (line = reader.readLine())) {
if (line.startsWith("robocopy")) {
beginIndex = line.lastIndexOf("\"") + 2;
endIndex = line.indexOf("/njh", beginIndex) - 1;
oplogName = line.substring(beginIndex, endIndex);
this.oplogFileNames.add(oplogName);
this.oplogLineMap.put(oplogName, line);
} else if (line.startsWith("IF")) {
continue;
} else if (line.contains(RestoreScript.EXIT_MARKER)) {
break;
}
}
}
@Override
protected String getIncrementalMarker() {
return INCREMENTAL_MARKER;
}
@Override
protected File getRestoreFile(final File backupDir) {
return new File(backupDir, RESTORE_FILE);
}
}
/**
* A BackupInspector for Unix platforms.
*/
class UnixBackupInspector extends BackupInspector {
/**
* When found indicates that the restore script was produced from an incremental backup.
*/
private static final String INCREMENTAL_MARKER = "#Incremental backup";
/**
* Restore file for windows platform.
*/
static final String RESTORE_FILE = "restore.sh";
UnixBackupInspector(File backupDir) throws IOException {
super(backupDir);
}
@Override
public String getCopyFromForOplogFile(String oplogFileName) {
String copyFrom = null;
String line = this.oplogLineMap.get(oplogFileName);
if (null != line) {
String[] parts = line.split("\\s");
copyFrom = parts[2].substring(1, parts[2].length() - 1);
}
return copyFrom;
}
@Override
public String getCopyToForOplogFile(String oplogFileName) {
String copyTo = null;
String line = this.oplogLineMap.get(oplogFileName);
if (null != line) {
String[] parts = line.split("\\s");
copyTo = parts[3].substring(1, parts[3].length() - 1);
}
return copyTo;
}
@Override
/**
* Parses out operation log data from the incremental backup portion of the restore script.
*
* @param reader restore file reader.
* @throws IOException
*/
protected void parseOplogLines(BufferedReader reader) throws IOException {
String line = null;
while (null != (line = reader.readLine())) {
int beginIndex = line.lastIndexOf(File.separator) + 1;
int endIndex = line.length() - 1;
String oplogName = line.substring(beginIndex, endIndex);
this.oplogFileNames.add(oplogName);
this.oplogLineMap.put(oplogName, line);
}
}
@Override
protected String getIncrementalMarker() {
return INCREMENTAL_MARKER;
}
@Override
protected File getRestoreFile(final File backupDir) {
return new File(backupDir, RESTORE_FILE);
}
}