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

com.gemstone.org.jgroups.Message.old Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
// $Id: Message.java,v 1.35 2005/07/15 05:27:21 belaban Exp $

package org.jgroups;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.stack.IpAddress;
import org.jgroups.util.ContextObjectInputStream;
import org.jgroups.util.Marshaller;
import org.jgroups.util.Streamable;
import org.jgroups.util.Util;

import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;


/**
 * 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 */ public class Message implements Externalizable, Streamable { protected Address dest_addr=null; protected Address src_addr=null; /** The payload */ private byte[] buf=null; /** The index into the payload (usually 0) */ protected transient int offset=0; /** The number of bytes in the buffer (usually buf.length is buf != null) */ protected transient int length=0; /** Map */ protected Map headers; protected static final Log log=LogFactory.getLog(Message.class); static final long serialVersionUID=-1137364035832847034L; static final byte DEST_SET=1; static final byte SRC_SET=2; static final byte BUF_SET=4; static final byte HDRS_SET=8; static final byte IPADDR_DEST=16; static final byte IPADDR_SRC=32; static final byte SRC_HOST_NULL=64; static final HashSet nonStreamableHeaders=new HashSet(); // todo: remove when all headers are streamable /** Public constructor * @param dest Address of receiver. If it is null or a string, then * it is sent to the group (either to current group or to the group as given * in the string). If it is a Vector, then it contains a number of addresses * to which it must be sent. Otherwise, it contains a single destination.

* Addresses are generally untyped (all are of type Object. A channel * instance must know what types of addresses it expects and downcast * accordingly. * @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) { dest_addr=dest; src_addr=src; setBuffer(buf); headers=createHeaders(5); } /** * 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. * @param dest Address of receiver. If it is null or a string, then * it is sent to the group (either to current group or to the group as given * in the string). If it is a Vector, then it contains a number of addresses * to which it must be sent. Otherwise, it contains a single destination.

* Addresses are generally untyped (all are of type Object. A channel * instance must know what types of addresses it expects and downcast * accordingly. * @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) { dest_addr=dest; src_addr=src; setBuffer(buf, offset, length); headers=createHeaders(5); } /** Public constructor * @param dest Address of receiver. If it is null or a string, then * it is sent to the group (either to current group or to the group as given * in the string). If it is a Vector, then it contains a number of addresses * to which it must be sent. Otherwise, it contains a single destination.

* Addresses are generally untyped (all are of type Object. A channel * instance must know what types of addresses it expects and downcast * accordingly. * @param src Address of sender * @param obj The object will be serialized into the byte buffer. Object * has to be serializable ! Note that 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(). */ public Message(Address dest, Address src, Serializable obj) { dest_addr=dest; src_addr=src; setObject(obj); headers=createHeaders(5); } /** Only used for Externalization (creating an initial object) */ public Message() { } // should not be called as normal constructor 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 */ 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; } } 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 */ 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 { offset=length=0; } } /** 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; } public Map getHeaders() { return headers; } public void setObject(Serializable obj) { if(obj == null) return; try { ByteArrayOutputStream out_stream=new ByteArrayOutputStream(); ObjectOutputStream out=new ObjectOutputStream(out_stream); out.writeObject(obj); setBuffer(out_stream.toByteArray()); } catch(IOException ex) { throw new IllegalArgumentException(ex.toString()); } } public Object getObject() { if(buf == null) return null; try { ByteArrayInputStream in_stream=new ByteArrayInputStream(buf, offset, length); // ObjectInputStream in=new ObjectInputStream(in_stream); ObjectInputStream in=new ContextObjectInputStream(in_stream); // put it back on norbert's request return in.readObject(); } catch(Exception ex) { throw new IllegalArgumentException(ex.toString()); } } /** * Nulls all fields of this message so that the message can be reused. Removes all headers from the * hashmap, but keeps the hashmap */ public void reset() { dest_addr=src_addr=null; setBuffer(null); headers.clear(); } /*---------------------- Used by protocol layers ----------------------*/ /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ public void putHeader(String key, Header hdr) { headers.put(key, hdr); } public Header removeHeader(String key) { return (Header)headers.remove(key); } public void removeHeaders() { headers.clear(); } public Header getHeader(String key) { return (Header)headers.get(key); } /*---------------------------------------------------------------------*/ 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 */ public Message copy(boolean copy_buffer) { Message retval=new Message(); retval.dest_addr=dest_addr; retval.src_addr=src_addr; 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, null, null); } public String toString() { StringBuffer ret=new StringBuffer(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); int size; if(headers != null && (size=headers.size()) > 0) ret.append(" (" + size + " headers)"); ret.append(", size = "); if(buf != null && length > 0) ret.append(length); else ret.append('0'); ret.append(" bytes"); ret.append(']'); return ret.toString(); } /** Tries to read an object from the message's buffer and prints it */ public String toStringAsObject() { Object obj; if(buf == null) return null; try { obj=getObject(); return obj != null ? obj.toString() : ""; } catch(Exception e) { // it is not an object return ""; } } /** * Returns size of buffer, plus some constant overhead for src and dest, plus number of headers time * some estimated size/header. The latter is needed because we don't want to marshal all headers just * to find out their size requirements. If a header implements Sizeable, the we can get the correct * size.

Size estimations don't have to be very accurate since this is mainly used by FRAG to * determine whether to fragment a message or not. Fragmentation will then serialize the message, * therefore getting the correct value. */ public long size() { long retval=Global.BYTE_SIZE // leading byte + length // buffer + (buf != null? Global.INT_SIZE : 0); // if buf != null 4 bytes for length if(dest_addr != null) retval+=dest_addr.size(); if(src_addr != null) retval+=(src_addr).size(); Map.Entry entry; String key; Header hdr; retval+=Global.INT_SIZE; // size (int) for(Iterator it=headers.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=(String)entry.getKey(); retval+=key.length() +2; // not the same as writeUTF(), but almost hdr=(Header)entry.getValue(); retval+=5; // 1 for presence of magic number, 4 for magic number retval+=hdr.size(); } return retval; } public String printObjectHeaders() { StringBuffer sb=new StringBuffer(); Map.Entry entry; if(headers != null) { for(Iterator it=headers.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); } } return sb.toString(); } /* ----------------------------------- Interface Externalizable ------------------------------- */ public void writeExternal(ObjectOutput out) throws IOException { int len; Externalizable hdr; Map.Entry entry; if(dest_addr != null) { out.writeBoolean(true); Marshaller.write(dest_addr, out); } else { out.writeBoolean(false); } if(src_addr != null) { out.writeBoolean(true); Marshaller.write(src_addr, out); } else { out.writeBoolean(false); } if(buf == null) out.writeInt(0); else { out.writeInt(length); out.write(buf, offset, length); } if(headers == null) out.writeInt(0); else { len=headers.size(); out.writeInt(len); for(Iterator it=headers.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); out.writeUTF((String)entry.getKey()); hdr=(Externalizable)entry.getValue(); Marshaller.write(hdr, out); } } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int len; boolean destAddressExist=in.readBoolean(); boolean srcAddressExist; Object key, value; if(destAddressExist) { dest_addr=(Address)Marshaller.read(in); } srcAddressExist=in.readBoolean(); if(srcAddressExist) { src_addr=(Address)Marshaller.read(in); } int i=in.readInt(); if(i != 0) { buf=new byte[i]; in.readFully(buf); offset=0; length=buf.length; } len=in.readInt(); if(len > 0) headers=createHeaders(len); while(len-- > 0) { key=in.readUTF(); value=Marshaller.read(in); headers.put(key, value); } } /* --------------------------------- End of Interface Externalizable ----------------------------- */ /* ----------------------------------- Interface Streamable ------------------------------- */ /** * Streams all members (dest and src addresses, buffer and headers to the output stream * @param outstream * @throws IOException */ public void writeTo(DataOutputStream out) throws IOException { Map.Entry entry; byte leading=0; if(dest_addr != null) { leading+=DEST_SET; if(dest_addr instanceof IpAddress) leading+=IPADDR_DEST; } if(src_addr != null) { leading+=SRC_SET; if(src_addr instanceof IpAddress) { leading+=IPADDR_SRC; if(((IpAddress)src_addr).getIpAddress() == null) { leading+=SRC_HOST_NULL; } } } if(buf != null) leading+=BUF_SET; if(headers != null && headers.size() > 0) leading+=HDRS_SET; // 1. write the leading byte first out.write(leading); // 2. dest_addr if(dest_addr != null) { if(dest_addr instanceof IpAddress) dest_addr.writeTo(out); else Util.writeAddress(dest_addr, out); } // 3. src_addr if(src_addr != null) { if(src_addr instanceof IpAddress) { src_addr.writeTo(out); } else { Util.writeAddress(src_addr, out); } } // 4. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 5. headers int size; if(headers != null && (size=headers.size()) > 0) { out.writeShort(size); for(Iterator it=headers.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); out.writeUTF((String)entry.getKey()); writeHeader((Header)entry.getValue(), out); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { int len, leading; String hdr_name; Header hdr; // 1. read the leading byte first leading=in.readByte(); // 1. dest_addr if((leading & DEST_SET) == DEST_SET) { if((leading & IPADDR_DEST) == IPADDR_DEST) { dest_addr=new IpAddress(); dest_addr.readFrom(in); } else { dest_addr=Util.readAddress(in); } } // 2. src_addr if((leading & SRC_SET) == SRC_SET) { if((leading & IPADDR_SRC) == IPADDR_SRC) { src_addr=new IpAddress(); src_addr.readFrom(in); } else { src_addr=Util.readAddress(in); } } // 3. buf if((leading & BUF_SET) == BUF_SET) { len=in.readInt(); buf=new byte[len]; in.read(buf, 0, len); length=len; } // 4. headers if((leading & HDRS_SET) == HDRS_SET) { len=in.readShort(); headers(len); for(int i=0; i < len; i++) { hdr_name=in.readUTF(); hdr=readHeader(in); headers.put(hdr_name, hdr); } } } /* --------------------------------- End of Interface Streamable ----------------------------- */ /* ----------------------------------- Private methods ------------------------------- */ private Map headers(int len) { return headers != null ? headers : (headers=createHeaders(len)); } private void writeHeader(Header value, DataOutputStream out) throws IOException { int magic_number; String classname; ObjectOutputStream oos=null; try { magic_number=ClassConfigurator.getInstance(false).getMagicNumber(value.getClass()); // write the magic number or the class name if(magic_number == -1) { out.writeBoolean(false); classname=value.getClass().getName(); out.writeUTF(classname); } else { out.writeBoolean(true); out.writeInt(magic_number); } // write the contents if(value instanceof Streamable) { ((Streamable)value).writeTo(out); } else { oos=new ObjectOutputStream(out); value.writeExternal(oos); if(!nonStreamableHeaders.contains(value.getClass())) { nonStreamableHeaders.add(value.getClass()); if(log.isTraceEnabled()) log.trace("encountered non-Streamable header: " + value.getClass()); } } } catch(ChannelException e) { log.error("failed writing the header", e); } finally { if(oos != null) oos.close(); } } private Header readHeader(DataInputStream in) throws IOException { Header hdr; boolean use_magic_number=in.readBoolean(); int magic_number; String classname; Class clazz; ObjectInputStream ois=null; try { if(use_magic_number) { magic_number=in.readInt(); clazz=ClassConfigurator.getInstance(false).get(magic_number); } else { classname=in.readUTF(); clazz=ClassConfigurator.getInstance(false).get(classname); } hdr=(Header)clazz.newInstance(); if(hdr instanceof Streamable) { ((Streamable)hdr).readFrom(in); } else { ois=new ObjectInputStream(in); hdr.readExternal(ois); } } catch(Exception ex) { throw new IOException("failed read header: " + ex.toString()); } finally { if(ois != null) ois.close(); } return hdr; } private Map createHeaders(int size) { return new HashMap(size); } private Map createHeaders(Map m) { return new HashMap(m); } /* ------------------------------- End of Private methods ---------------------------- */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy