/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.cache.tier.sockets; import org.apache.geode.SerializationException; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.Assert; import org.apache.geode.internal.HeapDataOutputStream; import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.TXManagerImpl; import org.apache.geode.internal.cache.tier.MessageType; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; import org.apache.geode.internal.logging.log4j.LocalizedMessage; import org.apache.geode.internal.offheap.StoredObject; import org.apache.geode.internal.offheap.annotations.Unretained; import org.apache.geode.internal.util.BlobHelper; import org.apache.logging.log4j.Logger; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * This class encapsulates the wire protocol. It provides accessors to encode and decode a message * and serialize it out to the wire. * * <PRE> * msgType - int - 4 bytes type of message, types enumerated below * * msgLength - int - 4 bytes total length of variable length payload * * numberOfParts - int - 4 bytes number of elements (LEN-BYTE* pairs) * contained in the payload. Message can * be a multi-part message * * transId - int - 4 bytes filled in by the requestor, copied back into * the response * * flags - byte- 1 byte filled in by the requestor * len1 * part1 * . * . * . * lenn * partn * </PRE> * * We read the fixed length 16 bytes into a byte[] and populate a bytebuffer We read the fixed * length header tokens from the header parse the header and use information contained in there to * read the payload. * * <P> * * See also <a href="package-summary.html#messages">package description</a>. * * @see org.apache.geode.internal.cache.tier.MessageType * */ public class Message { public static final int DEFAULT_MAX_MESSAGE_SIZE = 1073741824; /** * maximum size of an outgoing message. See GEODE-478 */ public static int MAX_MESSAGE_SIZE = Integer.getInteger(DistributionConfig.GEMFIRE_PREFIX + "client.max-message-size", DEFAULT_MAX_MESSAGE_SIZE).intValue(); private static final Logger logger = LogService.getLogger(); private static final int PART_HEADER_SIZE = 5; // 4 bytes for length, 1 byte for isObject private static final int FIXED_LENGTH = 17; private static final ThreadLocal<ByteBuffer> tlCommBuffer = new ThreadLocal<>(); private static final byte[] TRUE; private static final byte[] FALSE; static { try { HeapDataOutputStream hdos = new HeapDataOutputStream(10, null); BlobHelper.serializeTo(Boolean.TRUE, hdos); TRUE = hdos.toByteArray(); } catch (Exception e) { throw new IllegalStateException(e); } try { HeapDataOutputStream hdos = new HeapDataOutputStream(10, null); BlobHelper.serializeTo(Boolean.FALSE, hdos); FALSE = hdos.toByteArray(); } catch (Exception e) { throw new IllegalStateException(e); } } protected int msgType; protected int payloadLength = 0; protected int numberOfParts = 0; protected int transactionId = TXManagerImpl.NOTX; protected int currentPart = 0; protected Part[] partsList = null; protected ByteBuffer cachedCommBuffer; protected Socket socket = null; protected SocketChannel sockCh = null; protected OutputStream os = null; protected InputStream is = null; protected boolean messageModified = true; /** is this message a retry of a previously sent message? */ protected boolean isRetry; private byte flags = 0x00; protected MessageStats msgStats = null; protected ServerConnection sc = null; private int maxIncomingMessageLength = -1; private Semaphore dataLimiter = null; // private int MAX_MSGS = -1; private Semaphore msgLimiter = null; private boolean hdrRead = false; private int chunkSize = 1024;// Default Chunk Size. protected Part securePart = null; private boolean isMetaRegion = false; // These two statics are fields shoved into the flags byte for transmission. // The MESSAGE_IS_RETRY bit is stripped out during deserialization but the other // is left in place public static final byte MESSAGE_HAS_SECURE_PART = (byte) 0x02; public static final byte MESSAGE_IS_RETRY = (byte) 0x04; public static final byte MESSAGE_IS_RETRY_MASK = (byte) 0xFB; // Tentative workaround to avoid OOM stated in #46754. public static final ThreadLocal<Integer> messageType = new ThreadLocal<Integer>(); Version version; /** * Creates a new message with the given number of parts */ public Message(int numberOfParts, Version destVersion) { this.version = destVersion; Assert.assertTrue(destVersion != null, "Attempt to create an unversioned message"); partsList = new Part[numberOfParts]; this.numberOfParts = numberOfParts; for (int i = 0; i < partsList.length; i++) { partsList[i] = new Part(); } } public boolean isSecureMode() { return securePart != null; } public byte[] getSecureBytes() throws IOException, ClassNotFoundException { return (byte[]) this.securePart.getObject(); } public void setMessageType(int msgType) { this.messageModified = true; if (!MessageType.validate(msgType)) { throw new IllegalArgumentException( LocalizedStrings.Message_INVALID_MESSAGETYPE.toLocalizedString()); } this.msgType = msgType; } public void setVersion(Version clientVersion) { this.version = clientVersion; } public void setMessageHasSecurePartFlag() { this.flags = (byte) (this.flags | MESSAGE_HAS_SECURE_PART); } public void clearMessageHasSecurePartFlag() { this.flags = (byte) (this.flags & MESSAGE_HAS_SECURE_PART); } /** * Sets and builds the {@link Part}s that are sent in the payload of the Message * * @param numberOfParts */ public void setNumberOfParts(int numberOfParts) { // hitesh: need to add security header here from server // need to insure it is not chunked message // should we look message type to avoid internal message like ping this.messageModified = true; this.currentPart = 0; this.numberOfParts = numberOfParts; if (numberOfParts > this.partsList.length) { Part[] newPartsList = new Part[numberOfParts]; for (int i = 0; i < numberOfParts; i++) { if (i < this.partsList.length) { newPartsList[i] = this.partsList[i]; } else { newPartsList[i] = new Part(); } } this.partsList = newPartsList; } } /** * For boundary testing we may need to inject mock parts * * @param parts */ void setParts(Part[] parts) { this.partsList = parts; } public void setTransactionId(int transactionId) { this.messageModified = true; this.transactionId = transactionId; } public void setIsRetry() { this.isRetry = true; } /** * This returns true if the message has been marked as having been previously transmitted to a * different server. */ public boolean isRetry() { return this.isRetry; } /* Sets size for HDOS chunk. */ public void setChunkSize(int chunkSize) { this.chunkSize = chunkSize; } /** * When building a Message this will return the number of the next Part to be added to the message */ public int getNextPartNumber() { return this.currentPart; } public void addStringPart(String str) { addStringPart(str, false); } private static final Map<String, byte[]> CACHED_STRINGS = new ConcurrentHashMap<String, byte[]>(); public void addStringPart(String str, boolean enableCaching) { if (str == null) { addRawPart((byte[]) null, false); } else { Part part = partsList[this.currentPart]; if (enableCaching) { byte[] bytes = CACHED_STRINGS.get(str); if (bytes == null) { HeapDataOutputStream hdos = new HeapDataOutputStream(str); bytes = hdos.toByteArray(); CACHED_STRINGS.put(str, bytes); } part.setPartState(bytes, false); } else { HeapDataOutputStream hdos = new HeapDataOutputStream(str); this.messageModified = true; part.setPartState(hdos, false); } this.currentPart++; } } /* * Adds a new part to this message that contains a <code>byte</code> array (as opposed to a * serialized object). * * @see #addPart(byte[], boolean) */ public void addBytesPart(byte[] newPart) { addRawPart(newPart, false); } public void addStringOrObjPart(Object o) { if (o instanceof String || o == null) { addStringPart((String) o); } else { // Note even if o is a byte[] we need to serialize it. // This could be cleaned up but it would require C client code to change. serializeAndAddPart(o, false); } } public void addDeltaPart(HeapDataOutputStream hdos) { this.messageModified = true; Part part = partsList[this.currentPart]; part.setPartState(hdos, false); this.currentPart++; } public void addObjPart(Object o) { addObjPart(o, false); } /** * Like addObjPart(Object) but also prefers to reference objects in the part instead of copying * them into a byte buffer. */ public void addObjPartNoCopying(Object o) { if (o == null || o instanceof byte[]) { addRawPart((byte[]) o, false); } else { serializeAndAddPartNoCopying(o); } } public void addObjPart(Object o, boolean zipValues) { if (o == null || o instanceof byte[]) { addRawPart((byte[]) o, false); } else if (o instanceof Boolean) { addRawPart((Boolean) o ? TRUE : FALSE, true); } else { serializeAndAddPart(o, zipValues); } } public void addPartInAnyForm(@Unretained Object o, boolean isObject) { if (o == null) { addRawPart((byte[]) o, false); } else if (o instanceof byte[]) { addRawPart((byte[]) o, isObject); } else if (o instanceof StoredObject) { // It is possible it is an off-heap StoredObject that contains a simple non-object byte[]. this.messageModified = true; Part part = partsList[this.currentPart]; part.setPartState((StoredObject) o, isObject); this.currentPart++; } else { serializeAndAddPart(o, false); } } private void serializeAndAddPartNoCopying(Object o) { HeapDataOutputStream hdos; Version v = version; if (version.equals(Version.CURRENT)) { v = null; } // create the HDOS with a flag telling it that it can keep any byte[] or ByteBuffers/ByteSources // passed to it. hdos = new HeapDataOutputStream(chunkSize, v, true); try { BlobHelper.serializeTo(o, hdos); } catch (IOException ex) { throw new SerializationException("failed serializing object", ex); } this.messageModified = true; Part part = partsList[this.currentPart]; part.setPartState(hdos, true); this.currentPart++; } private void serializeAndAddPart(Object o, boolean zipValues) { if (zipValues) { throw new UnsupportedOperationException("zipValues no longer supported"); } else { HeapDataOutputStream hdos; Version v = version; if (version.equals(Version.CURRENT)) { v = null; } hdos = new HeapDataOutputStream(chunkSize, v); try { BlobHelper.serializeTo(o, hdos); } catch (IOException ex) { throw new SerializationException("failed serializing object", ex); } this.messageModified = true; Part part = partsList[this.currentPart]; part.setPartState(hdos, true); this.currentPart++; } } public void addIntPart(int v) { this.messageModified = true; Part part = partsList[this.currentPart]; part.setInt(v); this.currentPart++; } public void addLongPart(long v) { this.messageModified = true; Part part = partsList[this.currentPart]; part.setLong(v); this.currentPart++; } /** * Adds a new part to this message that may contain a serialized object. */ public void addRawPart(byte[] newPart, boolean isObject) { this.messageModified = true; Part part = partsList[this.currentPart]; part.setPartState(newPart, isObject); this.currentPart++; } public int getMessageType() { return this.msgType; } public int getPayloadLength() { return this.payloadLength; } public int getHeaderLength() { return FIXED_LENGTH; } public int getNumberOfParts() { return this.numberOfParts; } public int getTransactionId() { return this.transactionId; } public Part getPart(int index) { if (index < this.numberOfParts) { Part p = partsList[index]; if (this.version != null) { p.setVersion(this.version); } return p; } return null; } public static ByteBuffer setTLCommBuffer(ByteBuffer bb) { ByteBuffer result = tlCommBuffer.get(); tlCommBuffer.set(bb); return result; } public ByteBuffer getCommBuffer() { if (this.cachedCommBuffer != null) { return this.cachedCommBuffer; } else { return tlCommBuffer.get(); } } public void clear() { this.isRetry = false; int len = this.payloadLength; if (len != 0) { this.payloadLength = 0; } if (this.hdrRead) { if (this.msgStats != null) { this.msgStats.decMessagesBeingReceived(len); } } ByteBuffer buffer = getCommBuffer(); if (buffer != null) { buffer.clear(); } clearParts(); if (len != 0 && this.dataLimiter != null) { this.dataLimiter.release(len); this.dataLimiter = null; this.maxIncomingMessageLength = 0; } if (this.hdrRead) { if (this.msgLimiter != null) { this.msgLimiter.release(1); this.msgLimiter = null; } this.hdrRead = false; } this.flags = 0; } protected void packHeaderInfoForSending(int msgLen, boolean isSecurityHeader) { // hitesh: setting second bit of flags byte for client // this is not require but this makes all changes easily at client side right now // just see this bit and process security header byte flagsByte = this.flags; if (isSecurityHeader) { flagsByte |= MESSAGE_HAS_SECURE_PART; } if (this.isRetry) { flagsByte |= MESSAGE_IS_RETRY; } getCommBuffer().putInt(this.msgType).putInt(msgLen).putInt(this.numberOfParts) .putInt(this.transactionId).put(flagsByte); } protected Part getSecurityPart() { if (this.sc != null) { // look types right put get etc return this.sc.updateAndGetSecurityPart(); } return null; } public void setSecurePart(byte[] bytes) { this.securePart = new Part(); this.securePart.setPartState(bytes, false); } public void setMetaRegion(boolean isMetaRegion) { this.isMetaRegion = isMetaRegion; } public boolean getAndResetIsMetaRegion() { boolean isMetaRegion = this.isMetaRegion; this.isMetaRegion = false; return isMetaRegion; } /** * Sends this message out on its socket. */ protected void sendBytes(boolean clearMessage) throws IOException { if (this.sc != null) { // Keep track of the fact that we are making progress. this.sc.updateProcessingMessage(); } if (this.socket == null) { throw new IOException(LocalizedStrings.Message_DEAD_CONNECTION.toLocalizedString()); } try { final ByteBuffer cb = getCommBuffer(); if (cb == null) { throw new IOException("No buffer"); } int msgLen = 0; synchronized (cb) { long totalPartLen = 0; long headerLen = 0; int partsToTransmit = this.numberOfParts; for (int i = 0; i < this.numberOfParts; i++) { Part part = this.partsList[i]; headerLen += PART_HEADER_SIZE; totalPartLen += part.getLength(); } Part securityPart = this.getSecurityPart(); if (securityPart == null) { securityPart = this.securePart; } if (securityPart != null) { headerLen += PART_HEADER_SIZE; totalPartLen += securityPart.getLength(); partsToTransmit++; } if ((headerLen + totalPartLen) > Integer.MAX_VALUE) { throw new MessageTooLargeException( "Message size (" + (headerLen + totalPartLen) + ") exceeds maximum integer value"); } msgLen = (int) (headerLen + totalPartLen); if (msgLen > MAX_MESSAGE_SIZE) { throw new MessageTooLargeException("Message size (" + msgLen + ") exceeds gemfire.client.max-message-size setting (" + MAX_MESSAGE_SIZE + ")"); } cb.clear(); packHeaderInfoForSending(msgLen, (securityPart != null)); for (int i = 0; i < partsToTransmit; i++) { Part part = (i == this.numberOfParts) ? securityPart : partsList[i]; if (cb.remaining() < PART_HEADER_SIZE) { flushBuffer(); } int partLen = part.getLength(); cb.putInt(partLen); cb.put(part.getTypeCode()); if (partLen <= cb.remaining()) { part.writeTo(cb); } else { flushBuffer(); if (this.sockCh != null) { part.writeTo(this.sockCh, cb); } else { part.writeTo(this.os, cb); } if (this.msgStats != null) { this.msgStats.incSentBytes(partLen); } } } if (cb.position() != 0) { flushBuffer(); } this.messageModified = false; if (this.sockCh == null) { this.os.flush(); } } } finally { if (clearMessage) { clearParts(); } } } protected void flushBuffer() throws IOException { final ByteBuffer cb = getCommBuffer(); if (this.sockCh != null) { cb.flip(); do { this.sockCh.write(cb); } while (cb.remaining() > 0); } else { this.os.write(cb.array(), 0, cb.position()); } if (this.msgStats != null) { this.msgStats.incSentBytes(cb.position()); } cb.clear(); } private void read() throws IOException { clearParts(); // TODO:Hitesh ??? for server changes make sure sc is not null as this class also used by client // :( readHeaderAndPayload(); } /** * Read the actual bytes of the header off the socket */ protected final void fetchHeader() throws IOException { final ByteBuffer cb = getCommBuffer(); cb.clear(); // msgType is invalidated here and can be used as an indicator // of problems reading the message this.msgType = MessageType.INVALID; int hdr = 0; final int headerLength = getHeaderLength(); if (this.sockCh != null) { cb.limit(headerLength); do { int bytesRead = this.sockCh.read(cb); // System.out.println("DEBUG: fetchHeader read " + bytesRead + " bytes commBuffer=" + cb); if (bytesRead == -1) { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_THE_HEADER .toLocalizedString()); } if (this.msgStats != null) { this.msgStats.incReceivedBytes(bytesRead); } } while (cb.remaining() > 0); cb.flip(); } else { do { int bytesRead = -1; bytesRead = this.is.read(cb.array(), hdr, headerLength - hdr); if (bytesRead == -1) { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_THE_HEADER .toLocalizedString()); } hdr += bytesRead; if (this.msgStats != null) { this.msgStats.incReceivedBytes(bytesRead); } } while (hdr < headerLength); // now setup the commBuffer for the caller to parse it cb.rewind(); } } private void readHeaderAndPayload() throws IOException { fetchHeader(); final ByteBuffer cb = getCommBuffer(); final int type = cb.getInt(); final int len = cb.getInt(); final int numParts = cb.getInt(); final int txid = cb.getInt(); byte bits = cb.get(); cb.clear(); if (!MessageType.validate(type)) { throw new IOException(LocalizedStrings.Message_INVALID_MESSAGE_TYPE_0_WHILE_READING_HEADER .toLocalizedString(Integer.valueOf(type))); } int timeToWait = 0; if (this.sc != null) { // Keep track of the fact that a message is being processed. this.sc.setProcessingMessage(); timeToWait = sc.getClientReadTimeout(); } this.hdrRead = true; if (this.msgLimiter != null) { for (;;) { this.sc.getCachedRegionHelper().checkCancelInProgress(null); boolean interrupted = Thread.interrupted(); try { if (timeToWait == 0) { this.msgLimiter.acquire(1); } else { if (!this.msgLimiter.tryAcquire(1, timeToWait, TimeUnit.MILLISECONDS)) { if (this.msgStats != null && this.msgStats instanceof CacheServerStats) { ((CacheServerStats) this.msgStats).incConnectionsTimedOut(); } throw new IOException( LocalizedStrings.Message_OPERATION_TIMED_OUT_ON_SERVER_WAITING_ON_CONCURRENT_MESSAGE_LIMITER_AFTER_WAITING_0_MILLISECONDS .toLocalizedString(Integer.valueOf(timeToWait))); } } break; } catch (InterruptedException e) { interrupted = true; } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } // for } if (len > 0) { if (this.maxIncomingMessageLength > 0 && len > this.maxIncomingMessageLength) { throw new IOException(LocalizedStrings.Message_MESSAGE_SIZE_0_EXCEEDED_MAX_LIMIT_OF_1 .toLocalizedString(new Object[] {Integer.valueOf(len), Integer.valueOf(this.maxIncomingMessageLength)})); } if (this.dataLimiter != null) { for (;;) { if (sc != null) { this.sc.getCachedRegionHelper().checkCancelInProgress(null); } boolean interrupted = Thread.interrupted(); try { if (timeToWait == 0) { this.dataLimiter.acquire(len); } else { int newTimeToWait = timeToWait; if (this.msgLimiter != null) { // may have waited for msg limit so recalc time to wait newTimeToWait -= (int) sc.getCurrentMessageProcessingTime(); } if (newTimeToWait <= 0 || !this.msgLimiter.tryAcquire(1, newTimeToWait, TimeUnit.MILLISECONDS)) { throw new IOException( LocalizedStrings.Message_OPERATION_TIMED_OUT_ON_SERVER_WAITING_ON_CONCURRENT_DATA_LIMITER_AFTER_WAITING_0_MILLISECONDS .toLocalizedString(timeToWait)); } } this.payloadLength = len; // makes sure payloadLength gets set now so we will release // the semaphore break; // success } catch (InterruptedException e) { interrupted = true; } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } } } if (this.msgStats != null) { this.msgStats.incMessagesBeingReceived(len); this.payloadLength = len; // makes sure payloadLength gets set now so we will dec on clear } this.isRetry = (bits & MESSAGE_IS_RETRY) != 0; bits = (byte) (bits & MESSAGE_IS_RETRY_MASK); this.flags = bits; this.msgType = type; readPayloadFields(numParts, len); // Set the header and payload fields only after receiving all the // socket data, providing better message consistency in the face // of exceptional conditions (e.g. IO problems, timeouts etc.) this.payloadLength = len; // this.numberOfParts = numParts; Already set in setPayloadFields via setNumberOfParts this.transactionId = txid; this.flags = bits; if (this.sc != null) { // Keep track of the fact that a message is being processed. this.sc.updateProcessingMessage(); } } protected void readPayloadFields(final int numParts, final int len) throws IOException { if (len > 0 && numParts <= 0 || len <= 0 && numParts > 0) { throw new IOException( LocalizedStrings.Message_PART_LENGTH_0_AND_NUMBER_OF_PARTS_1_INCONSISTENT .toLocalizedString(new Object[] {Integer.valueOf(len), Integer.valueOf(numParts)})); } Integer msgType = messageType.get(); if (msgType != null && msgType == MessageType.PING) { messageType.set(null); // set it to null right away. int pingParts = 10; // Some number which will not throw OOM but still be acceptable for a ping // operation. if (numParts > pingParts) { throw new IOException("Part length ( " + numParts + " ) is inconsistent for " + MessageType.getString(msgType) + " operation."); } } setNumberOfParts(numParts); if (numParts <= 0) return; if (len < 0) { logger.info(LocalizedMessage.create(LocalizedStrings.Message_RPL_NEG_LEN__0, len)); throw new IOException(LocalizedStrings.Message_DEAD_CONNECTION.toLocalizedString()); } final ByteBuffer cb = getCommBuffer(); cb.clear(); cb.flip(); int readSecurePart = 0; readSecurePart = checkAndSetSecurityPart(); int bytesRemaining = len; for (int i = 0; ((i < numParts + readSecurePart) || ((readSecurePart == 1) && (cb.remaining() > 0))); i++) { int bytesReadThisTime = readPartChunk(bytesRemaining); bytesRemaining -= bytesReadThisTime; Part part; if (i < numParts) { part = this.partsList[i]; } else { part = this.securePart; } int partLen = cb.getInt(); byte partType = cb.get(); byte[] partBytes = null; if (partLen > 0) { partBytes = new byte[partLen]; int alreadyReadBytes = cb.remaining(); if (alreadyReadBytes > 0) { if (partLen < alreadyReadBytes) { alreadyReadBytes = partLen; } cb.get(partBytes, 0, alreadyReadBytes); } // now we need to read partLen - alreadyReadBytes off the wire int off = alreadyReadBytes; int remaining = partLen - off; while (remaining > 0) { if (this.sockCh != null) { int bytesThisTime = remaining; cb.clear(); if (bytesThisTime > cb.capacity()) { bytesThisTime = cb.capacity(); } cb.limit(bytesThisTime); int res = this.sockCh.read(cb); if (res != -1) { cb.flip(); bytesRemaining -= res; remaining -= res; cb.get(partBytes, off, res); off += res; if (this.msgStats != null) { this.msgStats.incReceivedBytes(res); } } else { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_A_PART .toLocalizedString()); } } else { int res = 0; res = this.is.read(partBytes, off, remaining); if (res != -1) { bytesRemaining -= res; remaining -= res; off += res; if (this.msgStats != null) { this.msgStats.incReceivedBytes(res); } } else { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_A_PART .toLocalizedString()); } } } } part.init(partBytes, partType); } } protected int checkAndSetSecurityPart() { if ((this.flags | MESSAGE_HAS_SECURE_PART) == this.flags) { this.securePart = new Part(); return 1; } else { this.securePart = null; return 0; } } /** * @param bytesRemaining the most bytes we can read * @return the number of bytes read into commBuffer */ private int readPartChunk(int bytesRemaining) throws IOException { final ByteBuffer cb = getCommBuffer(); if (cb.remaining() >= PART_HEADER_SIZE) { // we already have the next part header in commBuffer so just return return 0; } if (cb.position() != 0) { cb.compact(); } else { cb.position(cb.limit()); cb.limit(cb.capacity()); } int bytesRead = 0; if (this.sc != null) { // Keep track of the fact that we are making progress this.sc.updateProcessingMessage(); } if (this.sockCh != null) { int remaining = cb.remaining(); if (remaining > bytesRemaining) { remaining = bytesRemaining; cb.limit(cb.position() + bytesRemaining); } while (remaining > 0) { int res = this.sockCh.read(cb); if (res != -1) { remaining -= res; bytesRead += res; if (this.msgStats != null) { this.msgStats.incReceivedBytes(res); } } else { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_THE_PAYLOAD .toLocalizedString()); } } } else { int bufSpace = cb.capacity() - cb.position(); int bytesToRead = bufSpace; if (bytesRemaining < bytesToRead) { bytesToRead = bytesRemaining; } int pos = cb.position(); while (bytesToRead > 0) { int res = 0; res = this.is.read(cb.array(), pos, bytesToRead); if (res != -1) { bytesToRead -= res; pos += res; bytesRead += res; if (this.msgStats != null) { this.msgStats.incReceivedBytes(res); } } else { throw new EOFException( LocalizedStrings.Message_THE_CONNECTION_HAS_BEEN_RESET_WHILE_READING_THE_PAYLOAD .toLocalizedString()); } } cb.position(pos); } cb.flip(); return bytesRead; } /** * Gets rid of all the parts that have been added to this message. */ public void clearParts() { for (int i = 0; i < partsList.length; i++) { partsList[i].clear(); } this.currentPart = 0; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("type=").append(MessageType.getString(msgType)); sb.append("; payloadLength=").append(payloadLength); sb.append("; numberOfParts=").append(numberOfParts); sb.append("; transactionId=").append(transactionId); sb.append("; currentPart=").append(currentPart); sb.append("; messageModified=").append(messageModified); sb.append("; flags=").append(Integer.toHexString(flags)); for (int i = 0; i < numberOfParts; i++) { sb.append("; part[").append(i).append("]={"); sb.append(this.partsList[i].toString()); sb.append("}"); } return sb.toString(); } public void setComms(ServerConnection sc, Socket socket, ByteBuffer bb, MessageStats msgStats) throws IOException { this.sc = sc; setComms(socket, bb, msgStats); } public void setComms(Socket socket, ByteBuffer bb, MessageStats msgStats) throws IOException { this.sockCh = socket.getChannel(); if (this.sockCh == null) { setComms(socket, socket.getInputStream(), socket.getOutputStream(), bb, msgStats); } else { setComms(socket, null, null, bb, msgStats); } } public void setComms(Socket socket, InputStream is, OutputStream os, ByteBuffer bb, MessageStats msgStats) throws IOException { Assert.assertTrue(socket != null); this.socket = socket; this.sockCh = socket.getChannel(); this.is = is; this.os = os; this.cachedCommBuffer = bb; this.msgStats = msgStats; } /** * Undo any state changes done by setComms. * * @since GemFire 5.7 */ public void unsetComms() { this.socket = null; this.sockCh = null; this.is = null; this.os = null; this.cachedCommBuffer = null; this.msgStats = null; } /** * Sends this message to its receiver over its setOutputStream?? output stream. */ public void send() throws IOException { send(true); } public void send(ServerConnection servConn) throws IOException { if (this.sc != servConn) throw new IllegalStateException("this.sc was not correctly set"); send(true); } /** * Sends this message to its receiver over its setOutputStream?? output stream. */ public void send(boolean clearMessage) throws IOException { sendBytes(clearMessage); } /** * Populates the stats of this <code>Message</code> with information received via its socket */ public void recv() throws IOException { if (this.socket != null) { synchronized (getCommBuffer()) { read(); } } else { throw new IOException(LocalizedStrings.Message_DEAD_CONNECTION.toLocalizedString()); } } public void recv(ServerConnection sc, int maxMessageLength, Semaphore dataLimiter, Semaphore msgLimiter) throws IOException { this.sc = sc; this.maxIncomingMessageLength = maxMessageLength; this.dataLimiter = dataLimiter; this.msgLimiter = msgLimiter; recv(); } }