Source code: Freenet/Message.java
1 package Freenet;
2 import Freenet.node.*;
3 import Freenet.message.TimedOut;
4 import Freenet.support.*;
5 import java.lang.reflect.*;
6 import java.net.*;
7 import java.util.Hashtable;
8 import java.util.Enumeration;
9 /*
10 This code is part of the Java Adaptive Network Client by Ian Clarke.
11 It is distributed under the GNU Public Licence (GPL) version 2. See
12 http://www.gnu.org/ for further details of the GPL.
13
14 Explanation of Code Versions:
15 0.0.0 = Initial Description
16 0.0.1 = API Specified
17 0.x (x>0) = Partial Implementation
18 x.0 (x>0) = Operational
19
20 Requires Classes: Node (1.0)
21 Address (1.0)
22 */
23
24 /**
25 * This is the abstract superclass of all messages
26 *
27 * @see Node
28 * @see Address
29 * @author Brandon Wiley (blanu@uts.cc.utexas.edu)
30 * @author Ian Clarke (I.Clarke@strs.co.uk)
31 **/
32
33 public abstract class Message
34 {
35 public static final String messageName="";
36
37 /** The Address of the node where this message originated **/
38 public Address source;
39 /** Address of this node on the network that this message came from **/
40 public Address receivedAt = null;
41 /** ConnectionHandler that accepted that received message **/
42 public ConnectionHandler receivedWith = null;
43 /** The number of times this message should be forwarded before
44 generating a TimedOutMessage **/
45 public long hopsToLive;
46 /** The number number of hops this message is from its originating node **/
47 public long depth;
48 /** A randomly generated unique ID used to identify related messages **/
49 public long id;
50 /** Whether the connection should be kept open after sending this **/
51 public boolean keepAlive = true;
52
53 /** Any unknown / unused fields **/
54 private Hashtable otherFields = new Hashtable();
55 protected String popField(String s) {String rs = (String) otherFields.get(s);otherFields.remove(s);return rs;}
56
57
58 public Message(long idnum, long htl, long dpth)
59 {
60 id=idnum;
61 hopsToLive=htl;
62 depth=dpth;
63 }
64
65 public Message(RawMessage raw) throws InvalidMessageException
66 {
67 try {
68
69 for (Enumeration e = raw.fieldNames(); e.hasMoreElements();) {
70 String key = (String) e.nextElement();
71 otherFields.put(key, raw.readField(key));
72 }
73 id=hextolong(popField("UniqueID"));
74 hopsToLive= Long.parseLong(popField("HopsToLive"));
75 depth=Long.parseLong(popField("Depth"));
76 popField("KeepAlive"); // this is not preserved
77 // Source is not set by the other constructer, so I'll be consistant, and take it away here (it is set by a call to initSources() or sending())
78 // source = raw.readField("Source") == null ? null : new Address(raw.readField("Source"));
79 } catch (ClassCastException e) {
80 throw new InvalidMessageException("Bad field");
81 } catch (NumberFormatException e) {
82 throw new InvalidMessageException("Bad number");
83 }
84 }
85
86 /* I know, you're thinking Brandon, why don't you use Long.valueOf(hex,
87 16). Well, because Long.toHexString() generates unsigned values and
88 Long.valueOf expects a signed value. So if you know the elegant way to do
89 this, please tell me. It's one line in C and Python. */
90 private long hextolong(String hex) throws NumberFormatException
91 {
92 long l=0;
93 char c;
94
95 int len=hex.length();
96 int p=len-1;
97
98 for (int i=0; i<len; i++) {
99 c=hex.charAt(i);
100 if((c>='0')&&(c<='9')) l=l+((c-'0')*(long)Math.pow(16,p-i));
101 else if ((c>='a')&&(c<='f')) l=l+((c-'a'+10)*(long)Math.pow(16,p-i));
102 else if ((c>='A')&&(c<='F')) l=l+((c-'A'+10)*(long)Math.pow(16,p-i));
103 else throw new NumberFormatException();
104 }
105 return l;
106 }
107
108 public RawMessage toRawMessage()
109 {
110 RawMessage r = new RawMessage("");
111 r.trailingFieldName="EndMessage";
112 r.setField("UniqueID", Long.toHexString(id));
113 r.setField("HopsToLive", String.valueOf(hopsToLive));
114 r.setField("Depth", String.valueOf(depth));
115 r.setField("Source", source.toString());
116
117 for (Enumeration e = otherFields.keys(); e.hasMoreElements();) {
118 String key = (String) e.nextElement();
119 r.setField(key, (String) otherFields.get(key));
120 }
121 if (!keepAlive)
122 r.setField("KeepAlive","false");
123 return r;
124 }
125
126
127 /**
128 * Called by the connection handler after right constructing
129 * the message, while still in touch with the connection
130 * @param me The address of this node that the message was
131 * received on.
132 * @param peer The address of the node the message was received
133 * from.
134 **/
135
136 public void initSources(Address me, Address peer, ConnectionHandler ch)
137 {
138 receivedAt = me;
139 source = peer;
140 receivedWith = ch;
141 }
142
143 /**
144 * Called by a node after this message is received. This allows
145 * message handling code to be packaged within the message class
146 * that is being handled.
147 * @param n The node that called this message. This should be used
148 * to make any nescessary modifications to the DataStore etc.
149 * @param sb Null unless this node has been seen before. If non-null it
150 * is the Object returned by the received method of the
151 * last message seen with this ID.
152 * @return The object to be passed to any messages received in the near
153 * future with the same ID as this one. Null if no object
154 * should be passed.
155 **/
156 public final MessageMemory received(Node n, MessageMemory sb)
157 {
158 // Reduce the lifespan of this message
159 hopsToLive--;
160
161 if(hopsToLive<=0) { // Message expired. Inform sender.
162 return timeOut(n, sb);
163 } else {
164 // Increase the depth
165 depth++;
166 return pReceived(n, sb); // Actually do something with the message
167 }
168 }
169
170 public void sending(Core n, ConnectionHandler ch) throws SendFailedException {
171 sending(n,ch.peer(),ch.local(n.myAddress));
172 }
173
174 /**
175 * Called just before the message is sent, and makes any last minute
176 * changes.
177 *
178 * @param n The node that is sending the message
179 * @param peer The address to which the message is being sent
180 * @param myAddress The sending nodes address with respect to peer
181 **/
182
183 public void sending(Core n, Address peer, Address myAddress) throws SendFailedException
184 {
185 if (myAddress.equals(peer))
186 throw new SendFailedException(peer); // don't send to yourself
187 source = myAddress;
188 }
189
190 /**
191 * An overridable version of the received message.
192 * @param n The node that called this message. This should be used
193 * to make any nescessary modifications to the DataStore etc.
194 * @param sb Null unless this node has been seen before. If non-null it
195 * is the Object returned by the received method of the
196 * last message seen with this ID.
197 * @return The object to be passed to any messages received in the near
198 * future with the same ID as this one. Null if no object
199 * should be passed.
200 **/
201
202 abstract protected MessageMemory pReceived(Node n, MessageMemory sb);
203
204
205 protected MessageMemory timeOut(Node n, MessageMemory sb)
206 {
207 Logger.log("Message.java","Message timed out, sending timeout to original source",Logger.MINOR);
208 TimedOut to=new TimedOut(id, depth); // this message is going in
209
210 try {
211 (sendReply(n,to)).close();
212 } catch(SendFailedException sfe) {
213 Logger.log("Message.java","Send of timeout failed",Logger.NORMAL);
214 }
215
216 return sb;
217 }
218
219 /**
220 * Sends a message back to the node from which this message was received.
221 * If a connection is open, that will be used, otherwise a new one opened
222 * @param n this node
223 * @param m the message to send.
224 * @return if there was an open connection back, or if there was a
225 * a connection back that was closed, the connection over which
226 * the message was sent is returned. If there was no connection
227 * back, the new connection is closed after send, and null
228 * is returned.
229 *
230 **/
231
232 protected ConnectionHandler sendReply(Node n, Message m) throws SendFailedException {
233 try {
234 ConnectionHandler ch;
235 if (receivedWith == null) {
236 ch = n.makeConnection(source);
237 m.keepAlive = false; // close again after sending message
238 } else if (!receivedWith.isOpen())
239 ch = n.makeConnection(source);
240 else
241 ch = receivedWith;
242
243 m.sending(n, receivedWith);
244 receivedWith.sendMessage(m);
245
246 return receivedWith == null ? null : ch;
247 } catch (ConnectFailedException e) {
248 throw new SendFailedException(e.peer);
249 }
250 }
251
252 /**
253 * Sends this message back to the node from which the chain was originally
254 * received.
255 * @param n this node.
256 * @param mm The MessageMemory for this chain of messages.
257 * @return If there was an open connection back in the MessageMemory,
258 * or if there was a connection back that had been closed,
259 * the connection which this message is sent on is returned.
260 * If there was no connection back stored, null is returned.
261 **/
262
263 public ConnectionHandler sendBack(Node n, MessageMemory mm) throws SendFailedException {
264 try {
265 ConnectionHandler ch;
266 if (mm.replyCon == null) {
267 ch = n.makeConnection(mm.origRec);
268 keepAlive = false;
269 } else if (!mm.replyCon.isOpen()) {
270 ch = n.makeConnection(mm.origRec);
271 } else {
272 ch = mm.replyCon;
273 }
274 sending(n, ch);
275 ch.sendMessage(this, null, null);
276 return mm.replyCon == null ? null : ch;
277 } catch (ConnectFailedException e) {
278 throw new SendFailedException(e.peer);
279 }
280 }
281
282 public String toString()
283 {
284 // RawMessage r = toRawMessage();
285 return "Src:"+source+" htl:"+hopsToLive+
286 " depth:" + depth + " id:"+Long.toHexString(id) +
287 " type:" + this.getClass().getName();
288 }
289 }
290
291