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