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