/* * (C) 2007-2012 Alibaba Group Holding Limited. * * 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. * Authors: * wuhua <wq163@163.com> , boyan <killme2008@gmail.com> */ package com.taobao.metamorphosis.utils; import java.nio.ByteBuffer; import java.util.List; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.MessageAccessor; import com.taobao.metamorphosis.exception.InvalidCheckSumException; import com.taobao.metamorphosis.exception.InvalidMessageException; import com.taobao.metamorphosis.network.ByteUtils; import com.taobao.metamorphosis.network.PutCommand; public final class MessageUtils { public final static class DecodedMessage { public final int newOffset; public final Message message; public final ByteBuffer buf; public DecodedMessage(final int newOffset, final Message message, final ByteBuffer buf) { super(); this.newOffset = newOffset; this.message = message; this.buf = buf; } } /** * ������Ϣbuffer��ʵ�ʴ洢�ڷ������Ľṹ���£� * <ul> * <li>message length(4 bytes),including attribute and payload</li> * <li>checksum(4 bytes)</li> * <li>message id(8 bytes)</li> * <li>message flag(4 bytes)</li> * <li>attribute length(4 bytes) + attribute,optional</li> * <li>payload</li> * </ul> * * @param req * @return */ public static final ByteBuffer makeMessageBuffer(final long msgId, final PutCommand req) { // message length + checksum + id +flag + data final ByteBuffer buffer = ByteBuffer.allocate(4 + 4 + 8 + 4 + req.getData().length); buffer.putInt(req.getData().length); int checkSum = CheckSum.crc32(req.getData()); // If client passes checksum,compare them if (req.getCheckSum() != -1) { if (checkSum != req.getCheckSum()) { throw new InvalidCheckSumException( "Checksum failure,message may be corrupted when transfering on networking."); } } buffer.putInt(checkSum); buffer.putLong(msgId); buffer.putInt(req.getFlag()); buffer.put(req.getData()); buffer.flip(); return buffer; } public static final ByteBuffer makeMessageBuffer(final List<Long> msgIds, final List<PutCommand> reqs) { if (msgIds == null || reqs == null) { throw new IllegalArgumentException("Null id list or request list"); } if (msgIds.size() != reqs.size()) { throw new IllegalArgumentException("id list is not adapte to request list"); } int capacity = 0; for (final PutCommand req : reqs) { capacity += 4 + 4 + 8 + 4 + req.getData().length; } final ByteBuffer buffer = ByteBuffer.allocate(capacity); for (int i = 0; i < reqs.size(); i++) { final PutCommand req = reqs.get(i); final long msgId = msgIds.get(i); buffer.putInt(req.getData().length); buffer.putInt(CheckSum.crc32(req.getData())); buffer.putLong(msgId); buffer.putInt(req.getFlag()); buffer.put(req.getData()); } buffer.flip(); return buffer; } /** * ��binary�����н����Ϣ * * @param topic * @param data * @param offset * @return * @throws InvalidMessageException */ public static final DecodedMessage decodeMessage(final String topic, final byte[] data, final int offset) throws InvalidMessageException { final ByteBuffer buf = ByteBuffer.wrap(data, offset, HEADER_LEN); final int msgLen = buf.getInt(); final int checksum = buf.getInt(); vailidateMessage(offset + HEADER_LEN, msgLen, checksum, data); final long id = buf.getLong(); // ȡflag final int flag = buf.getInt(); String attribute = null; int payLoadOffset = offset + HEADER_LEN; int payLoadLen = msgLen; if (payLoadLen > MAX_READ_BUFFER_SIZE) { throw new InvalidMessageException("Too much long payload length:" + payLoadLen); } // ��������ԣ���Ҫ�������� if (MessageFlagUtils.hasAttribute(flag)) { // ȡ4���ֽڵ����Գ��� final int attrLen = getInt(offset + HEADER_LEN, data); // ȡ��Ϣ���� final byte[] attrData = new byte[attrLen]; System.arraycopy(data, offset + HEADER_LEN + 4, attrData, 0, attrLen); attribute = ByteUtils.getString(attrData); // ����payloadOffset������4���ֽڵ���Ϣ���Ⱥ���Ϣ���ȱ��� payLoadOffset = offset + HEADER_LEN + 4 + attrLen; // payload���ȵݼ�����ȥ4���ֽڵ���Ϣ���Ⱥ���Ϣ���ȱ��� payLoadLen = msgLen - 4 - attrLen; } // ��ȡpayload final byte[] payload = new byte[payLoadLen]; System.arraycopy(data, payLoadOffset, payload, 0, payLoadLen); final Message msg = new Message(topic, payload); MessageAccessor.setFlag(msg, flag); msg.setAttribute(attribute); MessageAccessor.setId(msg, id); return new DecodedMessage(payLoadOffset + payLoadLen, msg, ByteBuffer.wrap(data, offset, payLoadOffset + payLoadLen - offset)); } /** * У��checksum * * @param msg * @param checksum */ public static final void vailidateMessage(final int offset, final int msgLen, final int checksum, final byte[] data) throws InvalidMessageException { if (checksum != CheckSum.crc32(data, offset, msgLen)) { throw new InvalidMessageException("Invalid message"); } } public static final int getInt(final int offset, final byte[] data) { return ByteBuffer.wrap(data, offset, 4).getInt(); } /** * 20���ֽڵ�ͷ�� * * <ul> * <li>message length(4 bytes),including attribute and payload</li> * <li>checksum(4 bytes)</li> * <li>message id(8 bytes)</li> * <li>message flag(4 bytes)</li> * </ul> */ public static final int HEADER_LEN = 20; public static final int MAX_READ_BUFFER_SIZE = Integer.parseInt(System.getProperty( "notify.remoting.max_read_buffer_size", "2097152")); /** * ����Ϣ���Ժ���Ϣpayload������ṹ���£�</br></br> 0����1������attribute + payload * * @param message * @return */ public final static byte[] encodePayload(final Message message) { final byte[] payload = message.getData(); final String attribute = message.getAttribute(); byte[] attrData = null; if (attribute != null) { attrData = ByteUtils.getBytes(attribute); } else { return payload; } final int attrLen = attrData == null ? 0 : attrData.length; final ByteBuffer buffer = ByteBuffer.allocate(4 + attrLen + payload.length); if (attribute != null) { buffer.putInt(attrLen); if (attrData != null) { buffer.put(attrData); } } buffer.put(payload); return buffer.array(); } }