OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / tools / gnu / classpath / tools / rmid / PersistentBidiHashTable.java
1 /* PersistentBidiHasthable.java -- Bidirectional persistent hash table.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.classpath.tools.rmid;
39
40 import gnu.classpath.tools.common.Persistent;
41 import gnu.classpath.tools.rmid.ActivationSystemImpl;
42 import gnu.java.rmi.activation.BidiTable;
43
44 import java.io.BufferedInputStream;
45 import java.io.BufferedOutputStream;
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileOutputStream;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.ObjectInputStream;
52 import java.io.ObjectOutputStream;
53 import java.util.Hashtable;
54 import java.util.Iterator;
55 import java.util.Map;
56 import java.util.TimerTask;
57
58 /**
59  * The persistent bidirectional hash table, maps both a to b and b to a. The
60  * changes are written to dist after SAVE_AT_MOST_AFTER time from the latest
61  * database change or at most after ALWAYS_UPDATE, if the database is updated
62  * very frequently. To ensure that no information is lost, the shutdown method
63  * must be called before exit.
64  * 
65  * @author Audrius Meskauskas (audriusa@bioinformatics.org)
66  */
67 public class PersistentBidiHashTable extends BidiTable implements
68     Persistent
69 {
70   class WriteToDiskTask extends TimerTask
71   {
72     /**
73      * Save the database.
74      */
75     public void run()
76     {
77       writeContent();
78       sheduled = null;
79     }
80   }
81
82   /**
83    * Replaces instances of ActivationSystemImpl into the currently active
84    * instance of the ActivationSystemImpl
85    */
86   class AdaptedReader extends ObjectInputStream
87   {
88     AdaptedReader(InputStream in) throws IOException
89     {
90       super(in);
91       enableResolveObject(true);
92     }
93
94     protected Object resolveObject(Object obj) throws IOException
95     {
96       if (obj instanceof ActivationSystemImpl)
97         return ActivationSystemImpl.singleton2;
98       else
99         return obj;
100     }
101   }
102
103   /**
104    * The database file.
105    */
106   File database;
107
108   /**
109    * The currently sheduled write to disk task, null if none.
110    */
111   WriteToDiskTask sheduled = null;
112
113   /**
114    * The time, when the disk database was last updated.
115    */
116   long lastUpdated;
117
118   /**
119    * Create the unitialised instance that must be initalised when
120    * ActivationSystemImpl.singleton2 is assigned.
121    */
122   public PersistentBidiHashTable()
123   {
124     // Do not initalise the table fields - the initalise method must be
125     // called later.
126     super(0);
127   }
128
129   /**
130    * Create a new persistent table that stores its information into the given
131    * file. The ActivationSystemImpl.singleton2 must be assigned.
132    * 
133    * @param file
134    *          the file, where the table stores its information.
135    * @param coldStart
136    *          if true, the existing file with this name will be erased and
137    *          ignored. Otherwise, it will be assumed that the file contains the
138    *          persistent table information.
139    */
140   public void init(File file, boolean coldStart)
141   {
142     try
143       {
144         database = file;
145         if (database.exists())
146           {
147             if (coldStart)
148               {
149                 k2v = new Hashtable();
150                 v2k = new Hashtable();
151                 database.delete();
152               }
153             else
154               {
155                 FileInputStream fi = new FileInputStream(file);
156                 BufferedInputStream b = new BufferedInputStream(fi);
157                 ObjectInputStream oin = new AdaptedReader(b);
158
159                 k2v = (Map) oin.readObject();
160                 oin.close();
161
162                 v2k = new Hashtable(k2v.size());
163
164                 // Reguild v2k from k2v:
165                 Iterator en = k2v.keySet().iterator();
166                 Object key;
167                 while (en.hasNext())
168                   {
169                     key = en.next();
170                     v2k.put(k2v.get(key), key);
171                   }
172               }
173           }
174         else
175           {
176             k2v = new Hashtable();
177             v2k = new Hashtable();
178           }
179       }
180     catch (Exception ioex)
181       {
182         InternalError ierr = new InternalError("Unable to intialize with file "
183                                                + file);
184         ierr.initCause(ioex);
185         throw ierr;
186       }
187   }
188
189   /**
190    * Write the database content to the disk.
191    */
192   public synchronized void writeContent()
193   {
194     try
195       {
196         FileOutputStream fou = new FileOutputStream(database);
197         BufferedOutputStream b = new BufferedOutputStream(fou);
198         ObjectOutputStream oout = new ObjectOutputStream(b);
199         oout.writeObject(k2v);
200         oout.close();
201       }
202     catch (Exception ioex)
203       {
204         InternalError ierr = new InternalError(
205                                                "Failed to write database to disk: "
206                                                    + database);
207         ierr.initCause(ioex);
208         throw ierr;
209       }
210   }
211
212   /**
213    * Mark the modified database as modified. The database will be written after
214    * several seconds, unless another modification occurs.
215    */
216   public void markDirty()
217   {
218     if (System.currentTimeMillis() - lastUpdated > ALWAYS_UPDATE)
219       {
220         // Force storing to disk under intensive operation.
221         writeContent();
222         lastUpdated = System.currentTimeMillis();
223         if (sheduled != null)
224           {
225             sheduled.cancel();
226             sheduled = null;
227           }
228       }
229     else
230       {
231         // Otherwise coalesce the disk database copy update events.
232         if (sheduled != null)
233           sheduled.cancel();
234         sheduled = new WriteToDiskTask();
235         timer.schedule(sheduled, SAVE_AT_MOST_AFTER);
236       }
237   }
238
239   /**
240    * Save the current database state to the disk before exit.
241    */
242   public void shutdown()
243   {
244     if (sheduled != null)
245       {
246         writeContent();
247         sheduled = null;
248       }
249   }
250
251   /**
252    * Update the memory maps and mark as should be written to the disk.
253    */
254   public void put(Object key, Object value)
255   {
256     super.put(key, value);
257     markDirty();
258   }
259
260   /**
261    * Update the memory maps and mark as should be written to the disk.
262    */
263   public void removeKey(Object key)
264   {
265     super.removeKey(key);
266     markDirty();
267   }
268
269 }