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