update nikiroo-utils
[nikiroo-utils.git] / src / be / nikiroo / fanfix / Instance.java
1 package be.nikiroo.fanfix;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Date;
6
7 import be.nikiroo.fanfix.bundles.Config;
8 import be.nikiroo.fanfix.bundles.ConfigBundle;
9 import be.nikiroo.fanfix.bundles.StringId;
10 import be.nikiroo.fanfix.bundles.StringIdBundle;
11 import be.nikiroo.fanfix.bundles.UiConfig;
12 import be.nikiroo.fanfix.bundles.UiConfigBundle;
13 import be.nikiroo.fanfix.library.BasicLibrary;
14 import be.nikiroo.fanfix.library.CacheLibrary;
15 import be.nikiroo.fanfix.library.LocalLibrary;
16 import be.nikiroo.fanfix.library.RemoteLibrary;
17 import be.nikiroo.utils.Cache;
18 import be.nikiroo.utils.IOUtils;
19 import be.nikiroo.utils.TempFiles;
20 import be.nikiroo.utils.TraceHandler;
21 import be.nikiroo.utils.resources.Bundles;
22
23 /**
24 * Global state for the program (services and singletons).
25 *
26 * @author niki
27 */
28 public class Instance {
29 private static ConfigBundle config;
30 private static UiConfigBundle uiconfig;
31 private static StringIdBundle trans;
32 private static DataLoader cache;
33 private static BasicLibrary lib;
34 private static File coverDir;
35 private static File readerTmp;
36 private static File remoteDir;
37 private static String configDir;
38 private static TraceHandler tracer;
39 private static TempFiles tempFiles;
40
41 static {
42 // Before we can configure it:
43 tracer = new TraceHandler(true, checkEnv("DEBUG"), checkEnv("DEBUG"));
44
45 // config dir:
46 configDir = getConfigDir();
47 if (!new File(configDir).exists()) {
48 new File(configDir).mkdirs();
49 }
50
51 // Most of the rest is dependent upon this:
52 createConfigs(configDir, false);
53
54 // update tracer:
55 boolean debug = Instance.getConfig()
56 .getBoolean(Config.DEBUG_ERR, false);
57 boolean trace = Instance.getConfig().getBoolean(Config.DEBUG_TRACE,
58 false);
59
60 if (checkEnv("DEBUG")) {
61 debug = true;
62 trace = true;
63 }
64
65 tracer = new TraceHandler(true, debug, trace);
66
67 // default Library
68 remoteDir = new File(configDir, "remote");
69 lib = createDefaultLibrary(remoteDir);
70
71 // create cache
72 File tmp = getFile(Config.CACHE_DIR);
73 if (tmp == null) {
74 // Could have used: System.getProperty("java.io.tmpdir")
75 tmp = new File(configDir, "tmp");
76 }
77 String ua = config.getString(Config.USER_AGENT);
78 try {
79 int hours = config.getInteger(Config.CACHE_MAX_TIME_CHANGING, -1);
80 int hoursLarge = config
81 .getInteger(Config.CACHE_MAX_TIME_STABLE, -1);
82 cache = new DataLoader(tmp, ua, hours, hoursLarge);
83 } catch (IOException e) {
84 tracer.error(new IOException(
85 "Cannot create cache (will continue without cache)", e));
86 cache = new DataLoader(ua);
87 }
88
89 cache.setTraceHandler(tracer);
90
91 // readerTmp / coverDir
92 readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER);
93 if (readerTmp == null) {
94 readerTmp = new File(configDir, "tmp-reader");
95 }
96
97 coverDir = getFile(Config.DEFAULT_COVERS_DIR);
98 if (coverDir != null && !coverDir.exists()) {
99 tracer.error(new IOException(
100 "The 'default covers' directory does not exists: "
101 + coverDir));
102 coverDir = null;
103 }
104
105 try {
106 tempFiles = new TempFiles("fanfix");
107 } catch (IOException e) {
108 tracer.error(new IOException("Cannot create temporary directory", e));
109 }
110 }
111
112 /**
113 * The traces handler for this {@link Cache}.
114 * <p>
115 * It is never NULL.
116 *
117 * @return the traces handler (never NULL)
118 */
119 public static TraceHandler getTraceHandler() {
120 return tracer;
121 }
122
123 /**
124 * The traces handler for this {@link Cache}.
125 *
126 * @param tracer
127 * the new traces handler or NULL
128 */
129 public static void setTraceHandler(TraceHandler tracer) {
130 if (tracer == null) {
131 tracer = new TraceHandler(false, false, false);
132 }
133
134 Instance.tracer = tracer;
135 cache.setTraceHandler(tracer);
136 }
137
138 /**
139 * Get the (unique) configuration service for the program.
140 *
141 * @return the configuration service
142 */
143 public static ConfigBundle getConfig() {
144 return config;
145 }
146
147 /**
148 * Get the (unique) UI configuration service for the program.
149 *
150 * @return the configuration service
151 */
152 public static UiConfigBundle getUiConfig() {
153 return uiconfig;
154 }
155
156 /**
157 * Reset the configuration.
158 *
159 * @param resetTrans
160 * also reset the translation files
161 */
162 public static void resetConfig(boolean resetTrans) {
163 String dir = Bundles.getDirectory();
164 Bundles.setDirectory(null);
165 try {
166 try {
167 ConfigBundle config = new ConfigBundle();
168 config.updateFile(configDir);
169 } catch (IOException e) {
170 tracer.error(e);
171 }
172 try {
173 UiConfigBundle uiconfig = new UiConfigBundle();
174 uiconfig.updateFile(configDir);
175 } catch (IOException e) {
176 tracer.error(e);
177 }
178
179 if (resetTrans) {
180 try {
181 StringIdBundle trans = new StringIdBundle(null);
182 trans.updateFile(configDir);
183 } catch (IOException e) {
184 tracer.error(e);
185 }
186 }
187 } finally {
188 Bundles.setDirectory(dir);
189 }
190 }
191
192 /**
193 * Get the (unique) {@link DataLoader} for the program.
194 *
195 * @return the {@link DataLoader}
196 */
197 public static DataLoader getCache() {
198 return cache;
199 }
200
201 /**
202 * Get the (unique) {link StringIdBundle} for the program.
203 *
204 * @return the {link StringIdBundle}
205 */
206 public static StringIdBundle getTrans() {
207 return trans;
208 }
209
210 /**
211 * Get the (unique) {@link LocalLibrary} for the program.
212 *
213 * @return the {@link LocalLibrary}
214 */
215 public static BasicLibrary getLibrary() {
216 if (lib == null) {
217 throw new NullPointerException("We don't have a library to return");
218 }
219
220 return lib;
221 }
222
223 /**
224 * Return the directory where to look for default cover pages.
225 *
226 * @return the default covers directory
227 */
228 public static File getCoverDir() {
229 return coverDir;
230 }
231
232 /**
233 * Return the directory where to store temporary files for the local reader.
234 *
235 * @return the directory
236 */
237 public static File getReaderDir() {
238 return readerTmp;
239 }
240
241 /**
242 * Return the directory where to store temporary files for the remote
243 * {@link LocalLibrary}.
244 *
245 * @param host
246 * the remote for this host
247 *
248 * @return the directory
249 */
250 public static File getRemoteDir(String host) {
251 return getRemoteDir(remoteDir, host);
252 }
253
254 /**
255 * Return the directory where to store temporary files for the remote
256 * {@link LocalLibrary}.
257 *
258 * @param remoteDir
259 * the base remote directory
260 * @param host
261 * the remote for this host
262 *
263 * @return the directory
264 */
265 private static File getRemoteDir(File remoteDir, String host) {
266 remoteDir.mkdirs();
267
268 if (host != null) {
269 return new File(remoteDir, host);
270 }
271
272 return remoteDir;
273 }
274
275 /**
276 * Check if we need to check that a new version of Fanfix is available.
277 *
278 * @return TRUE if we need to
279 */
280 public static boolean isVersionCheckNeeded() {
281 try {
282 long wait = config.getInteger(Config.UPDATE_INTERVAL, 1) * 24 * 60
283 * 60 * 1000;
284 if (wait >= 0) {
285 String lastUpString = IOUtils.readSmallFile(new File(configDir,
286 "LAST_UPDATE"));
287 long delay = new Date().getTime()
288 - Long.parseLong(lastUpString);
289 if (delay > wait) {
290 return true;
291 }
292 } else {
293 return false;
294 }
295 } catch (Exception e) {
296 // No file or bad file:
297 return true;
298 }
299
300 return false;
301 }
302
303 /**
304 * Notify that we checked for a new version of Fanfix.
305 */
306 public static void setVersionChecked() {
307 try {
308 IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE",
309 Long.toString(new Date().getTime()));
310 } catch (IOException e) {
311 tracer.error(e);
312 }
313 }
314
315 /**
316 * The facility to use temporary files in this program.
317 * <p>
318 * <b>MUST</b> be closed at end of program.
319 *
320 * @return the facility
321 */
322 public static TempFiles getTempFiles() {
323 return tempFiles;
324 }
325
326 /**
327 * The configuration directory (will check, in order of preference,
328 * {@link Bundles#getDirectory()}, the system properties, the environment
329 * and then defaults to $HOME/.fanfix).
330 *
331 * @return the config directory
332 */
333 private static String getConfigDir() {
334 String configDir = Bundles.getDirectory();
335
336 if (configDir == null) {
337 configDir = System.getProperty("CONFIG_DIR");
338 }
339
340 if (configDir == null) {
341 configDir = System.getenv("CONFIG_DIR");
342 }
343
344 if (configDir == null) {
345 configDir = new File(getHome(), ".fanfix").getPath();
346 }
347
348 return configDir;
349 }
350
351 /**
352 * Create the config variables ({@link Instance#config},
353 * {@link Instance#uiconfig} and {@link Instance#trans}).
354 *
355 * @param configDir
356 * the directory where to find the configuration files
357 * @param refresh
358 * TRUE to reset the configuration files from the default
359 * included ones
360 */
361 private static void createConfigs(String configDir, boolean refresh) {
362 if (!refresh) {
363 Bundles.setDirectory(configDir);
364 }
365
366 try {
367 config = new ConfigBundle();
368 config.updateFile(configDir);
369 } catch (IOException e) {
370 tracer.error(e);
371 }
372
373 try {
374 uiconfig = new UiConfigBundle();
375 uiconfig.updateFile(configDir);
376 } catch (IOException e) {
377 tracer.error(e);
378 }
379
380 // No updateFile for this one! (we do not want the user to have custom
381 // translations that won't accept updates from newer versions)
382 trans = new StringIdBundle(getLang());
383
384 // Fix an old bug (we used to store custom translation files by
385 // default):
386 if (trans.getString(StringId.INPUT_DESC_CBZ) == null) {
387 trans.deleteFile(configDir);
388 }
389
390 if (checkEnv("NOUTF")) {
391 trans.setUnicode(false);
392 }
393
394 Bundles.setDirectory(configDir);
395 }
396
397 /**
398 * Create the default library as specified by the config.
399 *
400 * @param remoteDir
401 * the base remote directory if needed
402 *
403 * @return the default {@link BasicLibrary}
404 */
405 private static BasicLibrary createDefaultLibrary(File remoteDir) {
406 BasicLibrary lib = null;
407
408 String remoteLib = config.getString(Config.DEFAULT_LIBRARY);
409 if (remoteLib == null || remoteLib.trim().isEmpty()) {
410 String libDir = System.getProperty("fanfix.libdir");
411 if (libDir == null || libDir.isEmpty()) {
412 libDir = config.getString(Config.LIBRARY_DIR);
413 }
414 try {
415 lib = new LocalLibrary(getFile(libDir));
416 } catch (Exception e) {
417 tracer.error(new IOException(
418 "Cannot create library for directory: "
419 + getFile(libDir), e));
420 }
421 } else {
422 int pos = remoteLib.lastIndexOf(":");
423 if (pos >= 0) {
424 String port = remoteLib.substring(pos + 1).trim();
425 remoteLib = remoteLib.substring(0, pos);
426 pos = remoteLib.lastIndexOf(":");
427 if (pos >= 0) {
428 String host = remoteLib.substring(pos + 1).trim();
429 String key = remoteLib.substring(0, pos).trim();
430
431 try {
432 tracer.trace("Selecting remote library " + host + ":"
433 + port);
434 lib = new RemoteLibrary(key, host,
435 Integer.parseInt(port));
436 lib = new CacheLibrary(getRemoteDir(remoteDir, host),
437 lib);
438
439 } catch (Exception e) {
440 }
441 }
442 }
443
444 if (lib == null) {
445 tracer.error(new IOException(
446 "Cannot create remote library for: " + remoteLib));
447 }
448 }
449
450 return lib;
451 }
452
453 /**
454 * Return a path, but support the special $HOME variable.
455 *
456 * @return the path
457 */
458 private static File getFile(Config id) {
459 return getFile(config.getString(id));
460 }
461
462 /**
463 * Return a path, but support the special $HOME variable.
464 *
465 * @return the path
466 */
467 private static File getFile(UiConfig id) {
468 return getFile(uiconfig.getString(id));
469 }
470
471 /**
472 * Return a path, but support the special $HOME variable.
473 *
474 * @return the path
475 */
476 private static File getFile(String path) {
477 File file = null;
478 if (path != null && !path.isEmpty()) {
479 path = path.replace('/', File.separatorChar);
480 if (path.contains("$HOME")) {
481 path = path.replace("$HOME", getHome());
482 }
483
484 file = new File(path);
485 }
486
487 return file;
488 }
489
490 /**
491 * Return the home directory from the system properties.
492 *
493 * @return the home
494 */
495 private static String getHome() {
496 String home = System.getProperty("fanfix.home");
497 if (home != null && new File(home).isFile()) {
498 home = null;
499 }
500
501 if (home == null || home.trim().isEmpty()) {
502 home = System.getProperty("user.home");
503 if (!new File(home).isDirectory()) {
504 home = null;
505 }
506 }
507
508 if (home == null || home.trim().isEmpty()) {
509 home = System.getProperty("java.io.tmpdir");
510 if (!new File(home).isDirectory()) {
511 home = null;
512 }
513 }
514
515 if (home == null) {
516 home = "";
517 }
518
519 return home;
520 }
521
522 /**
523 * The language to use for the application (NULL = default system language).
524 *
525 * @return the language
526 */
527 private static String getLang() {
528 String lang = config.getString(Config.LANG);
529
530 if (System.getenv("LANG") != null && !System.getenv("LANG").isEmpty()) {
531 lang = System.getenv("LANG");
532 }
533
534 if (lang != null && lang.isEmpty()) {
535 lang = null;
536 }
537
538 return lang;
539 }
540
541 /**
542 * Check that the given environment variable is "enabled".
543 *
544 * @param key
545 * the variable to check
546 *
547 * @return TRUE if it is
548 */
549 private static boolean checkEnv(String key) {
550 String value = System.getenv(key);
551 if (value != null) {
552 value = value.trim().toLowerCase();
553 if ("yes".equals(value) || "true".equals(value)
554 || "on".equals(value) || "1".equals(value)
555 || "y".equals(value)) {
556 return true;
557 }
558 }
559
560 return false;
561 }
562 }