1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.tools; |
7 | |
8 | import java.io.IOException; |
9 | import java.io.InputStream; |
10 | import java.io.OutputStream; |
11 | import java.sql.SQLException; |
12 | import java.util.zip.ZipEntry; |
13 | import java.util.zip.ZipInputStream; |
14 | |
15 | import org.h2.engine.Constants; |
16 | import org.h2.engine.SysProperties; |
17 | import org.h2.message.DbException; |
18 | import org.h2.store.fs.FileUtils; |
19 | import org.h2.util.IOUtils; |
20 | import org.h2.util.Tool; |
21 | |
22 | /** |
23 | * Restores a H2 database by extracting the database files from a .zip file. |
24 | * @h2.resource |
25 | */ |
26 | public class Restore extends Tool { |
27 | |
28 | /** |
29 | * Options are case sensitive. Supported options are: |
30 | * <table> |
31 | * <tr><td>[-help] or [-?]</td> |
32 | * <td>Print the list of options</td></tr> |
33 | * <tr><td>[-file <filename>]</td> |
34 | * <td>The source file name (default: backup.zip)</td></tr> |
35 | * <tr><td>[-dir <dir>]</td> |
36 | * <td>The target directory (default: .)</td></tr> |
37 | * <tr><td>[-db <database>]</td> |
38 | * <td>The target database name (as stored if not set)</td></tr> |
39 | * <tr><td>[-quiet]</td> |
40 | * <td>Do not print progress information</td></tr> |
41 | * </table> |
42 | * @h2.resource |
43 | * |
44 | * @param args the command line arguments |
45 | */ |
46 | public static void main(String... args) throws SQLException { |
47 | new Restore().runTool(args); |
48 | } |
49 | |
50 | @Override |
51 | public void runTool(String... args) throws SQLException { |
52 | String zipFileName = "backup.zip"; |
53 | String dir = "."; |
54 | String db = null; |
55 | for (int i = 0; args != null && i < args.length; i++) { |
56 | String arg = args[i]; |
57 | if (arg.equals("-dir")) { |
58 | dir = args[++i]; |
59 | } else if (arg.equals("-file")) { |
60 | zipFileName = args[++i]; |
61 | } else if (arg.equals("-db")) { |
62 | db = args[++i]; |
63 | } else if (arg.equals("-quiet")) { |
64 | // ignore |
65 | } else if (arg.equals("-help") || arg.equals("-?")) { |
66 | showUsage(); |
67 | return; |
68 | } else { |
69 | showUsageAndThrowUnsupportedOption(arg); |
70 | } |
71 | } |
72 | execute(zipFileName, dir, db); |
73 | } |
74 | |
75 | private static String getOriginalDbName(String fileName, String db) |
76 | throws IOException { |
77 | InputStream in = null; |
78 | try { |
79 | in = FileUtils.newInputStream(fileName); |
80 | ZipInputStream zipIn = new ZipInputStream(in); |
81 | String originalDbName = null; |
82 | boolean multiple = false; |
83 | while (true) { |
84 | ZipEntry entry = zipIn.getNextEntry(); |
85 | if (entry == null) { |
86 | break; |
87 | } |
88 | String entryName = entry.getName(); |
89 | zipIn.closeEntry(); |
90 | String name = getDatabaseNameFromFileName(entryName); |
91 | if (name != null) { |
92 | if (db.equals(name)) { |
93 | originalDbName = name; |
94 | // we found the correct database |
95 | break; |
96 | } else if (originalDbName == null) { |
97 | originalDbName = name; |
98 | // we found a database, but maybe another one |
99 | } else { |
100 | // we have found multiple databases, but not the correct |
101 | // one |
102 | multiple = true; |
103 | } |
104 | } |
105 | } |
106 | zipIn.close(); |
107 | if (multiple && !db.equals(originalDbName)) { |
108 | throw new IOException("Multiple databases found, but not " + db); |
109 | } |
110 | return originalDbName; |
111 | } finally { |
112 | IOUtils.closeSilently(in); |
113 | } |
114 | } |
115 | |
116 | /** |
117 | * Extract the name of the database from a given file name. |
118 | * Only files ending with .h2.db are considered, all others return null. |
119 | * |
120 | * @param fileName the file name (without directory) |
121 | * @return the database name or null |
122 | */ |
123 | private static String getDatabaseNameFromFileName(String fileName) { |
124 | if (fileName.endsWith(Constants.SUFFIX_PAGE_FILE)) { |
125 | return fileName.substring(0, |
126 | fileName.length() - Constants.SUFFIX_PAGE_FILE.length()); |
127 | } |
128 | if (fileName.endsWith(Constants.SUFFIX_MV_FILE)) { |
129 | return fileName.substring(0, |
130 | fileName.length() - Constants.SUFFIX_MV_FILE.length()); |
131 | } |
132 | return null; |
133 | } |
134 | |
135 | /** |
136 | * Restores database files. |
137 | * |
138 | * @param zipFileName the name of the backup file |
139 | * @param directory the directory name |
140 | * @param db the database name (null for all databases) |
141 | * @throws DbException if there is an IOException |
142 | */ |
143 | public static void execute(String zipFileName, String directory, String db) { |
144 | InputStream in = null; |
145 | try { |
146 | if (!FileUtils.exists(zipFileName)) { |
147 | throw new IOException("File not found: " + zipFileName); |
148 | } |
149 | String originalDbName = null; |
150 | int originalDbLen = 0; |
151 | if (db != null) { |
152 | originalDbName = getOriginalDbName(zipFileName, db); |
153 | if (originalDbName == null) { |
154 | throw new IOException("No database named " + db + " found"); |
155 | } |
156 | if (originalDbName.startsWith(SysProperties.FILE_SEPARATOR)) { |
157 | originalDbName = originalDbName.substring(1); |
158 | } |
159 | originalDbLen = originalDbName.length(); |
160 | } |
161 | in = FileUtils.newInputStream(zipFileName); |
162 | ZipInputStream zipIn = new ZipInputStream(in); |
163 | while (true) { |
164 | ZipEntry entry = zipIn.getNextEntry(); |
165 | if (entry == null) { |
166 | break; |
167 | } |
168 | String fileName = entry.getName(); |
169 | // restoring windows backups on linux and vice versa |
170 | fileName = fileName.replace('\\', SysProperties.FILE_SEPARATOR.charAt(0)); |
171 | fileName = fileName.replace('/', SysProperties.FILE_SEPARATOR.charAt(0)); |
172 | if (fileName.startsWith(SysProperties.FILE_SEPARATOR)) { |
173 | fileName = fileName.substring(1); |
174 | } |
175 | boolean copy = false; |
176 | if (db == null) { |
177 | copy = true; |
178 | } else if (fileName.startsWith(originalDbName + ".")) { |
179 | fileName = db + fileName.substring(originalDbLen); |
180 | copy = true; |
181 | } |
182 | if (copy) { |
183 | OutputStream o = null; |
184 | try { |
185 | o = FileUtils.newOutputStream( |
186 | directory + SysProperties.FILE_SEPARATOR + fileName, false); |
187 | IOUtils.copy(zipIn, o); |
188 | o.close(); |
189 | } finally { |
190 | IOUtils.closeSilently(o); |
191 | } |
192 | } |
193 | zipIn.closeEntry(); |
194 | } |
195 | zipIn.closeEntry(); |
196 | zipIn.close(); |
197 | } catch (IOException e) { |
198 | throw DbException.convertIOException(e, zipFileName); |
199 | } finally { |
200 | IOUtils.closeSilently(in); |
201 | } |
202 | } |
203 | |
204 | } |