/*
 * Copyright (C) 2003-2010 eXo Platform SAS.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.services.jcr.ext.backup.usecase;

import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.PropertiesParam;
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.core.ManageableRepository;
import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
import org.exoplatform.services.jcr.ext.backup.BackupChain;
import org.exoplatform.services.jcr.ext.backup.BackupChainLog;
import org.exoplatform.services.jcr.ext.backup.BackupConfig;
import org.exoplatform.services.jcr.ext.backup.BackupJob;
import org.exoplatform.services.jcr.ext.backup.BackupManager;
import org.exoplatform.services.jcr.ext.backup.ExtendedBackupManager;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupChain;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupChainLog;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupConfig;
import org.exoplatform.services.jcr.ext.backup.impl.BackupManagerImpl;
import org.exoplatform.services.jcr.ext.backup.impl.JobExistingRepositoryRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobExistingRepositorySameConfigRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobExistingWorkspaceRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobExistingWorkspaceSameConfigRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobRepositoryRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobWorkspaceRestore;
import org.exoplatform.services.jcr.impl.backup.Backupable;
import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanException;
import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanService;
import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanerTool;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.core.SessionRegistry;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCDataContainerConfig.DatabaseStructureType;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
import org.exoplatform.services.jcr.impl.util.jdbc.DBInitializerHelper;
import org.exoplatform.services.jcr.util.IdGenerator;
import org.exoplatform.services.jcr.util.TesterConfigurationHelper;

import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
 * Created by The eXo Platform SAS.
 * 
 * <br>Date:
 *
 * @author <a href="karpenko.sergiy@gmail.com">Karpenko Sergiy</a> 
 * @version $Id: TestLoadBackup.java 111 2008-11-11 11:11:11Z serg $
 */
public class TestBackupRestore extends BaseStandaloneBackupRestoreTest
{
   private static final String NODE_NAME = "My Node \"with a '\"";

   private static final String PROPERTY_NAME = "My Property \"with a '\"";

   public static int index = 0;

   public void testBackupRestoreExistingRepositorySingleDB() throws Exception
   {
      repositoryBackupRestore("db3");
      repositoryBackupRestore("db3");
   }

   public void testBackupRestoreExistingRepositoryMultiDB() throws Exception
   {
      repositoryBackupRestore("db4");
      repositoryBackupRestore("db4");
      repositoryBackupRestore("db8");
      repositoryBackupRestore("db8");
   }

   public void testBackupRestoreExistingWorkspaceSingleDB() throws Exception
   {
      workspaceBackupRestore("db3");
      workspaceBackupRestore("db3");
   }

   public void testBackupRestoreExistingWorkspaceMultiDB() throws Exception
   {
      workspaceBackupRestore("db4");
      workspaceBackupRestore("db4");
      workspaceBackupRestore("db8");
      workspaceBackupRestore("db8");
   }

   public void testJobExistingRepositorySameConfigRestoreSingleDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db3");
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db3");
   }

   public void testJobExistingRepositorySameConfigRestoreMultiDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db4");
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db4");
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db8");
      repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore("db8");
   }

   public void testJobExistingRepositoryRestoreSingleDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db3");
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db3");
   }

   public void testJobExistingRepositoryRestoreMultiDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db4");
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db4");
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db8");
      repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore("db8");
   }

   public void testJobRepositoryRestoreSingleDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db3");
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db3");
   }

   public void testJobRepositoryRestoreMultiDB() throws Exception
   {
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db4");
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db4");
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db8");
      repositoryBackupRestoreDirectlyOverJobRepositoryRestore("db8");
   }

   public void testJobExistingWorkspaceSameConfigRestoreSingleDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db3");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db3");
   }

   public void testJobExistingWorkspaceSameConfigRestoreMultiDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db4");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db4");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db8");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore("db8");
   }

   public void testJobExistingWorkspaceRestoreSingleDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db3");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db3");
   }

   public void testJobExistingWorkspaceRestoreMultiDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db4");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db4");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db8");
      workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore("db8");
   }

   public void testJobWorkspaceRestoreSingleDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db3");
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db3");
   }

   public void testJobWorkspaceRestoreMultiDB() throws Exception
   {
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db4");
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db4");
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db8");
      workspaceBackupRestoreDirectlyOverJobWorkspaceRestore("db8");
   }
   
   public void testRepositoryFullBackupRestoreBackupSingleDBToIsolatedOnIsolated() throws Exception
   {
      repositoryFullBackupRestoreBackupSingleToIsolatedOnIsolated("db3");
      repositoryFullBackupRestoreBackupSingleToIsolatedOnIsolated("db3");
   }
   
   protected void repositoryFullBackupRestoreBackupSingleToIsolatedOnIsolated(String repositoryName) throws Exception
   {
      // prepare
      addConent(repositoryName);
      
      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      // backup
      File backDir = new File("target/backup/" + IdGenerator.generate());
      backDir.mkdirs();

      RepositoryBackupConfig config = new RepositoryBackupConfig();
      config.setRepository(repositoryName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      RepositoryBackupChain bch = backupManagerImpl.startBackup(config);
      
      // wait till full backup will stop
      while (bch.getState() != BackupJob.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }
      
      TesterConfigurationHelper helper = TesterConfigurationHelper.getInstance();
      RepositoryEntry repositoryEntryIsolated = helper.copyRepositoryEntry(repositoryService.getRepository(repositoryName).getConfiguration());
      
      for (WorkspaceEntry we : repositoryEntryIsolated.getWorkspaceEntries())
      {
         we.getContainer().addParameter(
            new SimpleParameterEntry(JDBCWorkspaceDataContainer.DB_STRUCTURE_TYPE, DatabaseStructureType.ISOLATED
               .toString()));
      }
      
      // restore single backup on structure single to isolated
      {
         RepositoryEntry newRE = helper.copyRepositoryEntry(repositoryEntryIsolated);
   
         File backLog = new File(bch.getLogFilePath());
         assertTrue(backLog.exists());
         RepositoryBackupChainLog bchLog = new RepositoryBackupChainLog(backLog);
         
         backupManagerImpl.restoreExistingRepository(bchLog, newRE, false);
         checkConent(newRE.getName());
         
         String dbStructureType = repositoryService.getRepository(repositoryName).getConfiguration().getWorkspaceEntries().get(0).getContainer().getParameterValue(JDBCWorkspaceDataContainer.DB_STRUCTURE_TYPE);
         assertTrue(DatabaseStructureType.ISOLATED.toString().equalsIgnoreCase(dbStructureType));
      }
      
      
      // restore  single backup on structure isolated to isolated
      {
         RepositoryEntry newRE = helper.copyRepositoryEntry(repositoryEntryIsolated);
   
         File backLog = new File(bch.getLogFilePath());
         assertTrue(backLog.exists());
   
         RepositoryBackupChainLog bchLog = new RepositoryBackupChainLog(backLog);
   
         backupManagerImpl.restoreExistingRepository(bchLog, newRE, false);
         checkConent(newRE.getName());
         
         String dbStructureType = repositoryService.getRepository(repositoryName).getConfiguration().getWorkspaceEntries().get(0).getContainer().getParameterValue(JDBCWorkspaceDataContainer.DB_STRUCTURE_TYPE);
         assertTrue(DatabaseStructureType.ISOLATED.toString().equalsIgnoreCase(dbStructureType));
      }
      
      // revert the single structure in repository
      {
         File backLog = new File(bch.getLogFilePath());
         assertTrue(backLog.exists());
   
         RepositoryBackupChainLog bchLog = new RepositoryBackupChainLog(backLog);
   
         backupManagerImpl.restoreExistingRepository(bchLog.getBackupId(), false);
         checkConent(repositoryName);
         String dbStructureType = repositoryService.getRepository(repositoryName).getConfiguration().getWorkspaceEntries().get(0).getContainer().getParameterValue(JDBCWorkspaceDataContainer.DB_STRUCTURE_TYPE);
         assertTrue(DatabaseStructureType.SINGLE.toString().equalsIgnoreCase(dbStructureType));
      }
   }

   protected void repositoryBackupRestoreDirectlyOverJobExistingRepositorySameConfigRestore(String repositoryName)
      throws Exception
   {
      addConent(repositoryName);

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      RepositoryBackupConfig config = new RepositoryBackupConfig();
      config.setRepository(repositoryName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      RepositoryBackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getState() != BackupJob.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      RepositoryBackupChainLog rblog = new RepositoryBackupChainLog(new File(bch.getLogFilePath()));

      Map<String, File> workspacesMapping = new HashMap<String, File>();
      Map<String, BackupChainLog> backups = new HashMap<String, BackupChainLog>();

      for (String path : rblog.getWorkspaceBackupsInfo())
      {
         BackupChainLog bLog = new BackupChainLog(new File(path));
         backups.put(bLog.getBackupConfig().getWorkspace(), bLog);
      }

      for (WorkspaceEntry wsEntry : rblog.getOriginalRepositoryEntry().getWorkspaceEntries())
      {
         workspacesMapping.put(wsEntry.getName(), new File(backups.get(wsEntry.getName()).getLogFilePath()));
      }

      JobExistingRepositorySameConfigRestore job =
         new JobExistingRepositorySameConfigRestore(repositoryService, backupManagerImpl, rblog
            .getOriginalRepositoryEntry(), workspacesMapping, new File(rblog.getLogFilePath()));

      job.run();
      assertEquals(JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   protected void repositoryBackupRestoreDirectlyOverJobExistingRepositoryRestore(String repositoryName)
      throws Exception
   {
      addConent(repositoryName);

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      RepositoryBackupConfig config = new RepositoryBackupConfig();
      config.setRepository(repositoryName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      RepositoryBackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getState() != BackupJob.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      RepositoryBackupChainLog rblog = new RepositoryBackupChainLog(new File(bch.getLogFilePath()));

      Map<String, File> workspacesMapping = new HashMap<String, File>();
      Map<String, BackupChainLog> backups = new HashMap<String, BackupChainLog>();

      for (String path : rblog.getWorkspaceBackupsInfo())
      {
         BackupChainLog bLog = new BackupChainLog(new File(path));
         backups.put(bLog.getBackupConfig().getWorkspace(), bLog);
      }

      for (WorkspaceEntry wsEntry : rblog.getOriginalRepositoryEntry().getWorkspaceEntries())
      {
         workspacesMapping.put(wsEntry.getName(), new File(backups.get(wsEntry.getName()).getLogFilePath()));
      }

      JobExistingRepositoryRestore job =
         new JobExistingRepositoryRestore(repositoryService, backupManagerImpl, rblog.getOriginalRepositoryEntry(),
            workspacesMapping, new File(rblog.getLogFilePath()));

      job.run();

      assertEquals(JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   private int forceCloseSession(String repositoryName, String workspaceName) throws RepositoryException,
      RepositoryConfigurationException
   {
      ManageableRepository mr = repositoryService.getRepository(repositoryName);
      WorkspaceContainerFacade wc = mr.getWorkspaceContainer(workspaceName);

      SessionRegistry sessionRegistry = (SessionRegistry)wc.getComponent(SessionRegistry.class);

      return sessionRegistry.closeSessions(workspaceName);
   }

   protected void repositoryBackupRestoreDirectlyOverJobRepositoryRestore(String repositoryName)
      throws Exception
   {
      addConent(repositoryName);

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      RepositoryBackupConfig config = new RepositoryBackupConfig();
      config.setRepository(repositoryName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      RepositoryBackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getState() != BackupJob.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      RepositoryBackupChainLog rblog = new RepositoryBackupChainLog(new File(bch.getLogFilePath()));

      // clean existing repository
      // list of components to clean
      List<Backupable> backupable = new ArrayList<Backupable>();

      //Create local copy of WorkspaceEntry for all workspaces
      ArrayList<WorkspaceEntry> workspaceList = new ArrayList<WorkspaceEntry>();
      workspaceList.addAll(rblog.getOriginalRepositoryEntry().getWorkspaceEntries());

      // get all backupable components
      for (WorkspaceEntry wEntry : workspaceList)
      {
         backupable.addAll(repositoryService.getRepository(rblog.getOriginalRepositoryEntry().getName())
            .getWorkspaceContainer(wEntry.getName()).getComponentInstancesOfType(Backupable.class));
      }

      //close all session
      for (WorkspaceEntry wEntry : workspaceList)
      {
         forceCloseSession(rblog.getOriginalRepositoryEntry().getName(), wEntry.getName());
      }

      //remove repository
      repositoryService.removeRepository(rblog.getOriginalRepositoryEntry().getName());

      // clean
      for (Backupable component : backupable)
      {
         component.clean();
      }

      Map<String, File> workspacesMapping = new HashMap<String, File>();
      Map<String, BackupChainLog> backups = new HashMap<String, BackupChainLog>();

      for (String path : rblog.getWorkspaceBackupsInfo())
      {
         BackupChainLog bLog = new BackupChainLog(new File(path));
         backups.put(bLog.getBackupConfig().getWorkspace(), bLog);
      }

      for (WorkspaceEntry wsEntry : rblog.getOriginalRepositoryEntry().getWorkspaceEntries())
      {
         workspacesMapping.put(wsEntry.getName(), new File(backups.get(wsEntry.getName()).getLogFilePath()));
      }

      JobRepositoryRestore job =
         new JobRepositoryRestore(repositoryService, backupManagerImpl, rblog.getOriginalRepositoryEntry(),
            workspacesMapping, new File(rblog.getLogFilePath()), false);

      job.run();
      assertEquals(JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   protected void workspaceBackupRestore(String repositoryName) throws Exception
   {
      addConent(repositoryName);
      String workspaceName =
         repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName();

      WorkspaceEntry wsEntry = null;
      for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration()
         .getWorkspaceEntries())
      {
         if (entry.getName().equals(workspaceName))
         {
            wsEntry = entry;
            break;
         }
      }

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      BackupConfig config = new BackupConfig();
      config.setRepository(repositoryName);
      config.setWorkspace(workspaceName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      BackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getFullBackupState() != BackupChain.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      File backLog = new File(bch.getLogFilePath());
      if (backLog.exists())
      {
         BackupChainLog bchLog = new BackupChainLog(backLog);

         assertNotNull(bchLog.getStartedTime());
         assertNotNull(bchLog.getFinishedTime());

         backupManagerImpl.restoreExistingWorkspace(bchLog, repositoryName, wsEntry, false);

         JobWorkspaceRestore restore = backupManagerImpl.getLastRestore(repositoryName, workspaceName);
         assertNotNull(restore);
      }
      else
      {
         fail("There are no backup files in " + backDir.getAbsolutePath());
      }

      checkConent(repositoryName);
   }

   protected void workspaceBackupRestoreDirectlyOverJobExistingWorkspaceSameConfigRestore(String repositoryName)
      throws Exception
   {
      addConent(repositoryName);
      String workspaceName =
         repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName();

      WorkspaceEntry wsEntry = null;
      for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration()
         .getWorkspaceEntries())
      {
         if (entry.getName().equals(workspaceName))
         {
            wsEntry = entry;
            break;
         }
      }

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      BackupConfig config = new BackupConfig();
      config.setRepository(repositoryName);
      config.setWorkspace(workspaceName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      BackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getFullBackupState() != BackupChain.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      BackupChainLog bclog = new BackupChainLog(new File(bch.getLogFilePath()));

      JobExistingWorkspaceSameConfigRestore job =
         new JobExistingWorkspaceSameConfigRestore(repositoryService, backupManagerImpl, repositoryName, new File(bclog.getLogFilePath()), bclog
            .getOriginalWorkspaceEntry());

      job.run();
      assertEquals(JobWorkspaceRestore.RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   /**
    * JobExistingWorkspaseRrestore is not support restore system workspace, 
    * because repository is not allowed removing system workspace.  
    */
   protected void workspaceBackupRestoreDirectlyOverJobExistingWorkspaceRestore(String repositoryName) throws Exception
   {
      addConent(repositoryName);

      WorkspaceEntry wsEntry = null;
      for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration()
         .getWorkspaceEntries())
      {
         if (!entry.getName().equals(
            repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName()))
         {
            wsEntry = entry;
            break;
         }
      }

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      BackupConfig config = new BackupConfig();
      config.setRepository(repositoryName);
      config.setWorkspace(wsEntry.getName());
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      BackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getFullBackupState() != BackupChain.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // restore
      BackupChainLog bclog = new BackupChainLog(new File(bch.getLogFilePath()));

      JobExistingWorkspaceRestore job =
         new JobExistingWorkspaceRestore(repositoryService, backupManagerImpl, repositoryName, new File(bclog.getLogFilePath()), bclog
            .getOriginalWorkspaceEntry());

      job.run();
      assertEquals(JobWorkspaceRestore.RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   /**
    * JobWorkspaseRrestore is not support restore system workspace, 
    * because repository is not allowed removing system workspace.  
    */
   protected void workspaceBackupRestoreDirectlyOverJobWorkspaceRestore(String repositoryName)
      throws Exception
   {
      addConent(repositoryName);

      WorkspaceEntry wsEntry = null;
      for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration()
         .getWorkspaceEntries())
      {
         if (!entry.getName().equals(
            repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName()))
         {
            wsEntry = entry;
            break;
         }
      }

      String workspaceName = wsEntry.getName();

      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      BackupConfig config = new BackupConfig();
      config.setRepository(repositoryName);
      config.setWorkspace(workspaceName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      BackupChain bch = backupManagerImpl.startBackup(config);

      // wait till full backup will stop
      while (bch.getFullBackupState() != BackupChain.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // clean existed workspace
      // get all backupable components
      List<Backupable> backupable =
         repositoryService.getRepository(repositoryName).getWorkspaceContainer(wsEntry.getName())
            .getComponentInstancesOfType(Backupable.class);

      // close all session
      forceCloseSession(repositoryName, wsEntry.getName());

      repositoryService.getRepository(repositoryName).removeWorkspace(wsEntry.getName());

      // clean
      for (Backupable component : backupable)
      {
         component.clean();
      }

      // restore
      BackupChainLog bclog = new BackupChainLog(new File(bch.getLogFilePath()));

      JobWorkspaceRestore job =
         new JobWorkspaceRestore(repositoryService, backupManagerImpl, repositoryName, new File(bclog.getLogFilePath()), bclog
            .getOriginalWorkspaceEntry());

      job.run();
      assertEquals(JobWorkspaceRestore.RESTORE_SUCCESSFUL, job.getStateRestore());

      checkConent(repositoryName);
   }

   protected void repositoryBackupRestore(String repositoryName) throws Exception
   {
      // prepare content
      addConent(repositoryName);

      // prepare backupManager
      BackupManagerImpl backupManagerImpl = (BackupManagerImpl)getBackupManager();
      backupManagerImpl.start();

      // backup
      File backDir = new File("target/backup/" + repositoryName);
      backDir.mkdirs();

      RepositoryBackupConfig config = new RepositoryBackupConfig();
      config.setRepository(repositoryName);
      config.setBackupType(BackupManager.FULL_BACKUP_ONLY);
      config.setBackupDir(backDir);

      RepositoryBackupChain bch = backupManagerImpl.startBackup(config);

      // wait end of backup
      while (bch.getState() != BackupJob.FINISHED)
      {
         Thread.yield();
         Thread.sleep(30);
      }

      if (bch != null)
      {
         backupManagerImpl.stopBackup(bch);
      }

      // clean repository via DBCleanService
      DBCleanService.cleanRepositoryData(repositoryService.getRepository(repositoryName).getConfiguration());
      checkEmptyTables(repositoryName);

      // restore
      restore(repositoryName, bch, backupManagerImpl);
      checkConent(repositoryName);

      // clean every workspace via DBCleanService
      ManageableRepository repository = repositoryService.getRepository(repositoryName);
      for (WorkspaceEntry wsEntry : repository.getConfiguration().getWorkspaceEntries())
      {
         DBCleanService.cleanWorkspaceData(wsEntry);
      }
      checkEmptyTables(repositoryName);

      // restore
      restore(repositoryName, bch, backupManagerImpl);
      checkConent(repositoryName);

      // clean again via DBCleaner
      repository = repositoryService.getRepository(repositoryName);

      WorkspaceEntry entry = repository.getConfiguration().getWorkspaceEntries().get(0);
      DataSource ds =
         (DataSource)new InitialContext().lookup(entry.getContainer().getParameterValue(
            JDBCWorkspaceDataContainer.SOURCE_NAME));

      Connection conn = ds.getConnection();
      conn.setAutoCommit(false);

      try
      {
         if (repositoryName.equals("db3"))
         {
            DBCleanerTool repositoryDBCleaner =
               DBCleanService.getRepositoryDBCleaner(conn, repository.getConfiguration());

            // clean and rollback first
            repositoryDBCleaner.clean();

            conn.rollback();

            repositoryDBCleaner.rollback();

            conn.commit();

            checkConent(repositoryName);

            // clean
            repositoryDBCleaner.clean();
            repositoryDBCleaner.commit();

            conn.commit();
         }
         else
         {
            try
            {
               DBCleanerTool repositoryDBCleaner =
                  DBCleanService.getRepositoryDBCleaner(conn, repository.getConfiguration());
               fail("Exception should be thrown");
            }
            catch (DBCleanException e)
            {
            }
         }
      }
      finally
      {
         conn.close();
      }

      if (repositoryName.equals("db3"))
      {
         checkEmptyTables(repositoryName);
      }

      // restore
      restore(repositoryName, bch, backupManagerImpl);
      checkConent(repositoryName);

      // clean every workspace again via DBCleaner
      repository = repositoryService.getRepository(repositoryName);
      for (WorkspaceEntry wsEntry : repository.getConfiguration().getWorkspaceEntries())
      {
         ds =
            (DataSource)new InitialContext().lookup(wsEntry.getContainer().getParameterValue(
               JDBCWorkspaceDataContainer.SOURCE_NAME));

         conn = ds.getConnection();
         conn.setAutoCommit(false);

         DBCleanerTool workspaceDBCleaner = DBCleanService.getWorkspaceDBCleaner(conn, wsEntry);

         try
         {
            // clean and rollback first
            workspaceDBCleaner.clean();

            conn.rollback();

            workspaceDBCleaner.rollback();

            conn.commit();

            checkConent(repositoryName);

            // clean
            workspaceDBCleaner.clean();
            workspaceDBCleaner.commit();

            conn.commit();
         }
         finally
         {
            conn.close();
         }
      }

      checkEmptyTables(repositoryName);

      // restore
      restore(repositoryName, bch, backupManagerImpl);
      checkConent(repositoryName);
   }

   protected ExtendedBackupManager getBackupManager()
   {
      InitParams initParams = new InitParams();
      PropertiesParam pps = new PropertiesParam();

      pps.setProperty(BackupManagerImpl.FULL_BACKUP_TYPE,
         "org.exoplatform.services.jcr.ext.backup.impl.rdbms.FullBackupJob");
      pps.setProperty(BackupManagerImpl.BACKUP_DIR, "target/backup");
      initParams.put(BackupManagerImpl.BACKUP_PROPERTIES, pps);

      return new BackupManagerImpl(initParams, repositoryService);
   }

   protected void addConent(String repositoryName) throws Exception
   {
      index++;

      ManageableRepository repository = repositoryService.getRepository(repositoryName);
      for (String wsName : repository.getWorkspaceNames())
      {
         SessionImpl session = (SessionImpl)repository.login(credentials, wsName);
         try
         {
            Node rootNode = session.getRootNode().addNode("test" + index);
            rootNode.addNode("node1").setProperty("prop1", "value1");
            rootNode.addNode(NODE_NAME).setProperty(PROPERTY_NAME, "value1");
            session.save();
         }
         finally
         {
            session.logout();
         }
      }
   }

   protected void checkConent(String repositoryName) throws Exception
   {
      ManageableRepository repository = repositoryService.getRepository(repositoryName);
      for (String wsName : repository.getWorkspaceNames())
      {
         SessionImpl session = (SessionImpl)repository.login(credentials, wsName);
         try
         {
            Node rootNode = session.getRootNode().getNode("test" + index);
            assertEquals(rootNode.getNode("node1").getProperty("prop1").getString(), "value1");
            assertEquals(rootNode.getNode(NODE_NAME).getProperty(PROPERTY_NAME).getString(), "value1");
         }
         finally
         {
            session.logout();
         }
      }
   }

   private void restore(String repositoryName, RepositoryBackupChain bch, BackupManagerImpl backupManagerImpl)
      throws Exception
   {
      // restore
      File backLog = new File(bch.getLogFilePath());
      assertTrue(backLog.exists());

      RepositoryBackupChainLog bchLog = new RepositoryBackupChainLog(backLog);

      assertNotNull(bchLog.getStartedTime());
      assertNotNull(bchLog.getFinishedTime());

      backupManagerImpl.restoreExistingRepository(bchLog, repositoryService.getRepository(repositoryName)
         .getConfiguration(), false);

      JobRepositoryRestore restore = backupManagerImpl.getLastRepositoryRestore(repositoryName);
      if (restore != null)
      {
         if (restore.getStateRestore() != JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL)
         {
            fail(restore.getRestoreException().getMessage());
         }
      }
   }

   private void checkEmptyTables(String repositoryName) throws Exception
   {
      ManageableRepository repository = repositoryService.getRepository(repositoryName);
      for (WorkspaceEntry wsEntry : repository.getConfiguration().getWorkspaceEntries())
      {
         String itemTableName = DBInitializerHelper.getItemTableName(wsEntry);
         String valueTableName = DBInitializerHelper.getValueTableName(wsEntry);
         String refTableName = DBInitializerHelper.getRefTableName(wsEntry);

         DataSource ds =
            (DataSource)new InitialContext().lookup(wsEntry.getContainer().getParameterValue(
               JDBCWorkspaceDataContainer.SOURCE_NAME));
         Connection conn = ds.getConnection();
         try
         {
            ResultSet result = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + itemTableName);
            try
            {
               assertTrue(result.next());
               assertEquals(1, result.getInt(1));
            }
            finally
            {
               result.close();
            }

            result = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + valueTableName);
            try
            {
               assertTrue(result.next());
               assertEquals(0, result.getInt(1));
            }
            finally
            {
               result.close();
            }

            result = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + refTableName);
            try
            {
               assertTrue(result.next());
               assertEquals(0, result.getInt(1));
            }
            finally
            {
               result.close();
            }
         }
         finally
         {
            conn.close();
         }
      }
   }
}
