001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.command.dml;
007:
008: import java.io.IOException;
009: import java.io.InputStream;
010: import java.io.OutputStream;
011: import java.sql.SQLException;
012: import java.util.ArrayList;
013: import java.util.zip.ZipEntry;
014: import java.util.zip.ZipOutputStream;
015:
016: import org.h2.api.DatabaseEventListener;
017: import org.h2.command.Prepared;
018: import org.h2.constant.ErrorCode;
019: import org.h2.engine.Constants;
020: import org.h2.engine.Database;
021: import org.h2.engine.Session;
022: import org.h2.log.LogFile;
023: import org.h2.log.LogSystem;
024: import org.h2.message.Message;
025: import org.h2.result.LocalResult;
026: import org.h2.store.DiskFile;
027: import org.h2.store.FileLister;
028: import org.h2.util.FileUtils;
029: import org.h2.util.IOUtils;
030: import org.h2.util.ObjectArray;
031:
032: /**
033: * This class represents the statement
034: * BACKUP
035: */
036: public class BackupCommand extends Prepared {
037:
038: private String fileName;
039:
040: public BackupCommand(Session session) {
041: super (session);
042: }
043:
044: public void setFileName(String fileName) {
045: this .fileName = fileName;
046: }
047:
048: public int update() throws SQLException {
049: session.getUser().checkAdmin();
050: backupTo(fileName);
051: return 0;
052: }
053:
054: private void backupTo(String fileName) throws SQLException {
055: Database db = session.getDatabase();
056: if (!db.isPersistent()) {
057: throw Message
058: .getSQLException(ErrorCode.DATABASE_IS_NOT_PERSISTENT);
059: }
060: try {
061: String name = db.getName();
062: name = FileUtils.getFileName(name);
063: OutputStream zip = FileUtils.openFileOutputStream(fileName,
064: false);
065: ZipOutputStream out = new ZipOutputStream(zip);
066: LogSystem log = db.getLog();
067: try {
068: log.flush();
069: log.updateKeepFiles(1);
070: String fn = db.getName() + Constants.SUFFIX_DATA_FILE;
071: backupDiskFile(out, fn, db.getDataFile());
072: fn = db.getName() + Constants.SUFFIX_INDEX_FILE;
073: String base = FileUtils.getParent(fn);
074: backupDiskFile(out, fn, db.getIndexFile());
075: ObjectArray list = log.getActiveLogFiles();
076: int max = list.size();
077: // synchronize on the database, to avoid concurrent temp file
078: // creation / deletion / backup
079: synchronized (db.getLobSyncObject()) {
080: for (int i = 0; i < list.size(); i++) {
081: LogFile lf = (LogFile) list.get(i);
082: fn = lf.getFileName();
083: backupFile(out, base, fn);
084: db
085: .setProgress(
086: DatabaseEventListener.STATE_BACKUP_FILE,
087: name, i, max);
088: }
089: String prefix = db.getDatabasePath();
090: String dir = FileUtils.getParent(prefix);
091: ArrayList fileList = FileLister.getDatabaseFiles(
092: dir, name, true);
093: for (int i = 0; i < fileList.size(); i++) {
094: fn = (String) fileList.get(i);
095: if (fn.endsWith(Constants.SUFFIX_HASH_FILE)
096: || fn
097: .endsWith(Constants.SUFFIX_LOB_FILE)) {
098: backupFile(out, base, fn);
099: }
100: }
101: }
102: out.close();
103: zip.close();
104: } finally {
105: log.updateKeepFiles(-1);
106: }
107: } catch (IOException e) {
108: throw Message.convertIOException(e, fileName);
109: }
110: }
111:
112: private void backupDiskFile(ZipOutputStream out, String fileName,
113: DiskFile file) throws SQLException, IOException {
114: Database db = session.getDatabase();
115: fileName = FileUtils.getFileName(fileName);
116: out.putNextEntry(new ZipEntry(fileName));
117: int pos = -1;
118: int max = file.getReadCount();
119: while (true) {
120: pos = file.copyDirect(pos, out);
121: if (pos < 0) {
122: break;
123: }
124: db.setProgress(DatabaseEventListener.STATE_BACKUP_FILE,
125: fileName, pos, max);
126: }
127: out.closeEntry();
128: }
129:
130: private void backupFile(ZipOutputStream out, String base, String fn)
131: throws SQLException, IOException {
132: String f = FileUtils.getAbsolutePath(fn);
133: base = FileUtils.getAbsolutePath(base);
134: if (!f.startsWith(base)) {
135: throw Message.getInternalError(f + " does not start with "
136: + base);
137: }
138: f = f.substring(base.length());
139: f = correctFileName(f);
140: out.putNextEntry(new ZipEntry(f));
141: InputStream in = FileUtils.openFileInputStream(fn);
142: IOUtils.copyAndCloseInput(in, out);
143: out.closeEntry();
144: }
145:
146: public boolean isTransactional() {
147: return true;
148: }
149:
150: /**
151: * Fix the file name, replacing backslash with slash.
152: *
153: * @param f the file name
154: * @return the corrected file name
155: */
156: public static String correctFileName(String f) {
157: f = f.replace('\\', '/');
158: if (f.startsWith("/")) {
159: f = f.substring(1);
160: }
161: return f;
162: }
163:
164: public boolean needRecompile() {
165: return false;
166: }
167:
168: public LocalResult queryMeta() {
169: return null;
170: }
171:
172: }
|