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 | } |