code: /*
   * 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();
      }
  }