001 package ca.uhn.hl7v2.util;
002
003 import java.io.File;
004 import java.io.FileNotFoundException;
005 import java.io.FileReader;
006 import java.io.FileWriter;
007 import java.io.IOException;
008
009 import org.slf4j.Logger;
010 import 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 */
028 public 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 }