1 /* -----------------------------------------------------------------------
2 * Copyright 2014 Equinox Software, Inc.
3 * Bill Erickson <berick@esilibrary.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * -----------------------------------------------------------------------
16 package org.evergreen_ils.hatch;
19 import java.util.LinkedList;
20 import java.util.Arrays;
21 import java.util.logging.Logger;
25 /** All files are read from and written to this directory */
29 // routine for scrubbing invalid chars from file names / paths
30 // http://stackoverflow.com/questions/1155107/is-there-a-cross-platform-java-method-to-remove-filename-special-chars
31 final static int[] illegalChars = {
32 34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
33 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
37 static { Arrays.sort(illegalChars); }
39 public static String cleanFileName(String badFileName) {
41 StringBuilder cleanName = new StringBuilder();
42 for (int i = 0; i < badFileName.length(); i++) {
43 int c = (int)badFileName.charAt(i);
44 if (Arrays.binarySearch(illegalChars, c) < 0) {
45 cleanName.append((char)c);
48 // avoid dupe-ing the placeholder chars, since that
49 // relays no useful information (apart from the number
50 // of illegal characters)
51 // This is usefuf or things like https:// with dupe "/" chars
53 cleanName.append('_');
57 return cleanName.toString();
59 // --------------------------------------------------
62 static final Logger logger = Logger.getLogger("org.evergreen_ils.hatch");
65 * Constructs a new FileIO with the provided base path.
67 * @param directory Directory to use in conjuction with the origin
68 * (see below) as the base path for all file operations.
69 * directory is assumed to be a valid path.
70 * @param origin Origin domain of this request.
72 public FileIO(String directory, String origin) {
74 originDomain = cleanFileName(origin);
78 * Returns the base directory as a File for all file IO actions
80 protected File baseDir() {
83 File dir = new File(basePath);
86 //logger.info("Unable to create directory: " + dir.getName());
91 // basePath + originDomain directory
92 File subDir = new File(basePath, originDomain);
93 if (!subDir.exists()) {
94 if (!subDir.mkdir()) {
95 //logger.info("Unable to create directory: " + subDir.getName());
100 //logger.info("baseDir: " + subDir.getName());
105 * Locates the requested file by name within our configured base path.
107 * @param key The relative file name (key)
108 * @return The File object if found.
110 protected File getFile(String key) {
111 File baseDir = baseDir();
112 if (baseDir == null) return null;
113 key = cleanFileName(key);
114 return new File(baseDir, key);
118 * Sets the content of a file.
120 * @param key The relative file name (key)
121 * @param text The new file content
123 * @return success or failure
125 public boolean set(String key, String text) {
126 //logger.info("set => " + key);
127 File file = getFile(key);
129 if (text == null) return false;
133 // delete the file if it exists
134 if (!file.exists() && !file.createNewFile()) {
136 //"Unable to create file: " + file.getCanonicalPath());
140 // destructive write (replace existing text)
141 Writer outStream = new BufferedWriter(
142 new FileWriter(file.getAbsoluteFile()));
144 outStream.write(text);
147 } catch(IOException e) {
148 //logger.warn("Error calling set() with key " + key);
157 * Appends content to a file.
159 * If the file does not exist, it is first created.
161 * @param key The relative file name (key)
162 * @param text The content to append to the file
163 * @return success or failure
165 public boolean append(String key, String text) {
166 //logger.info("append => " + key);
167 File file = getFile(key);
171 // create the file if it doesn's already exist
172 if (!file.exists() && !file.createNewFile()) {
174 //"Unable to create file: " + file.getCanonicalPath());
178 // non-destructive write (append)
179 Writer outStream = new BufferedWriter(
180 new FileWriter(file.getAbsoluteFile(), true));
181 outStream.write(text);
184 } catch(IOException e) {
185 //logger.warn("Error in append() with key " + key);
194 * Gets the text contents of a file.
196 * @param key The relative file name (key)
197 * @return The text content of the file
199 public String get(String key) {
200 //logger.info("get => " + key);
201 File file = getFile(key);
202 if (!file.exists()) return null;
205 StringBuffer buf = new StringBuffer();
208 BufferedReader reader = new BufferedReader(
209 new FileReader(file.getAbsoluteFile()));
211 while ( (line = reader.readLine()) != null) {
214 } catch (IOException e) {
215 //logger.warn("Error reading key: " + key);
220 return buf.toString();
224 * Removes (deletes) a file.
226 * @param The relative file name (key)
227 * @return success or failure
229 public boolean remove(String key) {
230 //logger.info("remove => " + key);
231 File file = getFile(key);
233 if (file.exists() && !file.delete()) {
235 //"Unable to delete file: " + file.getCanonicalPath());
239 //} catch (IOException e) {
240 //logger.warn("Error deleting key: " + key);
247 * Returns the full list of stored keys.
249 * @return Array of relative file names
251 public String[] keys() {
256 * Returns all keys begining with the specified prefix.
258 * @param prefix The initial substring used to limit the return set
259 * of keys. If the prefix is null, all keys are returned.
260 * @return Array of keys
262 public String[] keys(String prefix) {
263 //logger.info("keys => " + prefix);
265 File dir = baseDir();
266 if (dir == null || !dir.exists())
267 return new String[0];
269 LinkedList<String> nameList = new LinkedList<String>();
270 File[] files = dir.listFiles();
272 for (File file : files) {
274 String name = file.getName();
275 if (prefix == null) {
278 if (name.startsWith(prefix)) {
285 return (String[]) nameList.toArray(new String[0]);
289 // Migrate stored values from an old profile directory to a new one.
290 // If the destination path does not exists for the origin,
291 // create the path and copy data from the old profile dir to
292 // the new profile dir. The source directory is unmodified.
293 public void migrateProfileData(String newDir, String oldDir, String origin) {
294 origin = cleanFileName(origin);
296 // basePath directory
297 File baseDir = new File(newDir);
298 if (!baseDir.exists()) {
299 if (!baseDir.mkdir()) {
300 logger.warning("Unable to create baseDirectory: " + baseDir.getPath());
304 File originDir = new File(newDir, origin);
305 if (originDir.exists()) {
306 // New dir is already built. No need to migrate.
310 File oldOriginDir = new File(oldDir, origin);
312 if (oldOriginDir.exists()) {
313 logger.info("Migrating data from " +
314 oldDir + " to " + newDir + " for origin " + origin);
316 FileIO oldFiles = new FileIO(oldDir, origin);
317 for (String key: oldFiles.keys()) {
318 logger.info("Migrating key " + key);
319 String val = oldFiles.get(key);
324 // create a new directory
325 if (!originDir.mkdir()) {
327 "Error creating data directory: " + originDir.getPath());