All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bboss.org.jgroups.Message Maven / Gradle / Ivy

The newest version!

package bboss.org.jgroups;


import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Map;

import org.frameworkset.spi.remote.RPCMessage;
import org.frameworkset.spi.security.SecurityContext;
import bboss.org.jgroups.conf.ClassConfigurator;
import bboss.org.jgroups.logging.Log;
import bboss.org.jgroups.logging.LogFactory;
import bboss.org.jgroups.util.Buffer;
import bboss.org.jgroups.util.Headers;
import bboss.org.jgroups.util.Streamable;
import bboss.org.jgroups.util.Util;


/**
 * A Message encapsulates data sent to members of a group. It contains among other things the
 * address of the sender, the destination address, a payload (byte buffer) and a list of
 * headers. Headers are added by protocols on the sender side and removed by protocols
 * on the receiver's side.
 * 

* The byte buffer can point to a reference, and we can subset it using index and length. However, * when the message is serialized, we only write the bytes between index and length. * @author Bela Ban * @version $Id: Message.java,v 1.115 2010/04/07 15:34:29 belaban Exp $ */ public class Message implements Streamable { protected Address dest_addr; protected Address src_addr; /** The payload */ private byte[] buf; /** The index into the payload (usually 0) */ protected int offset; /** The number of bytes in the buffer (usually buf.length is buf not equal to null). */ protected int length; /** All headers are placed here */ protected Headers headers; /** * added by biaoping.yin */ protected org.frameworkset.spi.remote.Headers bbossHeaders; private volatile byte flags; private volatile byte transient_flags; // transient_flags is neither marshalled nor copied protected static final Log log=LogFactory.getLog(Message.class); static final byte DEST_SET = 1; static final byte SRC_SET = 2; static final byte BUF_SET = 4; // static final byte HDRS_SET=8; // bela July 15 2005: not needed, we always create headers // =============================== Flags ==================================== public static final byte OOB = 1 << 0; public static final byte LOOPBACK = 1 << 1; // if message was sent to self public static final byte DONT_BUNDLE = 1 << 2; // don't bundle message at the transport public static final byte NO_FC = 1 << 3; // bypass flow control public static final byte SCOPED = 1 << 4; // when a message has a scope // =========================== Transient flags ============================== public static final byte OOB_DELIVERED = 1 << 0; // OOB which has already been delivered up the stack //UPDATE ENCRYPT MESSAGE BODY FLAG protected boolean encrypt = false; /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

*/ public Message(Address dest) { setDest(dest); headers=createHeaders(3); } /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param buf Message to be sent. Note that this buffer must not be modified (e.g. buf[0]=0 is * not allowed), since we don't copy the contents on clopy() or clone(). */ public Message(Address dest, Address src, byte[] buf) { this(dest); setSrc(src); setBuffer(buf); } /** * Constructs a message. The index and length parameters allow to provide a reference to * a byte buffer, rather than a copy, and refer to a subset of the buffer. This is important when * we want to avoid copying. When the message is serialized, only the subset is serialized.
* * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would * retransmit a changed byte[] buffer ! * * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param buf A reference to a byte buffer * @param offset The index into the byte buffer * @param length The number of bytes to be used from buf. Both index and length are checked for * array index violations and an ArrayIndexOutOfBoundsException will be thrown if invalid */ public Message(Address dest, Address src, byte[] buf, int offset, int length) { this(dest); setSrc(src); setBuffer(buf, offset, length); } /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param obj The object will be serialized into the byte buffer. Object * has to be serializable ! The resulting buffer must not be modified * (e.g. buf[0]=0 is not allowed), since we don't copy the contents on clopy() or clone().

* Note that this is a convenience method and JGroups will use default Java serialization to * serialize obj into a byte buffer. */ public Message(Address dest, Address src, Serializable obj) { this(dest); setSrc(src); setObject(obj); } public Message() { headers=createHeaders(3); } public Message(boolean create_headers) { if(create_headers) headers=createHeaders(3); } public Address getDest() { return dest_addr; } public void setDest(Address new_dest) { dest_addr=new_dest; } public Address getSrc() { return src_addr; } public void setSrc(Address new_src) { src_addr=new_src; } /** * Returns a reference to the payload (byte buffer). Note that this buffer should not be modified as * we do not copy the buffer on copy() or clone(): the buffer of the copied message is simply a reference to * the old buffer.
* Even if offset and length are used: we return the entire buffer, not a subset. */ public byte[] getRawBuffer() { return buf; } /** * Returns a copy of the buffer if offset and length are used, otherwise a reference. * @return byte array with a copy of the buffer. */ final public byte[] getBuffer() { if(buf == null) return null; if(offset == 0 && length == buf.length) return buf; else { byte[] retval=new byte[length]; System.arraycopy(buf, offset, retval, 0, length); return retval; } } final public void setBuffer(byte[] b) { buf=b; if(buf != null) { offset=0; length=buf.length; } else { offset=length=0; } } /** * Set the internal buffer to point to a subset of a given buffer * @param b The reference to a given buffer. If null, we'll reset the buffer to null * @param offset The initial position * @param length The number of bytes */ final public void setBuffer(byte[] b, int offset, int length) { buf=b; if(buf != null) { if(offset < 0 || offset > buf.length) throw new ArrayIndexOutOfBoundsException(offset); if((offset + length) > buf.length) throw new ArrayIndexOutOfBoundsException((offset+length)); this.offset=offset; this.length=length; } else { this.offset=this.length=0; } } /** * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would * retransmit a changed byte[] buffer ! * */ public final void setBuffer(Buffer buf) { if(buf != null) { this.buf=buf.getBuf(); this.offset=buf.getOffset(); this.length=buf.getLength(); } } /** Returns the offset into the buffer at which the data starts */ public int getOffset() { return offset; } /** Returns the number of bytes in the buffer */ public int getLength() { return length; } /** Returns a reference to the headers hashmap, which is immutable. Any attempt to * modify the returned map will cause a runtime exception */ public Map getHeaders() { return headers.getHeaders(); } public String printHeaders() { return headers.printHeaders(); } public int getNumHeaders() { return headers.size(); } /** * Takes an object and uses Java serialization to generate the byte[] buffer which is set in the message. */ final public void setObject(Serializable obj) { if(obj == null) return; try { byte[] tmp=Util.objectToByteBuffer(obj); setBuffer(tmp); } catch(Exception ex) { throw new IllegalArgumentException(ex); } } private transient byte[] encryptbuf ; /** * Uses Java serialization to create an object from the buffer of the message. Note that this is dangerous when * using your own classloader, e.g. inside of an application server ! Most likely, JGroups will use the system * classloader to deserialize the buffer into an object, whereas (for example) a web application will want to * use the webapp's classloader, resulting in a ClassCastException. The recommended way is for the application to * use their own serialization and only pass byte[] buffer to JGroups. * @return */ final public Object getObject() { try { //UPDATE DECRYPT MESSAGE BODY byte[] buffer = getBuffer(); int offset = getOffset(); int length = getLength(); if(isEncrypt() ) { if(encryptbuf == null) { encryptbuf = SecurityContext.getSecurityManager().decode(buffer); offset = 0; length = buffer.length; } } else { encryptbuf = buffer; } return Util.objectFromByteBuffer(encryptbuf, offset, length); //return Util.objectFromByteBuffer(buf, offset, length); } catch(Exception ex) { throw new IllegalArgumentException(ex); } } public void setFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); flags |= flag; } public void clearFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); flags &= ~flag; } public boolean isFlagSet(byte flag) { return isFlagSet(flags, flag); } /** * Same as {@link #setFlag(byte)} but transient flags are never marshalled * @param flag */ public void setTransientFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); transient_flags |= flag; } /** * Atomically checks if a given flag is set and - if not - sets it. When multiple threads concurrently call this * method with the same flag, only one of them will be able to set the flag * @param flag * @return True if the flag could be set, false if not (was already set) */ public boolean setTransientFlagIfAbsent(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); synchronized(this) { if(isTransientFlagSet(flag)) return false; else setTransientFlag(flag); return true; } } public void clearTransientFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); transient_flags &= ~flag; } public boolean isTransientFlagSet(byte flag) { return isFlagSet(transient_flags, flag); } protected static boolean isFlagSet(byte flags, byte flag) { return (flags & flag) == flag; } public byte getFlags() { return flags; } public byte getTransientFlags() { return transient_flags; } public void setScope(short scope) { Util.setScope(this, scope); } public short getScope() { return Util.getScope(this); } /*---------------------- Used by protocol layers ----------------------*/ /** Puts a header given an ID into the hashmap. Overwrites potential existing entry. */ public void putHeader(short id, Header hdr) { if(id < 0) throw new IllegalArgumentException("An ID of " + id + " is invalid"); headers.putHeader(id, hdr); } /** * Puts a header given a key into the map, only if the key doesn't exist yet * @param id * @param hdr * @return the previous value associated with the specified key, or null if there was no mapping for the key. * (A null return can also indicate that the map previously associated null with the key, * if the implementation supports null values.) */ public Header putHeaderIfAbsent(short id, Header hdr) { if(id < 0) throw new IllegalArgumentException("An ID of " + id + " is invalid"); return headers.putHeaderIfAbsent(id, hdr); } /** * * @param key * @return the header associated with key * @deprecated Use getHeader() instead. The issue with removing a header is described in * http://jira.jboss.com/jira/browse/JGRP-393 */ public Header removeHeader(short id) { return getHeader(id); } public Header getHeader(short id) { if(id < 0) throw new IllegalArgumentException("An ID of " + id + " is invalid"); return headers.getHeader(id); } /*---------------------------------------------------------------------*/ public Message copy() { return copy(true); } /** * Create a copy of the message. If offset and length are used (to refer to another buffer), the copy will * contain only the subset offset and length point to, copying the subset into the new copy. * @param copy_buffer * @return Message with specified data */ public Message copy(boolean copy_buffer) { Message retval=new Message(false); retval.dest_addr=dest_addr; retval.src_addr=src_addr; retval.flags=flags; //UPDATE ENCRYPT MESSAGE BODY FLAG retval.encrypt = this.encrypt; retval.bbossHeaders = bbossHeaders; if(copy_buffer && buf != null) { // change bela Feb 26 2004: we don't resolve the reference retval.setBuffer(buf, offset, length); } retval.headers=createHeaders(headers); return retval; } protected Object clone() throws CloneNotSupportedException { return copy(); } public Message makeReply() { return new Message(src_addr); } public String toString() { StringBuilder ret=new StringBuilder(64); ret.append("[dst: "); if(dest_addr == null) ret.append(""); else ret.append(dest_addr); ret.append(", src: "); if(src_addr == null) ret.append(""); else ret.append(src_addr); ret.append(",encrypt:").append(this.isEncrypt()); int size; if((size=getNumHeaders()) > 0) ret.append(" (").append(size).append(" headers)"); ret.append(", size="); if(buf != null && length > 0) ret.append(length); else ret.append('0'); ret.append(" bytes"); if(flags > 0) ret.append(", flags=").append(flagsToString(flags)); if(transient_flags > 0) ret.append(", transient_flags=" + transientFlagsToString(transient_flags)); ret.append(']'); return ret.toString(); } /** Tries to read an object from the message's buffer and prints it */ public String toStringAsObject() { if(buf == null) return null; try { Object obj=getObject(); return obj != null ? obj.toString() : ""; } catch(Exception e) { // it is not an object return ""; } } public String printObjectHeaders() { return headers.printObjectHeaders(); } /* ----------------------------------- Interface Streamable ------------------------------- */ /** * Streams all members (dest and src addresses, buffer and headers) to the output stream. * @param out * @throws IOException */ public void writeTo(DataOutputStream out) throws IOException { byte leading=0; if(dest_addr != null) leading=Util.setFlag(leading, DEST_SET); if(src_addr != null) leading=Util.setFlag(leading, SRC_SET); if(buf != null) leading=Util.setFlag(leading, BUF_SET); // 1. write the leading byte first out.write(leading); // 2. the flags (e.g. OOB, LOW_PRIO) out.write(flags); //UPDATE SERIALIZABLE ENCRYPT FLAG //added by biaoping.yin out.writeBoolean(this.encrypt); if(this.bbossHeaders != null) { out.writeInt( 1); bbossHeaders.writeTo(out); } else { out.writeInt(0); } // 3. dest_addr if(dest_addr != null) Util.writeAddress(dest_addr, out); // 4. src_addr if(src_addr != null) Util.writeAddress(src_addr, out); // 5. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 6. headers int size=headers.size(); out.writeShort(size); final short[] ids=headers.getRawIDs(); final Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { out.writeShort(ids[i]); writeHeader(hdrs[i], out); } } } /** * Writes the message to the output stream, but excludes the dest and src addresses unless the src address given * as argument is different from the message's src address * @param src * @param out * @throws IOException */ public void writeToNoAddrs(Address src, DataOutputStream out) throws IOException { byte leading=0; boolean write_src_addr=src == null || src_addr != null && !src_addr.equals(src); if(write_src_addr) leading=Util.setFlag(leading, SRC_SET); if(buf != null) leading=Util.setFlag(leading, BUF_SET); // 1. write the leading byte first out.write(leading); // 2. the flags (e.g. OOB, LOW_PRIO) out.write(flags); //UPDATE SERIALIZABLE ENCRYPT FLAG //added by biaoping.yin out.writeBoolean(this.encrypt); if(this.bbossHeaders != null) { out.writeInt( 1); bbossHeaders.writeTo(out); } else { out.writeInt(0); } // 4. src_addr if(write_src_addr) Util.writeAddress(src_addr, out); // 5. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 6. headers int size=headers.size(); out.writeShort(size); final short[] ids=headers.getRawIDs(); final Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < ids.length; i++) { if(ids[i] > 0) { out.writeShort(ids[i]); writeHeader(hdrs[i], out); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { // 1. read the leading byte first byte leading=in.readByte(); // 2. the flags flags=in.readByte(); //UPDATE DESERIALIZABLE ENCRYPT FLAG this.encrypt = in.readBoolean(); int headset = in.readInt(); if(headset > 0) { ObjectInputStream in_ = null; ByteArrayInputStream bin = null; int size = in.readInt(); byte[] bytes = new byte[size]; try { in.read(bytes); bin = new ByteArrayInputStream(bytes); in_ = new ObjectInputStream(bin); try { this.bbossHeaders = (org.frameworkset.spi.remote.Headers) in_ .readObject(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } finally { try { if (bin != null) bin.close(); } catch (Exception e2) { // TODO: handle exception } try { if (in_ != null) in_.close(); } catch (Exception e2) { // TODO: handle exception } } } // 3. dest_addr if(Util.isFlagSet(leading, DEST_SET)) dest_addr=Util.readAddress(in); // 4. src_addr if(Util.isFlagSet(leading, SRC_SET)) src_addr=Util.readAddress(in); // 5. buf if(Util.isFlagSet(leading, BUF_SET)) { int len=in.readInt(); buf=new byte[len]; in.readFully(buf, 0, len); length=len; } // 6. headers int len=in.readShort(); headers=createHeaders(len); short[] ids=headers.getRawIDs(); Header[] hdrs=headers.getRawHeaders(); for(int i=0; i < len; i++) { short id=in.readShort(); Header hdr=readHeader(in); ids[i]=id; hdrs[i]=hdr; } } /* --------------------------------- End of Interface Streamable ----------------------------- */ /** * Returns the exact size of the marshalled message. Uses method size() of each header to compute the size, so if * a Header subclass doesn't implement size() we will use an approximation. However, most relevant header subclasses * have size() implemented correctly. (See bboss.org.jgroups.tests.SizeTest). * @return The number of bytes for the marshalled message */ public long size() { long retval=Global.BYTE_SIZE // leading byte + Global.BYTE_SIZE; // flags if(dest_addr != null) retval+=Util.size(dest_addr); if(src_addr != null) retval+=Util.size(src_addr); if(buf != null) retval+=Global.INT_SIZE // length (integer) + length; // number of bytes in the buffer retval+=Global.SHORT_SIZE; // number of headers retval+=headers.marshalledSize(); return retval; } /* ----------------------------------- Private methods ------------------------------- */ public static String flagsToString(byte flags) { StringBuilder sb=new StringBuilder(); boolean first=true; if(isFlagSet(flags, OOB)) { first=false; sb.append("OOB"); } if(isFlagSet(flags, LOOPBACK)) { if(!first) sb.append("|"); else first=false; sb.append("LOOPBACK"); } if(isFlagSet(flags, DONT_BUNDLE)) { if(!first) sb.append("|"); else first=false; sb.append("DONT_BUNDLE"); } if(isFlagSet(flags, NO_FC)) { if(!first) sb.append("|"); else first=false; sb.append("NO_FC"); } if(isFlagSet(flags, SCOPED)) { if(!first) sb.append("|"); else first=false; sb.append("SCOPED"); } return sb.toString(); } public static String transientFlagsToString(byte flags) { StringBuilder sb=new StringBuilder(); boolean first=true; if(isFlagSet(flags, OOB_DELIVERED)) { if(!first) sb.append("|"); else first=false; sb.append("OOB_DELIVERED"); } return sb.toString(); } private static void writeHeader(Header hdr, DataOutputStream out) throws IOException { short magic_number=ClassConfigurator.getMagicNumber(hdr.getClass()); out.writeShort(magic_number); hdr.writeTo(out); } private static Header readHeader(DataInputStream in) throws IOException { try { short magic_number=in.readShort(); Class clazz=ClassConfigurator.get(magic_number); if(clazz == null) throw new IllegalArgumentException("magic number " + magic_number + " is not available in magic map"); Header hdr=(Header)clazz.newInstance(); hdr.readFrom(in); return hdr; } catch(Exception ex) { IOException io_ex=new IOException("failed reading header"); io_ex.initCause(ex); throw io_ex; } } private static Headers createHeaders(int size) { return size > 0? new Headers(size) : new Headers(3); } private static Headers createHeaders(Headers m) { return new Headers(m); } /* ------------------------------- End of Private methods ---------------------------- */ //UPDATE ENCRYPT MESSAGE BODY FLAG public boolean isEncrypt() { return encrypt; } //UPDATE ENCRYPT MESSAGE BODY FLAG public void setEncrypt(boolean encrypt) { this.encrypt = encrypt; } public org.frameworkset.spi.remote.Headers getBbossHeaders() { return bbossHeaders; } public void setBbossHeaders(org.frameworkset.spi.remote.Headers bbossHeaders) { if(bbossHeaders != null && bbossHeaders.size() > 0) { if(this.bbossHeaders == null) this.bbossHeaders = RPCMessage.createHeaders(4); this.bbossHeaders.putAll(bbossHeaders); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy