/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * 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.infinispan.loaders; import org.infinispan.Cache; import org.infinispan.loaders.modifications.Modification; import org.infinispan.loaders.modifications.Remove; import org.infinispan.loaders.modifications.Store; import org.infinispan.marshall.StreamingMarshaller; import org.infinispan.transaction.xa.GlobalTransaction; import org.infinispan.util.concurrent.ConcurrentMapFactory; import org.infinispan.util.concurrent.WithinThreadExecutor; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * An abstract {@link org.infinispan.loaders.CacheStore} that holds common implementations for some methods * * @author Manik Surtani * @author Mircea.Markus@jboss.com * @since 4.0 */ public abstract class AbstractCacheStore extends AbstractCacheLoader implements CacheStore { private static final Log log = LogFactory.getLog(AbstractCacheStore.class); private Map<GlobalTransaction, List<? extends Modification>> transactions; private AbstractCacheStoreConfig config; protected ExecutorService purgerService; private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(0); protected boolean multiThreadedPurge = false; @Override public void init(CacheLoaderConfig config, Cache<?, ?> cache, StreamingMarshaller m) throws CacheLoaderException{ super.init(config, cache, m); this.config = (AbstractCacheStoreConfig) config; } protected final int getConcurrencyLevel() { return cache == null || cache.getConfiguration() == null? 16 : cache.getConfiguration().getConcurrencyLevel(); } @Override public void start() throws CacheLoaderException { if (config == null) throw new IllegalStateException("Make sure you call super.init() from CacheStore extension"); if (config.isPurgeSynchronously()) { purgerService = new WithinThreadExecutor(); } else { multiThreadedPurge = supportsMultiThreadedPurge() && config.getPurgerThreads() > 1; final String loaderName = getClass().getSimpleName(); purgerService = Executors.newFixedThreadPool(supportsMultiThreadedPurge() ? config.getPurgerThreads() : 1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { // Thread name: <cache>-<CacheStore>-<purger>-ID Thread t = new Thread(r, (cache == null ? "" : cache.getName() + '-') + loaderName + '-' + THREAD_COUNTER.getAndIncrement()); t.setDaemon(true); return t; } }); } transactions = ConcurrentMapFactory.makeConcurrentMap(64, getConcurrencyLevel()); } protected boolean supportsMultiThreadedPurge() { return false; } @Override public void stop() throws CacheLoaderException { purgerService.shutdownNow(); } @Override public void purgeExpired() throws CacheLoaderException { if (purgerService == null) throw new IllegalStateException("purgerService is null (did you call super.start() from cache loader implementation ?"); purgerService.execute(new Runnable() { @Override public void run() { try { purgeInternal(); } catch (CacheLoaderException e) { log.problemPurgingExpired(e); } } }); } protected abstract void purgeInternal() throws CacheLoaderException; protected void applyModifications(List<? extends Modification> mods) throws CacheLoaderException { for (Modification m : mods) { switch (m.getType()) { case STORE: Store s = (Store) m; store(s.getStoredEntry()); break; case CLEAR: clear(); break; case REMOVE: Remove r = (Remove) m; remove(r.getKey()); break; default: throw new IllegalArgumentException("Unknown modification type " + m.getType()); } } } @Override public void prepare(List<? extends Modification> mods, GlobalTransaction tx, boolean isOnePhase) throws CacheLoaderException { if (isOnePhase) { applyModifications(mods); } else { transactions.put(tx, mods); } } @Override public void rollback(GlobalTransaction tx) { transactions.remove(tx); } @Override public CacheStoreConfig getCacheStoreConfig() { return config; } @Override public void commit(GlobalTransaction tx) throws CacheLoaderException { List<? extends Modification> list = transactions.remove(tx); if (list != null && !list.isEmpty()) applyModifications(list); } @Override public void removeAll(Set<Object> keys) throws CacheLoaderException { if (keys != null && !keys.isEmpty()) { for (Object key : keys) remove(key); } } protected static void safeClose(InputStream stream) throws CacheLoaderException { if (stream == null) return; try { stream.close(); } catch (Exception e) { throw new CacheLoaderException("Problems closing input stream", e); } } protected static void safeClose(OutputStream stream) throws CacheLoaderException { if (stream == null) return; try { stream.close(); } catch (Exception e) { throw new CacheLoaderException("Problems closing output stream", e); } } protected StreamingMarshaller getMarshaller() { return marshaller; } }