package votorola.s.wap; // Copyright 2011, Michael Allan. 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 com.google.gson.stream.*;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import javax.servlet.*;
import javax.servlet.http.*;
import votorola.a.*;
import votorola.a.voter.*;
import votorola.a.web.wic.*;
import votorola.g.lang.*;
import votorola.g.logging.*;
import votorola.g.web.*;
import votorola.s.gwt.scene.dum.*;
import votorola.s.gwt.scene.feed.ss.*;
/** The server side of the dummy feed, implementing the feed service. A single instance
* of DummyFeedSS is constructed by the servlet container (Tomcat), as defined in its
* web.xml
* configuration file. The service path is /xfDum
. Here is an example of a
* service query:
*
*
http://reluk.ca:8080/v/xfDum?callback=foo&form=json&pretty
*
* The service responds with a series of {@value BITE_COUNT} dummy bites. Query
* parameters for this service are:
*
*
*
* Key |
* Value |
* Default |
*
* callback |
*
* The name of the callback function for a JSONP response, or leave it
* unspecified for a plain JSON response. This is a qualifier for the 'form'
* parameter whenever that parameter is set to 'json'. |
*
* Null, specifying a plain JSON response. |
*
*
* form |
*
* The form of the response. Currently only 'json' is supported, which gives
* either JSON or JSONP depending on the value of the 'callback' parameter. |
*
* None, a value is required. |
*
*
* pretty |
*
* Specify 'pretty' or 'pretty=y' for a more human-readable response. This
* adds whitespace and forces a content type of 'text/plain'. |
*
* 'n' |
*
*
*
*/
public final @ThreadSafe class DummyFeedSS extends HttpServlet // FIX by using WAP, per a/web/context/WEB-INF/web.xml
{
public @Override void init() throws ServletException
{
try
{
vsRun = new VoteServer( VOWicket.contextPathToVoteServerName( // OPT for space: if whole vote-server really needed, servlets ought to share it
getServletContext().getContextPath() )).new Run( /*isSingleThreaded*/false );
vsRun.init_done(); // nothing to do here anymore
}
catch( RuntimeException x ) { throw x; }
catch( Exception x ) { throw new ServletException( x ); }
}
// ------------------------------------------------------------------------------------
/** The number of dummy bites in each response.
*/
static final int BITE_COUNT = 10;
// 100 was causing client timeout on obsidian, as polls take too long to construct.
// - H t t p - S e r v l e t ----------------------------------------------------------
protected void doGet( final HttpServletRequest request, final HttpServletResponse response )
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( HTTPServletRequestX.buildRequestSummary(request).toString() );
final boolean isPretty = HTTPServletRequestX.getBooleanParameter( "pretty", request );
final String callback;
{
final String form = HTTPServletRequestX.getParameterRequired( "form", request );
if( !"json".equals( form ))
{
throw new HTTPRequestException( /*400*/HttpServletResponse.SC_BAD_REQUEST,
"unrecognized 'form' value: " + form );
}
callback = HTTPServletRequestX.getParameterNonEmpty( "callback", request );
}
// Commit to response.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
final String characterEncoding = "UTF-8";
response.setCharacterEncoding( characterEncoding );
final Writer outBuf = new BufferedWriter( new OutputStreamWriter(
response.getOutputStream(), characterEncoding ));
if( callback != null )
{
outBuf.append( callback );
outBuf.append( '(' );
}
// note that response.isCommitted() when outBuf flushes
try
{
final JsonWriter outJSON = new JsonWriter( outBuf );
final String contentType;
if( isPretty )
{
contentType = "text/plain";
outJSON.setIndent( " " ); // otherwise it would be packed
}
else contentType = callback == null? "application/json": "application/javascript";
response.setContentType( contentType );
final Random random = new Random();
final BiteJig bite = new BiteJig();
final Biter[] biters = Biter.U.assembleBiters( vsRun );
int iUsernameRandom = -1;
outJSON.beginArray();
for( int b = 0; b < BITE_COUNT; ++b )
{
// Configure bite properties known to this feed
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for( int p = 0; p < 2; ++p )
{
for( ;; )
{
int i = random.nextInt( Dummy.USERNAME_ARRAY.length );
if( i == iUsernameRandom ) continue; // avoid duplicates
iUsernameRandom = i;
break;
}
bite.addPerson().setUser( IDPair.fromUsername(
Dummy.USERNAME_ARRAY[iUsernameRandom] ));
}
final PollJig poll = bite.setPoll();
poll.setName( Dummy.POLL_NAME_ARRAY[random.nextInt(
Dummy.POLL_NAME_ARRAY.length )]);
poll.setIssueType( "Issue" ); // fake it, not actually used by client
// Maybe configure other properties, too
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for( Biter biter: biters ) biter.configure( bite );
// Write configuration on wire
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bite.serialize( outJSON ); // clears bite, ready for re-configuration
}
outJSON.endArray();
if( callback != null )
{
outJSON.flush();
outBuf.append( ");" );
}
outJSON.close(); // throws exception if JSON imperfect, so don't close in finally block where it may mask a previous exception
}
finally { outBuf.close(); } // if not already closed
}
catch( Exception x )
{
if( !response.isCommitted() )
{
response.sendError( /*500*/HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
x.toString() );
}
logger.log( LoggerX.WARNING, "Error in service of request (" + request.getQueryString() + ")", x );
}
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
private static final Logger logger = LoggerX.i( DummyFeedSS.class );
private volatile VoteServer.Run vsRun; // final after init
}