1 /* PersistentMap.java -- The persistent object naming map
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 
39 package gnu.classpath.tools.orbd;
40 
41 import gnu.CORBA.NamingService.NamingMap;
42 
43 import java.io.BufferedOutputStream;
44 import java.io.BufferedReader;
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.InputStreamReader;
50 import java.io.OutputStream;
51 import java.util.Iterator;
52 import java.util.Map;
53 
54 import org.omg.CORBA.ORB;
55 import org.omg.CosNaming.NameComponent;
56 import org.omg.CosNaming.NamingContextPackage.AlreadyBound;
57 import org.omg.CosNaming.NamingContextPackage.InvalidName;
58 
59 /**
60  * The persistent object naming map for the persistent naming service. The
61  * inherited (super.) naming map implementation is transient and is used as a
62  * cache. During the normal work, the naming map does not read from the disk,
63  * just stores the changes there. Map only reads from the disk when it starts.
64  *
65  * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
66  */
67 public class PersistentMap
68     extends NamingMap
69 {
70   /**
71    * The data entry.
72    */
73   public static class Entry
74   {
75     String id;
76 
77     String kind;
78 
79     String ior;
80 
81     /**
82      * Get the name component node.
83      */
getComponent()84     public NameComponent getComponent()
85     {
86       return new NameComponent(id, kind);
87     }
88 
89     /**
90      * Write the naming map entry to the output stream.
91      */
write(OutputStream out)92     public void write(OutputStream out) throws IOException
93     {
94       // Format: id.kind <eoln> ior <eoln><eoln>
95       out.write(getKey(id, kind).getBytes());
96       out.write('\n');
97       out.write(ior.getBytes());
98       out.write('\n');
99       out.close();
100     }
101 
102     /**
103      * Read the name component from the input stream
104      */
read(BufferedReader in)105     public boolean read(BufferedReader in) throws IOException
106     {
107       String key = in.readLine();
108       String xior = in.readLine();
109 
110       if (key != null && xior != null)
111         {
112           if (key.length() < 2)
113             {
114               // A single char key cannot have the kind part.
115               id = key;
116               kind = "";
117             }
118           else
119             {
120               // Search for the id/kind splitter, dot:
121               int iks = - 1;
122               for (int i = 1; i < key.length(); i++)
123                 {
124                   if (key.charAt(i) == '.')
125                     // The id is separated from kind by dot, unless preceeded by
126                     // the
127                     // escape character, \.
128                     if (key.charAt(i - 1) != '\\')
129                       {
130                         iks = i;
131                         break;
132                       }
133                 }
134 
135               // May also end by dot, if the kind field is missing.
136               if (iks < 0)
137                 {
138                   id = key;
139                   kind = "";
140                 }
141               else if (iks == key.length() - 1)
142                 {
143                   id = key.substring(0, key.length() - 1);
144                   kind = "";
145                 }
146               else
147                 {
148                   id = key.substring(0, iks);
149                   kind = key.substring(iks + 1);
150                 }
151             }
152           ior = xior;
153           return true;
154         }
155       else
156         return false;
157     }
158 
159     /**
160      * Get the key value from the name component.
161      *
162      * @param id the component id
163      * @param kind the component kind
164      * @return the key value
165      */
getKey(String id, String kind)166     public String getKey(String id, String kind)
167     {
168       StringBuilder b = new StringBuilder(id.length() + 8);
169       appEscaping(b, id);
170       b.append('.');
171       if (kind != null && kind.length() > 0)
172         appEscaping(b, kind);
173       return b.toString();
174     }
175 
176     /**
177      * Append the contents of the string to this string buffer, inserting the
178      * escape sequences, where required.
179      *
180      * @param b a buffer to append the contents to.
181      * @param s a string to append.
182      */
appEscaping(StringBuilder b, String s)183     void appEscaping(StringBuilder b, String s)
184     {
185       char c;
186       for (int i = 0; i < s.length(); i++)
187         {
188           c = s.charAt(i);
189           switch (c)
190             {
191             case '.':
192             case '/':
193             case '\\':
194               b.append('\\');
195               b.append(c);
196               break;
197 
198             default:
199               b.append(c);
200               break;
201             }
202         }
203     }
204   }
205 
206   /**
207    * The file, where the persistent naming map stores the information. The
208    * format of this file is n*(id LF kind LF ior LFLF).
209    */
210   public final File file;
211 
212   /**
213    * The naming service ORB, used to obtain and produce the object stringified
214    * references.
215    */
216   ORB orb;
217 
218   /**
219    * If true, all existing data on the file system are discarded.
220    */
221   boolean reset;
222 
223   /**
224    * Create the persistent map that stores information in the given file.
225    *
226    * @param an_orb the naming service ORB, used to obtain and produce the object
227    *          stringified references.
228    * @param mapFile the file, where the persistent information is stored.
229    * @param a_reset if true, the previous naming data are discarded. If false
230    *          (normally expected), they are loaded from the persistent memory to
231    *          provide the persistence.
232    */
PersistentMap(ORB an_orb, File mapFile, boolean a_reset)233   public PersistentMap(ORB an_orb, File mapFile, boolean a_reset)
234   {
235     super();
236     orb = an_orb;
237     file = mapFile;
238     reset = a_reset;
239 
240     // Initialise the persistent map with existing data.
241     if (file.exists() && ! reset)
242       {
243 
244         BufferedReader in;
245         try
246           {
247             FileInputStream fin = new FileInputStream(file);
248             in = new BufferedReader(new InputStreamReader(fin));
249             Entry e = new Entry();
250             boolean ok;
251 
252             while (e.read(in))
253               {
254                 org.omg.CORBA .Object object = string_to_object(e.ior);
255                 orb.connect(object);
256                 map.put(e.getComponent(), object);
257               }
258           }
259         catch (Exception ex)
260           {
261             InternalError ierr = new InternalError(file.getAbsolutePath());
262             ierr.initCause(ex);
263             throw ierr;
264           }
265       }
266   }
267 
268   /**
269    * Restore object from its string description.
270    *
271    * @param description the string, describing the object
272    *
273    * @return the object.
274    */
string_to_object(String description)275   protected org.omg.CORBA.Object string_to_object(String description)
276   {
277     return orb.string_to_object(description);
278   }
279 
280   /**
281    * Convert the object to its string description
282    *
283    * @param object the object to convert
284    * @return the string description of the object
285    */
object_to_string(org.omg.CORBA .Object object)286   protected String object_to_string(org.omg.CORBA .Object object)
287   {
288       return orb.object_to_string(object);
289   }
290 
291   /**
292    * Put the given GIOP object, specifying the given name as a key. If the entry
293    * with the given name already exists, or if the given object is already
294    * mapped under another name, the {@link AlreadyBound} exception will be
295    * thrown.
296    *
297    * @param name the name
298    * @param object the object
299    */
bind(NameComponent name, org.omg.CORBA.Object object)300   public void bind(NameComponent name, org.omg.CORBA.Object object)
301       throws AlreadyBound, InvalidName
302   {
303     if (!containsKey(name))
304       {
305         super.bind(name, object);
306         register(name, object);
307       }
308     else
309       throw new AlreadyBound(name.id + "." + name.kind);
310   }
311 
312   /**
313    * Put the given CORBA object, specifying the given name as a key. Remove all
314    * pre - existing mappings for the given name and object.
315    *
316    * @param name the name.
317    * @param object the object
318    */
rebind(NameComponent name, org.omg.CORBA.Object object)319   public void rebind(NameComponent name, org.omg.CORBA.Object object)
320       throws InvalidName
321   {
322     if (containsKey(name))
323       {
324         org.omg.CORBA.Object existing = get(name);
325         String ior = object_to_string(object);
326         String xior = object_to_string(existing);
327 
328         // Same name and same ior - nothing to do.
329         if (ior.equals(xior))
330           return;
331         else
332           remove(name);
333       }
334 
335     Iterator iter = entries().iterator();
336     Map.Entry item;
337 
338     // Remove the existing mapping for the given object, if present.
339     while (iter.hasNext())
340       {
341         item = (Map.Entry) iter.next();
342         if (item.getValue().equals(object))
343           iter.remove();
344       }
345 
346     map.put(name, object);
347     register(name, object);
348   }
349 
350   /**
351    * Removes the given name, if present.
352    *
353    * @param name a name to remove.
354    */
remove(NameComponent name)355   public void remove(NameComponent name)
356   {
357     super.remove(name);
358     unregister(name);
359   }
360 
361   /**
362    * Register this name - object pair in the persistent storage.
363    *
364    * @param name the name.
365    * @param object the object
366    */
register(NameComponent name, org.omg.CORBA.Object object)367   public void register(NameComponent name, org.omg.CORBA.Object object)
368   {
369     // If this key is already known, and this is the same object,
370     // then return without action.
371     String ior = object_to_string(object);
372 
373     synchronized (file)
374       {
375         try
376           {
377             FileOutputStream fou;
378 
379             if (! file.exists())
380               fou = new FileOutputStream(file);
381             else
382               fou = new FileOutputStream(file, true);
383 
384             Entry e = new Entry();
385             e.id = name.id;
386             e.kind = name.kind;
387             e.ior = ior;
388             e.write(fou);
389             fou.close();
390           }
391         catch (Exception e)
392           {
393             InternalError ierr = new InternalError(file.getAbsolutePath());
394             ierr.initCause(e);
395             throw ierr;
396           }
397       }
398   }
399 
400   /**
401    * Remove this name from the persistent storage.
402    *
403    * @param name the name to remove
404    */
unregister(NameComponent name)405   public void unregister(NameComponent name)
406   {
407     synchronized (file)
408       {
409         try
410           {
411             File nf = new File(file.getParent(), file.getName() + "_t");
412             FileInputStream fin = new FileInputStream(file);
413             FileOutputStream fou = new FileOutputStream(nf);
414             BufferedOutputStream ou = new BufferedOutputStream(fou);
415 
416             BufferedReader in = new BufferedReader(new InputStreamReader(fin));
417             String s;
418             String nk = name.kind;
419             if (nk == null)
420               nk = "";
421 
422             Entry e = new Entry();
423 
424             while (e.read(in))
425               {
426                 if (e.id.equals(name.id) && e.kind.equals(nk))
427                   {
428                     // Do nothing - skip.
429                   }
430                 else
431                   {
432                     e.write(ou);
433                   }
434               }
435 
436             File deleteIt = new File(file.getParent(), file.getName() + "_d");
437             if (deleteIt.exists())
438               deleteIt.delete();
439 
440             if (! file.renameTo(deleteIt))
441               throw new IOException(file.getAbsolutePath() + " rename failed");
442 
443             if (! nf.renameTo(file))
444               throw new IOException(file.getAbsolutePath() + " rename failed");
445           }
446         catch (Exception e)
447           {
448             InternalError ierr = new InternalError(file.getAbsolutePath());
449             ierr.initCause(e);
450             throw ierr;
451           }
452       }
453   }
454 }
455