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(""); 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(""); pw.println(""); } pw.println("
" + font + "Option" + font + "Result
" + font + vs[i].getAnswer() + "" + font + count + "" + font + df.format(qf) + "%"); pw.println("
" : q2 + "%'> ")+" 
"); 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(""); pw.println(""); if(columnFlag) { row1 = ""; row2 = ""; } else { pw.println(""); } Enumeration e = config.getAnswers(); while(e.hasMoreElements()) { String value = (String) e.nextElement(); pw.println(row1+"" + row2); } pw.println(row1+"" + row2); if(!columnFlag) pw.println(""); pw.println("
"+font+"" + value + ""+font+"
"); 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; } } }