/*
* JBoss, Home of Professional Open Source
* Copyright 2010 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.container;
import net.jcip.annotations.ThreadSafe;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.container.versioning.EntryVersion;
import org.infinispan.eviction.EvictionStrategy;
import org.infinispan.eviction.EvictionThreadPolicy;
import org.infinispan.util.Util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
/**
* DefaultDataContainer is both eviction and non-eviction based data container.
*
*
* @author Manik Surtani
* @author Galder ZamarreƱo
* @author Vladimir Blagojevic
* @author <a href="http://gleamynode.net/">Trustin Lee</a>
* @author Pedro Ruivo
* @author Sebastiano Peluso
* @since 4.0
*/
@ThreadSafe
public class DefaultDataContainer extends AbstractDataContainer<InternalCacheEntry> {
public DefaultDataContainer(int concurrencyLevel) {
super(concurrencyLevel);
}
protected DefaultDataContainer(int concurrencyLevel, int maxEntries, EvictionStrategy strategy, EvictionThreadPolicy policy) {
super(concurrencyLevel, maxEntries, strategy, policy);
}
public static DataContainer boundedDataContainer(int concurrencyLevel, int maxEntries,
EvictionStrategy strategy, EvictionThreadPolicy policy) {
return new DefaultDataContainer(concurrencyLevel, maxEntries, strategy, policy);
}
public static DataContainer unBoundedDataContainer(int concurrencyLevel) {
return new DefaultDataContainer(concurrencyLevel);
}
@Override
public InternalCacheEntry peek(Object key, EntryVersion version) {
return entries.get(key);
}
@Override
public InternalCacheEntry get(Object k, EntryVersion version) {
InternalCacheEntry e = peek(k, version);
if (e != null && e.canExpire()) {
long currentTimeMillis = System.currentTimeMillis();
if (e.isExpired(currentTimeMillis)) {
entries.remove(k);
e = null;
} else {
e.touch(currentTimeMillis);
}
}
return e;
}
@Override
public void put(Object k, Object v, EntryVersion version, long lifespan, long maxIdle) {
InternalCacheEntry e = entries.get(k);
if (e != null) {
e.setValue(v);
InternalCacheEntry original = e;
e.setVersion(version);
e = entryFactory.update(e, lifespan, maxIdle);
// we have the same instance. So we need to reincarnate.
if(original == e) {
e.reincarnate();
}
} else {
// this is a brand-new entry
e = entryFactory.create(k, v, version, lifespan, maxIdle);
}
entries.put(k, e);
}
@Override
public boolean containsKey(Object k, EntryVersion version) {
InternalCacheEntry ice = peek(k, null);
if (ice != null && ice.canExpire() && ice.isExpired(System.currentTimeMillis())) {
entries.remove(k);
ice = null;
}
return ice != null;
}
@Override
public InternalCacheEntry remove(Object k, EntryVersion version) {
InternalCacheEntry e = entries.remove(k);
return e == null || (e.canExpire() && e.isExpired(System.currentTimeMillis())) ? null : e;
}
@Override
public int size(EntryVersion version) {
return entries.size();
}
@Override
public void clear() {
entries.clear();
}
@Override
public void clear(EntryVersion version) {
clear();
}
@Override
public void purgeExpired() {
long currentTimeMillis = System.currentTimeMillis();
for (Iterator<InternalCacheEntry> purgeCandidates = entries.values().iterator(); purgeCandidates.hasNext();) {
InternalCacheEntry e = purgeCandidates.next();
if (e.isExpired(currentTimeMillis)) {
purgeCandidates.remove();
}
}
}
@Override
protected Map<Object, InternalCacheEntry> getCacheEntries(Map<Object, InternalCacheEntry> evicted) {
return evicted;
}
@Override
protected InternalCacheEntry getCacheEntry(InternalCacheEntry evicted) {
return evicted;
}
@Override
protected InternalCacheEntry getCacheEntry(InternalCacheEntry entry, EntryVersion version) {
return entry;
}
@Override
protected EntryIterator createEntryIterator(EntryVersion version) {
return new DefaultEntryIterator(entries.values().iterator());
}
@Override
public final boolean dumpTo(String filePath) {
BufferedWriter bufferedWriter = Util.getBufferedWriter(filePath);
if (bufferedWriter == null) {
return false;
}
try {
for (Map.Entry<Object, InternalCacheEntry> entry : entries.entrySet()) {
Util.safeWrite(bufferedWriter, entry.getKey());
Util.safeWrite(bufferedWriter, "=");
Util.safeWrite(bufferedWriter, entry.getValue().getValue());
Util.safeWrite(bufferedWriter, "=");
Util.safeWrite(bufferedWriter, entry.getValue().getVersion());
bufferedWriter.newLine();
bufferedWriter.flush();
}
return true;
} catch (IOException e) {
return false;
} finally {
Util.close(bufferedWriter);
}
}
@Override
public void gc(EntryVersion minimumVersion) {
//no-op
}
public static class DefaultEntryIterator extends EntryIterator {
private final Iterator<InternalCacheEntry> it;
DefaultEntryIterator(Iterator<InternalCacheEntry> it){this.it=it;}
@Override
public InternalCacheEntry next() {
return it.next();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}