| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| FileBasedGenerator |
|
| 2.8181818181818183;2.818 |
| 1 | /** | |
| 2 | The contents of this file are subject to the Mozilla Public License Version 1.1 | |
| 3 | (the "License"); you may not use this file except in compliance with the License. | |
| 4 | You may obtain a copy of the License at http://www.mozilla.org/MPL/ | |
| 5 | Software distributed under the License is distributed on an "AS IS" basis, | |
| 6 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the | |
| 7 | specific language governing rights and limitations under the License. | |
| 8 | ||
| 9 | The Original Code is "FileBasedGenerator.java". Description: | |
| 10 | "Replacement for the legagy MessageIDGenerator class" | |
| 11 | ||
| 12 | The Initial Developer of the Original Code is University Health Network. Copyright (C) | |
| 13 | 2001. All Rights Reserved. | |
| 14 | ||
| 15 | Contributor(s): ______________________________________. | |
| 16 | ||
| 17 | Alternatively, the contents of this file may be used under the terms of the | |
| 18 | GNU General Public License (the "GPL"), in which case the provisions of the GPL are | |
| 19 | applicable instead of those above. If you wish to allow use of your version of this | |
| 20 | file only under the terms of the GPL and not to allow others to use your version | |
| 21 | of this file under the MPL, indicate your decision by deleting the provisions above | |
| 22 | and replace them with the notice and other provisions required by the GPL License. | |
| 23 | If you do not delete the provisions above, a recipient may use your version of | |
| 24 | this file under either the MPL or the GPL. | |
| 25 | */ | |
| 26 | package ca.uhn.hl7v2.util.idgenerator; | |
| 27 | ||
| 28 | import java.io.BufferedReader; | |
| 29 | import java.io.File; | |
| 30 | import java.io.FileInputStream; | |
| 31 | import java.io.FileOutputStream; | |
| 32 | import java.io.IOException; | |
| 33 | import java.io.InputStreamReader; | |
| 34 | import java.io.PrintWriter; | |
| 35 | import java.util.concurrent.locks.ReentrantLock; | |
| 36 | ||
| 37 | import org.slf4j.Logger; | |
| 38 | import org.slf4j.LoggerFactory; | |
| 39 | ||
| 40 | import ca.uhn.hl7v2.util.Home; | |
| 41 | ||
| 42 | /** | |
| 43 | * Replacement for {@link ca.uhn.hl7v2.util.MessageIDGenerator}. You should not use this class | |
| 44 | * directly, however, but wrap it into a {@link DelegatingHiLoGenerator} generator. Its primary | |
| 45 | * improvement over {@link ca.uhn.hl7v2.util.MessageIDGenerator} is that you can set path and file | |
| 46 | * name. | |
| 47 | * <p> | |
| 48 | * Reading and writing to the file is thread-safe, however, you should not use the same file from | |
| 49 | * different Java processes because no read/write locks are being checked. | |
| 50 | */ | |
| 51 | public class FileBasedGenerator extends InMemoryIDGenerator { | |
| 52 | ||
| 53 | 5 | private static final Logger LOG = LoggerFactory.getLogger(FileBasedGenerator.class.getName()); |
| 54 | ||
| 55 | 5555 | private String directory = Home.getHomeDirectory().getAbsolutePath(); |
| 56 | 5555 | private String fileName = "id_file"; |
| 57 | 5555 | private boolean neverFail = true; |
| 58 | 5555 | private boolean used = false; |
| 59 | 5555 | private boolean minimizeReads = false; |
| 60 | 5555 | private ReentrantLock lock = new ReentrantLock(); |
| 61 | ||
| 62 | public FileBasedGenerator() { | |
| 63 | 35 | this(1L); |
| 64 | 35 | } |
| 65 | ||
| 66 | FileBasedGenerator(long increment) { | |
| 67 | 5555 | super(increment); |
| 68 | 5555 | } |
| 69 | ||
| 70 | public String getID() throws IOException { | |
| 71 | try { | |
| 72 | 6855 | lock.lock(); |
| 73 | ||
| 74 | // If ID is 0, read initial value from file if possible | |
| 75 | 6855 | if (!minimizeReads || !used) { |
| 76 | 6855 | long readInitialValue = readInitialValue(getFilePath()); |
| 77 | 6850 | if (readInitialValue >= 0) { |
| 78 | 6835 | set(readInitialValue); |
| 79 | } | |
| 80 | 6850 | used = true; |
| 81 | } | |
| 82 | ||
| 83 | 6850 | String id = super.getID(); |
| 84 | // The id held in the file is always <increment> larger so that | |
| 85 | // the ID is still unique after a restart. | |
| 86 | 6850 | writeNextValue(Long.parseLong(id) + getIncrement()); |
| 87 | 13700 | return id; |
| 88 | } finally { | |
| 89 | 6855 | lock.unlock(); |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | ||
| 94 | private void writeNextValue(long id) throws IOException { | |
| 95 | 6885 | PrintWriter pw = null; |
| 96 | try { | |
| 97 | 6885 | pw = new PrintWriter(new FileOutputStream(getFilePath(), false)); |
| 98 | 6875 | pw.println(Long.toString(id)); |
| 99 | 10 | } catch (IOException e) { |
| 100 | 10 | if (neverFail) { |
| 101 | 20 | LOG.warn("Could not write ID to file {}, going to use internal ID generator. {}", |
| 102 | 10 | getFilePath(), e.getMessage()); |
| 103 | 10 | return; |
| 104 | } | |
| 105 | 0 | throw e; |
| 106 | } finally { | |
| 107 | 6885 | if (pw != null) |
| 108 | 6875 | pw.close(); |
| 109 | } | |
| 110 | 6875 | } |
| 111 | ||
| 112 | private long readInitialValue(String path) throws IOException { | |
| 113 | 6855 | BufferedReader br = null; |
| 114 | 6855 | String id = null; |
| 115 | try { | |
| 116 | 6855 | br = new BufferedReader(new InputStreamReader(new FileInputStream(path))); |
| 117 | 6835 | id = br.readLine(); |
| 118 | 13670 | return Long.parseLong(id); |
| 119 | 20 | } catch (IOException e) { |
| 120 | 20 | LOG.info("Could not read ID file {} ", path); |
| 121 | 20 | if (!neverFail) { |
| 122 | 5 | throw e; |
| 123 | } | |
| 124 | 30 | return -1; |
| 125 | 0 | } catch (NumberFormatException e) { |
| 126 | 0 | LOG.info("ID {} read from file is not a number", id); |
| 127 | 0 | return -1; |
| 128 | } finally { | |
| 129 | 6855 | if (br != null) |
| 130 | try { | |
| 131 | 6835 | br.close(); |
| 132 | 0 | } catch (IOException e) { |
| 133 | 6840 | } |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | private String getFilePath() { | |
| 138 | 13750 | return new File(directory, fileName).getAbsolutePath(); |
| 139 | } | |
| 140 | ||
| 141 | public void setDirectory(String directory) { | |
| 142 | try { | |
| 143 | 15 | lock.lock(); |
| 144 | 15 | this.directory = directory; |
| 145 | } finally { | |
| 146 | 15 | lock.unlock(); |
| 147 | 15 | } |
| 148 | 15 | } |
| 149 | ||
| 150 | public void setFileName(String fileName) { | |
| 151 | try { | |
| 152 | 15 | lock.lock(); |
| 153 | 15 | this.fileName = fileName; |
| 154 | } finally { | |
| 155 | 15 | lock.unlock(); |
| 156 | 15 | } |
| 157 | 15 | } |
| 158 | ||
| 159 | /** | |
| 160 | * If set to <code>true</code> (default is <code>false</code>) the generator | |
| 161 | * minimizes the number of disk reads by caching the last read value. This means | |
| 162 | * one less disk read per X number of IDs generated, but also means that multiple | |
| 163 | * instances of this generator may clobber each other's values. | |
| 164 | */ | |
| 165 | public void setMinimizeReads(boolean theMinimizeReads) { | |
| 166 | 0 | minimizeReads = theMinimizeReads; |
| 167 | 0 | } |
| 168 | ||
| 169 | /** | |
| 170 | * If set to <code>false</code> (default is <code>true</code>), | |
| 171 | * retrieving a new ID may fail if the ID file in the home | |
| 172 | * directory can not be written/read. If set to true, failures | |
| 173 | * will be ignored, which means that IDs may be repeated after | |
| 174 | * a JVM restart. | |
| 175 | */ | |
| 176 | public void setNeverFail(boolean neverFail) { | |
| 177 | 10 | this.neverFail = neverFail; |
| 178 | 10 | } |
| 179 | ||
| 180 | public void reset() { | |
| 181 | try { | |
| 182 | 35 | lock.lock(); |
| 183 | 35 | super.reset(); |
| 184 | 35 | writeNextValue(0l); |
| 185 | 0 | } catch (IOException e) { |
| 186 | 0 | throw new IllegalStateException("Cannot initialize persistent ID generator", e); |
| 187 | } finally { | |
| 188 | 35 | lock.unlock(); |
| 189 | 35 | } |
| 190 | 35 | } |
| 191 | ||
| 192 | } |