001package votorola.s.line; // Copyright 2012, Christian Weilbach. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import java.io.IOException; 004import java.io.OutputStream; 005import java.io.PrintStream; 006import java.net.URISyntaxException; 007import java.sql.SQLException; 008import java.util.Arrays; 009import java.util.Collections; 010import java.util.LinkedList; 011import java.util.List; 012import java.util.Map; 013import java.util.logging.Level; 014import java.util.logging.Logger; 015 016import javax.script.ScriptException; 017 018import votorola.a.PageProperty; 019import votorola.a.PagePropertyReader; 020import votorola.a.VoteServer; 021import votorola.a.WikiCache; 022import votorola.a.diff.harvest.HarvestReporter; 023import votorola.a.diff.harvest.StateTable; 024import votorola.a.diff.harvest.cache.HarvestCache; 025import votorola.a.diff.harvest.kick.Kicker; 026import votorola.a.diff.harvest.kick.UpdateKick; 027import votorola.a.response.line.CommandLine; 028import votorola.g.logging.LoggerX; 029import votorola.g.option.GetoptX; 030import votorola.g.option.Option; 031 032/** 033 * Main class of the executable <code>voharvest</code> - a tool to gather diff 034 * messages from different remote web forums. See 035 * {@linkplain votorola.a.diff.harvest.cache.HarvestCache Cache} for more 036 * details. 037 * 038 * @see <a href='../../../../../s/manual.xht#line-voharvest' target='_top'>voharvest</a> 039 */ 040public final class VOHarvest { 041 private static final Logger LOGGER = LoggerX.i(VOTrace.class); 042 043 /** 044 * Runs the tool from the command line. 045 * 046 * @param argv 047 * command line argument array 048 */ 049 public static void main(final String[] argv) { 050 LOGGER.info("voharvest is running with arguments " 051 + Arrays.toString(argv)); 052 053 try { 054 new VOHarvest().run(argv); 055 } catch (Exception e) { 056 LOGGER.log(Level.SEVERE, "Harvesting failed.", e); 057 System.exit(1); 058 } 059 } 060 061 private static enum Action { 062 NONE, UPDATE, REMOVE 063 }; 064 065 private void run(final String[] argv) throws IOException, ScriptException, 066 SQLException, URISyntaxException { 067 068 final Map<String, Option> optionMap = CommandLine.compileBaseOptions(); 069 GetoptX.parse("voharvest", argv, optionMap); 070 071 final List<String> realArgs = new LinkedList<String>(); 072 for(final String arg : Arrays.asList(argv)){ 073 if(!arg.startsWith("-")){ 074 realArgs.add(arg); 075 } 076 } 077 int argCount = realArgs.size(); 078 079 if (optionMap.get("help").hasOccured() || argCount == 0) { 080 printHelp(); 081 return; 082 } 083 084 // try to parse command 085 Action action = Action.NONE; // first argument 086 final String command = realArgs.remove(0); // remove command 087 088 if (command.equals("update")) { 089 action = Action.UPDATE; 090 } else if (command.equals("remove")) { 091 action = Action.REMOVE; 092 } 093 094 // setup verbosity 095 096 boolean verbosity = false; 097 if (optionMap.get("verbose").hasOccured()) { 098 verbosity = true; 099 } 100 101 102 // setup cache for operation 103 VoteServer.Run vsRun = null; 104 vsRun = new VoteServer(System.getProperty("user.name")).new Run(false); 105 106 HarvestCache.init(vsRun); 107 final WikiCache wc = vsRun.voteServer().pollwiki().cache(); 108 109 110 // execute action 111 if (action.equals(Action.UPDATE)) { 112 113 if (argCount < 2) { 114 System.err 115 .println("voharvest: wrong number of arguments\n" 116 + "\"update\" needs to know one or more archive page(s) in the Pollwiki. \n" 117 + "For example: \n" 118 + "voharvest update 'Stuff:Votorola/mailing list' \n"); 119 return; 120 } 121 122 123 // iterate over all arguments 124 for (String arg : realArgs) { 125 final Archive arch = getArchive(arg, wc); 126 Kicker.i().broadcast( 127 UpdateKick.create(arch.design, arch.url, 128 createReporter(arg, verbosity))); 129 } 130 131 } else if (action.equals(Action.REMOVE)) { 132 if (argCount != 2) { 133 System.err 134 .println("voharvest: wrong number of arguments\n" 135 + "You need to supply two arguments, " 136 + "\"remove\" and the archive descriptor in the Pollwiki. \n" 137 + "For example: \n" 138 + "voharvest remove 'Stuff:Votorola/mailing list'"); 139 return; 140 } 141 142 final Archive arch = getArchive(realArgs.get(0), wc); 143 144 int removeCount = HarvestCache.i().getTable() 145 .removeArchive(arch.url); 146 if (verbosity) { 147 System.out.println(removeCount + " messages removed."); 148 } 149 150 new StateTable(HarvestCache.i().getDatabase()) 151 .removeArchive(arch.url); 152 } 153 } 154 155 private void maybeExit() { 156 if (openReports.isEmpty()) { 157 System.exit(0); 158 } 159 } 160 161 // ------------------- Archive --------------------- 162 163 class Archive { 164 Archive(final String design, final String url) { 165 this.design = design; 166 this.url = url; 167 } 168 169 public final String design; 170 public final String url; 171 } 172 173 private HarvestReporter createReporter(final String _archive, 174 final boolean _verbose) { 175 final HarvestReporter reporter = new HarvestReporter() { 176 final String archive = _archive; 177 final boolean verbose = _verbose; 178 179 @Override 180 public PrintStream printStream() { 181 if (verbose) { 182 return System.out; 183 } else { 184 return new PrintStream(new OutputStream() { 185 @Override 186 public void write(int b) throws IOException { 187 // nope 188 } 189 }); 190 } 191 } 192 193 @Override 194 public void proccessFinished() { 195 openReports.remove(this); 196 if(verbose){ 197 System.out.println(archive + " update finished."); 198 } 199 maybeExit(); 200 } 201 202 }; 203 openReports.add(reporter); 204 return reporter; 205 } 206 207 private final List<HarvestReporter> openReports = Collections 208 .synchronizedList(new LinkedList<HarvestReporter>()); 209 210 private Archive getArchive(final String archivePage, final WikiCache wc) 211 throws IOException { 212 final PageProperty designP = new PageProperty("Archive design"); 213 final PagePropertyReader designR = new PagePropertyReader(wc, 214 archivePage, designP); 215 216 final PageProperty urlP = new PageProperty("Archive URL"); 217 final PagePropertyReader urlR = new PagePropertyReader(wc, archivePage, 218 urlP); 219 220 if (designR.hasNext()) { 221 final String design = designR.read().getValue(); 222 final String url = urlR.read().getValue(); 223 return new Archive(design, url); 224 } 225 throw new IllegalArgumentException(archivePage 226 + " does not resolve to a proper archive definition."); 227 } 228 229 private void printHelp() { 230 System.err 231 .print("Usage: voharvest update PAGE_NAME_OF_ARCHIVE_IN_POLLWIKI* [--verbose]\n" 232 + " or voharvest remove PAGE_NAME_OF_ARCHIVE_IN_POLLWIKI [--verbose]\n"); 233 } 234}