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 com.google.gson.stream.JsonWriter; import java.util.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.util.logging.Level; import java.util.logging.Logger; import javax.mail.internet.AddressException; import votorola.g.lang.ThreadRestricted; import votorola.g.logging.LoggerX; import votorola.a.diff.harvest.cache.HarvestCache; import votorola.a.diff.DiffKeyParse; 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; import votorola.s.gwt.scene.feed.ss.BiteJig; import votorola.s.gwt.scene.feed.ss.Biter; import votorola.s.gwt.scene.feed.ss.DiffLookJig; import votorola.s.gwt.scene.feed.ss.MessageJig; import votorola.s.gwt.scene.feed.ss.PersonJig; import votorola.s.gwt.scene.feed.ss.PollJig; /** * A web API for the harvested messages. An example request is: * *
* *
 * http://reluk.ca:8080/v/wap?wCall=hHarvest&hPoll=Sys/p/sandbox&hUser=Jill-ProviderNet&hStartDate=1330969452&wPretty
 * 
* *
* *

Query parameters

* *

* These are the specific parameters for the cache engine API. See also the * general parameters for the {@linkplain WAP web API}. The examples shown below * assume a call prefix of 'h', but the actual prefix depends on the * {@linkplain WAP wCall} request parameter. Newest are first in the returned * JSON Array. *

* * * * * * * * * * * * * * * * * * * * * * * * * * *
KeyValueDefault
hPollNames the poll.Empty, optional item.
hUserSpecify 'hUser=Jim-MailCom' to request messages from a specific user(s).Empty, optional item.
hIdSpecify 'hId=1234' to only request messages newer than this serial. This * allows you to update a list by only fetching newer slices.Empty, optional item.
* *

Response

* *

* The response includes the following components. These are shown in JSON * format, with explanatory comments: *

* *
 * {
 *    "{@linkplain votorola.s.gwt.scene.feed.BiteJS bite}": [ 
 *        // bites in order of sent date, newest first
 *        { // {@linkplain votorola.a.diff.harvest.cache.DiffMessage diff messages}
 *            "{@linkplain votorola.a.diff.DiffLook difference}": { 
 *                "key": KEY
 *            },
 *            "{@linkplain votorola.a.diff.harvest.Message message}": {
 *                "content": "SUMMARY",
 *                "location": "WEB-URL",
 *                "sentDate": "SENT-DATE";
 *            }
 *            "poll": "POLLNAME"
 *            "{@linkplain votorola.s.gwt.scene.feed.PersonJS persons}": [
 *                { 
 *                    ...
 *                    "username": "VOTERMAILISHUSERNAME",
 *                    ...
 *                },
 *                {
 *                    ...
 *                    "username": "CANDIDATEMAILISHUSERNAME",
 *                    ...
 *                }
 *            ],
 *        },
 *        // and so on, up to maximum server side provided messages or hSize
 *    ]
 * }
 * 
*/ public @ThreadRestricted("constructor") final class DiffFeedWAP extends Call { /** * The name to use in the {@link WAP wCall} query parameter, which is * * * {@value} . For example: wCall=hHarvest. */ public static final String CALL_TYPE = "DiffFeed"; private static final Logger LOGGER = LoggerX.i(DiffFeedWAP.class); // error tracking private final static String ERROR = "Could not initialize diff-feed WAP service."; private final boolean isInit; private final String errorMessage; // params parsed from HTTP-GET request private final String poll; private final String[] users; private final int id; /** * Constructs a DiffFeedWAP (former HarvestWAP). * * @param _prefix * @param _req * @param _resConfig * @throws HTTPRequestException */ public DiffFeedWAP(String _prefix, Requesting _req, ResponseConfiguration _resConfig) throws HTTPRequestException { super(_prefix, _req, _resConfig); boolean tmpInit = false; int tempId = 0; String tempPoll = null; String tempUsers = null; String tempMessage = null; try { tempPoll = HTTPServletRequestX.getParameterNonEmpty(prefix() + "Poll", req().request()); tempUsers = HTTPServletRequestX.getParameterNonEmpty(prefix() + "Users", req().request()); final String preId = HTTPServletRequestX.getParameterNonEmpty( prefix() + "Id", req().request()); tempId = preId == null ? 0 : Integer.valueOf(preId); tmpInit = true; } catch (Exception e) { tempMessage = e.getMessage(); LOGGER.log(Level.WARNING, ERROR, e); } poll = tempPoll; users = tempUsers.split(","); id = tempId; isInit = tmpInit; errorMessage = tempMessage; } /** * Method implementing the servlet response. */ @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 } // synchronization to ensure the statement is not reused while we // still use // the result set. this is only for prototyping and very slow synchronized (req().wap().vsRun().database()) { ResultSet rs = HarvestCache.init(req().wap().vsRun()) .getTable().get(poll, users, id); out.name(prefix()).beginArray(); final Biter[] biters = Biter.U.assembleBiters(req().wap() .vsRun()); final BiteJig bite = new BiteJig(); // id, sender, addressee, pollname, content, base_url, path, // sent_ts, a, ar, b, br, selectand while (rs.next()) { // Configure bite properties final IDPair authorIDPair = IDPair.fromEmail(rs .getString(2)); final PersonJig author = bite.addPerson(); author.setUser(authorIDPair); final IDPair addresseeIDPair = IDPair.fromEmail(rs .getString(3)); final PersonJig addressee = bite.addPerson(); addressee.setUser(addresseeIDPair); final PollJig poll = bite.setPoll(); poll.setName(rs.getString(4)); final MessageJig msg = bite.addMessage(); msg.setContent(rs.getString(5)); msg.setLocation(rs.getString(6) + rs.getString(7)); bite.setSentDate(new Date(rs.getTimestamp(8).getTime())); // temporary up-to-date fetch-based calculation for lack of // historical vote data (static relations on sent_ts) bite.setRelation(HarvestCache .i() .relation(rs.getString(2), rs.getString(3), rs.getString(4)).symbol()); final DiffLookJig look = bite.setDifference(); look.setKey(new DiffKeyParse(rs.getInt(9), rs.getInt(10), // CWFIX obsolete form rs.getInt(11), rs.getInt(12)).key()); // Maybe configure other properties, too // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - // - - - - for (Biter biter : biters) biter.configure(bite); // Write configuration on wire // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - // - - - - bite.serialize(out); // clears bite, ready for // re-configuration } rs.close(); } out.endArray(); } catch (RuntimeException | SQLException | AddressException x) { LOGGER.log(LoggerX.WARNING, /* message */"", x); } } private void error(final JsonWriter out, final String errorMessage) throws IOException { out.name("error").value(errorMessage); } }