1 package be
.nikiroo
.fanfix
;
4 import java
.io
.IOException
;
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
.StringIdGuiBundle
;
12 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
13 import be
.nikiroo
.fanfix
.bundles
.UiConfigBundle
;
14 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
15 import be
.nikiroo
.fanfix
.library
.CacheLibrary
;
16 import be
.nikiroo
.fanfix
.library
.LocalLibrary
;
17 import be
.nikiroo
.fanfix
.library
.RemoteLibrary
;
18 import be
.nikiroo
.utils
.Cache
;
19 import be
.nikiroo
.utils
.IOUtils
;
20 import be
.nikiroo
.utils
.Image
;
21 import be
.nikiroo
.utils
.Proxy
;
22 import be
.nikiroo
.utils
.TempFiles
;
23 import be
.nikiroo
.utils
.TraceHandler
;
24 import be
.nikiroo
.utils
.resources
.Bundles
;
27 * Global state for the program (services and singletons).
31 public class Instance
{
32 private static ConfigBundle config
;
33 private static UiConfigBundle uiconfig
;
34 private static StringIdBundle trans
;
35 private static DataLoader cache
;
36 private static StringIdGuiBundle transGui
;
37 private static BasicLibrary lib
;
38 private static File coverDir
;
39 private static File readerTmp
;
40 private static File remoteDir
;
41 private static String configDir
;
42 private static TraceHandler tracer
;
43 private static TempFiles tempFiles
;
45 private static boolean init
;
48 * Initialise the instance -- if already initialised, nothing will happen.
50 * Before calling this method, you may call
51 * {@link Bundles#setDirectory(String)} if wanted.
53 static public void init() {
58 * Initialise the instance -- if already initialised, nothing will happen
59 * unless you pass TRUE to <tt>force</tt>.
61 * Before calling this method, you may call
62 * {@link Bundles#setDirectory(String)} if wanted.
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.
72 * force the initialisation even if already initialised
74 static public void init(boolean force
) {
81 // Before we can configure it:
82 Boolean debug
= checkEnv("DEBUG");
83 boolean trace
= debug
!= null && debug
;
84 tracer
= new TraceHandler(true, trace
, trace
);
87 configDir
= getConfigDir();
88 if (!new File(configDir
).exists()) {
89 new File(configDir
).mkdirs();
92 // Most of the rest is dependent upon this:
93 createConfigs(configDir
, false);
96 Proxy
.use(Instance
.getConfig().getString(Config
.NETWORK_PROXY
));
100 debug
= Instance
.getConfig().getBoolean(Config
.DEBUG_ERR
, false);
101 trace
= Instance
.getConfig().getBoolean(Config
.DEBUG_TRACE
, false);
104 tracer
= new TraceHandler(true, debug
, trace
);
107 remoteDir
= new File(configDir
, "remote");
108 lib
= createDefaultLibrary(remoteDir
);
110 // create cache and TMP
111 File tmp
= getFile(Config
.CACHE_DIR
, new File(configDir
, "tmp"));
112 if (!tmp
.isAbsolute()) {
113 tmp
= new File(configDir
, tmp
.getPath());
115 Image
.setTemporaryFilesRoot(new File(tmp
.getParent(), "tmp.images"));
117 String ua
= config
.getString(Config
.NETWORK_USER_AGENT
, "");
119 int hours
= config
.getInteger(Config
.CACHE_MAX_TIME_CHANGING
, 0);
120 int hoursLarge
= config
.getInteger(Config
.CACHE_MAX_TIME_STABLE
, 0);
121 cache
= new DataLoader(tmp
, ua
, hours
, hoursLarge
);
122 } catch (IOException e
) {
123 tracer
.error(new IOException(
124 "Cannot create cache (will continue without cache)", e
));
125 cache
= new DataLoader(ua
);
128 cache
.setTraceHandler(tracer
);
130 // readerTmp / coverDir
131 readerTmp
= getFile(UiConfig
.CACHE_DIR_LOCAL_READER
, new File(
132 configDir
, "tmp-reader"));
134 coverDir
= getFile(Config
.DEFAULT_COVERS_DIR
, new File(configDir
,
139 tempFiles
= new TempFiles("fanfix");
140 } catch (IOException e
) {
141 tracer
.error(new IOException("Cannot create temporary directory", e
));
146 * The traces handler for this {@link Cache}.
150 * @return the traces handler (never NULL)
152 public static TraceHandler
getTraceHandler() {
157 * The traces handler for this {@link Cache}.
160 * the new traces handler or NULL
162 public static void setTraceHandler(TraceHandler tracer
) {
163 if (tracer
== null) {
164 tracer
= new TraceHandler(false, false, false);
167 Instance
.tracer
= tracer
;
168 cache
.setTraceHandler(tracer
);
172 * Get the (unique) configuration service for the program.
174 * @return the configuration service
176 public static ConfigBundle
getConfig() {
181 * Get the (unique) UI configuration service for the program.
183 * @return the configuration service
185 public static UiConfigBundle
getUiConfig() {
190 * Reset the configuration.
193 * also reset the translation files
195 public static void resetConfig(boolean resetTrans
) {
196 String dir
= Bundles
.getDirectory();
197 Bundles
.setDirectory(null);
200 ConfigBundle config
= new ConfigBundle();
201 config
.updateFile(configDir
);
202 } catch (IOException e
) {
206 UiConfigBundle uiconfig
= new UiConfigBundle();
207 uiconfig
.updateFile(configDir
);
208 } catch (IOException e
) {
214 StringIdBundle trans
= new StringIdBundle(null);
215 trans
.updateFile(configDir
);
216 } catch (IOException e
) {
221 Bundles
.setDirectory(dir
);
226 * Get the (unique) {@link DataLoader} for the program.
228 * @return the {@link DataLoader}
230 public static DataLoader
getCache() {
235 * Get the (unique) {link StringIdBundle} for the program.
237 * This is used for the translations of the core parts of Fanfix.
239 * @return the {link StringIdBundle}
241 public static StringIdBundle
getTrans() {
246 * Get the (unique) {link StringIdGuiBundle} for the program.
248 * This is used for the translations of the GUI parts of Fanfix.
250 * @return the {link StringIdGuiBundle}
252 public static StringIdGuiBundle
getTransGui() {
257 * Get the (unique) {@link LocalLibrary} for the program.
259 * @return the {@link LocalLibrary}
261 public static BasicLibrary
getLibrary() {
263 throw new NullPointerException("We don't have a library to return");
270 * Return the directory where to look for default cover pages.
272 * @return the default covers directory
274 public static File
getCoverDir() {
279 * Return the directory where to store temporary files for the local reader.
281 * @return the directory
283 public static File
getReaderDir() {
288 * Return the directory where to store temporary files for the remote
289 * {@link LocalLibrary}.
292 * the remote for this host
294 * @return the directory
296 public static File
getRemoteDir(String host
) {
297 return getRemoteDir(remoteDir
, host
);
301 * Return the directory where to store temporary files for the remote
302 * {@link LocalLibrary}.
305 * the base remote directory
307 * the remote for this host
309 * @return the directory
311 private static File
getRemoteDir(File remoteDir
, String host
) {
315 return new File(remoteDir
, host
);
322 * Check if we need to check that a new version of Fanfix is available.
324 * @return TRUE if we need to
326 public static boolean isVersionCheckNeeded() {
328 long wait
= config
.getInteger(Config
.NETWORK_UPDATE_INTERVAL
, 0)
329 * 24 * 60 * 60 * 1000;
331 String lastUpString
= IOUtils
.readSmallFile(new File(configDir
,
333 long delay
= new Date().getTime()
334 - Long
.parseLong(lastUpString
);
341 } catch (Exception e
) {
342 // No file or bad file:
350 * Notify that we checked for a new version of Fanfix.
352 public static void setVersionChecked() {
354 IOUtils
.writeSmallFile(new File(configDir
), "LAST_UPDATE",
355 Long
.toString(new Date().getTime()));
356 } catch (IOException e
) {
362 * The facility to use temporary files in this program.
364 * <b>MUST</b> be closed at end of program.
366 * @return the facility
368 public static TempFiles
getTempFiles() {
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).
377 * @return the config directory
379 private static String
getConfigDir() {
380 String configDir
= System
.getProperty("CONFIG_DIR");
382 if (configDir
== null) {
383 configDir
= System
.getenv("CONFIG_DIR");
386 if (configDir
== null) {
387 configDir
= new File(getHome(), ".fanfix").getPath();
394 * Create the config variables ({@link Instance#config},
395 * {@link Instance#uiconfig}, {@link Instance#trans} and
396 * {@link Instance#transGui}).
399 * the directory where to find the configuration files
401 * TRUE to reset the configuration files from the default
404 private static void createConfigs(String configDir
, boolean refresh
) {
406 Bundles
.setDirectory(configDir
);
410 config
= new ConfigBundle();
411 config
.updateFile(configDir
);
412 } catch (IOException e
) {
417 uiconfig
= new UiConfigBundle();
418 uiconfig
.updateFile(configDir
);
419 } catch (IOException e
) {
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());
426 transGui
= new StringIdGuiBundle(getLang());
428 // Fix an old bug (we used to store custom translation files by
430 if (trans
.getString(StringId
.INPUT_DESC_CBZ
) == null) {
431 trans
.deleteFile(configDir
);
434 Boolean noutf
= checkEnv("NOUTF");
435 if (noutf
!= null && noutf
) {
436 trans
.setUnicode(false);
437 transGui
.setUnicode(false);
440 Bundles
.setDirectory(configDir
);
444 * Create the default library as specified by the config.
447 * the base remote directory if needed
449 * @return the default {@link BasicLibrary}
451 private static BasicLibrary
createDefaultLibrary(File remoteDir
) {
452 BasicLibrary lib
= null;
454 String remoteLib
= config
.getString(Config
.DEFAULT_LIBRARY
);
455 if (remoteLib
== null || remoteLib
.trim().isEmpty()) {
456 String libDir
= System
.getenv("BOOKS_DIR");
457 if (libDir
== null || libDir
.isEmpty()) {
458 libDir
= config
.getString(Config
.LIBRARY_DIR
, "$HOME/Books");
459 if (!getFile(libDir
).isAbsolute()) {
460 libDir
= new File(configDir
, libDir
).getPath();
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
));
472 int pos
= remoteLib
.lastIndexOf(":");
474 String port
= remoteLib
.substring(pos
+ 1).trim();
475 remoteLib
= remoteLib
.substring(0, pos
);
476 pos
= remoteLib
.lastIndexOf(":");
478 String host
= remoteLib
.substring(pos
+ 1).trim();
479 String key
= remoteLib
.substring(0, pos
).trim();
482 tracer
.trace("Selecting remote library " + host
+ ":"
484 lib
= new RemoteLibrary(key
, host
,
485 Integer
.parseInt(port
));
486 lib
= new CacheLibrary(getRemoteDir(remoteDir
, host
),
489 } catch (Exception e
) {
496 tracer
.error(new IOException(
497 "Cannot create remote library for: " + remoteLib
, ex
));
505 * Return a path, but support the special $HOME variable.
509 private static File
getFile(Config id
, File def
) {
510 String path
= config
.getString(id
, def
.getPath());
511 return getFile(path
);
515 * Return a path, but support the special $HOME variable.
519 private static File
getFile(UiConfig id
, File def
) {
520 String path
= uiconfig
.getString(id
, def
.getPath());
521 return getFile(path
);
525 * Return a path, but support the special $HOME variable.
529 private static File
getFile(String path
) {
531 if (path
!= null && !path
.isEmpty()) {
532 path
= path
.replace('/', File
.separatorChar
);
533 if (path
.contains("$HOME")) {
534 path
= path
.replace("$HOME", getHome());
537 file
= new File(path
);
544 * Return the home directory from the environment (FANFIX_DIR) or the system
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.
553 private static String
getHome() {
554 String home
= System
.getenv("FANFIX_DIR");
555 if (home
!= null && new File(home
).isFile()) {
559 if (home
== null || home
.trim().isEmpty()) {
560 home
= System
.getProperty("fanfix.home");
561 if (home
!= null && new File(home
).isFile()) {
566 if (home
== null || home
.trim().isEmpty()) {
567 home
= System
.getProperty("user.home");
568 if (!new File(home
).isDirectory()) {
573 if (home
== null || home
.trim().isEmpty()) {
574 home
= System
.getProperty("java.io.tmpdir");
575 if (!new File(home
).isDirectory()) {
588 * The language to use for the application (NULL = default system language).
590 * @return the language
592 private static String
getLang() {
593 String lang
= config
.getString(Config
.LANG
);
595 if (lang
== null || lang
.isEmpty()) {
596 if (System
.getenv("LANG") != null
597 && !System
.getenv("LANG").isEmpty()) {
598 lang
= System
.getenv("LANG");
602 if (lang
!= null && lang
.isEmpty()) {
610 * Check that the given environment variable is "enabled".
613 * the variable to check
615 * @return TRUE if it is
617 private static Boolean
checkEnv(String key
) {
618 String value
= System
.getenv(key
);
620 value
= value
.trim().toLowerCase();
621 if ("yes".equals(value
) || "true".equals(value
)
622 || "on".equals(value
) || "1".equals(value
)
623 || "y".equals(value
)) {