/* * 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.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.geode.internal.FileUtil; /** * This class is used to automatically generate a restore script for a backup. It keeps a list of * files that were backed up, and a list of files that we should test for to avoid overriding when * we restore the backup. * * It generates either a restore.sh for unix or a restore.bat for windows. * * */ public class RestoreScript { public static final String EXIT_MARKER = "Exit Functions"; private static final ScriptGenerator UNIX_GENERATOR = new UnixScriptGenerator(); private static final ScriptGenerator WINDOWS_GENERATOR = new WindowsScriptGenerator(); private Map<File, File> baselineFiles = new HashMap<File, File>(); private final Map<File, File> backedUpFiles = new LinkedHashMap<File, File>(); private final List<File> existenceTests = new ArrayList<File>(); public void addBaselineFiles(Map<File, File> baselineFiles) { this.baselineFiles.putAll(baselineFiles); } public void addFile(File originalFile, File backupFile) { backedUpFiles.put(backupFile, originalFile.getAbsoluteFile()); } public void addExistenceTest(File originalFile) { existenceTests.add(originalFile.getAbsoluteFile()); } public void generate(File outputDir) throws FileNotFoundException { if (isWindows()) { generateWindowsScript(outputDir); } else { generateUnixScript(outputDir); } } private void generateWindowsScript(File outputDir) throws FileNotFoundException { File outputFile = new File(outputDir, "restore.bat"); generateScript(outputDir, outputFile, WINDOWS_GENERATOR); } private void generateUnixScript(File outputDir) throws FileNotFoundException { File outputFile = new File(outputDir, "restore.sh"); generateScript(outputDir, outputFile, UNIX_GENERATOR); } private void generateScript(File outputDir, File outputFile, ScriptGenerator osGenerator) throws FileNotFoundException { PrintWriter writer = new PrintWriter(outputFile); try { osGenerator.writePreamble(writer); writer.println(); osGenerator.writeComment(writer, "Restore a backup of gemfire persistent data to the location it was backed up"); osGenerator.writeComment(writer, "from."); osGenerator.writeComment(writer, "This script will refuse to restore if the original data still exists."); writer.println(); osGenerator.writeComment(writer, "This script was automatically generated by the gemfire backup utility."); writer.println(); osGenerator.writeComment(writer, "Test for existing originals. If they exist, do not restore the backup."); for (File file : existenceTests) { osGenerator.writeExistenceTest(writer, file); } writer.println(); osGenerator.writeComment(writer, "Restore data"); for (Map.Entry<File, File> entry : backedUpFiles.entrySet()) { File backup = entry.getKey(); boolean backupHasFiles = backup.isDirectory() && backup.list().length != 0; backup = FileUtil.removeParent(outputDir, backup); File original = entry.getValue(); if (original.isDirectory()) { osGenerator.writeCopyDirectoryContents(writer, backup, original, backupHasFiles); } else { osGenerator.writeCopyFile(writer, backup, original); } } // Write out baseline file copies in restore script (if there are any) if this is a restore // for an incremental backup if (!this.baselineFiles.isEmpty()) { writer.println(); osGenerator.writeComment(writer, "Incremental backup. Restore baseline originals from previous backups."); for (Map.Entry<File, File> entry : this.baselineFiles.entrySet()) { osGenerator.writeCopyFile(writer, entry.getKey(), entry.getValue()); } } if (isWindows()) { osGenerator.writeExit(writer); } } finally { writer.close(); } outputFile.setExecutable(true, true); } // TODO prpersist - We've got this code replicated // in 10 different places in our product. Maybe we // need to put this method somewhere :) private boolean isWindows() { String os = System.getProperty("os.name"); if (os != null) { if (os.indexOf("Windows") != -1) { return true; } } return false; } private static interface ScriptGenerator { void writePreamble(PrintWriter writer); void writeExit(PrintWriter writer); void writeCopyFile(PrintWriter writer, File backup, File original); void writeCopyDirectoryContents(PrintWriter writer, File backup, File original, boolean backupHasFiles); void writeExistenceTest(PrintWriter writer, File file); void writeComment(PrintWriter writer, String string); }; private static class WindowsScriptGenerator implements ScriptGenerator { final String ERROR_CHECK = "IF %ERRORLEVEL% GEQ 4 GOTO Exit_Bad"; public void writePreamble(PrintWriter writer) { writer.println("echo off"); } public void writeComment(PrintWriter writer, String string) { writer.println("rem " + string); } public void writeCopyDirectoryContents(PrintWriter writer, File backup, File original, boolean backupHasFiles) { writer.println("mkdir \"" + original + "\""); writer.println("C:\\Windows\\System32\\Robocopy.exe \"" + backup + "\" \"" + original + "\" /e /njh /njs"); writer.println(ERROR_CHECK); } public void writeCopyFile(PrintWriter writer, File source, File destination) { String fileName = source.getName(); String sourcePath = source.getParent() == null ? "." : source.getParent(); String destinationPath = destination.getParent() == null ? "." : destination.getParent(); writer.println("C:\\Windows\\System32\\Robocopy.exe \"" + sourcePath + "\" \"" + destinationPath + "\" " + fileName + " /njh /njs"); writer.println(ERROR_CHECK); } public void writeExistenceTest(PrintWriter writer, File file) { writer.println("IF EXIST \"" + file + "\" echo \"Backup not restored. Refusing to overwrite " + file + "\" && exit /B 1 "); } public void writeExit(PrintWriter writer) { writeComment(writer, EXIT_MARKER); writer.println(":Exit_Good\nexit /B 0\n\n:Exit_Bad\nexit /B 1"); } } private static class UnixScriptGenerator implements ScriptGenerator { public void writePreamble(PrintWriter writer) { writer.println("#!/bin/bash -e"); writer.println("cd `dirname $0`"); } public void writeComment(PrintWriter writer, String string) { writer.println("#" + string); } public void writeCopyDirectoryContents(PrintWriter writer, File backup, File original, boolean backupHasFiles) { writer.println("mkdir -p '" + original + "'"); if (backupHasFiles) { writer.println("cp -rp '" + backup + "'/* '" + original + "'"); } } public void writeCopyFile(PrintWriter writer, File backup, File original) { writer.println("cp -p '" + backup + "' '" + original + "'"); } public void writeExistenceTest(PrintWriter writer, File file) { writer.println("test -e '" + file + "' && echo 'Backup not restored. Refusing to overwrite " + file + "' && exit 1 "); } public void writeExit(PrintWriter writer) {} } }