/*
 * Copyright (C) 2009 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.services.jcr.ext.backup.server;

import org.exoplatform.commons.utils.PrivilegedFileHelper;
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.WorkspaceEntry;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
import org.exoplatform.services.jcr.ext.app.ThreadLocalSessionProviderService;
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.BackupConfigurationException;
import org.exoplatform.services.jcr.ext.backup.BackupManager;
import org.exoplatform.services.jcr.ext.backup.BackupOperationException;
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.RestoreConfigurationException;
import org.exoplatform.services.jcr.ext.backup.WorkspaceRestoreException;
import org.exoplatform.services.jcr.ext.backup.impl.BackupLogsFilter;
import org.exoplatform.services.jcr.ext.backup.impl.JobRepositoryRestore;
import org.exoplatform.services.jcr.ext.backup.impl.JobWorkspaceRestore;
import org.exoplatform.services.jcr.ext.backup.impl.RepositoryBackupLogsFilter;
import org.exoplatform.services.jcr.ext.backup.server.bean.BackupConfigBean;
import org.exoplatform.services.jcr.ext.backup.server.bean.response.BackupServiceInfoBean;
import org.exoplatform.services.jcr.ext.backup.server.bean.response.DetailedInfo;
import org.exoplatform.services.jcr.ext.backup.server.bean.response.DetailedInfoEx;
import org.exoplatform.services.jcr.ext.backup.server.bean.response.ShortInfo;
import org.exoplatform.services.jcr.ext.backup.server.bean.response.ShortInfoList;
import org.exoplatform.services.jcr.impl.core.RepositoryImpl;
import org.exoplatform.services.jcr.impl.core.SessionRegistry;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.rest.resource.ResourceContainer;

import java.io.File;
import java.io.FilenameFilter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
 * Created by The eXo Platform SAS.
 * 
 * <br/>
 * Date: 24.02.2009
 * 
 * @author <a href="mailto:alex.reshetnyak@exoplatform.com.ua">Alex Reshetnyak</a>
 * @version $Id: BackupServer.java 111 2008-11-11 11:11:11Z rainf0x $
 */

@Path("/jcr-backup/")
public class HTTPBackupAgent implements ResourceContainer
{

   /**
    * Definition the constants.
    */
   public static final class Constants
   {

      /**
       * The date format RFC_1123.
       */
      public static final String DATE_FORMAT_RFC_1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";

      /**
       * The base path to this service.
       */
      public static final String BASE_URL = "/jcr-backup";

      /**
       * Definition the operation types.
       */
      public static final class OperationType
      {
         /**
          * Start backup operation.
          */
         public static final String START_BACKUP = "/start";

         /**
          * Start backup repository operation.
          */
         public static final String START_BACKUP_REPOSITORY = "/start-backup-repository";

         /**
          * Restore operations.
          */
         public static final String RESTORE = "/restore";

         /**
          * Restore operations from backup set.
          */
         public static final String RESTORE_BACKUP_SET = "/restore/backup-set";

         /**
          * Restore repository operations.
          */
         public static final String RESTORE_REPOSITORY = "/restore-repository";

         /**
          * Restore repository operations from backup set.
          */
         public static final String RESTORE_REPOSITORY_BACKUP_SET = "/restore-repository/backup-set";

         /**
          * Stop backup operations.
          */
         public static final String STOP_BACKUP = "/stop";

         /**
          * Stop repository backup operations.
          */
         public static final String STOP_BACKUP_REPOSITORY = "/stop-backup-repository";

         /**
          * The current and completed backups info operation.
          */
         public static final String CURRENT_AND_COMPLETED_BACKUPS_INFO = "/info/backup";

         /**
          * The current and completed repository backups info operation.
          */
         public static final String CURRENT_AND_COMPLETED_BACKUPS_REPOSITORY_INFO = "/info/backup-repository";

         /**
          * The current and completed backups info operation for specific workspace.
          */
         public static final String CURRENT_AND_COMPLETED_BACKUPS_INFO_ON_WS = "/info/backup";

         /**
          * The current backups info operations.
          */
         public static final String CURRENT_BACKUPS_INFO = "/info/backup/current";

         /**
          * The current repository backups info operations.
          */
         public static final String CURRENT_BACKUPS_REPOSITORY_INFO = "/info/backup-repository/current";

         /**
          * The current or completed backup info operations.
          */
         public static final String CURRENT_OR_COMPLETED_BACKUP_INFO = "/info/backup";

         /**
          * The current or completed repository backup info operations.
          */
         public static final String CURRENT_OR_COMPLETED_BACKUP_REPOSITORY_INFO = "/info/backup-repository-id";

         /**
          * The current restore info operations for specific workspace.
          */
         public static final String CURRENT_RESTORE_INFO_ON_WS = "/info/restore";

         /**
         * The current restore info operations for specific repository.
         */
         public static final String CURRENT_RESTORE_INFO_ON_REPOSITORY = "/info/restore-repository";

         /**
          * The current restores info operations.
          */
         public static final String CURRENT_RESTORES = "/info/restores";

         /**
          * The current repository restores info operations.
          */
         public static final String CURRENT_RESTORES_REPOSITORY = "/info/restores-repository";

         /**
          * The completed backups info operations.
          */
         public static final String COMPLETED_BACKUPS_INFO = "/info/backup/completed";

         /**
          * The completed repository backups info operations.
          */
         public static final String COMPLETED_BACKUPS_REPOSITORY_INFO = "/info/backup-repository/completed";

         /**
          * The backup service info operations.
          */
         public static final String BACKUP_SERVICE_INFO = "/info";

         /**
          * The drop workspace operations.
          */
         public static final String DROP_WORKSPACE = "/drop-workspace";

         /**
          * The get default workspace configuration.
          */
         public static final String GET_DEFAULT_WORKSPACE_CONFIG = "/info/default-ws-config";

         /**
          * The get default repository configuration.
          */
         public static final String GET_DEFAULT_REPOSITORY_CONFIG = "/info/default-repository-config";

         /**
          * OperationType constructor.
          */
         private OperationType()
         {
         }
      }

      /**
       * Constants constructor.
       */
      private Constants()
      {
      }
   }

   /**
    * The apache logger.
    */
   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.ext.HTTPBackupAgent");

   /**
    * To disable cache control.
    */
   private static final CacheControl noCache;

   static
   {
      noCache = new CacheControl();
      noCache.setNoCache(true);
      noCache.setNoStore(true);
   }

   /**
    * The repository service.
    */
   private RepositoryService repositoryService;

   /**
    * The backup manager.
    */
   private ExtendedBackupManager backupManager;

   /**
    * Will be get session over base authenticate.
    */
   private ThreadLocalSessionProviderService sessionProviderService;

   /**
    * ReplicationTestService constructor.
    * 
    * @param repoService
    *          the RepositoryService
    * @param backupManager
    *          the BackupManager
    * @param sessionProviderService
    *          the ThreadLocalSessionProviderService
    */
   public HTTPBackupAgent(RepositoryService repoService, BackupManager backupManager,
      ThreadLocalSessionProviderService sessionProviderService)
   {
      this.repositoryService = repoService;
      this.backupManager = (ExtendedBackupManager) backupManager;
      this.sessionProviderService = sessionProviderService;

      LOG.info("HTTPBackupAgent inited");
   }

   /**
    * The start backup.
    * 
    * @param bConfigBeen
    *          BackupConfigBeen, the been with backup configuration.
    * @param repository
    *          String, the repository name
    * @param workspace
    *          String, the workspace name
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/start/{repo}/{ws}")
   public Response start(BackupConfigBean bConfigBeen, @PathParam("repo") String repository,
      @PathParam("ws") String workspace)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         File backupDir;

         if (bConfigBeen.getBackupDir() == null)
         {
            backupDir = backupManager.getBackupDirectory();
         }
         else
         {
            backupDir = new File(bConfigBeen.getBackupDir());
            if (!PrivilegedFileHelper.exists(backupDir))
               throw new BackupDirNotFoundException("The backup folder not exists :  "
                        + PrivilegedFileHelper.getAbsolutePath(backupDir));
         }

         BackupConfig config = new BackupConfig();
         config.setBackupType(bConfigBeen.getBackupType());
         config.setRepository(repository);
         config.setWorkspace(workspace);
         config.setBackupDir(backupDir);
         config.setIncrementalJobPeriod(bConfigBeen.getIncrementalJobPeriod());
         config.setIncrementalJobNumber(bConfigBeen.getIncrementalRepetitionNumber());

         validateRepositoryName(repository);
         validateWorkspaceName(repository, workspace);
         validateOneBackupInstants(repository, workspace);

         BackupChain chain = backupManager.startBackup(config);

         ShortInfo shortInfo = new ShortInfo(ShortInfo.CURRENT, chain);

         return Response.ok(shortInfo).cacheControl(noCache).build();
      }
      catch (NoSuchWorkspaceException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (LoginException e)
      {
         exception = e;
         status = Response.Status.UNAUTHORIZED;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (WorkspaceRestoreExeption e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupOperationException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (BackupConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start backup", exception);

      return Response.status(status).entity("Can not start backup : " + failMessage).type(MediaType.TEXT_PLAIN)
         .cacheControl(noCache).build();
   }

   /**
    * The start repository backup.
    * 
    * @param bConfigBeen
    *          BackupConfigBeen, the been with backup configuration.
    * @param repository
    *          String, the repository name
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/start-backup-repository/{repo}")
   public Response startBackupRepository(BackupConfigBean bConfigBeen, @PathParam("repo") String repository)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         File backupDir;

         if (bConfigBeen.getBackupDir() == null)
         {
            backupDir = backupManager.getBackupDirectory();
         }
         else
         {
            backupDir = new File(bConfigBeen.getBackupDir());
            if (!PrivilegedFileHelper.exists(backupDir))
               throw new BackupDirNotFoundException("The backup folder not exists :  "
                        + PrivilegedFileHelper.getAbsolutePath(backupDir));
         }

         RepositoryBackupConfig config = new RepositoryBackupConfig();
         config.setBackupType(bConfigBeen.getBackupType());
         config.setRepository(repository);
         config.setBackupDir(backupDir);
         config.setIncrementalJobPeriod(bConfigBeen.getIncrementalJobPeriod());
         config.setIncrementalJobNumber(bConfigBeen.getIncrementalRepetitionNumber());

         validateRepositoryName(repository);

         RepositoryBackupChain chain = backupManager.startBackup(config);

         ShortInfo shortInfo = new ShortInfo(ShortInfo.CURRENT, chain);

         return Response.ok(shortInfo).cacheControl(noCache).build();
      }
      catch (LoginException e)
      {
         exception = e;
         status = Response.Status.UNAUTHORIZED;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupOperationException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (BackupConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start backup", exception);

      return Response.status(status).entity("Can not start backup : " + failMessage).type(MediaType.TEXT_PLAIN)
         .cacheControl(noCache).build();
   }

   /**
    * The delete workspace.
    * 
    * @param repository
    *          String, the repository name
    * @param workspace
    *          String, the workspace name
    * @param forceSessionClose
    *          Boolean, flag to force session close
    * @return Response return the response
    */
   @GET
   @RolesAllowed("administrators")
   @Path("/drop-workspace/{repo}/{ws}/{force-session-close}")
   public Response dropWorkspace(@PathParam("repo") String repository, @PathParam("ws") String workspace,
      @PathParam("force-session-close") Boolean forceSessionClose)
   {

      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         validateRepositoryName(repository);
         validateWorkspaceName(repository, workspace);

         if (forceSessionClose)
            forceCloseSession(repository, workspace);

         RepositoryImpl repositoryImpl = (RepositoryImpl)repositoryService.getRepository(repository);
         repositoryImpl.removeWorkspace(workspace);
         repositoryService.getConfig().retain(); // save configuration to persistence (file or persister)

         return Response.ok().cacheControl(noCache).build();

      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not drop the workspace '" + "/" + repository + "/" + workspace + "'", exception);

      return Response.status(status).entity(
         "Can not drop the workspace '" + "/" + repository + "/" + workspace + "' : " + failMessage).type(
         MediaType.TEXT_PLAIN).cacheControl(noCache).build();

   }

   /**
    * Restore the workspace.
    * 
    * @param wEntry
    *          WorkspaceEntry, the configuration to restored workspace
    * @param repository
    *          String, the repository name
    * @param backupId
    *          String, the identifier of backup
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore/{repo}/{id}")
   public Response restore(WorkspaceEntry wEntry, @PathParam("repo") String repository, @PathParam("id") String backupId)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         validateOneRestoreInstants(repository, wEntry.getName());

         File backupLog = getBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
            throw new BackupLogNotFoundException("The backup log file with id " + backupId + " not exists.");

         validateRepositoryName(repository);

         if (isWorkspaceExist(repository, wEntry.getName()))
            throw new WorkspaceRestoreException("Workspace " + wEntry.getName() + " already exist!");

         BackupChainLog backupChainLog = new BackupChainLog(backupLog);

         backupManager.restore(backupChainLog, repository, wEntry, true);

         /*
          * Sleeping
          * Restore must be initialized by job thread
          */

         Thread.sleep(100);

         /*
          * search necessary restore
          */

         List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();
         JobWorkspaceRestore restore = null;
         for (JobWorkspaceRestore curRestore : restoreJobs)
         {
            if (curRestore.getRepositoryName().equals(repository)
               && curRestore.getWorkspaceName().equals(wEntry.getName()))
            {
               restore = curRestore;
               break;
            }
         }

         if (restore != null)
         {
            ShortInfo info =
               new ShortInfo(ShortInfo.RESTORE, restore.getBackupChainLog(), restore.getStartTime(), restore
                  .getEndTime(), restore.getStateRestore(), restore.getRepositoryName(), restore.getWorkspaceName());
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.ok().cacheControl(noCache).build();
      }
      catch (WorkspaceRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
         + "' from backup log with id '" + backupId + "'", exception);

      return Response.status(status).entity(
         "Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
            + "' from backup log with id '" + backupId + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(
         noCache).build();
   }

   /**
    * Restore the workspace.
    * 
    * @param wEntry
    *          WorkspaceEntry, the configuration to restored workspace
    * @param repository
    *          String, the repository name
    * @param backupId
    *          String, the identifier of backup
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed workspace.  
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore/{repo}/{id}/{remove-Existing}")
   public Response restore(WorkspaceEntry wEntry, @PathParam("repo") String repository,
            @PathParam("id") String backupId, @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         validateOneRestoreInstants(repository, wEntry.getName());

         File backupLog = getBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
         {
            throw new BackupLogNotFoundException("The backup log file with id " + backupId + " not exists.");
         }

         validateRepositoryName(repository);

         BackupChainLog backupChainLog = new BackupChainLog(backupLog);

         if (removeExisting)
         {
            if (!isWorkspaceExist(repository, wEntry.getName()))
            {
               throw new WorkspaceRestoreException("Workspace " + wEntry.getName() + " is not exist!");
            }

            backupManager.restoreExistingWorkspace(backupChainLog, repository, wEntry, true);
         }
         else
         {
            if (isWorkspaceExist(repository, wEntry.getName()))
            {
               throw new Exception("Workspace " + wEntry.getName() + " already exist!");
            }

            backupManager.restore(backupChainLog, repository, wEntry, true);
         }

         /*
          * Sleeping
          * Restore must be initialized by job thread
          */

         Thread.sleep(100);

         /*
          * search necessary restore
          */

         List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();
         JobWorkspaceRestore restore = null;
         for (JobWorkspaceRestore curRestore : restoreJobs)
         {
            if (curRestore.getRepositoryName().equals(repository)
                     && curRestore.getWorkspaceName().equals(wEntry.getName()))
            {
               restore = curRestore;
               break;
            }
         }

         if (restore != null)
         {
            ShortInfo info =
                     new ShortInfo(ShortInfo.RESTORE, restore.getBackupChainLog(), restore.getStartTime(), restore
                              .getEndTime(), restore.getStateRestore(), restore.getRepositoryName(), restore
                              .getWorkspaceName());
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.ok().cacheControl(noCache).build();
      }
      catch (WorkspaceRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
               + "' from backup log with id '" + backupId + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
                        + "' from backup log with id '" + backupId + "' : " + failMessage).type(MediaType.TEXT_PLAIN)
               .cacheControl(noCache).build();
   }

   /**
    * Restore the workspace from backup set with changing configuration (WorkspaceEntry).
    * 
    * @param wEntry
    *          WorkspaceEntry, the configuration to restored workspace
    * @param repository
    *          String, the repository name
    * @param backupSetPath
    *          String, the path to backup set
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed workspace.  
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore/backup-set/{repo}/{remove-Existing}")
   public Response restoreBackupSet(WorkspaceEntry wEntry, @PathParam("repo") String repository,
            @QueryParam("backup-set-path") String backupSetPathEncoded, @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      String backupSetPath = null;

      try
      {
         backupSetPath = URLDecoder.decode(backupSetPathEncoded, "UTF-8");
      }
      catch (UnsupportedEncodingException e)
      {
         LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
                  + "' from backup set '" + backupSetPath + "'", e);

         return Response.status(Response.Status.BAD_REQUEST).entity(
                  "Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
                           + "' from backup set '" + backupSetPath + "' : " + e.getMessage())
                  .type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }

      try
      {
         validateOneRestoreInstants(repository, wEntry.getName());

         File backupSetDir = (new File(backupSetPath));

         if (!backupSetDir.exists())
         {
            throw new RestoreConfigurationException("Backup set directory is not exists :" + backupSetPath);
         }

         if (!backupSetDir.isDirectory())
         {
            throw new RestoreConfigurationException("Backup set directory is not directory :" + backupSetPath);
         }

         File[] cfs = PrivilegedFileHelper.listFiles(backupSetDir, new BackupLogsFilter());

         if (cfs.length == 0)
         {
            throw new RestoreConfigurationException("Can not found workspace backup log in directory : "
                     + backupSetPath);
         }

         if (cfs.length > 1)
         {
            throw new RestoreConfigurationException(
                     "Backup set directory should contains only one workspace backup log : " + backupSetPath);
         }

         validateRepositoryName(repository);

         BackupChainLog backupChainLog = new BackupChainLog(cfs[0]);

         if (removeExisting)
         {
            if (!isWorkspaceExist(repository, wEntry.getName()))
            {
               throw new WorkspaceRestoreException("Workspace " + wEntry.getName() + " is not exist!");
            }

            backupManager.restoreExistingWorkspace(backupChainLog, repository, wEntry, true);
         }
         else
         {
            if (isWorkspaceExist(repository, wEntry.getName()))
            {
               throw new Exception("Workspace " + wEntry.getName() + " already exist!");
            }

            backupManager.restore(backupChainLog, repository, wEntry, true);
         }

         /*
          * Sleeping
          * Restore must be initialized by job thread
          */

         Thread.sleep(100);

         /*
          * search necessary restore
          */

         List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();
         JobWorkspaceRestore restore = null;
         for (JobWorkspaceRestore curRestore : restoreJobs)
         {
            if (curRestore.getRepositoryName().equals(repository)
                     && curRestore.getWorkspaceName().equals(wEntry.getName()))
            {
               restore = curRestore;
               break;
            }
         }

         if (restore != null)
         {
            ShortInfo info =
                     new ShortInfo(ShortInfo.RESTORE, restore.getBackupChainLog(), restore.getStartTime(), restore
                              .getEndTime(), restore.getStateRestore(), restore.getRepositoryName(), restore
                              .getWorkspaceName());
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.ok().cacheControl(noCache).build();
      }
      catch (WorkspaceRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
               + "' from backup set '" + backupSetPath + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the workspace '" + "/" + repository + "/" + wEntry.getName()
                        + "' from backup set '" + backupSetPath + "' : " + failMessage).type(MediaType.TEXT_PLAIN)
               .cacheControl(noCache).build();
   }

   /**
    * Restore the workspace with original configuration (this configuration was stored in backup chain log).
    * 
    * @param repository
    *          String, the repository name
    * @param backupId
    *          String, the identifier of backup
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed workspace.  
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore/{id}/{remove-Existing}")
   public Response restore(@PathParam("id") String backupId, @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      String repository = null;
      String workspace = null;

      try
      {
         File backupLog = getBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
         {
            throw new BackupLogNotFoundException("The backup log file with id " + backupId + " not exists.");
         }

         BackupChainLog backupChainLog = new BackupChainLog(backupLog);

         repository = backupChainLog.getBackupConfig().getRepository();
         workspace = backupChainLog.getBackupConfig().getWorkspace();

         validateOneRestoreInstants(repository, workspace);

         validateRepositoryName(repository);

         //workspace name and repository name should equals original names from backup set.
         if (!repository.equals(backupChainLog.getBackupConfig().getRepository()))
         {
            throw new WorkspaceRestoreException("Repository name\"" + repository
                     + "\" should equals original repository name from backup set : \""
                     + backupChainLog.getBackupConfig().getRepository() + "\".");
         }

         if (!workspace.equals(backupChainLog.getBackupConfig().getWorkspace()))
         {
            throw new WorkspaceRestoreException("Workspace name\"" + workspace
                     + "\" should equals original workspace name from backup set : \""
                     + backupChainLog.getBackupConfig().getWorkspace() + "\".");
         }

         if (removeExisting)
         {
            if (!isWorkspaceExist(repository, workspace))
            {
               throw new WorkspaceRestoreException("Workspace " + workspace + " is not exists!");
            }

            backupManager.restoreExistingWorkspace(backupId, true);
         }
         else
         {
            if (isWorkspaceExist(repository, workspace))
            {
               throw new Exception("Workspace " + workspace + " already exists!");
            }

            backupManager.restoreWorkspace(backupId, true);
         }

         /*
          * Sleeping
          * Restore must be initialized by job thread
          */

         Thread.sleep(100);

         /*
          * search necessary restore
          */

         List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();
         JobWorkspaceRestore restore = null;
         for (JobWorkspaceRestore curRestore : restoreJobs)
         {
            if (curRestore.getRepositoryName().equals(repository)
                     && curRestore.getWorkspaceName().equals(workspace))
            {
               restore = curRestore;
               break;
            }
         }

         if (restore != null)
         {
            ShortInfo info =
                     new ShortInfo(ShortInfo.RESTORE, restore.getBackupChainLog(), restore.getStartTime(), restore
                              .getEndTime(), restore.getStateRestore(), restore.getRepositoryName(), restore
                              .getWorkspaceName());
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.ok().cacheControl(noCache).build();
      }
      catch (WorkspaceRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + workspace
               + "' from backup log with id '" + backupId + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the workspace '" + "/" + repository + "/" + workspace
                        + "' from backup log with id '" + backupId + "' : " + failMessage).type(MediaType.TEXT_PLAIN)
               .cacheControl(noCache).build();
   }

   /**
    * Restore the workspace or repository with original configuration (this configuration was stored in backup log).
    * 
    * @param backupId
    *          String, the identifier of backup
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed workspace.  
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore/backup-set/{remove-Existing}")
   public Response restoreFromBackupSet(@QueryParam("backup-set-path") String backupSetPathEncoded,
            @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage = null;
      Response.Status status = null;
      Throwable exception = null;
      
      String backupSetPath = null;

      try
      {
         backupSetPath = URLDecoder.decode(backupSetPathEncoded, "UTF-8");
      }
      catch (UnsupportedEncodingException e) 
      {
         LOG.error("Can not start restore from backup set '" + backupSetPathEncoded + "'", e);

         return Response.status(Response.Status.BAD_REQUEST).entity(
                  "Can not start restore from backup set  '" + backupSetPathEncoded + "' : " + e.getMessage()).type(
                  MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }

      File backupSetDir = (new File(backupSetPath));
      File backuplog = null;

      boolean restoreWorkspace = true;

      try
      {
         if (!backupSetDir.exists())
         {
            throw new RestoreConfigurationException("Backup set directory is not exists :" + backupSetPath);
         }

         if (!backupSetDir.isDirectory())
         {
            throw new RestoreConfigurationException("Backup set directory is not directory :" + backupSetPath);
         }

         File[] cfsw = PrivilegedFileHelper.listFiles(backupSetDir, new BackupLogsFilter());
         File[] cfsr = PrivilegedFileHelper.listFiles(backupSetDir, new RepositoryBackupLogsFilter());

         if (cfsw.length == 0 && cfsr.length == 0)
         {
            throw new RestoreConfigurationException("Can not found backup log in directory : " + backupSetPath);
         }
         else if ((cfsw.length == 1 && cfsr.length == 1) || (cfsw.length > 1) || (cfsr.length > 1))
         {
            throw new RestoreConfigurationException("Backup set directory should contains only one backup log : "
                     + backupSetPath);
         }
         else if (cfsw.length != 0 && cfsr.length == 0)
         {
            restoreWorkspace = true;
            backuplog = cfsw[0];
         }
         else if (cfsw.length == 0 && cfsr.length != 0)
         {
            restoreWorkspace = false;
            backuplog = cfsr[0];
         }

      }
      catch (RestoreConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }
      finally
      {
         if (exception != null)
         {
            LOG.error("Can not start restore from backup set '" + backupSetPath + "'", exception);

            return Response.status(status).entity(
                     "Can not start restore from backup set  '" + backupSetPath + "' : " + failMessage).type(
                     MediaType.TEXT_PLAIN).cacheControl(noCache).build();
         }
      }

      if (restoreWorkspace)
      {
         
         String repository = null;
         String workspace = null;

         try
         {

            BackupChainLog backupChainLog = new BackupChainLog(backuplog);

            repository = backupChainLog.getBackupConfig().getRepository();
            workspace = backupChainLog.getBackupConfig().getWorkspace();

            validateOneRestoreInstants(repository, workspace);

            validateRepositoryName(repository);

            if (removeExisting)
            {
               if (!isWorkspaceExist(repository, workspace))
               {
                  throw new WorkspaceRestoreException("Workspace " + workspace + " is not exists!");
               }

               backupManager.restoreExistingWorkspace(backupSetDir, true);
            }
            else
            {
               if (isWorkspaceExist(repository, workspace))
               {
                  throw new Exception("Workspace " + workspace + " already exists!");
               }

               backupManager.restoreWorkspace(backupSetDir, true);
            }

            /*
             * Sleeping
             * Restore must be initialized by job thread
             */

            Thread.sleep(100);

            /*
             * search necessary restore
             */

            List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();
            JobWorkspaceRestore restore = null;
            for (JobWorkspaceRestore curRestore : restoreJobs)
            {
               if (curRestore.getRepositoryName().equals(repository) && curRestore.getWorkspaceName().equals(workspace))
               {
                  restore = curRestore;
                  break;
               }
            }

            if (restore != null)
            {
               ShortInfo info =
                        new ShortInfo(ShortInfo.RESTORE, restore.getBackupChainLog(), restore.getStartTime(), restore
                                 .getEndTime(), restore.getStateRestore(), restore.getRepositoryName(), restore
                                 .getWorkspaceName());
               return Response.ok(info).cacheControl(noCache).build();
            }

            return Response.ok().cacheControl(noCache).build();
         }
         catch (WorkspaceRestoreExeption e)
         {
            exception = e;
            status = Response.Status.FORBIDDEN;
            failMessage = e.getMessage();
         }
         catch (RepositoryException e)
         {
            exception = e;
            status = Response.Status.NOT_FOUND;
            failMessage = e.getMessage();
         }
         catch (RepositoryConfigurationException e)
         {
            exception = e;
            status = Response.Status.NOT_FOUND;
            failMessage = e.getMessage();
         }
         catch (BackupLogNotFoundException e)
         {
            exception = e;
            status = Response.Status.NOT_FOUND;
            failMessage = e.getMessage();
         }
         catch (Throwable e)
         {
            exception = e;
            status = Response.Status.INTERNAL_SERVER_ERROR;
            failMessage = e.getMessage();
         }

         LOG.error("Can not start restore the workspace '" + "/" + repository + "/" + workspace
                  + "' from backup set \"" + backupSetPath + "\"", exception);

         return Response.status(status).entity(
                  "Can not start restore the workspace '" + "/" + repository + "/" + workspace + "' from backup set \""
                           + backupSetPath + "\" : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache)
                  .build();
      }
      else
      {
         String repository = null;

         try
         {

            RepositoryBackupChainLog backupChainLog = new RepositoryBackupChainLog(backuplog);
            repository = backupChainLog.getBackupConfig().getRepository();

            validateOneRepositoryRestoreInstants(repository);

            if (removeExisting)
            {
               if (!isRepositoryExist(repository))
               {
                  throw new RepositoryRestoreExeption("Repository " + repository + " is not exists!");
               }

               backupManager.restoreExistingRepository(backupSetDir, true);
            }
            else
            {
               if (isRepositoryExist(repository))
               {
                  throw new RepositoryRestoreExeption("Repository " + repository + " already exists!");
               }

               backupManager.restoreRepository(backupSetDir, true);
            }

            // Sleeping. Restore should be initialized by job thread
            Thread.sleep(100);

            // search necessary restore
            JobRepositoryRestore restore = backupManager.getLastRepositoryRestore(repository);
            ShortInfo info =
                     new ShortInfo(ShortInfo.RESTORE, restore.getRepositoryBackupChainLog(), restore.getStartTime(),
                              restore.getEndTime(), restore.getStateRestore(), restore.getRepositoryName());

            return Response.ok(info).cacheControl(noCache).build();
         }
         catch (RepositoryRestoreExeption e)
         {
            exception = e;
            status = Response.Status.FORBIDDEN;
            failMessage = e.getMessage();
         }
         catch (RepositoryException e)
         {
            exception = e;
            status = Response.Status.NOT_FOUND;
            failMessage = e.getMessage();
         }
         catch (RepositoryConfigurationException e)
         {
            exception = e;
            status = Response.Status.NOT_FOUND;
            failMessage = e.getMessage();
         }
         catch (Throwable e)
         {
            exception = e;
            status = Response.Status.INTERNAL_SERVER_ERROR;
            failMessage = e.getMessage();
         }

         LOG.error("Can not start restore the repository '" + "/" + repository + "' from backup set '" + backupSetPath
                  + "'", exception);

         return Response.status(status).entity(
                  "Can not start restore the repository '" + "/" + repository + "' from backup set  '" + backupSetPath
                           + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Restore the repository.
    * 
    * @param rEntry
    *          RepositoryEntry, the configuration to restored repository
    * @param repository
    *          String, the repository name
    * @param backupId
    *          String, the identifier of backup
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore-repository/{id}")
   public Response restoreRepository(RepositoryEntry rEntry, @PathParam("id") String backupId)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         validateOneRepositoryRestoreInstants(rEntry.getName());

         File backupLog = getRepositoryBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
         {
            throw new BackupLogNotFoundException("The repository backup log file with id " + backupId + " not exists.");
         }

         if (isRepositoryExist(rEntry.getName()))
         {
            throw new RepositoryRestoreExeption("Repository " + rEntry.getName() + " already exist!");
         }

         RepositoryBackupChainLog backupChainLog = new RepositoryBackupChainLog(backupLog);

         backupManager.restore(backupChainLog, rEntry, true);

         // Sleeping. Restore should be initialized by job thread
         Thread.sleep(100);

         // search necessary restore
         JobRepositoryRestore restore = backupManager.getLastRepositoryRestore(rEntry.getName());
         ShortInfo info =
            new ShortInfo(ShortInfo.RESTORE, restore.getRepositoryBackupChainLog(), restore.getStartTime(), restore
               .getEndTime(), restore.getStateRestore(), restore.getRepositoryName());

         return Response.ok(info).cacheControl(noCache).build();
      }
      catch (RepositoryRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup log with id '"
         + backupId + "'", exception);

      return Response.status(status).entity(
         "Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup log with id '" + backupId
            + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
   }

   /**
    * Restore the repository.
    * 
    * @param rEntry
    *          RepositoryEntry, the configuration to restored repository
    * @param repository
    *          String, the repository name
    * @param backupId
    *          String, the identifier of backup
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed repository.
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore-repository/{id}/{remove-Existing}")
   public Response restoreRepository(RepositoryEntry rEntry, @PathParam("id") String backupId,
            @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         validateOneRepositoryRestoreInstants(rEntry.getName());

         File backupLog = getRepositoryBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
         {
            throw new BackupLogNotFoundException("The repository backup log file with id " + backupId + " not exists.");
         }

         RepositoryBackupChainLog backupChainLog = new RepositoryBackupChainLog(backupLog);

         if (removeExisting)
         {
            if (!isRepositoryExist(rEntry.getName()))
            {
               throw new RepositoryRestoreExeption("Repository " + rEntry.getName() + " is not exists!");
            }

            backupManager.restoreExistingRepository(backupChainLog, rEntry, true);
         }
         else
         {
            if (isRepositoryExist(rEntry.getName()))
            {
               throw new RepositoryRestoreExeption("Repository " + rEntry.getName() + " already exists!");
            }

            backupManager.restore(backupChainLog, rEntry, true);
         }

         // Sleeping. Restore should be initialized by job thread
         Thread.sleep(100);

         // search necessary restore
         JobRepositoryRestore restore = backupManager.getLastRepositoryRestore(rEntry.getName());
         ShortInfo info =
                  new ShortInfo(ShortInfo.RESTORE, restore.getRepositoryBackupChainLog(), restore.getStartTime(),
                           restore.getEndTime(), restore.getStateRestore(), restore.getRepositoryName());

         return Response.ok(info).cacheControl(noCache).build();
      }
      catch (RepositoryRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup log with id '"
               + backupId + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup log with id '"
                        + backupId + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
   }

   /**
    * Restore the repository from backup set with changing configuration (RepositoryEntry).
    * 
    * @param rEntry
    *          RepositoryEntry, the configuration to restored repository
    * @param repository
    *          String, the repository name
    * @param backupSetPath
    *          String, the path to backup set
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed repository.
    * @return Response return the response
    */
   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore-repository/backup-set/{remove-Existing}")
   public Response restoreRepositoryBackupSet(RepositoryEntry rEntry,
            @QueryParam("backup-set-path") String backupSetPathEncoded,
            @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      String backupSetPath = null;

      try
      {
         backupSetPath = URLDecoder.decode(backupSetPathEncoded, "UTF-8");
      }
      catch (UnsupportedEncodingException e)
      {
         LOG.error("Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup set '"
                  + backupSetPath + "'", e);

         return Response.status(Response.Status.BAD_REQUEST).entity(
                  "Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup set '"
                           + backupSetPath + "' : " + e.getMessage()).type(MediaType.TEXT_PLAIN).cacheControl(noCache)
                  .build();
      }

      try
      {
         validateOneRepositoryRestoreInstants(rEntry.getName());

         File backupSetDir = (new File(backupSetPath));

         if (!backupSetDir.exists())
         {
            throw new RestoreConfigurationException("Backup set directory is not exists :" + backupSetPath);
         }

         if (!backupSetDir.isDirectory())
         {
            throw new RestoreConfigurationException("Backup set directory is not directory :" + backupSetPath);
         }

         File[] cfs = PrivilegedFileHelper.listFiles(backupSetDir, new RepositoryBackupLogsFilter());

         if (cfs.length == 0)
         {
            throw new RestoreConfigurationException("Can not found repository backup log in directory : "
                     + backupSetPath);
         }

         if (cfs.length > 1)
         {
            throw new RestoreConfigurationException(
                     "Backup set directory should contains only one repository backup log : " + backupSetPath);
         }

         RepositoryBackupChainLog backupChainLog = new RepositoryBackupChainLog(cfs[0]);

         if (removeExisting)
         {
            if (!isRepositoryExist(rEntry.getName()))
            {
               throw new RepositoryRestoreExeption("Repository " + rEntry.getName() + " is not exists!");
            }

            backupManager.restoreExistingRepository(backupChainLog, rEntry, true);
         }
         else
         {
            if (isRepositoryExist(rEntry.getName()))
            {
               throw new RepositoryRestoreExeption("Repository " + rEntry.getName() + " already exists!");
            }

            backupManager.restore(backupChainLog, rEntry, true);
         }

         // Sleeping. Restore should be initialized by job thread
         Thread.sleep(100);

         // search necessary restore
         JobRepositoryRestore restore = backupManager.getLastRepositoryRestore(rEntry.getName());
         ShortInfo info =
                  new ShortInfo(ShortInfo.RESTORE, restore.getRepositoryBackupChainLog(), restore.getStartTime(),
                           restore.getEndTime(), restore.getStateRestore(), restore.getRepositoryName());

         return Response.ok(info).cacheControl(noCache).build();
      }
      catch (RepositoryRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup set '"
               + backupSetPath + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the repository '" + "/" + rEntry.getName() + "' from backup set '"
                        + backupSetPath + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache)
               .build();
   }

   /**
    * Restore the repository.
    * 
    * @param backupId
    *          String, the identifier of backup
    * @param removeExisting
    *          Boolean, if 'true' will be removed fully (db, value storage, index) existed repository.
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/restore-repository/{id}/{remove-Existing}")
   public Response restoreRepository(@PathParam("id") String backupId,
            @PathParam("remove-Existing") Boolean removeExisting)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      String repository = null;
      
      try
      {
         File backupLog = getRepositoryBackupLogbyId(backupId);

         // validate backup log file
         if (backupLog == null)
         {
            throw new BackupLogNotFoundException("The repository backup log file with id " + backupId + " not exists.");
         }

         RepositoryBackupChainLog backupChainLog = new RepositoryBackupChainLog(backupLog);
         repository = backupChainLog.getBackupConfig().getRepository();

         validateOneRepositoryRestoreInstants(repository);

         if (removeExisting)
         {
            if (!isRepositoryExist(repository))
            {
               throw new RepositoryRestoreExeption("Repository " + repository + " is not exists!");
            }

            backupManager.restoreExistingRepository(backupId, true);
         }
         else
         {
            if (isRepositoryExist(repository))
            {
               throw new RepositoryRestoreExeption("Repository " + repository + " already exists!");
            }

            backupManager.restoreRepository(backupId, true);
         }

         // Sleeping. Restore should be initialized by job thread
         Thread.sleep(100);

         // search necessary restore
         JobRepositoryRestore restore =
                  backupManager.getLastRepositoryRestore(repository);
         ShortInfo info =
                  new ShortInfo(ShortInfo.RESTORE, restore.getRepositoryBackupChainLog(), restore.getStartTime(),
                           restore.getEndTime(), restore.getStateRestore(), restore.getRepositoryName());

         return Response.ok(info).cacheControl(noCache).build();
      }
      catch (RepositoryRestoreExeption e)
      {
         exception = e;
         status = Response.Status.FORBIDDEN;
         failMessage = e.getMessage();
      }
      catch (RepositoryException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (RepositoryConfigurationException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (BackupLogNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not start restore the repository '" + "/" + repository + "' from backup log with id '"
               + backupId + "'", exception);

      return Response.status(status).entity(
               "Can not start restore the repository '" + "/" + repository + "' from backup log with id '"
                        + backupId + "' : " + failMessage).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
   }

   /**
    * The backup stop by 'id'.
    * 
    * @param backupId
    *          String, the identifier to backup
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/stop/{id}")
   public Response stop(@PathParam("id") String backupId)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         BackupChain bch = backupManager.findBackup(backupId);

         if (bch != null)
            backupManager.stopBackup(bch);
         else
            throw new BackupNotFoundException("No active backup with id '" + backupId + "'");

         ShortInfo shortInfo = null;
         for (BackupChainLog chainLog : backupManager.getBackupsLogs())
            if (backupId.equals(chainLog.getBackupId()))
            {
               shortInfo = new ShortInfo(ShortInfo.COMPLETED, chainLog);
               break;
            }

         if (shortInfo == null)
            throw new BackupNotFoundException("No completed backup with id '" + backupId + "'");

         return Response.ok(shortInfo).cacheControl(noCache).build();
      }
      catch (BackupNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not stop backup", exception);

      return Response.status(status).entity("Can not stop backup : " + failMessage).type(MediaType.TEXT_PLAIN)
         .cacheControl(noCache).build();
   }

   /**
    * The repository backup stop by 'id'.
    * 
    * @param backupId
    *          String, the identifier to backup
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/stop-backup-repository/{id}")
   public Response stopBackupRepository(@PathParam("id") String backupId)
   {
      String failMessage;
      Response.Status status;
      Throwable exception;

      try
      {
         RepositoryBackupChain bch = backupManager.findRepositoryBackupId(backupId);

         if (bch != null)
            backupManager.stopBackup(bch);
         else
            throw new BackupNotFoundException("No active repository backup with id '" + backupId + "'");

         ShortInfo shortInfo = null;
         for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
            if (backupId.equals(chainLog.getBackupId()))
            {
               shortInfo = new ShortInfo(ShortInfo.COMPLETED, chainLog);
               break;
            }

         if (shortInfo == null)
            throw new BackupNotFoundException("No completed backup with id '" + backupId + "'");

         return Response.ok(shortInfo).cacheControl(noCache).build();
      }
      catch (BackupNotFoundException e)
      {
         exception = e;
         status = Response.Status.NOT_FOUND;
         failMessage = e.getMessage();
      }
      catch (Throwable e)
      {
         exception = e;
         status = Response.Status.INTERNAL_SERVER_ERROR;
         failMessage = e.getMessage();
      }

      LOG.error("Can not stop repository backup ", exception);

      return Response.status(status).entity("Can not stop repository backup : " + failMessage).type(
         MediaType.TEXT_PLAIN).cacheControl(noCache).build();
   }

   /**
    * Will be returned the backup service info.
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info")
   public Response info()
   {
      try
      {
         BackupServiceInfoBean infoBeen =
            new BackupServiceInfoBean(backupManager.getFullBackupType(), backupManager.getIncrementalBackupType(),
               PrivilegedFileHelper.getAbsolutePath(backupManager.getBackupDirectory()),
               backupManager.getDefaultIncrementalJobPeriod());

         return Response.ok(infoBeen).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about backup service", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about backup service : " + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current and completed backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup")
   public Response infoBackup()
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (BackupChain chain : backupManager.getCurrentBackups())
            list.add(new ShortInfo(ShortInfo.CURRENT, chain));

         for (BackupChainLog chainLog : backupManager.getBackupsLogs())
            if (backupManager.findBackup(chainLog.getBackupId()) == null)
               list.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed backups" + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current and completed repository backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup-repository")
   public Response infoBackupRepository()
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (RepositoryBackupChain chain : backupManager.getCurrentRepositoryBackups())
            list.add(new ShortInfo(ShortInfo.CURRENT, chain));

         for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
            if (backupManager.findRepositoryBackupId(chainLog.getBackupId()) == null)
               list.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed reposioty backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed repository backups" + e.getMessage()).type(
            MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the detailed info of current or completed backup by 'id'.
    * 
    * @param id
    *          String, the identifier to backup
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup/{id}")
   public Response infoBackupId(@PathParam("id") String id)
   {
      try
      {
         BackupChain current = backupManager.findBackup(id);

         if (current != null)
         {
            DetailedInfo info = new DetailedInfo(DetailedInfo.CURRENT, current);
            return Response.ok(info).cacheControl(noCache).build();
         }

         BackupChainLog completed = null;

         for (BackupChainLog chainLog : backupManager.getBackupsLogs())
            if (id.equals(chainLog.getBackupId()))
               completed = chainLog;

         if (completed != null)
         {
            DetailedInfo info = new DetailedInfo(DetailedInfo.COMPLETED, completed);
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.status(Response.Status.NOT_FOUND).entity("No current or completed backup with 'id' " + id)
            .type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed backup with 'id' " + id, e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed backup with 'id' " + id + " : " + e.getMessage()).type(
            MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the detailed info of current or completed repository backup by 'id'.
    * 
    * @param id
    *          String, the identifier to repository backup
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup-repository-id/{id}")
   public Response infoBackupRepositoryId(@PathParam("id") String id)
   {
      try
      {
         RepositoryBackupChain current = backupManager.findRepositoryBackupId(id);

         if (current != null)
         {
            DetailedInfo info = new DetailedInfo(DetailedInfo.CURRENT, current);
            return Response.ok(info).cacheControl(noCache).build();
         }

         RepositoryBackupChainLog completed = null;

         for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
            if (id.equals(chainLog.getBackupId()))
               completed = chainLog;

         if (completed != null)
         {
            DetailedInfo info = new DetailedInfo(DetailedInfo.COMPLETED, completed);
            return Response.ok(info).cacheControl(noCache).build();
         }

         return Response.status(Response.Status.NOT_FOUND).entity(
            "No current or completed repository backup with 'id' " + id).type(MediaType.TEXT_PLAIN).cacheControl(
            noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed repository backup with 'id' " + id, e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed repository backup with 'id' " + id + " : "
               + e.getMessage()).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup/current")
   public Response infoBackupCurrent()
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (BackupChain chain : backupManager.getCurrentBackups())
            list.add(new ShortInfo(ShortInfo.CURRENT, chain));

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current backups" + e.getMessage()).type(MediaType.TEXT_PLAIN).cacheControl(
            noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup-repository/current")
   public Response infoBackupRepositoryCurrent()
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (RepositoryBackupChain chain : backupManager.getCurrentRepositoryBackups())
            list.add(new ShortInfo(ShortInfo.CURRENT, chain));

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current repositorty backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current backups" + e.getMessage()).type(MediaType.TEXT_PLAIN).cacheControl(
            noCache).build();
      }
   }

   /**
    * Will be returned the list short info of completed backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup/completed")
   public Response infoBackupCompleted()
   {
      try
      {
         List<ShortInfo> completedList = new ArrayList<ShortInfo>();

         for (BackupChainLog chainLog : backupManager.getBackupsLogs())
            if (backupManager.findBackup(chainLog.getBackupId()) == null)
               completedList.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));

         ShortInfoList list = new ShortInfoList(completedList);

         return Response.ok(list).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about completed backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about completed backups" + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of completed backups .
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup-repository/completed")
   public Response infoBackupRepositoryCompleted()
   {
      try
      {
         List<ShortInfo> completedList = new ArrayList<ShortInfo>();

         for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
            if (backupManager.findRepositoryBackupId(chainLog.getBackupId()) == null)
               completedList.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));

         ShortInfoList list = new ShortInfoList(completedList);

         return Response.ok(list).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about completed backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about completed backups" + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current and completed backups. Filtered by specific
    * workspace.
    * 
    * @param repository
    *          String, the repository name
    * @param workspace
    *          String, the workspace name
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup/{repo}/{ws}")
   public Response infoBackupByWorkspace(@PathParam("repo") String repository, @PathParam("ws") String workspace)
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (BackupChain chain : backupManager.getCurrentBackups())
         {
            if (repository.equals(chain.getBackupConfig().getRepository())
               && workspace.equals(chain.getBackupConfig().getWorkspace()))
            {
               list.add(new ShortInfo(ShortInfo.CURRENT, chain));
            }
         }

         for (BackupChainLog chainLog : backupManager.getBackupsLogs())
         {
            if (backupManager.findBackup(chainLog.getBackupId()) == null
               && repository.equals(chainLog.getBackupConfig().getRepository())
               && workspace.equals(chainLog.getBackupConfig().getWorkspace()))
            {
               list.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));
            }
         }

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed backups" + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the list short info of current and completed backups. Filtered by specific
    * repository.
    * 
    * @param repository
    *          String, the repository name
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/backup-repository/{repo}")
   public Response infoBackupByRepository(@PathParam("repo") String repository)
   {
      try
      {
         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (RepositoryBackupChain chain : backupManager.getCurrentRepositoryBackups())
         {
            if (repository.equals(chain.getBackupConfig().getRepository()))
            {
               list.add(new ShortInfo(ShortInfo.CURRENT, chain));
            }
         }

         for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
         {
            if (backupManager.findRepositoryBackupId(chainLog.getBackupId()) == null
               && repository.equals(chainLog.getBackupConfig().getRepository()))
            {
               list.add(new ShortInfo(ShortInfo.COMPLETED, chainLog));
            }
         }

         ShortInfoList shortInfoList = new ShortInfoList(list);

         return Response.ok(shortInfoList).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current or completed repository backups", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current or completed repository backups" + e.getMessage()).type(
            MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the detailed information about last restore for specific workspace.
    * 
    * @param repository
    *          String, the repository name
    * @param workspace
    *          String, the workspace name
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/restore/{repo}/{ws}")
   public Response infoRestore(@PathParam("repo") String repository, @PathParam("ws") String workspace)
   {
      try
      {
         JobWorkspaceRestore restoreJob = backupManager.getLastRestore(repository, workspace);

         if (restoreJob != null)
         {
            DetailedInfoEx info =
               new DetailedInfoEx(DetailedInfo.RESTORE, restoreJob.getBackupChainLog(), restoreJob.getStartTime(),
                  restoreJob.getEndTime(), restoreJob.getStateRestore(), restoreJob.getRepositoryName(), restoreJob
                     .getWorkspaceName(),

                  restoreJob.getWorkspaceEntry(), restoreJob.getRestoreException() == null ? "" : restoreJob
                     .getRestoreException().getMessage());

            return Response.ok(info).cacheControl(noCache).build();
         }
         else
         {
            return Response.status(Response.Status.NOT_FOUND).entity(
               "No resrore for workspace /" + repository + "/" + workspace + "'").type(MediaType.TEXT_PLAIN)
               .cacheControl(noCache).build();
         }

      }
      catch (Throwable e)
      {
         LOG.error(
            "Can not get information about current restore for workspace /" + repository + "/" + workspace + "'", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current restore for workspace /" + repository + "/" + workspace + "' : "
               + e.getMessage()).type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }

   }

   /**
    * Will be returned the detailed information about last restore for specific repository.
    * 
    * @param repository
    *          String, the repository name
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/restore-repository/{repo}")
   public Response infoRestoreRepository(@PathParam("repo") String repository)
   {
      try
      {
         JobRepositoryRestore restoreJob = backupManager.getLastRepositoryRestore(repository);

         if (restoreJob != null)
         {
            DetailedInfoEx info =
               new DetailedInfoEx(DetailedInfo.RESTORE, restoreJob.getRepositoryBackupChainLog(), restoreJob
                  .getStartTime(), restoreJob.getEndTime(), restoreJob.getStateRestore(), restoreJob
                  .getRepositoryName(),

               restoreJob.getRepositoryEntry(), restoreJob.getRestoreException() == null ? "" : restoreJob
                  .getRestoreException().getMessage());

            return Response.ok(info).cacheControl(noCache).build();
         }
         else
         {
            return Response.status(Response.Status.NOT_FOUND).entity("No restore for repository /" + repository + "'")
               .type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
         }

      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current restore for repository /" + repository + "'", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current restore for repository /" + repository + "' : " + e.getMessage())
            .type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }

   }

   /**
    * Will be returned the detailed information about last restores.
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/restores")
   public Response infoRestores()
   {
      try
      {
         List<JobWorkspaceRestore> restoreJobs = backupManager.getRestores();

         List<JobWorkspaceRestore> jobs = new ArrayList<JobWorkspaceRestore>();

         for (int i = restoreJobs.size() - 1; i >= 0; i--)
         {
            JobWorkspaceRestore job = restoreJobs.get(i);
            boolean isUnique = true;
            for (JobWorkspaceRestore unJob : jobs)
            {
               if (unJob.getRepositoryName().equals(job.getRepositoryName())
                  && unJob.getWorkspaceName().equals(job.getWorkspaceName()))
                  isUnique = false;
            }

            if (isUnique)
               jobs.add(job);
         }

         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (JobWorkspaceRestore job : jobs)
         {
            ShortInfo info =
               new ShortInfo(ShortInfo.RESTORE, job.getBackupChainLog(), job.getStartTime(), job.getEndTime(), job
                  .getStateRestore(), job.getRepositoryName(), job.getWorkspaceName());
            list.add(info);
         }

         return Response.ok(new ShortInfoList(list)).cacheControl(noCache).build();

      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current restores.", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current restores : " + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the detailed information about last restores.
    * 
    * @return Response return the response
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/restores-repository")
   public Response infoRestoresRepository()
   {
      try
      {
         List<JobRepositoryRestore> restoreJobs = backupManager.getRepositoryRestores();

         List<JobRepositoryRestore> jobs = new ArrayList<JobRepositoryRestore>();

         for (int i = restoreJobs.size() - 1; i >= 0; i--)
         {
            JobRepositoryRestore job = restoreJobs.get(i);
            boolean isUnique = true;
            for (JobRepositoryRestore unJob : jobs)
            {
               if (unJob.getRepositoryName().equals(job.getRepositoryName()))
                  isUnique = false;
            }

            if (isUnique)
               jobs.add(job);
         }

         List<ShortInfo> list = new ArrayList<ShortInfo>();

         for (JobRepositoryRestore job : jobs)
         {
            ShortInfo info =
               new ShortInfo(ShortInfo.RESTORE, job.getRepositoryBackupChainLog(), job.getStartTime(),
                  job.getEndTime(), job.getStateRestore(), job.getRepositoryName());
            list.add(info);
         }

         return Response.ok(new ShortInfoList(list)).cacheControl(noCache).build();

      }
      catch (Throwable e)
      {
         LOG.error("Can not get information about current repository restores.", e);

         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get information about current repository restores : " + e.getMessage()).type(MediaType.TEXT_PLAIN)
            .cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the default workspace configuration.
    * 
    * @return Response return the JSON to WorkspaceEntry
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/default-ws-config")
   public Response getDefaultWorkspaceConfig()
   {
      try
      {
         String defaultWorkspaceName =
            repositoryService.getDefaultRepository().getConfiguration().getDefaultWorkspaceName();

         for (WorkspaceEntry wEntry : repositoryService.getDefaultRepository().getConfiguration().getWorkspaceEntries())
            if (defaultWorkspaceName.equals(wEntry.getName()))
               return Response.ok(wEntry).cacheControl(noCache).build();

         return Response.status(Response.Status.NOT_FOUND).entity("Can not get default workspace configuration.").type(
            MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get default workspace configuration.").type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * Will be returned the default repository configuration.
    * 
    * @return Response return the JSON to WorkspaceEntry
    */
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @RolesAllowed("administrators")
   @Path("/info/default-repository-config")
   public Response getDefaultRepositoryConfig()
   {
      try
      {
         return Response.ok(repositoryService.getDefaultRepository().getConfiguration()).cacheControl(noCache).build();
      }
      catch (Throwable e)
      {
         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
            "Can not get default workspace configuration.").type(MediaType.TEXT_PLAIN).cacheControl(noCache).build();
      }
   }

   /**
    * validateRepositoryName.
    * 
    * @param repositoryName
    *          the repository name
    * @throws RepositoryConfigurationException
    *           will be generated the exception RepositoryConfigurationException
    * @throws RepositoryException
    *           will be generated the exception RepositoryException
    */
   private void validateRepositoryName(String repositoryName) throws RepositoryException,
      RepositoryConfigurationException
   {
      repositoryService.getRepository(repositoryName);
   }

   /**
    * validateWorkspaceName.
    * 
    * @param repositoryName
    *          the repository name
    * @param workspaceName
    *          the workspace name
    * @throws RepositoryConfigurationException
    *           will be generated the exception RepositoryConfigurationException
    * @throws RepositoryException
    *           will be generated the exception RepositoryException
    * @throws NoSuchWorkspaceException
    *           will be generated the exception NoSuchWorkspaceException
    * @throws LoginException
    *           will be generated the exception LoginException
    */
   private void validateWorkspaceName(String repositoryName, String workspaceName) throws LoginException,
      NoSuchWorkspaceException, RepositoryException, RepositoryConfigurationException
   {
      Session ses =
         sessionProviderService.getSessionProvider(null).getSession(workspaceName,
            repositoryService.getRepository(repositoryName));
      ses.logout();
   }

   private boolean isWorkspaceExist(String repositoryName, String workspaceName) throws RepositoryException,
      RepositoryConfigurationException
   {
      for (String workspace : repositoryService.getRepository(repositoryName).getWorkspaceNames())
      {
         if (workspaceName.equals(workspace))
         {
            return true;
         }
      }

      return false;
   }

   private boolean isRepositoryExist(String repositoryName) throws RepositoryException,
      RepositoryConfigurationException
   {
      try
      {
         return repositoryService.getRepository(repositoryName) != null;
      }
      catch (RepositoryException e)
      {
         return false;
      }

   }

   /**
    * validateOneBackupInstants.
    * 
    * @param repositoryName
    *          the repository name
    * @param workspaceName
    *          the workspace name
    * @throws WorkspaceRestoreExeption
    *           will be generated WorkspaceRestoreExeption
    */
   private void validateOneBackupInstants(String repositoryName, String workspaceName) throws WorkspaceRestoreExeption
   {

      BackupChain bch = backupManager.findBackup(repositoryName, workspaceName);

      if (bch != null)
         throw new WorkspaceRestoreExeption("The backup is already working on workspace '" + "/" + repositoryName + "/"
            + workspaceName + "'");
   }

   /**
    * validateOneRestoreInstants.
    * 
    * @param repositoryName
    *          the repository name
    * @param workspaceName
    *          the workspace name
    * @throws WorkspaceRestoreExeption
    *           will be generated WorkspaceRestoreExeption
    */
   private void validateOneRestoreInstants(String repositoryName, String workspaceName) throws WorkspaceRestoreExeption
   {

      for (JobWorkspaceRestore job : backupManager.getRestores())
         if (repositoryName.equals(job.getRepositoryName())
            && workspaceName.endsWith(job.getWorkspaceName())
            && (job.getStateRestore() == JobWorkspaceRestore.RESTORE_INITIALIZED 
                     || job.getStateRestore() == JobWorkspaceRestore.RESTORE_STARTED))
         {
            throw new WorkspaceRestoreExeption("The workspace '" + "/" + repositoryName + "/" + workspaceName
               + "' is already restoring.");
         }
   }

   /**
    * validateOneRepositoryRestoreInstants.
    * 
    * @param repositoryName
    *          the repository name
    * @throws WorkspaceRestoreExeption
    *           will be generated WorkspaceRestoreExeption
    */
   private void validateOneRepositoryRestoreInstants(String repositoryName) throws RepositoryRestoreExeption
   {
      for (JobRepositoryRestore job : backupManager.getRepositoryRestores())
      {
         if (repositoryName.equals(job.getRepositoryName())
            && (job.getStateRestore() == JobWorkspaceRestore.RESTORE_INITIALIZED 
                     || job.getStateRestore() == JobWorkspaceRestore.RESTORE_STARTED))
         {
            throw new RepositoryRestoreExeption("The repository '" + "/" + repositoryName + "' is already restoring.");
         }
      }
   }

   /**
    * forceCloseSession. Close sessions on specific workspace.
    * 
    * @param repositoryName
    *          repository name
    * @param workspaceName
    *          workspace name
    * @return int return the how many sessions was closed
    * @throws RepositoryConfigurationException
    *           will be generate RepositoryConfigurationException
    * @throws RepositoryException
    *           will be generate RepositoryException
    */
   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);
   }

   /**
    * getBackupLogbyId.
    * 
    * @param backupId
    *          String, the backup identifier
    * @return File return backup log file
    */
   private File getBackupLogbyId(String backupId)
   {
      FilenameFilter backupLogsFilter = new FilenameFilter()
      {

         public boolean accept(File dir, String name)
         {
            return (name.endsWith(".xml") && name.startsWith("backup-"));
         }
      };

      File[] files = PrivilegedFileHelper.listFiles(backupManager.getBackupDirectory(), backupLogsFilter);

      if (files.length != 0)
         for (File f : files)
            if (f.getName().replaceAll(".xml", "").replaceAll("backup-", "").equals(backupId))
               return f;

      return null;
   }

   /**
    * getRepositoryBackupLogbyId.
    * 
    * @param backupId
    *          String, the backup identifier
    * @return File return backup log file
    */
   private File getRepositoryBackupLogbyId(String backupId)
   {
      FilenameFilter backupLogsFilter = new FilenameFilter()
      {

         public boolean accept(File dir, String name)
         {
            return (name.endsWith(".xml") && name.startsWith(RepositoryBackupChainLog.PREFIX));
         }
      };

      File[] files = PrivilegedFileHelper.listFiles(backupManager.getBackupDirectory(), backupLogsFilter);

      if (files.length != 0)
         for (File f : files)
            if (f.getName().replaceAll(".xml", "").replaceAll(RepositoryBackupChainLog.PREFIX, "").equals(backupId))
               return f;

      return null;
   }
}
