package votorola.s.wap; // 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. import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import com.google.gson.stream.JsonWriter; import java.sql.SQLException; import java.util.logging.Level; import java.util.logging.Logger; import javax.mail.internet.AddressException; import javax.script.ScriptException; import votorola.g.lang.ThreadRestricted; import votorola.g.logging.LoggerX; import votorola.a.PageProperty; import votorola.a.PagePropertyReader; import votorola.a.PollwikiVS; import votorola.a.VoteServer; import votorola.a.WikiCache; import votorola.a.count.Vote; import votorola.a.diff.harvest.HarvestReporter; import votorola.a.diff.harvest.cache.HarvestCache; import votorola.a.diff.harvest.kick.Kicker; import votorola.a.diff.harvest.kick.UpdateKick; import votorola.a.voter.IDPair; import votorola.a.web.wap.Call; import votorola.a.web.wap.Requesting; import votorola.a.web.wap.Responding; import votorola.a.web.wap.WAP; import votorola.a.web.wap.ResponseConfiguration; import votorola.g.web.HTTPRequestException; import votorola.g.web.HTTPServletRequestX; /** * A web API to kick the harvester to update forums. */ public @ThreadRestricted("constructor") final class KickWAP extends Call { /** * The name to use in the {@link WAP wCall} query parameter, which is * * * * * {@value} . For example: wCall=kKick. */ public static final String CALL_TYPE = "Kick"; private static final Logger LOGGER = LoggerX.i(KickWAP.class); // error handling private final static String ERROR = "Could not initialize kick WAP service."; private String errorMessage; private final boolean isInit; // started succesfully? // parameters extraced from HTTP GET request private final String poll; private final String user; /** * Constructs a KickWAP. This service is used by * {@linkplain votorola.s.gwt.stage.talk.TalkTrack} to trigger harvesters to * update a forum. * * @param _prefix * @param _req * @param _resConfig * @throws HTTPRequestException */ public KickWAP(String _prefix, Requesting _req, ResponseConfiguration _resConfig) throws HTTPRequestException { super(_prefix, _req, _resConfig); boolean tmpInit = false; String tempPoll = null, tempUser = null, tempMessage = null; try { tempPoll = HTTPServletRequestX.getParameterNonEmpty(prefix() + "Poll", req().request()); tempUser = HTTPServletRequestX.getParameterNonEmpty(prefix() + "User", req().request()); tmpInit = true; } catch (Exception e) { tempMessage = e.getMessage(); LOGGER.log(Level.WARNING, ERROR, e); } poll = tempPoll; user = tempUser; isInit = tmpInit; errorMessage = tempMessage; } /** * Servlet response, streaming out JSON from the * {@linkplain votorola.a.diff.harvest.cache.DiffMessageTable}. */ @Override public void respond(Responding res) throws IOException { // This method is also called by the default implementation of doHead(), // which discards the response body after learning its length. try { LOGGER.fine(votorola.g.web.HTTPServletRequestX.buildRequestSummary( res.request()).toString()); final JsonWriter out = res.outJSON(); if (!isInit) { error(out, errorMessage); return; // init error } final VoteServer.Run vsRun = req().wap().vsRun(); // ensure harvest services are initialized HarvestCache.init(vsRun); final PollwikiVS wiki = vsRun.voteServer().pollwiki(); final WikiCache wc = wiki.cache(); final IDPair id = IDPair.fromUsername(user); final Vote vote = new Vote(id, vsRun.scopePoll().ensurePoll(poll) .voterInputTable()); boolean triggered = false; // allow endcandidates to be triggered. if(vote.getCandidate().equals(IDPair.NOBODY)) { triggered |= triggerUpdates(wiki.positionPageName(vote.voter().username(), poll),wc); } else { triggered |= triggerUpdates(wiki.positionPageName(vote.getCandidate().username(), poll),wc); } out.name(prefix()).beginObject(); out.name("triggered").value(triggered); out.endObject(); } catch (RuntimeException | AddressException | SQLException | ScriptException x) { LOGGER.log(LoggerX.WARNING, /* message */"", x); } } /** * Creates {@linkplain UpdateKick} for each Forum linked on the page. * @param page here user positions. * @param wc * @return whether a forum has been found and triggered * @throws IOException */ private boolean triggerUpdates(final String page, final WikiCache wc) throws IOException { final PageProperty forumP = new PageProperty("Forum"); final PagePropertyReader forumR = new PagePropertyReader(wc, page, forumP); boolean triggered = false; while (forumR.hasNext()) { final String name = forumR.read().getValue(); final PageProperty designP = new PageProperty("Archive design"); final PagePropertyReader designR = new PagePropertyReader(wc, name, designP); final PageProperty urlP = new PageProperty("Archive URL"); final PagePropertyReader urlR = new PagePropertyReader(wc, name, urlP); if (designR.hasNext()) { triggered = true; final String design = designR.read().getValue(); final String url = urlR.read().getValue(); LOGGER.fine("Triggered " + name + " update for " + poll + ":" + user + " targeting " + design + " " + url); Kicker.i().broadcast( UpdateKick.create(design, url, new HarvestReporter() { @Override public void proccessFinished() { // we ignore feedback } @Override public PrintStream printStream() { return new PrintStream(new OutputStream() { @Override public void write(int b) throws IOException { // ignore } }); } })); } } return triggered; } private void error(final JsonWriter out, final String errorMessage) throws IOException { out.name("error").value(errorMessage); } }