/* * INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa * Copyright 2013 INESC-ID 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 3.0 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.commands.remote; import org.infinispan.commands.read.GetKeyValueCommand; import org.infinispan.container.entries.InternalCacheValue; import org.infinispan.container.entries.gmu.InternalGMUCacheEntry; import org.infinispan.container.versioning.VersionGenerator; import org.infinispan.container.versioning.gmu.GMUVersion; import org.infinispan.container.versioning.gmu.GMUVersionGenerator; import org.infinispan.context.Flag; import org.infinispan.context.InvocationContext; import org.infinispan.dataplacement.ClusterSnapshot; import org.infinispan.executors.ConditionalExecutorService; import org.infinispan.executors.ConditionalRunnable; import org.infinispan.remoting.transport.Address; import org.infinispan.transaction.gmu.CommitLog; import org.infinispan.transaction.gmu.VersionNotAvailableException; import org.infinispan.transaction.xa.GlobalTransaction; import org.jgroups.blocks.RequestHandler; import java.util.BitSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import static org.infinispan.transaction.gmu.GMUHelper.toGMUVersionGenerator; /** * Issues a remote get call. This is not a {@link org.infinispan.commands.VisitableCommand} and hence not passed up the * {@link org.infinispan.interceptors.base.CommandInterceptor} chain. * <p/> * * @author Pedro Ruivo * @author Sebastiano Peluso * @since 5.2 */ public class GMUClusteredGetCommand extends ClusteredGetCommand implements ConditionalRunnable { public static final byte COMMAND_ID = 32; //the transaction version. from this version and with the bit set, it calculates the max and min version to read private GMUVersion transactionVersion; private BitSet alreadyReadFrom; private CommitLog commitLog; private GMUVersionGenerator versionGenerator; private ConditionalExecutorService executorService; //calculated in the target node private GMUVersion maxGMUVersion; //calculated in the target node private GMUVersion minGMUVersion; //calculated in the target node private boolean alreadyReadOnThisNode; public GMUClusteredGetCommand(String cacheName) { super(cacheName); } public GMUClusteredGetCommand(Object key, String cacheName, Set<Flag> flags, boolean acquireRemoteLock, GlobalTransaction globalTransaction, GMUVersion txVersion, BitSet alreadyReadFrom) { super(key, cacheName, flags, acquireRemoteLock, globalTransaction); this.transactionVersion = txVersion; this.alreadyReadFrom = alreadyReadFrom == null || alreadyReadFrom.isEmpty() ? null : alreadyReadFrom; } public void initializeGMUComponents(CommitLog commitLog, ConditionalExecutorService executorService, VersionGenerator versionGenerator) { this.commitLog = commitLog; this.versionGenerator = toGMUVersionGenerator(versionGenerator); this.executorService = executorService; } @Override public Object perform(InvocationContext context) throws Throwable { initLocalVersions(); executorService.execute(this); return RequestHandler.DO_NOT_REPLY; } @Override public boolean isReady() { return minGMUVersion == null || alreadyReadOnThisNode || commitLog.tryWaitForVersion(minGMUVersion); } @Override public void run() { try { Object retVal = GMUClusteredGetCommand.super.perform(null); GMUClusteredGetCommand.this.sendReply(retVal, false); } catch (Throwable throwable) { GMUClusteredGetCommand.this.sendReply(throwable, true); } } @Override public byte getCommandId() { return COMMAND_ID; } @Override public Object[] getParameters() { Object[] original = super.getParameters(); Object[] retVal = new Object[original.length + 3]; System.arraycopy(original, 0, retVal, 0, original.length); int index = original.length; retVal[index++] = transactionVersion; retVal[index] = alreadyReadFrom; return retVal; } @Override public void setParameters(int commandId, Object[] args) { int index = args.length - 3; super.setParameters(commandId, args); transactionVersion = (GMUVersion) args[index++]; alreadyReadFrom = (BitSet) args[index]; } @Override public String toString() { return "GMUClusteredGetCommand{key=" + getKey() + ", flags=" + getFlags() + ", transactionVersion=" + transactionVersion + ", alreadyReadFrom=" + alreadyReadFrom + "}"; } @Override protected InvocationContext createInvocationContext(GetKeyValueCommand command) { InvocationContext context = super.createInvocationContext(command); context.setAlreadyReadOnThisNode(alreadyReadOnThisNode); context.setVersionToRead(commitLog.getAvailableVersionLessThan(maxGMUVersion)); return context; } @Override protected InternalCacheValue invoke(GetKeyValueCommand command, InvocationContext context) { super.invoke(command, context); InternalGMUCacheEntry gmuCacheEntry = context.getKeysReadInCommand().get(getKey()); if (gmuCacheEntry == null) { throw new VersionNotAvailableException(); } return gmuCacheEntry.toInternalCacheValue(); } private void initLocalVersions() { if (alreadyReadFrom != null) { int txViewId = transactionVersion.getViewId(); ClusterSnapshot clusterSnapshot = versionGenerator.getClusterSnapshot(txViewId); List<Address> addressList = new LinkedList<Address>(); for (int i = 0; i < clusterSnapshot.size(); ++i) { if (alreadyReadFrom.get(i)) { addressList.add(clusterSnapshot.get(i)); } } minGMUVersion = versionGenerator.calculateMinVersionToRead(transactionVersion, addressList); maxGMUVersion = versionGenerator.calculateMaxVersionToRead(transactionVersion, addressList); int myIndex = clusterSnapshot.indexOf(versionGenerator.getAddress()); //to be safe, is better to wait... alreadyReadOnThisNode = myIndex != -1 && alreadyReadFrom.get(myIndex); } else { minGMUVersion = transactionVersion; maxGMUVersion = null; alreadyReadOnThisNode = false; } } }