Package org.exoplatform.services.jcr.ext.repository.creation

Source Code of org.exoplatform.services.jcr.ext.repository.creation.RepositoryCreationServiceImpl

/*
* Copyright (C) 2003-2010 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.jcr.ext.repository.creation;

import org.exoplatform.commons.utils.PrivilegedFileHelper;
import org.exoplatform.services.database.creator.DBConnectionInfo;
import org.exoplatform.services.database.creator.DBCreator;
import org.exoplatform.services.database.creator.DBCreatorException;
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.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.RepositoryBackupChainLog;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer;
import org.exoplatform.services.jcr.util.IdGenerator;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.naming.InitialContextInitializer;
import org.exoplatform.services.rpc.RPCException;
import org.exoplatform.services.rpc.RPCService;
import org.exoplatform.services.rpc.RemoteCommand;
import org.exoplatform.ws.frameworks.json.JsonHandler;
import org.exoplatform.ws.frameworks.json.JsonParser;
import org.exoplatform.ws.frameworks.json.impl.BeanBuilder;
import org.exoplatform.ws.frameworks.json.impl.JsonDefaultHandler;
import org.exoplatform.ws.frameworks.json.impl.JsonException;
import org.exoplatform.ws.frameworks.json.impl.JsonGeneratorImpl;
import org.exoplatform.ws.frameworks.json.impl.JsonParserImpl;
import org.exoplatform.ws.frameworks.json.value.JsonValue;
import org.picocontainer.Startable;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.jcr.RepositoryException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.xml.stream.XMLStreamException;

/**
* Created by The eXo Platform SAS.
*
* <br/>Date:
*
* @author <a href="karpenko.sergiy@gmail.com">Karpenko Sergiy</a>
* @version $Id: RepositoryCreationServiceImpl.java 111 2008-11-11 11:11:11Z serg $
*/
public class RepositoryCreationServiceImpl implements RepositoryCreationService, Startable
{
   /**
    * The logger.
    */
   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.RepositoryCreationSynchronizer");

   private final RepositoryService repositoryService;

   /**
    * The RPC Service used to communicate with other nodes.
    */
   private final RPCService rpcService;

   /**
    * BackupManager used to restore repository from backup.
    */
   private final BackupManager backupManager;

   /**
    * DBCreator used to create database. Only database not tables, indexes, etc.
    */
   private final DBCreator dbCreator;

   /**
    * InitalContextInitalizer used to bind new datasource.
    */
   private final InitialContextInitializer initialContextInitializer;

   /**
    * Store of reserved repository names. {tokenname, repositoryname}
    */
   private final Map<String, String> pendingRepositories = new ConcurrentHashMap<String, String>();

   private RemoteCommand reserveRepositoryName;

   private RemoteCommand createRepository;

   private RemoteCommand startRepository;

   /**
    * Constructor RepositoryCreationServiceImpl.
    */
   public RepositoryCreationServiceImpl(RepositoryService repositoryService, BackupManager backupManager,
      DBCreator dbCreator, InitialContextInitializer initialContextInitializer)
   {
      this.repositoryService = repositoryService;
      this.backupManager = backupManager;
      this.rpcService = null;
      this.dbCreator = dbCreator;
      this.initialContextInitializer = initialContextInitializer;

      LOG.warn("RepositoryCreationService initialized without RPCService, so other cluser nodes will"
         + " not be notified about new repositories.");
   }

   /**
    * Constructor RepositoryCreationServiceImpl.
    */
   public RepositoryCreationServiceImpl(RepositoryService repositoryService, BackupManager backupManager,
      DBCreator dbCreator, InitialContextInitializer initialContextInitializer, final RPCService rpcService)
   {
      this.repositoryService = repositoryService;
      this.backupManager = backupManager;
      this.rpcService = rpcService;
      this.dbCreator = dbCreator;
      this.initialContextInitializer = initialContextInitializer;

      // register commands
      reserveRepositoryName = rpcService.registerCommand(new RemoteCommand()
      {

         public String getId()
         {
            return "org.exoplatform.services.jcr.ext.repository.creation.RepositoryCreationServiceImpl-reserveRepositoryName";
         }

         public Serializable execute(Serializable[] args) throws Throwable
         {
            String repositoryName = (String)args[0];
            return reserveRepositoryNameLocally(repositoryName);
         }
      });

      createRepository = rpcService.registerCommand(new RemoteCommand()
      {

         public String getId()
         {
            return "org.exoplatform.services.jcr.ext.repository.creation.RepositoryCreationServiceImpl-createRepository";
         }

         public Serializable execute(Serializable[] args) throws Throwable
         {
            //String backupId, RepositoryEntry rEntry, String rToken
            String backupId = (String)args[0];
            String stringRepositoryEntry = (String)args[1];
            String rToken = (String)args[2];

            try
            {
               RepositoryEntry rEntry =
                  (RepositoryEntry)(getObject(RepositoryEntry.class, stringRepositoryEntry
                     .getBytes(Constants.DEFAULT_ENCODING)));

               createRepositoryLocally(backupId, rEntry, rToken);
               return null;
            }
            finally
            {
               // release tokens
               pendingRepositories.remove(rToken);
            }
         }
      });

      startRepository = rpcService.registerCommand(new RemoteCommand()
      {
         public String getId()
         {
            return "org.exoplatform.services.jcr.ext.repository.creation.RepositoryCreationServiceImpl-startRepository";
         }

         public Serializable execute(Serializable[] args) throws Throwable
         {
            // must not be executed on coordinator node, since coordinator node already created the repository
            if (!rpcService.isCoordinator())
            {
               //RepositoryEntry (as String) rEntry
               String stringRepositoryEntry = (String)args[0];
               RepositoryEntry rEntry =
                  (RepositoryEntry)(getObject(RepositoryEntry.class, stringRepositoryEntry
                     .getBytes(Constants.DEFAULT_ENCODING)));

               startRepository(rEntry);
            }
            return null;
         }
      });

   }

   /**
    * {@inheritDoc}
    */
   public void createRepository(String backupId, RepositoryEntry rEntry) throws RepositoryConfigurationException,
      RepositoryCreationException
   {
      String rToken = reserveRepositoryName(rEntry.getName());
      createRepository(backupId, rEntry, rToken);
   }

   /**
    * {@inheritDoc}
    */
   public void createRepository(String backupId, RepositoryEntry rEntry, String rToken)
      throws RepositoryConfigurationException, RepositoryCreationException
   {
      if (rpcService != null)
      {
         String stringRepositoryEntry = null;
         try
         {
            JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl();
            JsonValue json = generatorImpl.createJsonObject(rEntry);
            stringRepositoryEntry = json.toString();
         }
         catch (JsonException e)
         {
            throw new RepositoryCreationException("Can not serialize repository entry: " + e.getMessage(), e);
         }

         // notify coordinator node to create repository
         try
         {
            Object result =
               rpcService.executeCommandOnCoordinator(createRepository, true, backupId, stringRepositoryEntry, rToken);

            if (result != null)
            {
               throw new RepositoryCreationException("CreateRepository command must not return any results.");
            }
         }
         catch (RPCException e)
         {
            Throwable cause = (e).getCause();
            if (cause instanceof RepositoryCreationException)
            {
               throw (RepositoryCreationException)cause;
            }
            else if (cause instanceof RepositoryConfigurationException)
            {
               throw (RepositoryConfigurationException)cause;
            }
            else
            {
               throw new RepositoryCreationException(e.getMessage(), e);
            }
         }

         // execute startRepository at all cluster nodes (coordinator will ignore this command)
         try
         {
            List<Object> results = rpcService.executeCommandOnAllNodes(startRepository, true, stringRepositoryEntry);

            for (Object result : results)
            {
               if (result instanceof RPCException)
               {
                  Throwable cause = ((RPCException)result).getCause();
                  if (cause instanceof RepositoryCreationException)
                  {
                     throw new RepositoryCreationException("Repository " + rEntry.getName()
                        + " created on coordinator, but can not be started at other cluster nodes: "
                        + cause.getMessage(), cause);
                  }
               }
               if (result instanceof Throwable)
               {
                  throw new RepositoryCreationException("Repository " + rEntry.getName()
                     + " created on coordinator, but can not be started at other cluster nodes: "
                     + ((Throwable)result).getMessage(), ((Throwable)result));
               }
            }
         }
         catch (RPCException e)
         {
            throw new RepositoryCreationException("Repository " + rEntry.getName()
               + " created on coordinator, can not be started at other cluster node: " + e.getMessage(), e);
         }
      }
      else
      {
         try
         {
            createRepositoryLocally(backupId, rEntry, rToken);
         }
         finally
         {
            pendingRepositories.remove(rToken);
         }
      }
   }

   /**
    * {@inheritDoc}
    */
   public String reserveRepositoryName(String repositoryName) throws RepositoryCreationException
   {
      if (rpcService != null)
      {
         // check does repository already created
         try
         {
            if (repositoryService.getRepository(repositoryName) != null)
            {
               throw new RepositoryCreationException("Repository " + repositoryName + " already exists.");
            }
         }
         catch (RepositoryConfigurationException e)
         {
            throw new RepositoryCreationException("Can not check does repository " + repositoryName + " exists: "
               + e.getMessage(), e);
         }
         catch (RepositoryException e)
         {
            //ok - repository does not exists
         }

         // reserve RepositoryName at coordinator-node
         try
         {
            Object result = rpcService.executeCommandOnCoordinator(reserveRepositoryName, true, repositoryName);

            if (result instanceof String)
            {
               return (String)result;
            }
            else
            {
               throw new RepositoryCreationException("ReserveRepositoryName command returns unknown type result.");
            }
         }
         catch (RPCException e)
         {
            Throwable cause = (e).getCause();
            if (cause instanceof RepositoryCreationException)
            {
               throw (RepositoryCreationException)cause;
            }
            else
            {
               throw new RepositoryCreationException("Can not reserve repository name " + repositoryName + " since: "
                  + e.getMessage(), e);
            }
         }
      }
      else
      {
         return reserveRepositoryNameLocally(repositoryName);
      }
   }

   protected String reserveRepositoryNameLocally(String repositoryName) throws RepositoryCreationException
   {
      // check does repository already created
      try
      {
         if (repositoryService.getRepository(repositoryName) != null)
         {
            throw new RepositoryCreationException("Repository " + repositoryName + " already exists.");
         }
      }
      catch (RepositoryConfigurationException e)
      {
         throw new RepositoryCreationException("Can not check does repository " + repositoryName + " exists: "
            + e.getMessage(), e);
      }
      catch (RepositoryException e)
      {
         //ok - repository does not exists
      }

      // check does this repository name already reserved, otherwise generate and return token
      if (!pendingRepositories.containsValue(repositoryName))
      {
         String rToken = repositoryName + IdGenerator.generate();
         pendingRepositories.put(rToken, repositoryName);
         return rToken;
      }
      else
      {
         throw new RepositoryCreationException("Repository name " + repositoryName + " already reserved.");
      }
   }

   protected void createRepositoryLocally(String backupId, RepositoryEntry rEntry, String rToken)
      throws RepositoryConfigurationException, RepositoryCreationException
   {
      // check does token registered
      if (!this.pendingRepositories.containsKey(rToken))
      {
         throw new RepositoryCreationException("Token " + rToken + " does not registered.");
      }

      // Prepare list of data-source names that must be binded to newly created databases.
      Set<String> dataSourceNames = extractDataSourceNames(rEntry, true);

      // create and bind related database to each data-source name
      for (String dataSource : dataSourceNames)
      {
         // create related DB
         Map<String, String> refAddr = null;
         try
         {
            DBConnectionInfo dbConnectionInfo = dbCreator.createDatabase(rEntry.getName() + "_" + dataSource);
            refAddr = dbConnectionInfo.getProperties();
         }
         catch (DBCreatorException e)
         {
            throw new RepositoryCreationException("Can not create new database for " + rEntry.getName()
               + " repository.", e);
         }

         // bind data-source
         try
         {
            initialContextInitializer.bind(dataSource, "javax.sql.DataSource",
               "org.apache.commons.dbcp.BasicDataSourceFactory", null, refAddr);
         }
         catch (NamingException e)
         {
            throw new RepositoryCreationException(e.getMessage(), e);
         }
         catch (FileNotFoundException e)
         {
            throw new RepositoryCreationException(e.getMessage(), e);
         }
         catch (XMLStreamException e)
         {
            throw new RepositoryCreationException(e.getMessage(), e);
         }
      }

      // restore repository from backup
      RepositoryBackupChainLog backupChain = null;
      for (RepositoryBackupChainLog chainLog : backupManager.getRepositoryBackupsLogs())
      {
         if (chainLog.getBackupId().equals(backupId))
         {
            backupChain = chainLog;
            break;
         }
      }

      if (backupChain == null)
      {
         throw new RepositoryCreationException("BackupChain by id " + backupId + " does not exists.");
      }

      File backLog = new File(backupChain.getLogFilePath());
      if (backLog != null && PrivilegedFileHelper.exists(backLog))
      {
         try
         {
            backupManager.restore(backupChain, rEntry, false);
         }
         catch (BackupOperationException e)
         {
            throw new RepositoryCreationException(e.getLocalizedMessage(), e);
         }
         catch (BackupConfigurationException e)
         {
            throw new RepositoryCreationException(e.getLocalizedMessage(), e);
         }
         catch (RepositoryException e)
         {
            throw new RepositoryCreationException(e.getLocalizedMessage(), e);
         }
      }
      else
      {
         throw new RepositoryCreationException("Backup log file by id " + backupId
            + (backLog != null ? (" and file path=" + PrivilegedFileHelper.getAbsolutePath(backLog)) : "")
            + " do not exists.");
      }
   }

   protected void startRepository(RepositoryEntry repositoryEntry) throws RepositoryCreationException
   {
      try
      {
         // Prepare list of data-source names that must be binded
         Set<String> dataSourceNames = extractDataSourceNames(repositoryEntry, false);

         for (String dataSource : dataSourceNames)
         {
            // get data base info
            Map<String, String> refAddr = null;
            try
            {
               DBConnectionInfo dbConnectionInfo =
                  dbCreator.getDBConnectionInfo(repositoryEntry.getName() + "_" + dataSource);
               refAddr = dbConnectionInfo.getProperties();
            }
            catch (DBCreatorException e)
            {
               throw new RepositoryCreationException("Can not fetch database information associated with "
                  + repositoryEntry.getName() + " repository and " + dataSource + " datasource.", e);
            }
            // bind data-source
            try
            {
               initialContextInitializer.bind(dataSource, "javax.sql.DataSource",
                  "org.apache.commons.dbcp.BasicDataSourceFactory", null, refAddr);
            }
            catch (NamingException e)
            {
               throw new RepositoryCreationException(e.getMessage(), e);
            }
            catch (FileNotFoundException e)
            {
               throw new RepositoryCreationException(e.getMessage(), e);
            }
            catch (XMLStreamException e)
            {
               throw new RepositoryCreationException(e.getMessage(), e);
            }
         }

         repositoryService.createRepository(repositoryEntry);
      }
      catch (RepositoryConfigurationException e)
      {
         throw new RepositoryCreationException(e.getMessage(), e);
      }
      catch (RepositoryException e)
      {
         throw new RepositoryCreationException(e.getMessage(), e);
      }
   }

   private Set<String> extractDataSourceNames(RepositoryEntry repositoryEntry, boolean checkDataSourceExistance)
      throws RepositoryConfigurationException, RepositoryCreationException
   {
      Set<String> dataSourceNames = new HashSet<String>();
      for (WorkspaceEntry wsEntry : repositoryEntry.getWorkspaceEntries())
      {
         boolean isMultiDB =
            Boolean.parseBoolean(wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.MULTIDB));
         String dbSourceName = wsEntry.getContainer().getParameterValue(JDBCWorkspaceDataContainer.SOURCE_NAME);

         if (isMultiDB && dataSourceNames.contains(dbSourceName))
         {
            throw new RepositoryCreationException("RepositoryEntry for new " + repositoryEntry.getName()
               + " repository contains workspaces that marked as multiDB but have same datasource " + dbSourceName
               + ".");
         }

         if (checkDataSourceExistance)
         {
            try
            {
               DataSource ds = (DataSource)initialContextInitializer.getInitialContext().lookup(dbSourceName);
               if (ds != null)
               {
                  throw new RepositoryConfigurationException("RepositoryEntry for new " + repositoryEntry.getName()
                     + " repository contains already binded datasource " + dbSourceName + ".");
               }
            }
            catch (NameNotFoundException e)
            {
               // skip this exception
            }
            catch (NamingException e)
            {
               throw new RepositoryConfigurationException(e.getMessage(), e);
            }
         }

         dataSourceNames.add(dbSourceName);
      }
      return dataSourceNames;
   }

   /**
    * Will be created the Object from JSON binary data.
    *
    * @param cl
    *          Class
    * @param data
    *          binary data (JSON)
    * @return Object
    * @throws Exception
    *           will be generated Exception
    */
   private Object getObject(Class cl, byte[] data) throws Exception
   {
      JsonHandler jsonHandler = new JsonDefaultHandler();
      JsonParser jsonParser = new JsonParserImpl();
      InputStream inputStream = new ByteArrayInputStream(data);
      jsonParser.parse(inputStream, jsonHandler);
      JsonValue jsonValue = jsonHandler.getJsonObject();

      return new BeanBuilder().createObject(cl, jsonValue);
   }

   /**
    * {@inheritDoc}
    */
   public void start()
   {
      // do nothing
   }

   /**
    * {@inheritDoc}
    */
   public void stop()
   {
      if (this.rpcService != null)
      {
         this.rpcService.unregisterCommand(reserveRepositoryName);
         this.rpcService.unregisterCommand(createRepository);
         this.rpcService.unregisterCommand(startRepository);
      }
   }
}
TOP

Related Classes of org.exoplatform.services.jcr.ext.repository.creation.RepositoryCreationServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.