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