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