View Javadoc

1   /*
2    * Copyright (c) 1998-2002 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.relacs.mss;
20  import java.util.NoSuchElementException;
21  
22  import jgroup.util.OutMessage;
23  
24  /**
25   *  Provides an iterator over the fragments of a message.
26   *
27   *  @author Hein Meling
28   *  @since Jgroup 1.2
29   */
30  class MsgFragmentIterator
31    implements FragmentIterator, MssTag
32  {
33  
34    ////////////////////////////////////////////////////////////////////////////////////////////
35    // Fields
36    ////////////////////////////////////////////////////////////////////////////////////////////
37  
38    /* Constants for the duration of this iterator */
39    private byte[][] fragments;
40    private int fragmentCount;
41    private int defaultFragLen;
42    private int lastFragLen;
43  
44    /* The actual message associated with this fragment iterator */
45    private Msg msg;
46  
47    /* The message control object */
48    private MsgCntrl msgCntrl;
49  
50    /* Indicates if the iterator has been initialized */
51    private boolean initialized;
52  
53    /* Pointer to the next fragment to be returned by the iterator */
54    private int nextFragment;
55  
56    /* The actual length of the fragment previously returned */
57    private int fragLen;
58  
59    /* The fragment identifier of the fragment previously returned */
60    private int fragId;
61  
62    /* The first fragment identifier represented by this iterator */
63    private int firstFragId;
64  
65  
66    ////////////////////////////////////////////////////////////////////////////////////////////
67    // Constructor
68    ////////////////////////////////////////////////////////////////////////////////////////////
69  
70    /**
71     *  Creates a new <code>MsgFragmentIterator</code> instance, used to
72     *  access the fragments of the given <code>OutMessage</code>.
73     */
74    MsgFragmentIterator(Msg msg, MsgCntrl msgCntrl)
75    {
76      this.msgCntrl = msgCntrl;
77      this.msg = msg;
78      reset(msg.getOutMessage());
79    }
80  
81  
82    ////////////////////////////////////////////////////////////////////////////////////////////
83    // Package methods
84    ////////////////////////////////////////////////////////////////////////////////////////////
85  
86    /**
87     *  Reset the iterator to start from the first fragment again.  Note
88     *  that this method should be used with care, especially in the case
89     *  when a message may potentially be resent, based on the fragId.
90     *  This is since, for each reuse, this iterator will change its
91     *  fragment identifier.  This requires that the iterator must be
92     *  reinitialized with the correct fragment identifier before reuse.
93     */
94    void reset(OutMessage outmsg)
95    {
96      initialized    = false;
97      fragments      = outmsg.getFragments();
98      fragmentCount  = outmsg.getFragmentCount();
99      defaultFragLen = outmsg.getPayload() + outmsg.getHeader();
100     lastFragLen    = outmsg.getLastPayload() + outmsg.getHeader();
101 
102     nextFragment = 0;
103     fragLen      = -1;
104     fragId       = -1;
105     firstFragId  = -1;
106   }
107 
108 
109   ////////////////////////////////////////////////////////////////////////////////////////////
110   // FragmentIterator methods
111   ////////////////////////////////////////////////////////////////////////////////////////////
112 
113   /**
114    *  Return the fragment associated with the given fragment identifier.
115    *
116    *  @exception IllegalStateException
117    *    Raised if the iterator has not been invoked prior to calling
118    *    this method.  This is since we cannot know the iterators
119    *    fragment identifiers until we have invoked next.
120    *  @exception IndexOutOfBoundsException
121    *    Raised if the given fragment identifier is not represented by
122    *    this iterator.
123    *  @exception NoSuchElementException
124    *    Thrown if the computed fragment position exceeds the fragment
125    *    count of the iterator.  This can never happen.
126    */
127   public byte[] getFragment(int fragId)
128   {
129     if (!initialized)
130       throw new IllegalStateException("Cannot seek in the iterator before having iterated once");
131 
132     int lastFragId = fragmentCount + firstFragId - 1;
133     if (fragId > lastFragId || fragId < firstFragId)
134       throw new IndexOutOfBoundsException("The fragment identifier ("
135         + fragId + ") is not represented by this iterator: " + this);
136 
137     nextFragment = fragId - firstFragId;
138     if (nextFragment >= fragmentCount)
139       throw new NoSuchElementException("There are no more fragments associated with this message.");
140 
141     if (fragId == lastFragId)
142       fragLen = lastFragLen;
143     else
144       fragLen = defaultFragLen;
145     this.fragId = fragId;
146 
147     return fragments[nextFragment];
148   }
149 
150 
151   /**
152    *  Returns the length (including the header) of the active
153    *  fragment; that is, the one most recently retrieved through the
154    *  <code>next()</code> method. <p>
155    *
156    *  The last fragment will have a different length than the other
157    *  fragments, for which this method will return the default payload
158    *  length of a packet.  Also a single fragment counts as a last
159    *  fragment, and may thus not use the entire payload length.  This
160    *  method always returns the correct length of the active fragment.
161    *  <p>
162    *
163    *  It is not possible to use the fragment.length field, since we
164    *  are not copying the data of the original; we are merely returning
165    *  its reference.
166    */
167   public int fragmentLength()
168   {
169     if (!initialized)
170       throw new IllegalStateException("No fragment is active; must invoke next() first");
171     return fragLen;
172   }
173 
174 
175   /**
176    *  Returns the fragment identifier of the active fragment.
177    */
178   public int getFid()
179   {
180     if (!initialized)
181       throw new IllegalStateException("No fragment is active; must invoke next() first");
182     return fragId;
183   }
184 
185 
186   /**
187    *  Returns the actul message object assocaited with this fragment
188    *  iterator.
189    */
190   public Msg getMsg()
191   {
192     if (msg == null)
193       throw new NullPointerException("The message is null; iterator is not correctly initialized");
194     return msg;
195   }
196 
197 
198   /**
199    *  Returns true if the message stream has more fragments; false is
200    *  returned otherwise.
201    */
202   public boolean hasNext()
203   {
204     return nextFragment != fragmentCount;
205   }
206 
207 
208   /**
209    *  Returns the next fragment of this message object's stream,
210    *  including the marshalled fragment header which includes the message
211    *  <code>tag</code> and <code>fragId</code>.  The latter constitute
212    *  the packet level header.
213    *
214    *  @see jgroup.relacs.mss.MssTag
215    *  @param broadcast
216    *    Indicates if the message fragment should be broadcast, and
217    *    thus sets a packet level broadcast field.
218    *  @return
219    *    A byte array containing the next fragment in the stream,
220    *    including the header.
221    */
222   public byte[] next(boolean broadcast)
223   {
224     if (nextFragment == fragmentCount)
225       throw new NoSuchElementException("There are no more fragments associated with this message.");
226 
227     byte tag = msg.getTag();
228 
229     /*
230      * Set the correct tag for this message fragment.  If there is only
231      * one fragment, it will be marked with the tag obtained from the
232      * message.  The message tag will always mark the last fragment.  If
233      * there are multiple fragments, the first <i>n - 1</i> fragments
234      * will be marked with the <code>NOTLASTFRAGMENT</code> tag, while
235      * the <i>n</i>-th fragment will be marked with the message tag.
236      */
237     if (nextFragment + 1 == fragmentCount) {
238       fragLen = lastFragLen;
239     } else {
240       fragLen = defaultFragLen;
241       tag = NOTLASTFRAGMENT;
242     }
243 
244     if (msg instanceof MsgJG) {
245       /* The msgSend() method blocks until the sending window is open */
246       fragId = msgCntrl.msgSend(this);
247     } else {
248       /* IAMALIVE, ROUTING, NACK and SYN messages provide their own fragId */
249       fragId = msg.getMid();
250     }
251 
252     if (firstFragId == -1)
253       firstFragId = fragId;
254 
255     /* The iterator has now been initialized. */
256     initialized = true;
257 
258     FragmentHeader.marshal(tag, broadcast, fragId, fragments[nextFragment]);
259 
260     return fragments[nextFragment++];
261   }
262 
263 
264   ////////////////////////////////////////////////////////////////////////////////////////////
265   // Methods from Object
266   ////////////////////////////////////////////////////////////////////////////////////////////
267 
268   public String toString()
269   {
270     StringBuilder buf = new StringBuilder("[MsgFragmentIterator: nxtFrag=");
271     buf.append(nextFragment);
272     buf.append(", fragLen=");
273     buf.append(fragLen);
274     buf.append(", fragId=");
275     buf.append(fragId);
276     buf.append(", firstFragId=");
277     buf.append(firstFragId);
278     buf.append(", lastFragId=");
279     buf.append((fragmentCount + firstFragId - 1));
280     buf.append(", fragCnt=");
281     buf.append(fragmentCount);
282     buf.append(", lastFragLen=");
283     buf.append(lastFragLen);
284     buf.append("]");
285     return buf.toString();
286   }
287 
288 } // END MsgFragmentIterator