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.mina.common.ByteBuffer;
32  import org.apache.mina.protocol.ProtocolViolationException;
33  import org.apache.mina.protocol.ProtocolEncoderOutput;
34  
35  import org.apache.log4j.Logger;
36  
37  import uk.ac.rdg.resc.jstyx.StyxUtils;
38  
39  /***
40   * Abstract superclass for all Styx messages.
41   *
42   * @author Jon Blower
43   * $Revision: 507 $
44   * $Date: 2005-12-01 08:21:56 +0000 (Thu, 01 Dec 2005) $
45   * $Log$
46   * Revision 1.14  2005/12/01 08:21:56  jonblower
47   * Fixed javadoc comments
48   *
49   * Revision 1.13  2005/11/03 17:09:27  jonblower
50   * Created more efficient RreadMessage that involves less copying of buffers (still reliable)
51   *
52   * Revision 1.12  2005/11/03 07:46:55  jonblower
53   * Trying to fix bug with sending RreadMessages
54   *
55   * Revision 1.11  2005/05/10 19:17:54  jonblower
56   * Added dispose() method
57   *
58   * Revision 1.10  2005/03/22 17:48:27  jonblower
59   * Removed debug code that tracked ByteBuffer allocation
60   *
61   * Revision 1.9  2005/03/21 17:57:11  jonblower
62   * Trying to fix ByteBuffer leak in SGS server
63   *
64   * Revision 1.8  2005/03/18 13:56:00  jonblower
65   * Improved freeing of ByteBuffers, and bug fixes
66   *
67   * Revision 1.7  2005/03/16 17:56:22  jonblower
68   * Replaced use of java.nio.ByteBuffer with MINA's ByteBuffer to minimise copying of buffers
69   *
70   * Revision 1.6  2005/03/15 16:56:19  jonblower
71   * Changed to allow re-use of ByteBuffers once message is finished with
72   *
73   * Revision 1.5  2005/03/15 09:01:48  jonblower
74   * Message type now stored as short, not int
75   *
76   * Revision 1.4  2005/03/11 14:02:15  jonblower
77   * Merged MINA-Test_20059309 into main line of development
78   *
79   * Revision 1.3.2.3  2005/03/11 12:30:46  jonblower
80   * Changed so that message payloads are always ints, not longs
81   *
82   * Revision 1.3.2.2  2005/03/10 14:05:26  jonblower
83   * Reinstated getFid() and getName() methods
84   *
85   * Revision 1.3.2.1  2005/03/10 11:50:59  jonblower
86   * Changed to fit with MINA framework
87   *
88   */
89  public abstract class StyxMessage
90  {
91      
92      private static final Logger log = Logger.getLogger(StyxMessage.class);
93      
94      protected static final String lock = new String(); // Just used to synchronize
95                                                         // writes to the network
96      
97      protected int length;  // The length of the StyxMessage (although in Styx
98                             // this is an *unsigned* int, we guarantee in
99                             // StyxMessageDecoder that we can't have messages
100                            // longer than Integer.MAX_VALUE
101     protected short type;    // The type of the StyxMessage
102     protected int tag;     // The tag of the StyxMessage
103     protected String name; // The name of the message (e.g. "Tversion")
104     protected ByteBuffer buf; // Contains the bytes of the body of the
105                               // StyxMessage (i.e. not the header)
106     private int bytesRead;  // The number of bytes we have read into the buffer
107     
108     /***
109      * Creates a new instance of StyxMessage.
110      */
111     protected StyxMessage(int length, short type, int tag)
112     {
113         this.length = length;
114         this.type = type;
115         this.tag = tag;
116         this.name = "StyxMessage"; // This will be overridden in subclasses
117         this.buf = null; // The buffer gets allocated later, when we're sure
118                          // what the message length is
119     }
120     
121     /***
122      * @return The name of this message (e.g. "Tread", "Rwalk", etc)
123      */
124     public final String getName()
125     {
126         return this.name;
127     }
128     
129     /***
130      * @return The total length of the StyxMessage in bytes
131      */
132     public final int getLength()
133     {
134         return this.length;
135     }
136     
137     /***
138      * @return The type of the message
139      */
140     public final short getType()
141     {
142         return this.type;
143     }
144     
145     /***
146      * @return The tag of the message
147      */
148     public final int getTag()
149     {
150         return this.tag;
151     }
152     
153     /***
154      * Sets the tag of the message
155      */
156     public final void setTag(int newTag)
157     {
158         this.tag = newTag;
159     }
160     
161     /***
162      * @return the buffer containing the body of this message
163      */
164     public final ByteBuffer getBuffer()
165     {
166         return this.buf;
167     }
168     
169     /***
170      * @return the fid associated with this message. This default implementation
171      * returns StyxUtils.NOFID; subclasses should override this.  This method
172      * only exists in this superclass as a convenience for the StyxMon application.
173      */
174     public long getFid()
175     {
176         return StyxUtils.NOFID;
177     }
178     
179     /***
180      * Read bytes from the given ByteBuffer into this Message. There may still
181      * be bytes remaining in the input buffer after this method has been called.
182      * @param in The org.apache.mina.common.ByteBuffer that contains the data.
183      * @return true if we now have a complete StyxMessage, false otherwise
184      * @throws ProtocolViolationException if the bytes do not represent a valid
185      * StyxMessage
186      */
187     public final boolean readBytesFrom(ByteBuffer in) throws ProtocolViolationException
188     {
189         int bodyLength = this.length - StyxUtils.HEADER_LENGTH;
190         if (bodyLength == 0)
191         {
192             // We don't need to read any bytes; this message has no body
193             return true;
194         }
195         if (this.buf == null)
196         {
197             this.bytesRead = 0;
198             // This is the first time we've called this method for this
199             // message.
200             if (in.remaining() == bodyLength)
201             {
202                 // If the input buffer contains the full body of the message (and
203                 // nothing more) we can just use the input buffer.  This is a very
204                 // common occurrence in practice.
205                 // TODO: should we allow this to happen if in.remaining() > bodyLength?
206                 log.debug("input buffer contains a whole message; won't create new buffer");
207                 // Increment the reference count for this buffer so it doesn't get
208                 // released before we want it to be
209                 in.acquire();
210                 this.buf = in;
211                 this.bytesRead = bodyLength; // Signify that we have read all of the body
212                 // We have the full message already. Decode it and return true
213                 this.decode();
214                 return true;
215             }
216             else
217             {
218                 // Create a buffer to hold the bytes. This buffer comes
219                 // from MINA's pool
220                 this.buf = ByteBuffer.allocate(bodyLength);
221             }
222         }
223         if (this.bytesRead >= bodyLength) // N.B. should never be > bodyLength
224         {
225             // We don't need to read any bytes; we already have the full message
226             return true;
227         }
228         
229         // Calculate the number of bytes we can read from the input buffer.
230         // We can't rely on this.buf.remaining() here because the buffer could be
231         // bigger than the message length
232         int bytesLeft = bodyLength - this.bytesRead;
233         int bytesToRead = bytesLeft < in.remaining() ? bytesLeft : in.remaining();
234         
235         // Read the bytes and write to this message
236         byte[] b = new byte[bytesToRead];
237         in.get(b);
238         this.buf.put(b);
239         this.bytesRead += b.length;
240         
241         // Return true if the buffer is now full (i.e. we have the whole message);
242         // false otherwise
243         if (this.bytesRead >= bodyLength) // N.B. Should never be > bodyLength
244         {
245             // We now have the full message. Decode these bytes into meaningful
246             // information
247             this.buf.flip();
248             this.decode();
249             return true;
250         }
251         else
252         {
253             return false;
254         }
255     }
256     
257     /***
258      * Called when we have a complete message. Simply wraps the buffer as a
259      * StyxBuffer to make it easy to read Styx primitives and calls
260      * this.decodeBody()
261      */
262     private void decode() throws ProtocolViolationException
263     {
264         StyxBuffer styxBuf = new StyxBuffer(this.buf);
265         this.decodeBody(styxBuf);
266     }
267     
268     /***
269      * Called when a complete message has arrived; signals that we are ready
270      * to interpret the raw bytes in the buffer and turn them into meaningful
271      * information. Subclasses should make sure that the buffer is no longer
272      * needed once this method has finished, as the underlying buffer will
273      * be reused.
274      * @throws ProtocolViolationException if the buffer doesn't contain a valid
275      * StyxMessage body
276      */
277     protected abstract void decodeBody(StyxBuffer styxBuf)
278         throws ProtocolViolationException;
279     
280     /***
281      * Called by StyxMessageEncoder to send a message to the output
282      */
283     public void write(ProtocolEncoderOutput out) throws ProtocolViolationException
284     {
285         // Encode this message into a ByteBuffer
286         this.encode();
287         synchronized(lock)
288         {
289             // Write this ByteBuffer
290             out.write(this.buf);
291         }
292     }
293     
294     /***
295      * Called by StyxMessageEncoder when we are about to send a message. Creates
296      * the underlying ByteBuffer, wraps it as a StyxBuffer, writes the header
297      * information, calls encodeBody() to write the body information, then 
298      * flips the buffer so that it is ready for writing to the output stream.
299      * @throws ProtocolViolationException if a problem occurred encoding the
300      * message (shouldn't happen)
301      */
302     public void encode() throws ProtocolViolationException
303     {
304         // Make sure we have a buffer of the appropriate length
305         log.debug("Allocating new ByteBuffer of length " + this.length);
306         this.buf = ByteBuffer.allocate(this.length);
307         // Wrap the buffer as a StyxBuffer to make it easy to write Styx
308         // primitives
309         StyxBuffer styxBuf = new StyxBuffer(this.buf);
310         styxBuf.putUInt(this.length).putUByte(this.type).putUShort(this.tag);
311         this.encodeBody(styxBuf);
312         this.buf.flip();
313     }
314     
315     /***
316      * Encode the body of the message into bytes in the underlying buffer
317      */
318     protected abstract void encodeBody(StyxBuffer styxBuf)
319         throws ProtocolViolationException;
320     
321     /***
322      * @return String representation of this StyxMessage
323      */
324     public String toString()
325     {
326         StringBuffer s = new StringBuffer(this.name);
327         s.append(": ");
328         s.append(this.length);
329         s.append(", ");
330         s.append(this.type);
331         s.append(", ");
332         s.append(this.tag);
333         s.append(this.getElements());
334         return s.toString();
335     }
336     
337     /***
338      * @return the body elements of this message as a string
339      */
340     protected abstract String getElements();
341     
342     /***
343      * @return a human-readable string that displays the contents of the message,
344      * without the header info. This default implementation simply calls 
345      * this.getElements(): subclasses should override this behaviour
346      */
347     public String toFriendlyString()
348     {
349         return this.getElements();
350     }
351     
352     /***
353      * Sends request to release the underlying ByteBuffer back to the pool.
354      * Actually, this just decrements the reference count for the buffer; the
355      * buffer is only released when this count reaches zero. This is called 
356      * once the StyxMessageDecoder.decode() method has finished.
357      */
358     void release()
359     {
360         if (this.buf != null)
361         {
362             this.buf.release();
363         }
364     }
365     
366     /***
367      * This is called <b>after</b> the message has been sent (in
368      * StyxServerProtocolHandler.messageSent()) and is a signal to free any
369      * resources associated with the message (e.g. an RreadMessage can release
370      * the ByteBuffer holding the payload). This default implementation does
371      * nothing: subclasses should override if necessary.
372      */
373     public void dispose()
374     {
375         return;
376     }
377     
378     /***
379      * Static factory method for creating a StyxMessage. Called by StyxMessageDecoder
380      * when the header of a message has been decoded. Returns the appropriate
381      * subclass of StyxMessage, depending on the provided type.
382      * @param length The total length of the message (header and body)
383      * @param type The numeric code representing the message type
384      * @param tag The message tag
385      * @return A StyxMessage of the appropriate type, depending on the tag
386      * @throws ProtocolViolationException if the message is of an unknown type
387      */
388     public static StyxMessage createStyxMessage(int length, short type, int tag)
389         throws ProtocolViolationException
390     {
391         if (log.isDebugEnabled())
392         {
393             log.debug("Creating StyxMessage(length = " + length + ", type = "
394                 + type + ", tag = " + tag + ")");
395         }
396         if (type == 100)
397         {
398             return new TversionMessage(length, type, tag);
399         }
400         else if (type == 101)
401         {
402             return new RversionMessage(length, type, tag);
403         }
404         else if (type == 102)
405         {
406             return new TauthMessage(length, type, tag);
407         }
408         else if (type == 103)
409         {
410             return new RauthMessage(length, type, tag);
411         }
412         else if (type == 104)
413         {
414             return new TattachMessage(length, type, tag);
415         }
416         else if (type == 105)
417         {
418             return new RattachMessage(length, type, tag);
419         }
420         // There is no message of type 106 ("Terror" doesn't exist)
421         else if (type == 107)
422         {
423             return new RerrorMessage(length, type, tag);
424         }
425         else if (type == 108)
426         {
427             return new TflushMessage(length, type, tag);
428         }
429         else if (type == 109)
430         {
431             return new RflushMessage(length, type, tag);
432         }
433         else if (type == 110)
434         {
435             return new TwalkMessage(length, type, tag);
436         }
437         else if (type == 111)
438         {
439             return new RwalkMessage(length, type, tag);
440         }
441         else if (type == 112)
442         {
443             return new TopenMessage(length, type, tag);
444         }
445         else if (type == 113)
446         {
447             return new RopenMessage(length, type, tag);
448         }
449         else if (type == 114)
450         {
451             return new TcreateMessage(length, type, tag);
452         }
453         else if (type == 115)
454         {
455             return new RcreateMessage(length, type, tag);
456         }
457         else if (type == 116)
458         {
459             return new TreadMessage(length, type, tag);
460         }
461         else if (type == 117)
462         {
463             return new RreadMessage(length, type, tag);
464         }
465         else if (type == 118)
466         {
467             return new TwriteMessage(length, type, tag);
468         }
469         else if (type == 119)
470         {
471             return new RwriteMessage(length, type, tag);
472         }
473         else if (type == 120)
474         {
475             return new TclunkMessage(length, type, tag);
476         }
477         else if (type == 121)
478         {
479             return new RclunkMessage(length, type, tag);
480         }
481         else if (type == 122)
482         {
483             return new TremoveMessage(length, type, tag);
484         }
485         else if (type == 123)
486         {
487             return new RremoveMessage(length, type, tag);
488         }
489         else if (type == 124)
490         {
491             return new TstatMessage(length, type, tag);
492         }
493         else if (type == 125)
494         {
495             return new RstatMessage(length, type, tag);
496         }
497         else if (type == 126)
498         {
499             return new TwstatMessage(length, type, tag);
500         }
501         else if (type == 127)
502         {
503             return new RwstatMessage(length, type, tag);
504         }
505         else
506         {
507             throw new ProtocolViolationException("Unknown message type " + type);
508         }
509     }
510     
511 }