Java tutorial
/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015 GAEL Systems * * This file is part of DHuS software sources. * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.service; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Paths; import java.security.MessageDigest; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Properties; import fr.gael.dhus.database.object.config.system.SupportConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.comparator.NameFileComparator; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer; import org.apache.solr.client.solrj.response.SolrResponseBase; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.CoreContainer; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hsqldb.lib.tar.DbBackupMain; import org.hsqldb.lib.tar.TarMalformatException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.codec.Hex; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import fr.gael.dhus.DHuS; import fr.gael.dhus.database.dao.ConfigurationDao; import fr.gael.dhus.database.dao.UserDao; import fr.gael.dhus.database.dao.interfaces.DHusDumpException; import fr.gael.dhus.database.object.User; import fr.gael.dhus.database.object.User.PasswordEncryption; import fr.gael.dhus.database.object.config.Configuration; import fr.gael.dhus.database.object.config.search.SolrConfiguration; import fr.gael.dhus.service.exception.UserBadEncryptionException; import fr.gael.dhus.system.config.ConfigurationManager; import org.apache.solr.client.solrj.SolrQuery; @Service public class SystemService extends WebService { private static final String BACKUP_DATABASE_NAME = "database"; private static final String BACKUP_INDEX_NAME = "index"; private static Logger logger = Logger.getLogger(SystemService.class); public static final String RESTORATION_PROPERTIES = "dhus-restoration-system.properties"; private static int SOLR_VERSION = 4; @Autowired private ConfigurationDao cfgDao; @Autowired private UserDao userDao; @Autowired private ConfigurationManager cfgManager; @PreAuthorize("hasRole('ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Configuration getCurrentConfiguration() { return cfgDao.getCurrentConfiguration(); } @PreAuthorize("hasRole('ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public Configuration saveSystemSettings(Configuration cfg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, CloneNotSupportedException { Configuration db_cfg = cfgDao.getCurrentConfiguration(); cfg = cfg.completeWith(db_cfg); db_cfg.setCronConfiguration(cfg.getCronConfiguration()); db_cfg.setGuiConfiguration(cfg.getGuiConfiguration()); db_cfg.setMessagingConfiguration(cfg.getMessagingConfiguration()); db_cfg.setNetworkConfiguration(cfg.getNetworkConfiguration()); db_cfg.setProductConfiguration(cfg.getProductConfiguration()); db_cfg.setSearchConfiguration(cfg.getSearchConfiguration()); db_cfg.setServerConfiguration(cfg.getServerConfiguration()); db_cfg.setSystemConfiguration(cfg.getSystemConfiguration()); cfgDao.update(db_cfg); return cfgDao.getCurrentConfiguration(); } @PreAuthorize("hasRole('ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public Configuration resetToDefaultConfiguration() throws Exception { cfgManager.reloadConfiguration(); return cfgDao.getCurrentConfiguration(); } @PreAuthorize("hasRole('ROLE_SYSTEM_MANAGER')") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void changeRootPassword(String new_pwd, String old_pwd) { User root = userDao.getByName(cfgManager.getAdministratorConfiguration().getName()); PasswordEncryption encryption = root.getPasswordEncryption(); if (encryption != PasswordEncryption.NONE) { try { MessageDigest md = MessageDigest.getInstance(encryption.getAlgorithmKey()); old_pwd = new String(Hex.encode(md.digest(old_pwd.getBytes("UTF-8")))); } catch (Exception e) { throw new UserBadEncryptionException("There was an error while encrypting password of root user", e); } } if ((old_pwd == null) || ("".equals(old_pwd)) || (!root.getPassword().equals(old_pwd))) throw new SecurityException("Wrong password."); if ((new_pwd == null) || "".equals(new_pwd.trim())) throw new SecurityException("New password cannot be empty."); String password = new_pwd.trim(); root.setPassword(password); userDao.update(root); } @PreAuthorize("hasRole('ROLE_SYSTEM_MANAGER')") public List<Date> getDumpDatabaseList() { List<Date> timestamps = new ArrayList<Date>(); File path_file = new File(cfgManager.getDatabaseConfiguration().getDumpPath()); File[] lst = path_file.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.startsWith("dump-")) return true; return false; } }); if (lst == null) { return timestamps; } for (File f : lst) { String stimesamp = f.getName().replaceAll("dump-(.*)", "$1"); long timestamp = Long.parseLong(stimesamp); Date date = new Date(timestamp); timestamps.add(date); } Collections.sort(timestamps, Collections.reverseOrder()); return timestamps; } /** * Restores the desired dump of the database and Solr index. To restore the * system must be stopped.This method produces the properties file to * generates new restored database and Solr index. * @param date of the dump to restore. * @throws DHusDumpException if date does not corresponds to an * existing dump. */ public void restoreDumpDatabase(Date date) { File retorationDir = new File(cfgManager.getDatabaseConfiguration().getDumpPath(), String.format("dump-%020d", date.getTime())); if (!(retorationDir.exists() && retorationDir.isDirectory())) { throw new DHusDumpException("Dump of \"" + date + "\" not found"); } try { String path = retorationDir.getAbsolutePath(); SolrConfiguration solrConfig = cfgManager.getSolrConfiguration(); FileWriter writer = new FileWriter(RESTORATION_PROPERTIES); // Database writer.append("dhus.db.backup=").append(path).append("/").append(BACKUP_DATABASE_NAME) .append(".tar.gz"); writer.append('\n'); writer.append("dhus.db.location=").append(getDBDirectory()); writer.append('\n'); // Solr index writer.append("dhus.solr.backup.name=").append(BACKUP_INDEX_NAME); writer.append('\n'); writer.append("dhus.solr.backup.location=").append(path); writer.append('\n'); writer.append("dhus.solr.core.name=").append(solrConfig.getCore()); writer.append('\n'); writer.append("dhus.solr.home=").append(solrConfig.getPath()); writer.append('\n'); writer.flush(); writer.close(); } catch (IOException e) { logger.warn("Can not perform restoration.", e); return; } DHuS.stop(8); } private String getDBDirectory() { String hsqlpath = cfgManager.getDatabaseConfiguration().getPath(); File db = new File(hsqlpath.replace('/', File.separatorChar)).getParentFile(); return db.getPath(); } /** * Generate a backup of DHuS system (database and Solr index) */ public void dumpDatabase() { Date date = new Date(); String dirName = String.format("dump-%020d", date.getTime()); File dir = new File(cfgManager.getDatabaseConfiguration().getDumpPath(), dirName); if (!(dir.mkdirs())) { logger.error("Can not create directory to save backup system."); return; } String path = dir.getAbsolutePath(); if (!(backupDatabase(path) && backupSolr(path))) { logger.warn("Deleting invalid backup system..."); try { FileUtils.deleteDirectory(dir); } catch (IOException e) { logger.error("Can not delete invalid backup system: " + path + ". Please delete it manually."); } } } /** * Performs a backup of database. * @param backupDirectory directory where put the backup. * @return true if backup is successful, otherwise false. */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) private boolean backupDatabase(final String backupDirectory) { return userDao.getHibernateTemplate().execute(new HibernateCallback<Boolean>() { @Override public Boolean doInHibernate(Session session) throws HibernateException, SQLException { String backup = backupDirectory + "/" + BACKUP_DATABASE_NAME + ".tar.gz"; String sql = "BACKUP DATABASE TO '" + backup + "' NOT BLOCKING"; try { session.createSQLQuery(sql).executeUpdate(); } catch (HibernateException e) { return Boolean.FALSE; } return Boolean.TRUE; } }); } /** * Performs a backup of Solr index. * @param backupDirectory directory where put the backup. * @return true if backup is successful, otherwise false. */ private boolean backupSolr(final String backupDirectory) { StringBuilder request = new StringBuilder(); request.append(cfgManager.getServerConfiguration().getUrl()); request.append("/solr/dhus/replication?"); request.append("command=backup&location=").append(backupDirectory); request.append("&name=").append(BACKUP_INDEX_NAME); try { URL url = new URL(request.toString()); HttpURLConnection con = (HttpURLConnection) url.openConnection(); InputStream input = con.getInputStream(); StringBuilder response = new StringBuilder(); byte[] buff = new byte[1024]; int length; while ((length = input.read(buff)) != -1) { response.append(new String(buff, 0, length)); } input.close(); con.disconnect(); logger.debug(response.toString()); } catch (IOException e) { return Boolean.FALSE; } return Boolean.TRUE; } public void cleanDumpDatabase(int keepno) { File[] dumps = new File(cfgManager.getDatabaseConfiguration().getDumpPath()) .listFiles(new FilenameFilter() { @Override public boolean accept(File path, String name) { if (name.startsWith("dump-")) return true; return false; } }); if ((dumps != null) && (dumps.length > keepno)) { Arrays.sort(dumps, NameFileComparator.NAME_COMPARATOR); int last = dumps.length - keepno; for (int index = 0; index < last; index++) { File dir = dumps[index]; try { Date date = new Date(Long.parseLong(dir.getName().replaceAll("dump-(.*)", "$1"))); logger.info("Cleaned dump of " + date); FileUtils.deleteDirectory(dir); } catch (IOException e) { logger.warn("Cannot delete directory " + dir.getPath() + " (" + e.getMessage() + ")"); } } } } /** * Restores DHuS in a previous state. */ public static boolean restore() { File restoreConfig = new File(RESTORATION_PROPERTIES); if (restoreConfig.exists() && restoreConfig.isFile()) { logger.info("Performing restoration DHuS system..."); try (FileInputStream stream = new FileInputStream(restoreConfig)) { Properties properties = new Properties(); properties.load(stream); restoreDatabase(properties); restoreSolrIndex(properties); } catch (UnsupportedOperationException e) { logger.error("Incomplete DHuS restoration file.", e); // DHuS integrity database and Solr index System.setProperty("Archive.check", "true"); } catch (Exception e) { logger.fatal("Restoration failure.", e); return false; } finally { restoreConfig.delete(); } } return true; } /** * Performs database restoration. * No need of transaction here: DBmain is called before starting datasource. * * @param properties properties containing arguments to execute the restoration. */ private static void restoreDatabase(Properties properties) throws IOException, TarMalformatException { String backup = properties.getProperty("dhus.db.backup"); String location = properties.getProperty("dhus.db.location"); if (backup == null || location == null) { throw new UnsupportedOperationException(); } FileUtils.deleteDirectory(new File(location)); String[] args = { "--extract", backup, location }; DbBackupMain.main(args); logger.info("Database restored."); } private static void restoreSolrIndex(Properties properties) throws IOException, SolrServerException { if (SOLR_VERSION == 4) restoreSolr4Index(properties); else restoreSolr5Index(properties); } /** * Performs Solr restoration. * * @param properties properties containing arguments to execute the restoration. */ private static void restoreSolr5Index(Properties properties) throws IOException, SolrServerException { String solrHome = properties.getProperty("dhus.solr.home"); String coreName = properties.getProperty("dhus.solr.core.name"); final String name = properties.getProperty("dhus.solr.backup.name"); final String location = properties.getProperty("dhus.solr.backup.location"); if (solrHome == null || coreName == null || name == null || location == null) { throw new UnsupportedOperationException(); } System.setProperty("solr.solr.home", solrHome); CoreContainer core = new CoreContainer(solrHome); EmbeddedSolrServer server = new EmbeddedSolrServer(core, coreName); server.getCoreContainer().load(); SolrQuery query = new SolrQuery(); query.setRequestHandler("/replication"); query.set("command", "restore"); query.set("name", name); query.set("location", location); server.query(query); server.shutdown(); logger.info("SolR indexes restored."); } /** * Performs Solr restoration. * * @param properties properties containing arguments to execute the restoration. */ private static void restoreSolr4Index(Properties properties) throws IOException, SolrServerException { String solr_home = properties.getProperty("dhus.solr.home"); String core_name = properties.getProperty("dhus.solr.core.name"); final String name = properties.getProperty("dhus.solr.backup.name"); final String location = properties.getProperty("dhus.solr.backup.location"); if (solr_home == null || core_name == null || name == null || location == null) throw new UnsupportedOperationException(); System.setProperty("solr.solr.home", solr_home); File index_path = new File(location, "snapshot." + name); File target_path = Paths.get(solr_home, core_name, "data", name).toFile(); if (!index_path.exists()) throw new UnsupportedOperationException("solr source to restore not found (" + index_path + ")."); if (!target_path.exists()) throw new UnsupportedOperationException("solr restore path not found (" + target_path + ")."); FileUtils.cleanDirectory(target_path); FileUtils.copyDirectory(index_path, target_path); logger.info("SolR indexes restored."); } }