001package ca.uhn.hl7v2.util; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import java.io.FileReader; 006import java.io.FileWriter; 007import java.io.IOException; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * <p> 014 * Creates unique message IDs. IDs are stored in a file called {@link Home#getHomeDirectory() hapi.home}/id_file for persistence 015 * across JVM sessions. Note that if one day you run the JVM with a new working directory, 016 * you must move or copy id_file into this directory so that new ID numbers will begin 017 * with the last one used, rather than starting over again. 018 * </p> 019 * <p> 020 * Note that as of HAPI 2.0, by default this class will not fail even if the id_file can 021 * not be read/written. In this case, HAPI will try to fail gracefully by simply generating 022 * a numeric sequence starting at zero. This behaviour can be overwritten using 023 * {@link #NEVER_FAIL_PROPERTY} 024 * </p> 025 * 026 * @author Neal Acharya 027 */ 028public class MessageIDGenerator { 029 030 private static final Logger ourLog = LoggerFactory.getLogger(MessageIDGenerator.class.getName()); 031 private static MessageIDGenerator messageIdGenerator; 032 033 /** 034 * Contains the complete path to the default ID file, which is a plain text file containing 035 * the number corresponding to the last generated ID 036 */ 037 public final static String DEFAULT_ID_FILE = Home.getHomeDirectory().getAbsolutePath() + "/id_file"; 038 039 /** 040 * System property key which indicates that this class should never fail. If this 041 * system property is set to false (default is true), as in the following code:<br> 042 * <code>System.setProperty(MessageIDGenerator.NEVER_FAIL_PROPERTY, Boolean.FALSE.toString());</code><br> 043 * this class will fail if the underlying disk file can not be 044 * read or written. This means you are roughly guaranteed a unique 045 * ID number between JVM sessions (barring the file getting lost or corrupted). 046 */ 047 public static final String NEVER_FAIL_PROPERTY = MessageIDGenerator.class.getName() + "_NEVER_FAIL_PROPERTY"; 048 049 private long id; 050 private FileWriter fileW; 051 052 /** 053 * Constructor 054 * Creates an instance of the class 055 * Its reads an id (longint#) from an external file, if one is not present then the external file 056 * is created and initialized to zero. 057 * This id is stored into the private field of id. 058 */ 059 private MessageIDGenerator() throws IOException { 060 initialize(); 061 }//end constructor code 062 063 064 /** 065 * Force the generator to re-load the ID file and initialize itself. 066 * 067 * This method is mostly provided as a convenience to unit tests, and does 068 * not normally need to be called. 069 */ 070 void initialize() throws IOException { 071 id = 0; 072 073 /*check external file for the last value unique id value generated by 074 this class*/ 075 try{ 076 // We should check to see if the external file for storing the unique ids exists 077 File extFile = new File(DEFAULT_ID_FILE); 078 if (extFile.createNewFile()== true){ 079 /*there was no existing file so a new one has been created with createNewFile method. The 080 file is stored at <hapi.home>/id_file.txt */ 081 // We can simply initialize the private id field to zero 082 id = 0; 083 084 }//end if 085 else{ 086 /*The file does exist which is why we received false from the 087 createNewFile method. We should now try to read from this file*/ 088 FileReader fileR = new FileReader(DEFAULT_ID_FILE); 089 char[] charArray = new char[100]; 090 int e = fileR.read(charArray); 091 if (e <= 0){ 092 /*We know the file exists but it has no value stored in it. So at this point we can simply initialize the 093 private id field to zero*/ 094 id = 0; 095 }//end if 096 else{ 097 /* Here we know that the file exists and has a stored value. we should read this value and set the 098 private id field to it*/ 099 String idStr = String.valueOf(charArray); 100 String idStrTrim = idStr.trim(); 101 102 try { 103 id = Long.parseLong(idStrTrim); 104 } catch (NumberFormatException nfe) { 105 ourLog.warn("Failed to parse message ID file value \"" + idStrTrim + "\". Defaulting to 0."); 106 } 107 108 }//end else 109 //Fix for bug 1100881: Close the file after writing. 110 fileR.close(); 111 }//end else 112 } catch (FileNotFoundException e) { 113 ourLog.error("Failed to locate message ID file. Message was: {}", e.getMessage()); 114 } catch (IOException e) { 115 if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) { 116 ourLog.warn("Could not retrieve message ID file, going to default to ID of 0. Message was: {}", e.getMessage()); 117 id = 0; 118 return; 119 } else { 120 throw e; 121 } 122 } 123 } 124 125 /** 126 * Synchronized method used to return the single (static) instance of the class 127 */ 128 public static synchronized MessageIDGenerator getInstance() throws IOException { 129 if (messageIdGenerator == null) 130 messageIdGenerator = new MessageIDGenerator(); 131 return messageIdGenerator; 132 }//end method 133 134 /** 135 * Synchronized method used to return the incremented id value 136 */ 137 public synchronized String getNewID() throws IOException{ 138 try { 139 //increment the private field 140 id = id + 1; 141 //write the id value to the file 142 String idStr = String.valueOf(id); 143 144 //create an instance of the Filewriter Object pointing to "C:\\extfiles\\Idfile.txt" 145 fileW = new FileWriter(DEFAULT_ID_FILE, false); 146 fileW.write(idStr); 147 fileW.flush(); 148 fileW.close(); 149 } catch (FileNotFoundException e) { 150 if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) { 151 ourLog.info("Failed to create message ID file. Message was: {}", e.getMessage()); 152 fileW = null; 153 } 154 } catch (IOException e) { 155 if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) { 156 ourLog.debug("Failed to create message ID file. Message was: {}", e.getMessage()); 157 fileW = null; 158 } else { 159 throw e; 160 } 161 } 162 return String.valueOf(id); 163 }//end method 164 165}