/*
* Computoser is a music-composition algorithm and a website to present the results
* Copyright (C) 2012-2014 Bozhidar Bozhanov
*
* Computoser is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Computoser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Computoser. If not, see <http://www.gnu.org/licenses/>.
*/
package com.music.scheduled;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.music.service.FileStorageService;
@Component
public class BackupJob {
private static final Logger log = LoggerFactory.getLogger(BackupJob.class);
private static final int ZIP_BUFFER = 2048;
private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormat.forPattern("dd-MM-yyyy-HH-mm");
@Resource(name="${filesystem.implementation}")
private FileStorageService fileStorageService;
@Value("${database.url}")
private String jdbcUrl;
@Value("${database.username}")
private String username;
@Value("${database.password}")
private String password;
@Value("${backup.dir}")
private String finalBackupDir;
@Value("${database.perform.backup}")
private boolean performBackup;
private String baseDir;
@PostConstruct
public void init() {
if (!finalBackupDir.endsWith("/")) {
throw new IllegalArgumentException("backup.dir property must end with a slash");
}
baseDir = System.getProperty("java.io.tmpdir") + finalBackupDir;
new File(baseDir).mkdirs();
}
@Scheduled(cron = "0 0 0 * * ?") //every midnight
public void run() {
if (performBackup) {
parseAndPerformMySQLBackup(username, password, jdbcUrl);
}
}
private void parseAndPerformMySQLBackup(String user, String password, String jdbcUrl) {
String port = "3306";
Pattern hostPattern = Pattern.compile("//((\\w)+)/");
Matcher m = hostPattern.matcher(jdbcUrl);
String host = null;
if (m.find()) {
host = m.group(1);
}
Pattern dbPattern = Pattern.compile("/((\\w)+)\\?");
m = dbPattern.matcher(jdbcUrl);
String db = null;
if (m.find()) {
db = m.group(1);
}
log.debug(host + ":" + port + ":" + user + ":***:" + db);
try {
createBackup(host, port, user, password, db);
} catch (Exception ex) {
log.error("Error during backup", ex);
}
}
private void createBackup(String host, String port, String user, String password,
String db) throws Exception {
String fileName = "backup-" + DATE_TIME_FORMAT.print(new DateTime());
String baseFilePath = new File(baseDir + fileName).getAbsolutePath();
String sqlFilePath = baseFilePath + ".sql";
String execString = "mysqldump --host=" + host + " --port=" + port + " --user="
+ user + (StringUtils.isNotBlank(password) ? " --password=" + password : "")
+ " --compact --complete-insert --extended-insert --single-transaction "
+ "--skip-comments --skip-triggers --default-character-set=utf8 " + db
+ " --result-file=" + sqlFilePath;
Process process = Runtime.getRuntime().exec(execString);
if (log.isDebugEnabled()) {
log.debug("Output: " + IOUtils.toString(process.getInputStream()));
log.debug("Error: " + IOUtils.toString(process.getErrorStream()));
}
if (process.waitFor() == 0) {
zipBackup(baseFilePath);
}
File zipFile = new File(baseFilePath + ".zip");
InputStream is = new BufferedInputStream(new FileInputStream(zipFile));
fileStorageService.storeFile(finalBackupDir + fileName + ".zip", is, zipFile.length());
// result = "SET FOREIGN_KEY_CHECKS = 0;\\n" + result
// + "\\nSET FOREIGN_KEY_CHECKS = 1;";
}
private void zipBackup(String baseFileName) throws IOException {
FileOutputStream fos = new FileOutputStream(baseFileName + ".zip");
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
File entryFile = new File(baseFileName + ".sql");
FileInputStream fi = new FileInputStream(entryFile);
InputStream origin = new BufferedInputStream(fi, ZIP_BUFFER);
ZipEntry entry = new ZipEntry("data.sql");
zos.putNextEntry(entry);
int count;
byte[] data = new byte[ZIP_BUFFER];
while ((count = origin.read(data, 0, ZIP_BUFFER)) != -1) {
zos.write(data, 0, count);
}
origin.close();
zos.close();
entryFile.delete();
}
}