/* * * 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.impl.core.lock.infinispan; import org.exoplatform.container.configuration.ConfigurationManager; import org.exoplatform.management.annotations.Managed; import org.exoplatform.management.jmx.annotations.NameTemplate; import org.exoplatform.management.jmx.annotations.Property; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; import org.exoplatform.services.jcr.config.WorkspaceEntry; import org.exoplatform.services.jcr.impl.core.lock.LockRemoverHolder; import org.exoplatform.services.jcr.impl.core.lock.LockTableHandler; import org.exoplatform.services.jcr.impl.core.lock.cacheable.AbstractCacheableLockManager; import org.exoplatform.services.jcr.impl.core.lock.cacheable.CacheableSessionLockManager; import org.exoplatform.services.jcr.impl.core.lock.cacheable.LockData; import org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspacePersistentDataManager; import org.exoplatform.services.jcr.infinispan.ISPNCacheFactory; import org.exoplatform.services.jcr.infinispan.PrivilegedISPNCacheHelper; import org.exoplatform.services.naming.InitialContextInitializer; import org.exoplatform.services.transaction.TransactionService; import org.infinispan.AdvancedCache; import org.infinispan.context.Flag; import org.infinispan.lifecycle.ComponentStatus; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.jcr.RepositoryException; import javax.jcr.lock.LockException; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import javax.transaction.TransactionManager; /** * @author <a href="anatoliy.bazko@exoplatform.org">Anatoliy Bazko</a> * @version $Id: InfinispanLockManagerImpl.java 111 2010-11-11 11:11:11Z tolusha $ */ @Managed @NameTemplate(@Property(key = "service", value = "lockmanager")) public class ISPNCacheableLockManagerImpl extends AbstractCacheableLockManager { /** * The name to property cache configuration. */ public static final String INFINISPAN_JDBC_CL_DATASOURCE = "infinispan-cl-cache.jdbc.datasource"; public static final String INFINISPAN_JDBC_CL_DIALECT = "infinispan-cl-cache.jdbc.dialect"; public static final String INFINISPAN_JDBC_CL_DATA_COLUMN = "infinispan-cl-cache.jdbc.data.type"; public static final String INFINISPAN_JDBC_CL_TIMESTAMP_COLUMN = "infinispan-cl-cache.jdbc.timestamp.type"; public static final String INFINISPAN_JDBC_CL_ID_COLUMN = "infinispan-cl-cache.jdbc.id.type"; public static final String INFINISPAN_JDBC_CL_ID_COLUMN_NAME = "infinispan-cl-cache.jdbc.id.column"; public static final String INFINISPAN_JDBC_TABLE_NAME = "infinispan-cl-cache.jdbc.table.name"; public static final String INFINISPAN_JDBC_CL_AUTO = "auto"; private AdvancedCache<Serializable, Object> cache; public ISPNCacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config, InitialContextInitializer context, TransactionService transactionService, ConfigurationManager cfm, LockRemoverHolder lockRemoverHolder) throws RepositoryConfigurationException, RepositoryException { this(dataManager, config, context, transactionService.getTransactionManager(), cfm, lockRemoverHolder); } public ISPNCacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config, InitialContextInitializer context, ConfigurationManager cfm, LockRemoverHolder lockRemoverHolder) throws RepositoryConfigurationException, RepositoryException { this(dataManager, config, context, (TransactionManager)null, cfm, lockRemoverHolder); } public ISPNCacheableLockManagerImpl(WorkspacePersistentDataManager dataManager, WorkspaceEntry config, InitialContextInitializer context, TransactionManager transactionManager, ConfigurationManager cfm, LockRemoverHolder lockRemoverHolder) throws RepositoryConfigurationException, RepositoryException { super(dataManager, config, transactionManager, lockRemoverHolder); // make cache if (config.getLockManager() != null) { // create cache using custom factory ISPNCacheFactory<Serializable, Object> factory = new ISPNCacheFactory<Serializable, Object>(cfm, transactionManager); try { String dataSourceName = config.getLockManager().getParameterValue(INFINISPAN_JDBC_CL_DATASOURCE); dataSource = (DataSource)new InitialContext().lookup(dataSourceName); } catch (NamingException e) { throw new RepositoryException(e.getMessage(), e); } // configure cache loader parameters with correct DB data-types ISPNCacheFactory.configureCacheStore(config.getLockManager(), INFINISPAN_JDBC_CL_DATASOURCE, INFINISPAN_JDBC_CL_DATA_COLUMN, INFINISPAN_JDBC_CL_ID_COLUMN, INFINISPAN_JDBC_CL_TIMESTAMP_COLUMN , INFINISPAN_JDBC_CL_DIALECT); cache = factory.createCache("L" + config.getUniqueName().replace("_", ""), config.getLockManager()) .getAdvancedCache(); } else { throw new RepositoryConfigurationException("Cache configuration not found"); } this.getNumLocks = new LockActionNonTxAware<Integer, Object>() { public Integer execute(Object arg) { return cache.size(); } }; this.hasLocks = new LockActionNonTxAware<Boolean, Object>() { public Boolean execute(Object arg) { return !cache.isEmpty(); } }; this.isLockLive = new LockActionNonTxAware<Boolean, String>() { public Boolean execute(String nodeId) { return cache.withFlags(Flag.SKIP_CACHE_LOAD).containsKey(nodeId); } }; this.refresh = new LockActionNonTxAware<Object, LockData>() { public Object execute(LockData newLockData) throws LockException { Object oldValue = PrivilegedISPNCacheHelper.put(cache, newLockData.getNodeIdentifier(), newLockData); if (oldValue == null) { throw new LockException("Can't refresh lock for node " + newLockData.getNodeIdentifier() + " since lock is not exist"); } return null; } }; this.lockExist = new LockActionNonTxAware<Boolean, String>() { public Boolean execute(String nodeId) throws LockException { return cache.withFlags(Flag.SKIP_CACHE_LOAD).containsKey(nodeId); } }; this.getLockDataById = new LockActionNonTxAware<LockData, String>() { public LockData execute(String nodeId) throws LockException { return (LockData)cache.withFlags(Flag.SKIP_CACHE_LOAD).get(nodeId); } }; this.getLockList = new LockActionNonTxAware<List<LockData>, Object>() { public List<LockData> execute(Object arg) throws LockException { Collection<Object> datas = cache.values(); List<LockData> locksData = new ArrayList<LockData>(); for (Object lockData : datas) { if (lockData != null) { locksData.add((LockData)lockData); } } return locksData; } }; } /** * {@inheritDoc} */ public void start() { PrivilegedISPNCacheHelper.start(cache); super.start(); } /** * {@inheritDoc} */ public void stop() { super.stop(); PrivilegedISPNCacheHelper.stop(cache); ISPNCacheFactory.releaseUniqueInstance(cache.getCacheManager()); } /** * {@inheritDoc} */ @Override protected synchronized void internalLock(String sessionId, String nodeIdentifier) throws LockException { CacheableSessionLockManager session = sessionLockManagers.get(sessionId); if (session != null && session.containsPendingLock(nodeIdentifier)) { LockData lockData = session.getPendingLock(nodeIdentifier); // this will return null if success. And old data if something exists... LockData oldLockData = doPut(lockData); if (oldLockData != null) { throw new LockException("Unable to write LockData. Node [" + lockData.getNodeIdentifier() + "] already has LockData!"); } session.notifyLockPersisted(nodeIdentifier); } else { throw new LockException("No lock in pending locks"); } } /** * {@inheritDoc} */ @Override protected synchronized void internalUnLock(String sessionId, String nodeIdentifier) throws LockException { LockData lData = getLockDataById(nodeIdentifier); if (lData != null) { doRemove(lData); CacheableSessionLockManager sessMgr = sessionLockManagers.get(sessionId); if (sessMgr != null) { sessMgr.notifyLockRemoved(nodeIdentifier); } } } /** * {@inheritDoc} */ @Override protected LockData doPut(LockData lockData) { return (LockData)PrivilegedISPNCacheHelper.putIfAbsent(cache, lockData.getNodeIdentifier(), lockData); } /** * {@inheritDoc} */ @Override protected void doRemove(LockData lockData) { cache.remove(lockData.getNodeIdentifier()); } /** * {@inheritDoc} */ @Override protected boolean isAloneInCluster() { return cache.getRpcManager() == null || cache.getCacheManager().getMembers().size() == 1; } /** * {@inheritDoc} */ @Override protected void doClean() { if (cache.getStatus() == ComponentStatus.RUNNING) { for (LockData lockData : getLockList()) { doRemove(lockData); } } } /** * {@inheritDoc} */ public LockTableHandler getLockTableHandler() { return new ISPNLockTableHandler(config, dataSource); } }