/* * Copyright (C) 2003-2017 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.management.backup.service.jcr; import org.exoplatform.commons.utils.ClassLoading; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; import org.exoplatform.services.jcr.config.RepositoryEntry; import org.exoplatform.services.jcr.config.SimpleParameterEntry; import org.exoplatform.services.jcr.config.WorkspaceEntry; import org.exoplatform.services.jcr.config.WorkspaceInitializerEntry; import org.exoplatform.services.jcr.core.ManageableRepository; import org.exoplatform.services.jcr.core.WorkspaceContainerFacade; import org.exoplatform.services.jcr.ext.backup.BackupChainLog; import org.exoplatform.services.jcr.ext.backup.BackupConfig; import org.exoplatform.services.jcr.ext.backup.BackupConfigurationException; import org.exoplatform.services.jcr.ext.backup.BackupOperationException; import org.exoplatform.services.jcr.ext.backup.JobEntryInfo; import org.exoplatform.services.jcr.ext.backup.RepositoryBackupChainLog; import org.exoplatform.services.jcr.ext.backup.RepositoryRestoreExeption; import org.exoplatform.services.jcr.ext.backup.impl.rdbms.RdbmsBackupWorkspaceInitializer; import org.exoplatform.services.jcr.ext.backup.impl.rdbms.RdbmsWorkspaceInitializer; import org.exoplatform.services.jcr.impl.core.RepositoryImpl; import org.exoplatform.services.jcr.impl.core.SessionRegistry; import org.exoplatform.services.jcr.impl.core.WorkspaceInitializer; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import java.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Map; import javax.jcr.NoSuchWorkspaceException; import javax.jcr.RepositoryException; /** * The Class JobRepositoryRestore. * * @author <a href="mailto:boubaker.khanfir@exoplatform.com">Boubaker * Khanfir</a> * @version $Revision$ */ public class JobRepositoryRestore extends Thread { /** The Constant LOG. */ protected static final Log LOG = ExoLogger.getLogger(JobRepositoryRestore.class); /** * REPOSITORY_RESTORE_STARTED. The state of start restore. */ public static final int REPOSITORY_RESTORE_STARTED = 1; /** * REPOSITORY_RESTORE_SUCCESSFUL. The state of restore successful. */ public static final int REPOSITORY_RESTORE_SUCCESSFUL = 2; /** * REPOSITORY_RESTORE_FAIL. The state of restore fail. */ public static final int REPOSITORY_RESTORE_FAIL = 3; /** * The state of restore. */ private int stateRestore; /** * The start time of restore. */ private Calendar startTime; /** * The end time of restore. */ private Calendar endTime; /** * The exception on restore. */ private Throwable restoreException = null; /** The repository service. */ protected RepositoryService repositoryService; /** The repository entry. */ protected RepositoryEntry repositoryEntry; /** The workspaces mapping. */ protected Map<String, File> workspacesMapping; /** The repository backup chain log file. */ private File repositoryBackupChainLogFile; /** * Instantiates a new job repository restore. * * @param repoService the repo service * @param repositoryEntry the repository entry * @param workspacesMapping the workspaces mapping * @param backupChainLog the backup chain log */ public JobRepositoryRestore(RepositoryService repoService, RepositoryEntry repositoryEntry, Map<String, File> workspacesMapping, File backupChainLog) { super("JobRepositoryRestore " + repositoryEntry.getName()); this.repositoryService = repoService; this.repositoryEntry = repositoryEntry; this.workspacesMapping = workspacesMapping; this.repositoryBackupChainLogFile = backupChainLog; } /** * Restore repository. Provide information about start and finish process. * * @throws RepositoryRestoreExeption * if exception occurred during restore */ public void restore() throws RepositoryRestoreExeption { try { stateRestore = REPOSITORY_RESTORE_STARTED; startTime = Calendar.getInstance(); restoreRepository(); stateRestore = REPOSITORY_RESTORE_SUCCESSFUL; endTime = Calendar.getInstance(); } catch (Exception e) { stateRestore = REPOSITORY_RESTORE_FAIL; restoreException = e; throw new RepositoryRestoreExeption(e.getMessage(), e); } } /** * Restore repository. * * @throws Exception the exception */ protected void restoreRepository() throws Exception { List<WorkspaceEntry> originalWorkspaceEntrys = repositoryEntry.getWorkspaceEntries(); WorkspaceInitializerEntry originalInitializer = initRepositoryParams(originalWorkspaceEntrys); String currennWorkspaceName = repositoryEntry.getSystemWorkspaceName(); boolean restored = true; try { LOG.info("Trying to create the repository '" + repositoryEntry.getName() + "'"); repositoryService.createRepository(repositoryEntry); restoreSystemWorkspace(originalInitializer, currennWorkspaceName); for (WorkspaceEntry wsEntry : originalWorkspaceEntrys) { if (!(wsEntry.getName().equals(repositoryEntry.getSystemWorkspaceName()))) { currennWorkspaceName = wsEntry.getName(); LOG.info("Trying to restore the workspace " + wsEntry.getName()); restoreOverInitializer(new BackupChainLog(workspacesMapping.get(wsEntry.getName())), repositoryEntry.getName(), wsEntry); } } } catch (Exception e) { restored = false; LOG.error("Can not restore workspace \"" + currennWorkspaceName + " in repository \"" + repositoryEntry.getName() + "\".", e); throw new RepositoryRestoreExeption("Can not restore workspace \"" + currennWorkspaceName + " in repository \"" + repositoryEntry.getName() + "\".", e); } finally { if (!restored) { try { removeRepository(repositoryService, repositoryEntry.getName()); } catch (Exception exp) { LOG.error("The partly restored repository \"" + repositoryEntry.getName() + "\" can not be removed.", exp); } } } } /** * Restore system workspace. * * @param originalInitializer the original initializer * @param currennWorkspaceName the currenn workspace name * @throws RepositoryException the repository exception * @throws RepositoryConfigurationException the repository configuration exception */ private void restoreSystemWorkspace(WorkspaceInitializerEntry originalInitializer, String currennWorkspaceName) throws RepositoryException, RepositoryConfigurationException { // set original initializer to created workspace. RepositoryImpl defRep = (RepositoryImpl) repositoryService.getRepository(repositoryEntry.getName()); WorkspaceContainerFacade wcf = defRep.getWorkspaceContainer(currennWorkspaceName); final WorkspaceInitializer workspaceInitializer = (WorkspaceInitializer) wcf.getComponent(WorkspaceInitializer.class); RestoreWorkspaceInitializer.setRestoreInProgress(true); // Force // initialized // flag on // WorkspaceInitializer // to be false wcf.setState(ManageableRepository.OFFLINE); try { workspaceInitializer.initWorkspace(); } finally { wcf.setState(ManageableRepository.ONLINE); } RestoreWorkspaceInitializer.setRestoreInProgress(true); // Force // initialized // flag on // WorkspaceInitializer // to be false WorkspaceEntry createdWorkspaceEntry = (WorkspaceEntry) wcf.getComponent(WorkspaceEntry.class); createdWorkspaceEntry.setInitializer(originalInitializer); } /** * Inits the repository params. * * @param originalWorkspaceEntrys the original workspace entrys * @return the workspace initializer entry * @throws RepositoryRestoreExeption the repository restore exeption * @throws BackupOperationException the backup operation exception * @throws ClassNotFoundException the class not found exception */ private WorkspaceInitializerEntry initRepositoryParams(List<WorkspaceEntry> originalWorkspaceEntrys) throws RepositoryRestoreExeption, BackupOperationException, ClassNotFoundException { // Getting system workspace entry WorkspaceEntry systemWorkspaceEntry = null; for (WorkspaceEntry wsEntry : originalWorkspaceEntrys) { if (wsEntry.getName().equals(repositoryEntry.getSystemWorkspaceName())) { systemWorkspaceEntry = wsEntry; break; } } if (systemWorkspaceEntry == null) { throw new RepositoryRestoreExeption("Can not restore workspace \"" + repositoryEntry.getSystemWorkspaceName() + " in repository \"" + repositoryEntry.getName() + "\"." + " The related configuration cannot be found."); } WorkspaceInitializerEntry wieOriginal = systemWorkspaceEntry.getInitializer(); // getting backup chail log to system workspace. BackupChainLog systemBackupChainLog = new BackupChainLog(workspacesMapping.get(systemWorkspaceEntry.getName())); WorkspaceInitializerEntry wiEntry = getWorkspaceInitializerEntry(systemBackupChainLog); // set initializer systemWorkspaceEntry.setInitializer(wiEntry); ArrayList<WorkspaceEntry> newEntries = new ArrayList<WorkspaceEntry>(); newEntries.add(systemWorkspaceEntry); repositoryEntry.setWorkspaceEntries(newEntries); return wieOriginal; } /** * Removes the repository. * * @param repositoryService the repository service * @param repositoryName the repository name * @throws RepositoryException the repository exception * @throws RepositoryConfigurationException the repository configuration exception */ protected void removeRepository(RepositoryService repositoryService, String repositoryName) throws RepositoryException, RepositoryConfigurationException { ManageableRepository mr = null; try { mr = repositoryService.getRepository(repositoryName); } catch (RepositoryException e) { // Nothing to catch, Repository was not found } if (mr != null) { closeAllSession(mr); repositoryService.removeRepository(repositoryName); } } /** * Gets the workspace initializer entry. * * @param systemBackupChainLog the system backup chain log * @return the workspace initializer entry * @throws BackupOperationException the backup operation exception * @throws ClassNotFoundException the class not found exception */ private WorkspaceInitializerEntry getWorkspaceInitializerEntry(BackupChainLog systemBackupChainLog) throws BackupOperationException, ClassNotFoundException { String fullBackupPath = systemBackupChainLog.getJobEntryInfos().get(0).getURL().getPath(); String fullbackupType = null; try { if ((ClassLoading.forName(systemBackupChainLog.getFullBackupType(), this).equals(FullBackupJob.class))) { fullbackupType = systemBackupChainLog.getFullBackupType(); } else { throw new BackupOperationException("Class \"" + systemBackupChainLog.getFullBackupType() + "\" is not support as full backup."); } } catch (ClassNotFoundException e) { throw new BackupOperationException("Class \"" + systemBackupChainLog.getFullBackupType() + "\" is not found.", e); } WorkspaceInitializerEntry wiEntry = new WorkspaceInitializerEntry(); if ((ClassLoading.forName(fullbackupType, this).equals(FullBackupJob.class))) { // set the initializer RdbmsBackupWorkspaceInitializer wiEntry.setType(RestoreWorkspaceInitializer.class.getCanonicalName()); List<SimpleParameterEntry> wieParams = new ArrayList<SimpleParameterEntry>(); wieParams.add(new SimpleParameterEntry(RdbmsBackupWorkspaceInitializer.RESTORE_PATH_PARAMETER, (new File(fullBackupPath).getParent()))); wiEntry.setParameters(wieParams); } else { throw new BackupOperationException("Class \"" + systemBackupChainLog.getFullBackupType() + "\" is not support as full backup."); } return wiEntry; } /** * Close all session. * * @param mr the mr * @throws NoSuchWorkspaceException the no such workspace exception */ private void closeAllSession(ManageableRepository mr) throws NoSuchWorkspaceException { for (String wsName : mr.getWorkspaceNames()) { if (!mr.canRemoveWorkspace(wsName)) { WorkspaceContainerFacade wc = mr.getWorkspaceContainer(wsName); SessionRegistry sessionRegistry = (SessionRegistry) wc.getComponent(SessionRegistry.class); sessionRegistry.closeSessions(wsName); } } } /** * {@inheritDoc} */ @Override public void run() { try { restore(); } catch (Throwable t) // NOSONAR { LOG.error("The restore was fail", t); } } /** * getRestoreException. * * @return Throwable return the exception of repository restore. */ public Throwable getRestoreException() { return restoreException; } /** * getStateRestore. * * @return int return state of restore. */ public int getStateRestore() { return stateRestore; } /** * getBeginTime. * * @return Calendar return the start time of restore */ public Calendar getStartTime() { return startTime; } /** * getEndTime. * * @return Calendar return the end time of restore */ public Calendar getEndTime() { return endTime; } /** * getRepositoryName. * * @return String the name of destination repository */ public String getRepositoryName() { return repositoryEntry.getName(); } /** * GetRepositoryBackupChainLog. * * @return repositoryBackupChainLog * @throws BackupOperationException the backup operation exception */ public RepositoryBackupChainLog getRepositoryBackupChainLog() throws BackupOperationException { return new RepositoryBackupChainLog(repositoryBackupChainLogFile); } /** * getRepositoryEntry. * * @return repositoryBackupChainLog */ public RepositoryEntry getRepositoryEntry() { return repositoryEntry; } /** * Restore over initializer. * * @param log the log * @param repositoryName the repository name * @param workspaceEntry the workspace entry * @throws BackupOperationException the backup operation exception * @throws RepositoryException the repository exception * @throws RepositoryConfigurationException the repository configuration exception * @throws BackupConfigurationException the backup configuration exception */ protected void restoreOverInitializer(BackupChainLog log, String repositoryName, WorkspaceEntry workspaceEntry) throws BackupOperationException, RepositoryException, RepositoryConfigurationException, BackupConfigurationException { List<JobEntryInfo> list = log.getJobEntryInfos(); BackupConfig config = log.getBackupConfig(); String reposytoryName = (repositoryName == null ? config.getRepository() : repositoryName); String workspaceName = workspaceEntry.getName(); String fullbackupType = null; try { if ((ClassLoading.forName(log.getFullBackupType(), this).equals(FullBackupJob.class))) { fullbackupType = log.getFullBackupType(); } else if ((ClassLoading.forName(log.getFullBackupType(), this).equals(org.exoplatform.services.jcr.ext.backup.impl.rdbms.FullBackupJob.class))) { fullbackupType = log.getFullBackupType(); } else { throw new BackupOperationException("Class \"" + log.getFullBackupType() + "\" is not support as full backup."); } } catch (ClassNotFoundException e) { throw new BackupOperationException("Class \"" + log.getFullBackupType() + "\" is not found.", e); } // ws should not exists. if (!workspaceAlreadyExist(reposytoryName, workspaceName)) { for (int i = 0; i < list.size(); i++) { try { fullRestoreOverInitializer(list.get(i).getURL().getPath(), reposytoryName, workspaceEntry, fullbackupType); } catch (Exception e) { throw new BackupOperationException("Restore error", e); } } } else { throw new BackupConfigurationException("Workspace \"" + workspaceName + "\" should not exists."); } } /** * Full restore over initializer. * * @param pathBackupFile the path backup file * @param repositoryName the repository name * @param workspaceEntry the workspace entry * @param fBackupType the f backup type * @throws Exception the exception */ private void fullRestoreOverInitializer(String pathBackupFile, String repositoryName, WorkspaceEntry workspaceEntry, String fBackupType) throws Exception { WorkspaceInitializerEntry wieOriginal = workspaceEntry.getInitializer(); RepositoryImpl defRep = (RepositoryImpl) repositoryService.getRepository(repositoryName); WorkspaceInitializerEntry wiEntry = new WorkspaceInitializerEntry(); if ((ClassLoading.forName(fBackupType, this).equals(FullBackupJob.class))) { // set the initializer RdbmsWorkspaceInitializer wiEntry.setType(RestoreWorkspaceInitializer.class.getCanonicalName()); List<SimpleParameterEntry> wieParams = new ArrayList<SimpleParameterEntry>(); wieParams.add(new SimpleParameterEntry(RdbmsWorkspaceInitializer.RESTORE_PATH_PARAMETER, new File(pathBackupFile).getParent())); wiEntry.setParameters(wieParams); } String wsName = workspaceEntry.getName(); if (defRep.isWorkspaceInitialized(wsName)) { if (defRep.canRemoveWorkspace(wsName)) { defRep.removeWorkspace(wsName); } else { LOG.error("Cannot initialize workspace {}", wsName); } } workspaceEntry.setInitializer(wiEntry); // Restore Workspace defRep.configWorkspace(workspaceEntry); RestoreWorkspaceInitializer.setRestoreInProgress(true); // Force initialized // flag on // WorkspaceInitializer // to be false defRep.createWorkspace(wsName); RestoreWorkspaceInitializer.setRestoreInProgress(false); // Resume // initialized flag // to its original // value // set original workspace initializer WorkspaceContainerFacade wcf = defRep.getWorkspaceContainer(wsName); WorkspaceEntry createdWorkspaceEntry = (WorkspaceEntry) wcf.getComponent(WorkspaceEntry.class); createdWorkspaceEntry.setInitializer(wieOriginal); } /** * Workspace already exist. * * @param repository the repository * @param workspace the workspace * @return true, if successful * @throws RepositoryException the repository exception * @throws RepositoryConfigurationException the repository configuration exception */ private boolean workspaceAlreadyExist(String repository, String workspace) throws RepositoryException, RepositoryConfigurationException { String[] ws = repositoryService.getRepository(repository).getWorkspaceNames(); for (int i = 0; i < ws.length; i++) { if (ws[i].equals(workspace)) { return true; } } return false; } }