View Javadoc

1   /*
2    * Copyright (c) 2005 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   * 
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.messages;
30  
31  import org.apache.log4j.Logger;
32  
33  import org.apache.mina.common.ByteBuffer;
34  
35  import org.apache.mina.protocol.ProtocolViolationException;
36  import org.apache.mina.protocol.ProtocolEncoderOutput;
37  
38  import uk.ac.rdg.resc.jstyx.types.DirEntry;
39  import uk.ac.rdg.resc.jstyx.StyxUtils;
40  
41  /***
42   * Message returned by the server in response to a TreadMessage
43   *
44   * @author Jon Blower
45   * $Revision: 507 $
46   * $Date: 2005-12-01 08:21:56 +0000 (Thu, 01 Dec 2005) $
47   * $Log$
48   * Revision 1.18  2005/12/01 08:21:56  jonblower
49   * Fixed javadoc comments
50   *
51   * Revision 1.17  2005/11/03 21:47:17  jonblower
52   * getElements() and toFriendlyString() now use getData()
53   *
54   * Revision 1.16  2005/11/03 17:09:27  jonblower
55   * Created more efficient RreadMessage that involves less copying of buffers (still reliable)
56   *
57   * Revision 1.15  2005/11/03 16:02:54  jonblower
58   * Created simplified version (less efficient, more reliable)
59   *
60   * Revision 1.13  2005/09/02 16:52:38  jonblower
61   * Fixed bugs that caused message payload to be printed as empty string
62   *
63   * Revision 1.11  2005/05/10 19:17:54  jonblower
64   * Added dispose() method
65   *
66   * Revision 1.10  2005/05/10 08:02:06  jonblower
67   * Changes related to implementing MonitoredFileOnDisk
68   *
69   * Revision 1.9  2005/03/22 17:48:27  jonblower
70   * Removed debug code that tracked ByteBuffer allocation
71   *
72   * Revision 1.8  2005/03/21 17:57:10  jonblower
73   * Trying to fix ByteBuffer leak in SGS server
74   *
75   * Revision 1.7  2005/03/18 13:56:00  jonblower
76   * Improved freeing of ByteBuffers, and bug fixes
77   *
78   * Revision 1.6  2005/03/16 22:16:43  jonblower
79   * Added Styx Grid Service classes to core module
80   *
81   * Revision 1.5  2005/03/16 17:56:22  jonblower
82   * Replaced use of java.nio.ByteBuffer with MINA's ByteBuffer to minimise copying of buffers
83   *
84   * Revision 1.4  2005/03/15 09:01:48  jonblower
85   * Message type now stored as short, not int
86   *
87   * Revision 1.3  2005/03/11 14:02:15  jonblower
88   * Merged MINA-Test_20059309 into main line of development
89   *
90   * Revision 1.2.2.2  2005/03/11 12:30:45  jonblower
91   * Changed so that message payloads are always ints, not longs
92   *
93   * Revision 1.2.2.1  2005/03/10 11:50:59  jonblower
94   * Changed to fit with MINA framework
95   *
96   * Revision 1.2  2005/02/24 07:44:43  jonblower
97   * Added getFriendlyString()
98   *
99   * Revision 1.1.1.1  2005/02/16 18:58:27  jonblower
100  * Initial import
101  *
102  */
103 public class RreadMessage extends StyxMessage
104 {
105 
106     private static final Logger log = Logger.getLogger(RreadMessage.class);
107     
108     private ByteBuffer data; // Contains the data
109     private int pos; // Position of the first byte of data in the buffer
110     private int count; // Number of data bytes in the buffer
111     
112     /*** 
113      * Creates a new RversionMessage 
114      * @param length The total length of the message (including all header info)
115      * @param type The type of the message (a number between 100 and 127)
116      * @param tag The tag that identifies this message
117      */
118     public RreadMessage(int length, short type, int tag)
119     {
120         super(length, type, tag);
121         this.name = "Rread";
122     }
123     
124     /***
125      * Creates an RreadMessage from the given byte array
126      */
127     public RreadMessage(byte[] bytes)
128     {
129         this(bytes, 0, bytes.length);
130     }
131     
132     /***
133      * Creates an RreadMessage from the given byte array. This will return
134      * <code>count</code> bytes, starting at position <code>pos</code> in the
135      * given array
136      * @throws IllegalArgumentException if <code>pos + count > bytes.length</code>
137      */
138     public RreadMessage(byte[] bytes, int pos, int count)
139     {
140         this(0, (short)117, 0); // We'll set the length and tag later
141         if (pos + count > bytes.length)
142         {
143             throw new IllegalArgumentException("Not enough bytes in the given byte array:" +
144                 " pos = " + pos + ", count = " + count + ", length = " + bytes.length);
145         }
146         this.data = ByteBuffer.wrap(bytes, pos, count);
147         this.pos = pos;
148         this.count = count;
149         this.length = StyxUtils.HEADER_LENGTH + 4 + this.count;
150         if (log.isDebugEnabled())
151         {
152             log.debug("Created RreadMessage from array with " + bytes.length
153             + " bytes, pos = " + pos + ", count = " + count + ", length  = " + this.length);
154         }
155     }
156     
157     /***
158      * Creates an RreadMessage from the given org.apache.mina.common.ByteBuffer.
159      * The position and limit of the buffer must be set correctly.  This method
160      * will not acquire() or release() the buffer: the buffer will be released
161      * automatically when the data are written to the network.  Users of this
162      * constructor therefore should not release the buffer after using this
163      * constructor otherwise the data will no longer be valid.
164      */
165     public RreadMessage(ByteBuffer data)
166     {
167         this(0, (short)117, 0); // We'll set the length and tag later
168         this.data = data;
169         this.pos = data.position();
170         this.count = data.remaining() - this.pos;
171         this.length = StyxUtils.HEADER_LENGTH + 4 + this.count;
172         if (log.isDebugEnabled())
173         {
174             log.debug("Created RreadMessage from buffer with pos = " +
175                 data.position() + ", limit = " + data.limit() +
176                 ", length  = " + this.length);
177         }
178     }
179     
180     /***
181      * @throws ProtocolViolationException if the payload of the message is more
182      * than Integer.MAX_VALUE
183      */
184     protected final void decodeBody(StyxBuffer buf)
185         throws ProtocolViolationException
186     {
187         long n = buf.getUInt();
188         if (n < 0 || n > Integer.MAX_VALUE)
189         {
190             throw new ProtocolViolationException("Payload of Rread message " +
191                 "cannot be less than 0 or greater than Integer.MAX_VALUE bytes");
192         }
193         this.count = (int)n; // We know this cast must be safe
194         
195         if (buf.remaining() == this.count)
196         {
197             // The buffer contains the payload bytes and no more. We can simply
198             // keep a reference to this buffer instead of copying it. This happens
199             // frequently in practice, so this could be a significant efficiency
200             this.data = buf.getBuffer();
201             
202             // Increment the reference count for the underlying ByteBuffer, so that
203             // it is not reused prematurely.
204             this.data.acquire();
205             
206             // Remember the position of this buffer
207             this.pos = this.data.position();
208         
209             // We need to set the position of the input buffer to its limit to make
210             // sure that we don't keep reading from this buffer. We'll set the position
211             // back to zero when we get the buffer using getData();
212             this.data.position(this.data.limit());
213         }
214         else
215         {
216             // We need to copy the data in this buffer.
217             log.debug("Need to make a copy of the data in this buffer: " +
218                 this.count + " bytes");
219             byte[] b = buf.getData(this.count);
220             this.data = ByteBuffer.wrap(b);
221             this.pos = 0;
222         }
223     }
224     
225     /***
226      * Writes the message into the given StyxBuffer.
227      */
228     protected final void encodeBody(StyxBuffer buf)
229     {
230         buf.putUInt(this.count).putData(this.getData());
231     }
232     
233     /***
234      * @return the data contained in this message as a MINA ByteBuffer.  Makes
235      * sure that the position and limit of the buffer are set correctly
236      */
237     public ByteBuffer getData()
238     {
239         // Set the position and limit correctly in case we have changed it
240         // elsewhere
241         this.data.position(this.pos).limit(this.pos + this.count);
242         return this.data;
243     }
244     
245     /***
246      * @return the number of bytes returned in the message (i.e. the payload size)
247      */
248     public int getCount()
249     {
250         return this.count;
251     }
252     
253     /***
254      * This is called <b>after</b> the message has been sent (in
255      * StyxServerProtocolHandler.messageSent().  This releases the ByteBuffer
256      * holding the payload of the message.
257      */
258     public void dispose()
259     {
260         this.data.release();
261     }
262     
263     protected String getElements()
264     {
265         StringBuffer s = new StringBuffer(", " + this.count + ", ");
266         s.append(StyxUtils.getDataSummary(30, this.getData()));
267         return s.toString();
268     }
269     
270     public String toFriendlyString()
271     {
272         StringBuffer s = new StringBuffer("count: ");
273         s.append(this.count);
274         s.append(", ");
275         s.append(StyxUtils.getDataSummary(30, this.getData()));
276         return s.toString();
277     }
278     
279 }