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