X-Git-Url: http://git.nikiroo.be/?p=nikiroo-utils.git;a=blobdiff_plain;f=Main.java;h=35365444682489d81adf7b25ed864416d6c47abe;hp=8d72803aaad4b0c35b38bd676a4961b2da06c454;hb=258e065f81071a861711ef935dca3ec5563f4360;hpb=9b75402fd752653f27158f1c1775a062672c7b27 diff --git a/Main.java b/Main.java index 8d72803..3536544 100644 --- a/Main.java +++ b/Main.java @@ -19,6 +19,8 @@ import be.nikiroo.fanfix.library.CacheLibrary; import be.nikiroo.fanfix.library.LocalLibrary; import be.nikiroo.fanfix.library.RemoteLibrary; import be.nikiroo.fanfix.library.RemoteLibraryServer; +import be.nikiroo.fanfix.library.WebLibrary; +import be.nikiroo.fanfix.library.WebLibraryServer; import be.nikiroo.fanfix.output.BasicOutput; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.fanfix.reader.BasicReader; @@ -28,7 +30,7 @@ import be.nikiroo.fanfix.supported.BasicSupport; import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; -import be.nikiroo.utils.serial.server.ServerObject; +import be.nikiroo.utils.VersionCheck; /** * Main program entry point. @@ -78,13 +80,29 @@ public class Main { *
  • --version: get the version of the program
  • *
  • --server: start the server mode (see config file for parameters)
  • *
  • --stop-server: stop the running server on this port if any
  • - *
  • --remote [key] [host] [port]: use a the given remote library
  • + *
  • --remote [key] [host] [port]: use the given remote library
  • * * * @param args * see method description */ public static void main(String[] args) { + new Main().start(args); + } + + /** + * Start the default handling for the application. + *

    + * If specific actions were asked (with correct parameters), they will be + * forwarded to the different protected methods that you can override. + *

    + * At the end of the method, {@link Main#exit(int)} will be called; by + * default, it calls {@link System#exit(int)} if the status is not 0. + * + * @param args + * the arguments received from the system + */ + public void start(String [] args) { // Only one line, but very important: Instance.init(); @@ -110,6 +128,9 @@ public class Main { int exitCode = 0; for (int i = 0; exitCode == 0 && i < args.length; i++) { + if (args[i] == null) + continue; + // Action (--) handling: if (!noMoreActions && args[i].startsWith("--")) { if (args[i].equals("--")) { @@ -331,7 +352,14 @@ public class Main { } else if (port == null) { port = Integer.parseInt(args[i]); - BasicLibrary lib = new RemoteLibrary(key, host, port); + BasicLibrary lib; + if (host.startsWith("http://") + || host.startsWith("https://")) { + lib = new WebLibrary(key, host, port); + } else { + lib = new RemoteLibrary(key, host, port); + } + lib = new CacheLibrary( Instance.getInstance().getRemoteDir(host), lib, Instance.getInstance().getUiConfig()); @@ -373,38 +401,58 @@ public class Main { Progress pg = new Progress(); mainProgress.addProgress(pg, mainProgress.getMax()); - VersionCheck updates = VersionCheck.check(); - if (updates.isNewVersionAvailable()) { - // Sent to syserr so not to cause problem if one tries to capture a - // story content in text mode - System.err - .println("A new version of the program is available at https://github.com/nikiroo/fanfix/releases"); - System.err.println(""); - for (Version v : updates.getNewer()) { - System.err.println("\tVersion " + v); - System.err.println("\t-------------"); - System.err.println(""); - for (String it : updates.getChanges().get(v)) { - System.err.println("\t- " + it); - } - System.err.println(""); - } - } + VersionCheck updates = checkUpdates(); if (exitCode == 0) { switch (action) { case IMPORT: - exitCode = imprt(urlString, pg); - updates.ok(); // we consider it read + if (updates != null) { + // we consider it read + Instance.getInstance().setVersionChecked(); + } + + try { + exitCode = imprt(BasicReader.getUrl(urlString), pg); + } catch (MalformedURLException e) { + Instance.getInstance().getTraceHandler().error(e); + exitCode = 1; + } + break; case EXPORT: - exitCode = export(luid, sourceString, target, pg); - updates.ok(); // we consider it read + if (updates != null) { + // we consider it read + Instance.getInstance().setVersionChecked(); + } + + OutputType exportType = OutputType.valueOfNullOkUC(sourceString, null); + if (exportType == null) { + Instance.getInstance().getTraceHandler().error(new Exception(trans(StringId.OUTPUT_DESC, sourceString))); + exitCode = 1; + break; + } + + exitCode = export(luid, exportType, target, pg); + break; case CONVERT: - exitCode = convert(urlString, sourceString, target, + if (updates != null) { + // we consider it read + Instance.getInstance().setVersionChecked(); + } + + OutputType convertType = OutputType.valueOfAllOkUC(sourceString, null); + if (convertType == null) { + Instance.getInstance().getTraceHandler() + .error(new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE, sourceString))); + + exitCode = 2; + break; + } + + exitCode = convert(urlString, convertType, target, plusInfo == null ? false : plusInfo, pg); - updates.ok(); // we consider it read + break; case LIST: exitCode = list(sourceString); @@ -441,8 +489,20 @@ public class Main { } try { + Integer chap = null; + if (chapString != null) { + try { + chap = Integer.parseInt(chapString); + } catch (NumberFormatException e) { + Instance.getInstance().getTraceHandler().error(new IOException( + "Chapter number cannot be parsed: " + chapString, e)); + exitCode = 2; + break; + } + } + BasicLibrary lib = Instance.getInstance().getLibrary(); - exitCode = read(lib.getStory(luid, null), chapString); + exitCode = read(lib.getStory(luid, null), chap); } catch (IOException e) { Instance.getInstance().getTraceHandler() .error(new IOException("Failed to read book", e)); @@ -458,6 +518,18 @@ public class Main { } try { + Integer chap = null; + if (chapString != null) { + try { + chap = Integer.parseInt(chapString); + } catch (NumberFormatException e) { + Instance.getInstance().getTraceHandler().error(new IOException( + "Chapter number cannot be parsed: " + chapString, e)); + exitCode = 2; + break; + } + } + BasicSupport support = BasicSupport .getSupport(BasicReader.getUrl(urlString)); if (support == null) { @@ -467,7 +539,7 @@ public class Main { break; } - exitCode = read(support.process(null), chapString); + exitCode = read(support.process(null), chap); } catch (IOException e) { Instance.getInstance().getTraceHandler() .error(new IOException("Failed to read book", e)); @@ -490,19 +562,22 @@ public class Main { break; } - try { - if (searchOn == null) { - new CliReader().listSearchables(); - } else if (search != null) { - - new CliReader().searchBooksByKeyword(searchOn, search, page, - item); - } else { - exitCode = 255; + if (searchOn == null) { + try { + search(); + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); + exitCode = 1; } - } catch (IOException e1) { - Instance.getInstance().getTraceHandler().error(e1); - exitCode = 20; + } else if (search != null) { + try { + searchKeywords(searchOn, search, page, item); + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); + exitCode = 20; + } + } else { + exitCode = 255; } break; @@ -527,10 +602,10 @@ public class Main { } try { - new CliReader().searchBooksByTag(searchOn, page, item, - tags.toArray(new Integer[] {})); - } catch (IOException e1) { - Instance.getInstance().getTraceHandler().error(e1); + searchTags(searchOn, page, item, + tags.toArray(new Integer[] {})); + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); } break; @@ -539,43 +614,65 @@ public class Main { exitCode = 0; break; case VERSION: + if (updates != null) { + // we consider it read + Instance.getInstance().setVersionChecked(); + } + System.out .println(String.format("Fanfix version %s" + "%nhttps://github.com/nikiroo/fanfix/" + "%n\tWritten by Nikiroo", Version.getCurrentVersion())); - updates.ok(); // we consider it read break; case START: try { - new CliReader().listBooks(null); + start(); } catch (IOException e) { Instance.getInstance().getTraceHandler().error(e); exitCode = 66; } break; case SERVER: - key = Instance.getInstance().getConfig().getString(Config.SERVER_KEY); - port = Instance.getInstance().getConfig().getInteger(Config.SERVER_PORT); - if (port == null) { - System.err.println("No port configured in the config file"); - exitCode = 15; - break; - } try { - ServerObject server = new RemoteLibraryServer(key, port); - server.setTraceHandler(Instance.getInstance().getTraceHandler()); - server.run(); + startServer(); } catch (IOException e) { Instance.getInstance().getTraceHandler().error(e); } - return; + + break; case STOP_SERVER: // Can be given via "--remote XX XX XX" - if (key == null) - key = Instance.getInstance().getConfig().getString(Config.SERVER_KEY); - if (port == null) + if (key == null) { + key = Instance.getInstance().getConfig() + .getString(Config.SERVER_KEY); + + // If a subkey in RW mode exists, use it + for (String subkey : Instance.getInstance().getConfig() + .getList(Config.SERVER_ALLOWED_SUBKEYS, + new ArrayList())) { + if ((subkey + "|").contains("|rw|")) { + key = key + "|" + subkey; + break; + } + } + } + + if (port == null) { port = Instance.getInstance().getConfig().getInteger(Config.SERVER_PORT); + } + + if (host == null) { + String mode = Instance.getInstance().getConfig() + .getString(Config.SERVER_MODE, "fanfix"); + if ("http".equals(mode)) { + host = "http://localhost"; + } else if ("https".equals(mode)) { + host = "https://localhost"; + } else if ("fanfix".equals(mode)) { + host = "fanfix://localhost"; + } + } if (port == null) { System.err.println("No port given nor configured in the config file"); @@ -583,7 +680,7 @@ public class Main { break; } try { - new RemoteLibrary(key, host, port).exit(); + stopServer(key, host, port); } catch (SSLException e) { Instance.getInstance().getTraceHandler().error( "Bad access key for remote library"); @@ -603,29 +700,114 @@ public class Main { try { Instance.getInstance().getTempFiles().close(); } catch (IOException e) { - Instance.getInstance().getTraceHandler().error(new IOException("Cannot dispose of the temporary files", e)); + Instance.getInstance().getTraceHandler().error(new IOException( + "Cannot dispose of the temporary files", e)); } if (exitCode == 255) { syntax(false); } - System.exit(exitCode); + exit(exitCode); + } + + /** + * A normal invocation of the program (without parameters or at least + * without "action" parameters). + *

    + * You will probably want to override that one if you offer a user + * interface. + * + * @throws IOException + * in case of I/O error + */ + protected void start() throws IOException { + new CliReader().listBooks(null); + } + + /** + * Will check if updates are available, synchronously. + *

    + * For this, it will simply forward the call to + * {@link Main#checkUpdates(String)} with a value of "nikiroo/fanfix". + *

    + * You may want to override it so you call the forward method with the right + * parameters (or also if you want it to be asynchronous). + * + * @return the newer version information or NULL if nothing new + */ + protected VersionCheck checkUpdates() { + return checkUpdates("nikiroo/fanfix"); } + /** + * Will check if updates are available on a specific GitHub project. + *

    + * Will be called by {@link Main#checkUpdates()}, but if you override that + * one you mall call it with another project. + * + * @param githubProject + * the GitHub project, for instance "nikiroo/fanfix" + * + * @return the newer version information or NULL if nothing new + */ + protected VersionCheck checkUpdates(String githubProject) { + try { + VersionCheck updates = VersionCheck.check(githubProject, + Instance.getInstance().getTrans().getLocale()); + if (updates.isNewVersionAvailable()) { + notifyUpdates(updates); + return updates; + } + } catch (IOException e) { + // Maybe no internet. Do not report any update. + } + + return null; + } + + /** + * Notify the user about available updates. + *

    + * Will only be called when a version is available. + *

    + * Note that you can call {@link Instance#setVersionChecked()} on it if the + * user has read the information (by default, it is marked read only on + * certain other actions). + * + * @param updates + * the new version information + */ + protected void notifyUpdates(VersionCheck updates) { + // Sent to syserr so not to cause problem if one tries to capture a + // story content in text mode + System.err.println( + "A new version of the program is available at https://github.com/nikiroo/fanfix/releases"); + System.err.println(""); + for (Version v : updates.getNewer()) { + System.err.println("\tVersion " + v); + System.err.println("\t-------------"); + System.err.println(""); + for (String it : updates.getChanges().get(v)) { + System.err.println("\t- " + it); + } + System.err.println(""); + } + } + /** * Import the given resource into the {@link LocalLibrary}. * - * @param urlString + * @param url * the resource to import * @param pg * the optional progress reporter * * @return the exit return code (0 = success) */ - public static int imprt(String urlString, Progress pg) { + protected static int imprt(URL url, Progress pg) { try { - MetaData meta = Instance.getInstance().getLibrary().imprt(BasicReader.getUrl(urlString), pg); + MetaData meta = Instance.getInstance().getLibrary().imprt(url, pg); System.out.println(meta.getLuid() + ": \"" + meta.getTitle() + "\" imported."); } catch (IOException e) { Instance.getInstance().getTraceHandler().error(e); @@ -641,7 +823,7 @@ public class Main { * * @param luid * the story LUID - * @param typeString + * @param type * the {@link OutputType} to use * @param target * the target @@ -650,14 +832,8 @@ public class Main { * * @return the exit return code (0 = success) */ - public static int export(String luid, String typeString, String target, + protected static int export(String luid, OutputType type, String target, Progress pg) { - OutputType type = OutputType.valueOfNullOkUC(typeString, null); - if (type == null) { - Instance.getInstance().getTraceHandler().error(new Exception(trans(StringId.OUTPUT_DESC, typeString))); - return 1; - } - try { Instance.getInstance().getLibrary().export(luid, type, target, pg); } catch (IOException e) { @@ -667,7 +843,7 @@ public class Main { return 0; } - + /** * List the stories of the given source from the {@link LocalLibrary} * (unless NULL is passed, in which case all stories will be listed). @@ -678,7 +854,7 @@ public class Main { * * @return the exit return code (0 = success) */ - private static int list(String source) { + protected int list(String source) { try { new CliReader().listBooks(source); } catch (IOException e) { @@ -694,24 +870,13 @@ public class Main { * * @param story * the story to read - * @param chapString + * @param chap * which {@link Chapter} to read (starting at 1), or NULL to get * the {@link Story} description * * @return the exit return code (0 = success) */ - private static int read(Story story, String chapString) { - Integer chap = null; - if (chapString != null) { - try { - chap = Integer.parseInt(chapString); - } catch (NumberFormatException e) { - Instance.getInstance().getTraceHandler().error(new IOException( - "Chapter number cannot be parsed: " + chapString, e)); - return 2; - } - } - + protected int read(Story story, Integer chap) { if (story != null) { try { if (chap == null) { @@ -726,7 +891,7 @@ public class Main { } } else { Instance.getInstance().getTraceHandler() - .error("Cannot find book: " + chapString); + .error("Cannot find book: " + story); return 2; } @@ -738,7 +903,7 @@ public class Main { * * @param urlString * the source {@link Story} to convert - * @param typeString + * @param type * the {@link OutputType} to convert to * @param target * the target file @@ -750,7 +915,7 @@ public class Main { * * @return the exit return code (0 = success) */ - public static int convert(String urlString, String typeString, + protected int convert(String urlString, OutputType type, String target, boolean infoCover, Progress pg) { int exitCode = 0; @@ -763,46 +928,42 @@ public class Main { sourceName = sourceName.substring("file://".length()); } - OutputType type = OutputType.valueOfAllOkUC(typeString, null); - if (type == null) { - Instance.getInstance().getTraceHandler() - .error(new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE, typeString))); + try { + BasicSupport support = BasicSupport.getSupport(source); - exitCode = 2; - } else { - try { - BasicSupport support = BasicSupport.getSupport(source); - - if (support != null) { - Instance.getInstance().getTraceHandler().trace("Support found: " + support.getClass()); - Progress pgIn = new Progress(); - Progress pgOut = new Progress(); - if (pg != null) { - pg.setMax(2); - pg.addProgress(pgIn, 1); - pg.addProgress(pgOut, 1); - } + if (support != null) { + Instance.getInstance().getTraceHandler() + .trace("Support found: " + support.getClass()); + Progress pgIn = new Progress(); + Progress pgOut = new Progress(); + if (pg != null) { + pg.setMax(2); + pg.addProgress(pgIn, 1); + pg.addProgress(pgOut, 1); + } - Story story = support.process(pgIn); - try { - target = new File(target).getAbsolutePath(); - BasicOutput.getOutput(type, infoCover, infoCover).process(story, target, pgOut); - } catch (IOException e) { - Instance.getInstance().getTraceHandler() - .error(new IOException(trans(StringId.ERR_SAVING, target), e)); - exitCode = 5; - } - } else { + Story story = support.process(pgIn); + try { + target = new File(target).getAbsolutePath(); + BasicOutput.getOutput(type, infoCover, infoCover) + .process(story, target, pgOut); + } catch (IOException e) { Instance.getInstance().getTraceHandler() - .error(new IOException(trans( StringId.ERR_NOT_SUPPORTED, source))); - - exitCode = 4; + .error(new IOException( + trans(StringId.ERR_SAVING, target), e)); + exitCode = 5; } - } catch (IOException e) { + } else { Instance.getInstance().getTraceHandler() - .error(new IOException(trans(StringId.ERR_LOADING, sourceName), e)); - exitCode = 3; + .error(new IOException( + trans(StringId.ERR_NOT_SUPPORTED, source))); + + exitCode = 4; } + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(new IOException( + trans(StringId.ERR_LOADING, sourceName), e)); + exitCode = 3; } } catch (MalformedURLException e) { Instance.getInstance().getTraceHandler().error(new IOException(trans(StringId.ERR_BAD_URL, sourceName), e)); @@ -812,18 +973,6 @@ public class Main { return exitCode; } - /** - * Simple shortcut method to call {link Instance#getTrans()#getString()}. - * - * @param id - * the ID to translate - * - * @return the translated result - */ - private static String trans(StringId id, Object... params) { - return Instance.getInstance().getTrans().getString(id, params); - } - /** * Display the correct syntax of the program to the user to stdout, or an * error message if the syntax used was wrong on stderr. @@ -831,7 +980,7 @@ public class Main { * @param showHelp * TRUE to show the syntax help, FALSE to show "syntax error" */ - private static void syntax(boolean showHelp) { + protected void syntax(boolean showHelp) { if (showHelp) { StringBuilder builder = new StringBuilder(); for (SupportType type : SupportType.values()) { @@ -856,4 +1005,137 @@ public class Main { System.err.println(trans(StringId.ERR_SYNTAX)); } } + + /** + * Starts a search operation (i.e., list the available web sites we can + * search on). + * + * @throws IOException + * in case of I/O errors + */ + protected void search() throws IOException { + new CliReader().listSearchables(); + } + + /** + * Search for books by keywords on the given supported web site. + * + * @param searchOn + * the web site to search on + * @param search + * the keyword to look for + * @param page + * the page of results to get, or 0 to inquire about the number + * of pages + * @param item + * the index of the book we are interested by, or 0 to query + * about how many books are in that page of results + * + * @throws IOException + * in case of I/O error + */ + protected void searchKeywords(SupportType searchOn, String search, + int page, Integer item) throws IOException { + new CliReader().searchBooksByKeyword(searchOn, search, page, item); + } + + /** + * Search for books by tags on the given supported web site. + * + * @param searchOn + * the web site to search on + * @param page + * the page of results to get, or 0 to inquire about the number + * of pages + * @param item + * the index of the book we are interested by, or 0 to query + * about how many books are in that page of results + * @param tags + * the tags to look for + * + * @throws IOException + * in case of I/O error + */ + protected void searchTags(SupportType searchOn, Integer page, Integer item, + Integer[] tags) throws IOException { + new CliReader().searchBooksByTag(searchOn, page, item, tags); + } + + /** + * Start a Fanfix server. + * + * @throws IOException + * in case of I/O errors + * @throws SSLException + * when the key was not accepted + */ + private void startServer() throws IOException { + String mode = Instance.getInstance().getConfig() + .getString(Config.SERVER_MODE, "fanfix"); + if (mode.equals("fanfix")) { + RemoteLibraryServer server = new RemoteLibraryServer(); + server.setTraceHandler(Instance.getInstance().getTraceHandler()); + server.run(); + } else if (mode.equals("http")) { + WebLibraryServer server = new WebLibraryServer(false); + server.setTraceHandler(Instance.getInstance().getTraceHandler()); + server.run(); + } else if (mode.equals("https")) { + WebLibraryServer server = new WebLibraryServer(true); + server.setTraceHandler(Instance.getInstance().getTraceHandler()); + server.run(); + } else { + throw new IOException("Unknown server mode: " + mode); + } + } + + /** + * Stop a running Fanfix server. + * + * @param key + * the key to contact the Fanfix server + * @param host + * the host on which it runs + * @param port + * the port on which it runs + * + * @throws IOException + * in case of I/O errors + * @throws SSLException + * when the key was not accepted + */ + private void stopServer(String key, String host, int port) + throws IOException, SSLException { + if (host.startsWith("http://") || host.startsWith("https://")) { + new WebLibrary(key, host, port).stop(); + } else { + new RemoteLibrary(key, host, port).stop(); + } + } + + /** + * We are done and ready to exit. + *

    + * By default, it will call {@link System#exit(int)} if the status is not 0. + * + * @param status + * the exit status + */ + protected void exit(int status) { + if (status != 0) { + System.exit(status); + } + } + + /** + * Simple shortcut method to call {link Instance#getTrans()#getString()}. + * + * @param id + * the ID to translate + * + * @return the translated result + */ + static private String trans(StringId id, Object... params) { + return Instance.getInstance().getTrans().getString(id, params); + } }