/* * Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.impl; import com.hazelcast.impl.base.DistributedLock; import com.hazelcast.impl.base.ScheduledAction; import com.hazelcast.impl.concurrentmap.ValueHolder; import com.hazelcast.nio.Address; import com.hazelcast.nio.Data; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static com.hazelcast.nio.IOUtil.toObject; @SuppressWarnings("VolatileLongOrDoubleField") public abstract class AbstractRecord extends AbstractSimpleRecord implements Record { protected volatile int hits = 0; protected volatile long version = 0; protected volatile long maxIdleMillis = Long.MAX_VALUE; protected volatile long writeTime = -1; protected volatile long removeTime = 0; protected volatile long lastAccessTime = 0; protected volatile long lastStoredTime = 0; protected volatile long creationTime = 0; protected volatile long expirationTime = Long.MAX_VALUE; protected volatile long lastUpdateTime = 0; protected volatile boolean dirty = false; protected volatile DistributedLock lock = null; protected volatile OptionalInfo optionalInfo = null; public AbstractRecord(CMap cmap, int blockId, Data key, long ttl, long maxIdleMillis, long id) { super(blockId, cmap, id, key); this.setCreationTime(System.currentTimeMillis()); this.setExpirationTime(ttl); this.maxIdleMillis = (maxIdleMillis == 0) ? Long.MAX_VALUE : maxIdleMillis; this.setVersion(0); } public void runBackupOps() { if (getBackupOps() != null) { if (getBackupOps().size() > 0) { Iterator<VersionedBackupOp> it = getBackupOps().iterator(); while (it.hasNext()) { VersionedBackupOp bo = it.next(); if (bo.getVersion() < getVersion() + 1) { it.remove(); } else if (bo.getVersion() == getVersion() + 1) { bo.run(); setVersion(bo.getVersion()); it.remove(); } else { return; } } } } } public void addBackupOp(VersionedBackupOp bo) { if (getBackupOps() == null) { setBackupOps(new TreeSet<VersionedBackupOp>()); } getBackupOps().add(bo); if (getBackupOps().size() > 4) { forceBackupOps(); } } public void forceBackupOps() { if (getBackupOps() == null) return; Iterator<VersionedBackupOp> it = getBackupOps().iterator(); while (it.hasNext()) { VersionedBackupOp v = it.next(); v.run(); setVersion(v.getVersion()); it.remove(); } } public Object getKey() { return toObject(key); } public Long[] getIndexes() { if (optionalInfo == null) return null; return getOptionalInfo().indexes; } public byte[] getIndexTypes() { if (optionalInfo == null) return null; return getOptionalInfo().indexTypes; } public void setIndexes(Long[] indexes, byte[] indexTypes) { if (indexes != null) { this.getOptionalInfo().indexes = indexes; this.getOptionalInfo().indexTypes = indexTypes; } } public boolean containsValue(Data value) { if (hasValueData()) { return this.getValueData().equals(value); } else if (getMultiValues() != null) { int count = getMultiValues().size(); if (count > 0) { return getMultiValues().contains(value); } } return false; } public boolean unlock(int threadId, Address address) { invalidateValueCache(); return lock == null || lock.unlock(address, threadId); } public boolean testLock(int threadId, Address address) { return lock == null || lock.testLock(threadId, address); } public boolean lock(int threadId, Address address) { invalidateValueCache(); if (lock == null) { lock = new DistributedLock(address, threadId); return true; } return lock.lock(address, threadId); } protected void invalidateValueCache() { } public void addScheduledAction(ScheduledAction scheduledAction) { if (getScheduledActions() == null) { setScheduledActions(new LinkedList<ScheduledAction>()); } getScheduledActions().add(scheduledAction); } public boolean isRemovable() { return !isActive() && valueCount() <= 0 && getLockCount() <= 0 && !hasListener() && (getScheduledActionCount() == 0) && getBackupOpCount() == 0; } public boolean isEvictable() { return (getLockCount() <= 0 && !hasListener() && (getScheduledActionCount() == 0)); } public boolean hasListener() { return (getListeners() != null && getListeners().size() > 0); } public void addListener(Address address, boolean returnValue) { if (getListeners() == null) setMapListeners(new ConcurrentHashMap<Address, Boolean>(1)); getListeners().put(address, returnValue); } public void removeListener(Address address) { if (getListeners() == null) return; getListeners().remove(address); } public void setLastUpdated() { if (expirationTime != Long.MAX_VALUE && expirationTime > 0) { long ttl = expirationTime - (lastUpdateTime > 0L ? lastUpdateTime : creationTime); setExpirationTime(ttl); } setLastUpdateTime(System.currentTimeMillis()); } public void setLastAccessed() { setLastAccessTime(System.currentTimeMillis()); incrementHits(); } public long getExpirationTime() { return expirationTime; } public long getRemainingTTL() { if (expirationTime == Long.MAX_VALUE) { return Long.MAX_VALUE; } else { long ttl = expirationTime - System.currentTimeMillis(); return (ttl < 0) ? 1 : ttl; } } public long getRemainingIdle() { if (maxIdleMillis == Long.MAX_VALUE) { return Long.MAX_VALUE; } else { long lastTouch = Math.max(lastAccessTime, creationTime); long idle = System.currentTimeMillis() - lastTouch; return maxIdleMillis - idle; } } public void setMaxIdle(long idle) { if (idle <= 0 || idle == Long.MAX_VALUE) { maxIdleMillis = Long.MAX_VALUE; } else { maxIdleMillis = idle; } } public void setExpirationTime(long ttl) { if (ttl <= 0 || ttl == Long.MAX_VALUE) { expirationTime = Long.MAX_VALUE; } else { expirationTime = System.currentTimeMillis() + ttl; } } public void setInvalid() { expirationTime = (System.currentTimeMillis() - 10); } public boolean isValid(long now) { if (expirationTime == Long.MAX_VALUE && maxIdleMillis == Long.MAX_VALUE) { return true; } long lastTouch = Math.max(lastAccessTime, creationTime); long idle = now - lastTouch; return expirationTime > now && (maxIdleMillis > idle); } public boolean isValid() { return active && isValid(System.currentTimeMillis()); } public void markRemoved() { setActive(false); setRemoveTime(System.currentTimeMillis()); } public void setActive() { setRemoveTime(0); setActive(true); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof AbstractRecord)) return false; Record record = (Record) o; return record.getId() == getId(); } public String toString() { return "Record key=" + getKeyData() + ", active=" + isActive() + ", version=" + getVersion() + ", removable=" + isRemovable(); } public long getVersion() { return version; } public void setVersion(long version) { this.version = version; } public void incrementVersion() { this.version++; } public long getCreationTime() { return creationTime; } public void setCreationTime(long newValue) { creationTime = newValue; } public long getLastAccessTime() { return lastAccessTime; } public void setLastAccessTime(long lastAccessTime) { this.lastAccessTime = lastAccessTime; } public long getLastUpdateTime() { return lastUpdateTime; } public void setLastUpdateTime(long lastUpdateTime) { this.lastUpdateTime = lastUpdateTime; } public int getHits() { return hits; } public void incrementHits() { hits++; } public void setActive(boolean active) { this.active = active; invalidateValueCache(); } public DistributedLock getLock() { return lock; } public void setLock(DistributedLock lock) { this.lock = lock; } public Collection<ValueHolder> getMultiValues() { if (optionalInfo == null) return null; return getOptionalInfo().lsMultiValues; } public void setMultiValues(Collection<ValueHolder> lsValues) { if (lsValues != null || optionalInfo != null) { this.getOptionalInfo().lsMultiValues = lsValues; } } public int getBackupOpCount() { if (optionalInfo == null) return 0; return (getOptionalInfo().backupOps == null) ? 0 : getOptionalInfo().backupOps.size(); } public SortedSet<VersionedBackupOp> getBackupOps() { return getOptionalInfo().backupOps; } public void setBackupOps(SortedSet<VersionedBackupOp> backupOps) { if (backupOps != null) { this.getOptionalInfo().backupOps = backupOps; } } public boolean isDirty() { return dirty; } public void setDirty(boolean dirty) { this.dirty = dirty; } public long getWriteTime() { return writeTime; } public void setWriteTime(long writeTime) { this.writeTime = writeTime; } public long getRemoveTime() { return removeTime; } public void setRemoveTime(long removeTime) { this.removeTime = removeTime; } public boolean hasScheduledAction() { return optionalInfo != null && optionalInfo.lsScheduledActions != null && optionalInfo.lsScheduledActions.size() > 0; } public List<ScheduledAction> getScheduledActions() { if (optionalInfo == null) return null; return getOptionalInfo().lsScheduledActions; } public void setScheduledActions(List<ScheduledAction> lsScheduledActions) { if (lsScheduledActions != null) { this.getOptionalInfo().lsScheduledActions = lsScheduledActions; } } public Map<Address, Boolean> getListeners() { if (optionalInfo == null) return null; return getOptionalInfo().mapListeners; } public void setMapListeners(Map<Address, Boolean> mapListeners) { if (mapListeners != null) { this.getOptionalInfo().mapListeners = mapListeners; } } public boolean isLocked() { return lock != null && lock.isLocked(); } public int getScheduledActionCount() { if (optionalInfo == null) return 0; return (getOptionalInfo().lsScheduledActions == null) ? 0 : getOptionalInfo().lsScheduledActions.size(); } public int getLockCount() { return (lock == null) ? 0 : lock.getLockCount(); } public void clearLock() { lock = null; } public Address getLockAddress() { return (lock == null) ? null : lock.getLockAddress(); } public OptionalInfo getOptionalInfo() { if (optionalInfo == null) { optionalInfo = new OptionalInfo(); } return optionalInfo; } public void setLastStoredTime(long lastStoredTime) { this.lastStoredTime = lastStoredTime; } public long getLastStoredTime() { return lastStoredTime; } public boolean isRemoved() { return !active && dirty && removeTime > 0; } /** * True if record is not removed (map.remove() ...) * and either is not active or not valid or has not value (may because of locking) */ public boolean isLoadable() { return !isRemoved() && (!isActive() || !isValid() || !hasValueData()); } class OptionalInfo { volatile Collection<ValueHolder> lsMultiValues = null; // multimap values Long[] indexes; // indexes of the current value; byte[] indexTypes; // index types of the current value; List<ScheduledAction> lsScheduledActions = null; SortedSet<VersionedBackupOp> backupOps = null; Map<Address, Boolean> mapListeners = null; } }