/* * Copyright 2011 Red Hat, Inc. and/or its affiliates. * * 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ package org.infinispan.marshall.exts; import org.infinispan.commands.RemoveCacheCommand; import org.infinispan.commands.control.CacheViewControlCommand; import org.infinispan.commands.control.LockControlCommand; import org.infinispan.commands.control.StateTransferControlCommand; import org.infinispan.commands.read.MapReduceCommand; import org.infinispan.commands.remote.CacheRpcCommand; import org.infinispan.commands.remote.ClusteredGetCommand; import org.infinispan.commands.remote.ConfigurationStateCommand; import org.infinispan.commands.remote.DataPlacementCommand; import org.infinispan.commands.remote.GMUClusteredGetCommand; import org.infinispan.commands.remote.GarbageCollectorControlCommand; import org.infinispan.commands.remote.MultipleRpcCommand; import org.infinispan.commands.remote.ReconfigurableProtocolCommand; import org.infinispan.commands.remote.SingleRpcCommand; import org.infinispan.commands.remote.recovery.CompleteTransactionCommand; import org.infinispan.commands.remote.recovery.GetInDoubtTransactionsCommand; import org.infinispan.commands.remote.recovery.GetInDoubtTxInfoCommand; import org.infinispan.commands.remote.recovery.TxCompletionNotificationCommand; import org.infinispan.commands.tx.CommitCommand; import org.infinispan.commands.tx.GMUCommitCommand; import org.infinispan.commands.tx.GMUPrepareCommand; import org.infinispan.commands.tx.PrepareCommand; import org.infinispan.commands.tx.RollbackCommand; import org.infinispan.commands.tx.VersionedCommitCommand; import org.infinispan.commands.tx.VersionedPrepareCommand; import org.infinispan.factories.ComponentRegistry; import org.infinispan.factories.GlobalComponentRegistry; import org.infinispan.factories.KnownComponentNames; import org.infinispan.io.ExposedByteArrayOutputStream; import org.infinispan.io.UnsignedNumeric; import org.infinispan.marshall.AbstractExternalizer; import org.infinispan.marshall.BufferSizePredictor; import org.infinispan.marshall.BufferSizePredictorFactory; import org.infinispan.marshall.Ids; import org.infinispan.marshall.StreamingMarshaller; import org.infinispan.marshall.jboss.ExtendedRiverUnmarshaller; import org.infinispan.util.Util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Set; /** * Externalizer in charge of marshalling cache specific commands. At read time, * this marshaller is able to locate the right cache marshaller and provide * it any externalizers implementations that follow. * * @author Galder ZamarreƱo * @since 5.1 */ public final class CacheRpcCommandExternalizer extends AbstractExternalizer<CacheRpcCommand> { private final GlobalComponentRegistry gcr; private final ReplicableCommandExternalizer cmdExt; private final StreamingMarshaller globalMarshaller; public CacheRpcCommandExternalizer(GlobalComponentRegistry gcr, ReplicableCommandExternalizer cmdExt) { this.cmdExt = cmdExt; this.gcr = gcr; //Cache this locally to avoid having to look it up often: this.globalMarshaller = gcr.getComponent(StreamingMarshaller.class, KnownComponentNames.GLOBAL_MARSHALLER); } @Override public Set<Class<? extends CacheRpcCommand>> getTypeClasses() { Set<Class<? extends CacheRpcCommand>> coreCommands = Util.asSet( MapReduceCommand.class, LockControlCommand.class, StateTransferControlCommand.class, ClusteredGetCommand.class, MultipleRpcCommand.class, SingleRpcCommand.class, CommitCommand.class, PrepareCommand.class, RollbackCommand.class, RemoveCacheCommand.class, TxCompletionNotificationCommand.class, GetInDoubtTransactionsCommand.class, GetInDoubtTxInfoCommand.class, CompleteTransactionCommand.class, CacheViewControlCommand.class, VersionedPrepareCommand.class, VersionedCommitCommand.class, DataPlacementCommand.class, GMUPrepareCommand.class, GMUCommitCommand.class, GMUClusteredGetCommand.class, ReconfigurableProtocolCommand.class, GarbageCollectorControlCommand.class, ConfigurationStateCommand.class); // Only interested in cache specific replicable commands coreCommands.addAll(gcr.getModuleProperties().moduleCacheRpcCommands()); return coreCommands; } @Override public void writeObject(ObjectOutput output, CacheRpcCommand command) throws IOException { cmdExt.writeCommandHeader(output, command); String cacheName = command.getCacheName(); output.writeUTF(cacheName); ComponentRegistry registry = gcr.getNamedComponentRegistry(cacheName); StreamingMarshaller marshaller; if (registry == null) { // TODO This is a hack to support global commands CacheViewControlCommand // but they should not be CacheRpcCommands at all marshaller = globalMarshaller; } else { marshaller = registry.getCacheMarshaller(); } // Take the cache marshaller and generate the payload for the rest of // the command using that cache marshaller and the write the bytes in // the original payload. ExposedByteArrayOutputStream os = marshallParameters(command, marshaller); UnsignedNumeric.writeUnsignedInt(output, os.size()); // Do not rely on the raw buffer's lenght which is likely to be much longer! output.write(os.getRawBuffer(), 0, os.size()); } private ExposedByteArrayOutputStream marshallParameters( CacheRpcCommand cmd, StreamingMarshaller marshaller) throws IOException { BufferSizePredictor sizePredictor = BufferSizePredictorFactory.getBufferSizePredictor(); int estimatedSize = sizePredictor.nextSize(cmd); ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(estimatedSize); ObjectOutput output = marshaller.startObjectOutput(baos, true, estimatedSize); try { cmdExt.writeCommandParameters(output, cmd); } finally { marshaller.finishObjectOutput(output); } return baos; } @Override public CacheRpcCommand readObject(ObjectInput input) throws IOException, ClassNotFoundException { byte type = input.readByte(); byte methodId = (byte) input.readShort(); String cacheName = input.readUTF(); ComponentRegistry registry = gcr.getNamedComponentRegistry(cacheName); StreamingMarshaller marshaller; if (registry == null) { // Even though the command is directed at a cache, it could happen // that the cache is not yet started, so fallback on global marshaller. marshaller = globalMarshaller; } else { marshaller = registry.getCacheMarshaller(); } byte[] paramsRaw = new byte[UnsignedNumeric.readUnsignedInt(input)]; // This is not ideal cos it forces the code to read all parameters into // memory and then splitting them, potentially leading to excessive // buffering. An alternative solution is shown in SharedStreamMultiMarshallerTest // but it requires some special treatment - iow, hacking :) input.readFully(paramsRaw); ByteArrayInputStream is = new ByteArrayInputStream(paramsRaw, 0, paramsRaw.length); ObjectInput paramsInput = marshaller.startObjectInput(is, true); // Not ideal, but the alternative (without changing API), would have been // using thread locals which are expensive to retrieve. // Remember that the aim with externalizers is for them to be stateless. if (paramsInput instanceof ExtendedRiverUnmarshaller) ((ExtendedRiverUnmarshaller) paramsInput).setInfinispanMarshaller(marshaller); try { Object[] args = cmdExt.readParameters(paramsInput); return cmdExt.fromStream(methodId, args, type, cacheName); } catch (IOException e) { throw e; } finally { marshaller.finishObjectInput(paramsInput); } } @Override public Integer getId() { return Ids.CACHE_RPC_COMMAND; } }