/* * 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.context.impl; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.Set; import org.infinispan.container.entries.gmu.InternalGMUCacheEntry; import org.infinispan.container.versioning.EntryVersion; import org.infinispan.container.versioning.VersionGenerator; import org.infinispan.context.Flag; import org.infinispan.context.InvocationContext; import org.infinispan.remoting.transport.Address; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Common features of transaction and invocation contexts * * @author Manik Surtani * @author Mircea.Markus@jboss.com * @author Pedro Ruivo * @author Sebastiano Peluso * @since 4.0 */ public abstract class AbstractInvocationContext implements InvocationContext { protected EnumSet<Flag> flags; // since this is finite, small, and strictly an internal API, it is cheaper/quicker to use bitmasking rather than // an EnumSet. protected byte contextFlags = 0; private Address origin; // Class loader associated with this invocation which supports AdvancedCache.with() functionality private ClassLoader classLoader; private Map<Object, InternalGMUCacheEntry> keysRead = null; private EntryVersion versionToRead; private boolean alreadyReadOnThisNode; // if this or any context subclass ever needs to store a boolean, always use a context flag instead. This is far // more space-efficient. Note that this value will be stored in a byte, which means up to 8 flags can be stored in // a single byte. Always start shifting with 0, the last shift cannot be greater than 7. protected enum ContextFlag { USE_FUTURE_RETURN_TYPE(1), // same as 1 << 0 ORIGIN_LOCAL(1 << 1); final byte mask; ContextFlag(int mask) { this.mask = (byte) mask; } } private String protocolId; /** * Tests whether a context flag is set. * * @param flag context flag to test * @return true if set, false otherwise. */ protected final boolean isContextFlagSet(ContextFlag flag) { return (contextFlags & flag.mask) != 0; } /** * Utility method that sets a given context flag. * * @param flag context flag to set */ protected final void setContextFlag(ContextFlag flag) { contextFlags |= flag.mask; } /** * Utility method that un-sets a context flag. * * @param flag context flag to unset */ protected final void unsetContextFlag(ContextFlag flag) { contextFlags &= ~flag.mask; } /** * Utility value that sets or un-sets a context flag based on a boolean passed in * * @param flag flag to set or unset * @param set if true, the context flag is set. If false, the context flag is unset. */ protected final void setContextFlag(ContextFlag flag, boolean set) { if (set) setContextFlag(flag); else unsetContextFlag(flag); } @Override public boolean hasFlag(Flag o) { return flags != null && flags.contains(o); } @Override public Set<Flag> getFlags() { return flags; } @Override public void setFlags(Flag... flags) { if (flags == null || flags.length == 0) return; if (this.flags == null) this.flags = EnumSet.copyOf(Arrays.asList(flags)); else this.flags.addAll(Arrays.asList(flags)); } @Override public void setFlags(Collection<Flag> flags) { if (flags == null || flags.isEmpty()) return; if (this.flags == null) this.flags = EnumSet.copyOf(flags); else this.flags.addAll(flags); } @Override public Address getOrigin() { return origin; } public void setOrigin(Address origin) { this.origin = origin; } @Override public void reset() { flags = null; contextFlags = 0; } public boolean isFlagsUninitialized() { return flags == null; } @Override public boolean hasLockedKey(Object key) { return getLockedKeys().contains(key); } @Override public boolean isUseFutureReturnType() { return isContextFlagSet(ContextFlag.USE_FUTURE_RETURN_TYPE); } @Override public void setUseFutureReturnType(boolean useFutureReturnType) { setContextFlag(ContextFlag.USE_FUTURE_RETURN_TYPE, useFutureReturnType); } @Override public AbstractInvocationContext clone() { try { AbstractInvocationContext dolly = (AbstractInvocationContext) super.clone(); if (flags != null) dolly.flags = flags.clone(); return dolly; } catch (CloneNotSupportedException e) { throw new IllegalStateException("Impossible!"); } } @Override public ClassLoader getClassLoader() { return classLoader; } @Override public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public String toString() { return getClass().getSimpleName() + "{" + "flags=" + flags + '}'; } public void addKeyReadInCommand(Object key, InternalGMUCacheEntry entry) { if (keysRead == null) { keysRead = new HashMap<Object, InternalGMUCacheEntry>(); } keysRead.put(key, entry); } @Override public void clearKeyReadInCommand() { keysRead = null; } @Override public Map<Object, InternalGMUCacheEntry> getKeysReadInCommand() { return keysRead == null ? Collections.<Object, InternalGMUCacheEntry>emptyMap() : keysRead; } @Override public EntryVersion calculateVersionToRead(VersionGenerator versionGenerator) { return versionToRead; } @Override public void setVersionToRead(EntryVersion entryVersion) { versionToRead = entryVersion; } @Override public boolean hasAlreadyReadOnThisNode() { return alreadyReadOnThisNode; } @Override public void setAlreadyReadOnThisNode(boolean value) { alreadyReadOnThisNode = value; } @Override public void setProtocolId(String protocolId) { this.protocolId = protocolId; } @Override public String getProtocolId() { return protocolId; } }