package de.spieleck.servlets;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.text.*;
import java.lang.Comparable;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* Simple Hack for a voting servlet service.
*/
public class VotingServlet
extends HttpServlet
{
/** Request parameter: */
public static final String ACTION = "a";
/** Request parameter (and ACTION value) */
public static final String VOTE = "v";
/** Request parameter ACTION value */
public static final String SHOW = "s";
/** User setting: */
public static final String LEGEND = "legend";
/** User setting: */
public static final String FORWARD = "forward";
/** User setting: */
public static final String TITLE = "title";
/** User setting: */
public static final String BGCOLOR = "bgcolor";
/** User setting: */
public static final String FGCOLOR = "fgcolor";
/** User setting: */
public static final String SIZE = "size";
/** User setting: */
public static final String FACE = "face";
/** User setting: */
public static final String ANSWER = "answer";
/** User setting: */
public static final String VOTECOLOR = "votecolor";
/** User setting: */
public static final String COLUMN = "column";
/** User setting: */
public static final String COOKIETIME = "cookieTime";
/** User setting: */
public static final String COOKIEMODE = "cookieMode";
/** User setting: */
public static final String COUNTSORT = "countsort";
/** User setting: */
public static final String LOCKED = "locked";
/** The name of the thing */
public static final String SERVLET = "VotingServlet";
public static final String VOTESERVLET = "FSN "+SERVLET;
protected static NumberFormat df = new DecimalFormat("#0.0");
public static Hashtable configs;
protected String basePath = null;
protected String configExt = null;
protected final static Properties defaultProps = new Properties();
static
{
defaultProps.put(BGCOLOR, "#FFFFFF");
defaultProps.put(FGCOLOR, "#000000");
defaultProps.put(COLUMN , "1");
defaultProps.put(COOKIETIME, ""+(31*24*60*60) );
defaultProps.put(TITLE , VOTESERVLET);
defaultProps.put(VOTECOLOR, "#FF0000");
defaultProps.put(COUNTSORT, "1");
defaultProps.put(COOKIEMODE, "0");
}
public VotingServlet() { }
public void init()
throws ServletException
{
configs = new Hashtable();
basePath = getServletConfig().getInitParameter("basePath");
configExt = getServletConfig().getInitParameter("configExt");
log("Start: "+VOTESERVLET
+" basePath="+basePath
+" configExt="+configExt);
if ( configExt == null )
configExt = "vote.conf";
if ( basePath == null )
basePath = "WEB-INF/votedata/";
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
doGet(req, res);
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
String confName = req.getPathInfo();
if ( confName == null )
confName = "";
else
confName = confName.substring(1);
//
Config config = getConfig(confName);
if(config == null)
{
res.sendError(res.SC_EXPECTATION_FAILED,
"Could not find config " + confName);
return;
}
String vote = req.getParameter(VOTE);
if ( config.getCfg(LOCKED) != null )
showStanding(config, res);
else if( vote != null )
takeVote(config, vote, req, res);
else if( SHOW.equals(req.getParameter(ACTION)) )
showStanding(config, res);
else
{
if( !config.getAnswers().hasMoreElements() )
res.sendError(res.SC_EXPECTATION_FAILED,
"No "+ANSWER+".x in config "+confName);
else
showForm(config, req, res);
}
}
protected Config getConfig(String cname)
{
Config config = (Config) configs.get(cname);
if( config != null )
{
if ( ! config.isRecent() )
{
configs.remove(cname);
config = null;
}
}
if ( config == null )
try
{
config = new Config(
getServletContext().getRealPath(basePath+cname+configExt));
configs.put(cname, config);
}
catch ( IOException io )
{
io.printStackTrace();
return null;
}
return config;
}
/**
HTML Help: put optional Attribute
*/
protected static String optionalAttr(String name, String value)
{
if ( value != null )
return fixedAttr(name, value);
return "";
}
/**
HTML Help: put mandatory Attribute
*/
protected static String fixedAttr(String name, String value)
{
StringBuffer sb = new StringBuffer(30);
sb.append(' ').append(name).append("='").append(value).append('\'');
return sb.toString();
}
/**
HTML Help: provide Font Tag
*/
public String makeFontTag(Config cfg)
{
return makeFontTag(cfg, cfg.getCfg(SIZE));
}
public String makeFontTag(Config cfg, String size)
{
StringBuffer sb = new StringBuffer(50);
sb.append("');
return sb.toString();
}
/**
HTML Help: start page
*/
protected void startHtml(PrintWriter pw, Config config)
{
String title = config == null ? VOTESERVLET : config.getCfg(TITLE);
pw.println("");
pw.println("" + title + "");
pw.print("");
if ( config != null )
pw.println(makeFontTag(config));
pw.println("" + title + "
");
pw.println("");
}
/**
HTML Help: end page
*/
protected void endHtml(PrintWriter pw, Config cfg)
{
pw.println("
");
if ( cfg != null )
pw.print(makeFontTag(cfg,"1"));
pw.println(new Date());
pw.println("");
pw.close();
}
/**
Page: show results
*/
protected void showStanding(Config config, HttpServletResponse res)
throws IOException
{
String font = makeFontTag(config);
Vote[] vs = config.getVotes();
int i, total = 0, tmax = -1;
for ( i = 0; i < vs.length; i++ )
{
int c = vs[i].getCount();
total += c;
if ( c > tmax )
tmax = c;
}
if ( total == 0 )
total = tmax = 1;
PrintWriter pw;
res.setContentType("text/html");
pw = res.getWriter();
startHtml(pw, config);
pw.println("
");
if ( "1".equals(config.getCfg(LEGEND)) )
pw.println("| " + font + "Option | " + font + "Result |
");
for ( i = 0; i < vs.length; i++ )
{
int count = vs[i].getCount();
double qf = 100.0 * count / total;
int q2 = (int) Math.round(100.0 * count / tmax);
pw.println("| " + font + vs[i].getAnswer() + " | " + font + count + " | " + font + df.format(qf) + "% | ");
pw.println("");
pw.println("| " : q2 + "%'> ")+" | | |
");
}
pw.println("
");
endHtml(pw,config);
}
/**
Page: show vote form
*/
protected void showForm(Config config,
HttpServletRequest req, HttpServletResponse res)
throws IOException
{
boolean columnFlag = "1".equals(config.getCfg(COLUMN));
String row1 = "";
String row2 = "";
String font = makeFontTag(config);
PrintWriter pw;
res.setContentType("text/html");
pw = res.getWriter();
startHtml(pw, config);
pw.println("");
endHtml(pw,config);
}
/**
Page: take a vote
*/
protected void takeVote(Config config, String vote,
HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
boolean cancel = false;
if(!(config.getCfg(COOKIETIME)).equals("0"))
{
String cookieName;
if ( config.getCfg(COOKIEMODE).equals("0") )
cookieName = config.getCookie();
else
cookieName = config.getCookie()+":"+vote;
Cookie acookie[] = req.getCookies();
if(acookie != null)
for(int i = 0; i < acookie.length; i++)
if(cookieName.equals(acookie[i].getName()))
{
cancel = true;
break;
}
Cookie c = new Cookie(cookieName, "*");
c.setMaxAge(Integer.parseInt(config.getCfg(COOKIETIME)));
res.addCookie(c);
}
if ( !cancel )
config.vote(req, vote);
String forward = config.getCfg(FORWARD);
if(forward == null)
showStanding(config, res);
else if(!forward.startsWith("http://"))
{
RequestDispatcher requestdispatcher = getServletConfig()
.getServletContext().getRequestDispatcher(forward);
requestdispatcher.forward(req, res);
}
else
res.sendRedirect(res.encodeRedirectURL(forward));
}
/**
subclass to contain configuration data
*/
protected static class Config
{
protected long edited;
// protected String path;
protected File configFile;
protected String cookie;
// protected String font;
protected String logName;
protected String countName;
protected Properties props;
protected Properties counts;
public Config(String path)
throws IOException
{
// this.path = path;
configFile = new File(path);
logName = path+".log";
countName = path+".count";
readConfig();
// Mangle name into a cookie
int i = path.lastIndexOf("/");
if ( i == -1 )
i = path.lastIndexOf("\\");
if ( i == -1 )
i = 0;
cookie = "v."+path.substring(i)+"."+path.hashCode()+".c";
appendLog("read");
}
public boolean isRecent()
{
return edited == configFile.lastModified();
}
public String getCookie()
{
return cookie;
}
public String getCfg(String key)
{
return props.getProperty(key);
}
protected synchronized void readConfig()
throws IOException
{
props = new Properties(defaultProps);
counts = new Properties();
// font = null;
edited = configFile.lastModified();
props.load(new FileInputStream(configFile));
File count = new File(countName);
if ( count.exists() )
{
try
{
counts.load(new FileInputStream(countName));
}
catch(IOException lost)
{
lost.printStackTrace();
}
}
Enumeration e = getAnswers();
while(e.hasMoreElements())
{
String value = (String) e.nextElement();
if ( counts.getProperty(value) == null )
counts.put(value, "0");
}
}
public Enumeration getAnswers()
{
return new Enumeration()
{
int nextPos = 1;
public boolean hasMoreElements()
{
return props.getProperty(ANSWER+"."+nextPos) != null;
}
public Object nextElement()
{
return props.getProperty(ANSWER+"."+nextPos++);
}
};
}
public synchronized Vote[] getVotes()
{
Vote[] vs = new Vote[counts.size()];
int pos = 0;
Enumeration e = counts.keys();
while ( e.hasMoreElements() )
{
String key = (String) e.nextElement();
vs[ pos++ ] = new Vote(key,
safeParseInt(counts.getProperty(key)));
}
if ( "1".equals(getCfg(COUNTSORT)) )
Arrays.sort(vs);
return vs;
}
protected int safeParseInt(String str)
{
if ( str == null )
return 0;
try
{
return Integer.parseInt(str);
}
catch ( NumberFormatException e )
{
e.printStackTrace();
return 0;
}
}
public void vote(HttpServletRequest req, String answer)
{
String remoteAddr = req.getRemoteAddr();
try
{
synchronized(this)
{
counts.put(answer, Integer.toString(safeParseInt(
counts.getProperty(answer))+1));
OutputStream os = new FileOutputStream(countName);
long t0 = appendLog(remoteAddr + " " + answer);
counts.save(os,getCfg(TITLE)+" "+t0);
os.close();
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
protected synchronized long appendLog(String line)
{
long t0 = System.currentTimeMillis();
try
{
FileWriter filewriter = new FileWriter(logName, true);
PrintWriter pw = new PrintWriter(filewriter);
pw.println(t0+" "+line);
pw.close();
}
catch ( IOException fail )
{
fail.printStackTrace();
}
return t0;
}
}
/**
subclass to hold a single votes statistics
*/
protected static class Vote
implements Comparable
{
protected String answer;
protected int count;
public Vote(String answer, int count)
{
this.answer = answer;
this.count = count;
}
public String getAnswer()
{
return answer;
}
public int getCount()
{
return count;
}
public int compareTo(Object o)
{
if ( o instanceof Vote )
return ((Vote)o).getCount() - getCount();
return 1;
}
}
}