Source code: Freenet/node/StandardDataStore.java
1 /*
2 *$Id: StandardDataStore.java,v 1.10 2000/04/08 20:48:17 hobbex Exp $
3
4 This code is part of the Java Adaptive Network Client by Ian Clarke.
5 It is distributed under the GNU Public Licence (GPL) version 2. See
6 http://www.gnu.org/ for further details of the GPL.
7
8 Explanation of Code Versions:
9 0.0.0 = Initial Description
10 0.0.1 = API Specified
11 0.x (x>0) = Partial Implementation
12 x.0 (x>0) = Operational
13
14 Requires Classes: Address
15 Data
16 */
17
18 /**
19 * DataStore.java
20 *
21 * This class is used by a node to store references and data.
22 *
23 * @version 0.0
24 * @author Ian Clarke (I.Clarke@strs.co.uk)
25 **/
26
27 package Freenet.node;
28 import Freenet.*;
29 import Freenet.support.*;
30 import java.io.*;
31 import java.util.*;
32
33 public class StandardDataStore implements DataStore
34 {
35 static private long write_interval;
36 static public String filename = ".freenet/store";
37
38 // Debugging stuff to test this class
39 /* not working since Ian removed test code from data class
40 public static void main(String[] args) {
41 System.out.println("DataStore test routine\n-------");
42 System.out.println("Creating datastore size 20 with room for 30 bytes of data");
43 DataStore ds = new DataStore(30, 20);
44 System.out.println("Adding 10 data items");
45 for (int x = 0; x < 100; x += 5)
46 ds.put(new StringKey("Key "+x), new Address("local/"+x),
47 new Data("Data "+x));
48 System.out.println("Adding 10 reference items");
49 for (int x = 100; x < 200; x += 5)
50 ds.put(new StringKey("Key "+x), new Address("local/"+x), null);
51 Key last = null;
52 for (int x = 0; x < 10; x++) {
53 System.out.println("Finding closest key to Key 138 with mask "+
54 last);
55 last = ds.findClosestKey(new StringKey("Key 138"), last);
56 System.out.println("Found "+last);
57 }
58 }
59 */
60 public static DataStore makeDataStore(Node node, long maxData, int maxItems)
61 {
62 Object o=null;
63
64 try
65 {
66 System.out.println(filename);
67 FileInputStream stream=new FileInputStream(filename);
68 ObjectInputStream ostream=new ObjectInputStream(stream);
69 o=ostream.readObject();
70 ostream.close();
71 if(!(o instanceof DataStore))
72 o=new StandardDataStore(maxData, maxItems);
73 }
74 catch(Exception e)
75 {
76 Logger.log("DataStore.java",
77 "Couldn't load DataStore from "+filename,
78 Logger.NORMAL);
79 o=new StandardDataStore(maxData, maxItems);
80 }
81
82 write_interval=1000*Node.params.getlong("checkpointInterval",5*60);
83 Node.timer.add(23, write_interval, (Callback)o);
84 return (DataStore)o;
85 }
86
87 public void tofile(String fname) throws IOException
88 {
89 synchronized(this)
90 {
91 Logger.log("DataStore.java","Writing datastore to disk",Logger.MINOR);
92 FileOutputStream stream=new FileOutputStream(fname);
93 ObjectOutputStream ostream=new ObjectOutputStream(stream);
94 ostream.writeObject(this);
95 ostream.close();
96 }
97 }
98
99 public void callback()
100 {
101 try {
102 tofile(filename);
103 Node.timer.add(23, write_interval, this);
104 } catch(Exception e) {e.printStackTrace();}
105 }
106
107 // Public Fields
108 public long maxData;
109
110 // Protected Fields
111 protected CyclicArray ar;
112 protected Hashtable h;
113
114 // Constructor
115
116 public StandardDataStore(long maxData, int maxItems) {
117 ar = new CyclicArray(maxItems);
118 h = new Hashtable();
119 this.maxData = maxData;
120 }
121
122 // Public Methods
123
124 public Enumeration keys()
125 {
126 return h.keys();
127 }
128
129 /**
130 * Searches for a key in the DataStore and if found returns
131 * any data associated with that key. If none is found,
132 * null is returned.
133 * @param k The key to search for
134 * @return The data associated with the key or null if not found
135 **/
136
137 public Data searchData(Key k) {
138 Logger.log("DataStore.java", "searchData("+k + ")", Logger.DEBUG);
139 synchronized (this)
140 {
141 DataStoreItem dsi = (DataStoreItem)h.get(k);
142 if(dsi==null || dsi.data==null)
143 {
144 Logger.log("DataStore.java", "Not found.", Logger.DEBUG);
145 return null;
146 }
147 // We have a hit, raise its priority
148 Logger.log("DataStore.java", "Data found.", Logger.DEBUG);
149 Data data = dsi.data;
150 Address addr = dsi.ref;
151 remove(k);
152 put(k, addr, data);
153 return data;
154 }
155 }
156
157 /**
158 * Searches for a key in the DataStore and if found returns
159 * any reference associated with that key. If none is found,
160 * null is returned.
161 * @param k The key to search for
162 * @return The reference associated with the key or null
163 * if not found
164 **/
165
166 public Address searchRef(Key k) {
167 if (k == null)
168 return null;
169 Logger.log("DataStore.java", "called searchRef("+k + ")", Logger.DEBUG);
170 synchronized (this)
171 {
172 DataStoreItem dsi = (DataStoreItem)h.get(k);
173 if(dsi==null)
174 {
175 Logger.log("DataStore.java", "Not found.", Logger.DEBUG);
176 return null;
177 }
178 Logger.log("DataStore.java", "Ref found.", Logger.DEBUG);
179 return dsi.ref;
180 }
181 }
182
183 /**
184 * Removes an item from the DataStore given its key
185 * @param k The key of the item to remove
186 **/
187
188 public void remove(Key k) {
189 synchronized (this) {
190 for (int x = 0; x < ar.length(); x++) {
191 DataStoreItem dsi = (DataStoreItem) ar.get(x);
192 Logger.log("DataStore.java",
193 "Testing "+dsi + " against "+k, Logger.DEBUGGING);
194 if (dsi != null)
195 if (dsi.key.equals(k)) {
196 ar.remove(x);
197 h.remove(k);
198 return;
199 }
200 }
201 }
202 }
203
204 /**
205 * Returns the reference associated with the closest key
206 * to k.
207 * @param k The key to search for
208 * @return The closest Key to k
209 **/
210 public Key findClosestKey(Key k) {
211 return findClosestKey(k, null);
212 }
213
214 /**
215 * Returns the reference associated with the closest key
216 * to k. The masking key allows the 'next best' key to
217 * be found. Imagine the entries in the datastore being
218 * sorted in terms of closeness to the key you specify.
219 * With no mask key the reference associated with the
220 * top-most item will be returned. However, with a mask
221 * key any references associated with keys above the mask
222 * key (including the mask key itself) will be ignored.
223 * This facility allows best-first backtracking.
224 * @param k The key to search for
225 * @param maskKey The masking key (or null for no mask)
226 * @return The closest Key to k (excluding masked keys) or
227 * null if all keys are masked.
228 **/
229
230 public Key findClosestKey(Key k, Key maskKey) {
231 int m = 0;
232 synchronized (this) {
233 // Ensure that maskKey exists
234 if ((maskKey != null) && (searchRef(maskKey) == null))
235 maskKey = null;
236 int indx = findNextClosestPos(k, maskKey);
237 if (indx < 0)
238 return null;
239 DataStoreItem dsi = (DataStoreItem) ar.get(indx);
240 if (dsi == null)
241 return null;
242 return dsi.key;
243 }
244 }
245
246 /**
247 * Adds an item to the DataStore
248 * @param k The key to add
249 * @param r The address to which requests should be directed for
250 * this key if the data is removed
251 * @param d The data associated with this key
252 **/
253
254 public void put(Key k, Address r, Data d) {
255 synchronized (this) {
256 DataStoreItem dsi = new DataStoreItem(k, r, d);
257 DataStoreItem old = (DataStoreItem) ar.put(dsi);
258 if (old != null)
259 h.remove(old.key);
260 h.put(k, dsi);
261 cleanUpData();
262 }
263 }
264
265 // Protected Methods
266
267 /**
268 * Will return the position in ar of the closest key to k that is not
269 * as close as nextk
270 * @param k Key to search for
271 * @param nextk Must not be as close as this key, if non-null
272 * @return Position in ar of closest key, -1 if there is no key in ar
273 * such that nextk is closer to k then that key
274 **/
275 protected int findNextClosestPos(Key k, Key nextk) {
276 synchronized (this) {
277 int cl = -1;
278 DataStoreItem bs = null;
279 for (int x = 0; x < ar.length(); x++) {
280 DataStoreItem ds = (DataStoreItem) ar.get(x);
281 if ((ds != null) &&
282 ((bs == null) || (k.compare(ds.key, bs.key)))) {
283 // ds is closer than bs
284 if ((nextk==null) || (k.compare(nextk, ds.key))) {
285 // nextk is closer than ds
286 // note that k.compare returns false on a tie
287 cl = x;
288 bs = ds;
289 }
290 }
291 }
292 return cl;
293 }
294 }
295
296 /**
297 * Removes excess data (ie. if there is more data than is
298 * permitted by the maxData field).
299 **/
300 protected void cleanUpData() {
301 long total = 0;
302 for (int x = 0; x < ar.length(); x++) {
303 Object o = ar.get(x);
304 if (o != null) {
305 DataStoreItem dsi = (DataStoreItem) o;
306 if (dsi.data != null) {
307 if (total + dsi.data.length() > maxData)
308 dsi.data = null;
309 else
310 total += dsi.data.length();
311 }
312 }
313 }
314 }
315
316 // Mainly for debugging
317 public String toString()
318 {
319 return h.toString();
320 }
321 }
322
323 class DataStoreItem implements Serializable {
324 public Key key;
325 public Address ref;
326 public Data data;
327
328 public DataStoreItem(Key k, Address a, Data d) {
329 key = k;
330 ref = a;
331 data = d;
332 }
333
334 public String toString() {
335 return "Key: "+key + " Ref: "+ref + " Data:"+data;
336 }
337 }
338
339 /*
340 *$Log: StandardDataStore.java,v $
341 *Revision 1.10 2000/04/08 20:48:17 hobbex
342 *Write DataStore to .freenet/store_<port>
343 *
344 *Revision 1.9 2000/04/08 19:19:52 hal
345 *synch when dumping datastore to disk so it can be modified in the middle.
346 *
347 *Revision 1.8 2000/04/08 15:22:19 hobbex
348 *Cleaned up my ugly code from yesterday
349 *
350 *Revision 1.7 2000/04/08 07:12:15 hal
351 *Move items to top of priority stack when searchData gets a hit. Not
352 *very efficient.
353 *
354 *Revision 1.6 2000/04/07 23:19:33 hobbex
355 *Remove entry from hash when it is overwritten in the array
356 *
357 *Revision 1.5 2000/04/07 20:34:09 hobbex
358 *Fixed building and running with Kaffe (scripts/kbuild.sh). Added canceling of timer callback and sending of requestfailed on loosing MessageMemory (not too well tested). And fixed a bug in the datastore + some exception catching in requestfailed.
359 *
360 *Revision 1.4 2000/04/07 06:03:42 hal
361 *Clean up StandardDataStore:
362 *Make searchRef use the hashtable like searchData, no searching necessary
363 *Clean up findClosestKey to only search once not multiple times.
364 *Fix findClosestKey, it was not looking at the most recently inserted item.
365 *
366 *Revision 1.3 2000/04/03 21:04:55 hal
367 *Fix findClosestPos() in StandardDataStore.
368 *
369 *Revision 1.2 2000/04/02 21:13:25 blanu
370 *Moved class loading into a separate class (Freenet.support.Loader)
371 *
372 *Revision 1.1 2000/03/29 18:49:14 hobbex
373 *Subclassed Node from Core, added Freenet.node package
374 *
375 *Revision 1.18 2000/03/25 17:42:40 sanity
376 *Minor tidying of debug messages.
377 *
378 *Revision 1.17 2000/03/22 18:19:36 blanu
379 *Added Hashtable for keys for faster lookup.
380 *
381 *Revision 1.16 2000/03/17 10:06:02 blanu
382 *Changed Freenet.support.Timer to Freenet.support.Ticker to avoid JDK1.3 name conflict.
383 *
384 *Revision 1.15 2000/02/23 20:52:42 blanu
385 *Fixed timeout in client.
386 *
387 *Revision 1.14 2000/02/14 21:08:14 blanu
388 *The datastore is now written to disk every checkpointInterval seconds as
389 *per the configuration file, with a default of every 5 minutes.
390 *
391 *Revision 1.13 2000/02/14 00:20:31 blanu
392 *Periodic writing of DataStore information to .freenet/store. It doesn't
393 *work yet. I think it might be something wrong with Ticker.
394 *
395 *Revision 1.12 2000/02/13 12:11:57 sanity
396 *Fixed numerous syntax errors.
397 *
398 *Revision 1.11 2000/02/09 23:56:39 hobbex
399 *Preliminary tunneling of DataReplies and DataInserts (for now)
400 *
401 *Revision 1.10 2000/02/08 16:22:06 sanity
402 *Removed printing of exception when datastore file is not found, replaced
403 *with logger message.
404 *
405 *Revision 1.9 2000/02/02 21:03:06 blanu
406 *Changed the fromfile method so that it should work without a store file.
407 *
408 *Revision 1.8 2000/02/02 11:26:40 sanity
409 *Merged changes.
410 *
411 *Revision 1.7 2000/02/02 05:13:28 blanu
412 *Datastore can now be read to / written from a file. Params can be fetched as various types.
413 *
414 *Revision 1.6 2000/01/03 10:20:48 hobbex
415 *Removed deleting of MM on Loop and Failed Request
416 *
417 *Revision 1.5 2000/01/03 00:09:03 michaels
418 *findClosestPos didn't respect a set ignore flag on first dsi in array (cl==0)
419 *now it does.
420 *
421 *Revision 1.4 2000/01/02 14:10:15 michaels
422 *findClosestKey doesnt't return a dsi anymore if its ignore-flag is set to true
423 *
424 *Revision 1.3 2000/01/02 13:05:06 michaels
425 *searchRef is used for checking that a maskKey exists (not searchData)
426 *
427 */