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}