<< Zune review | Home | Using the ODBC Data Source Administrator >>

Using Aldan3 library for writing simple web applications

Yet another library or revolutionary approach?

The Article demo downloadable zip file

There is a number of different servlet frameworks available helping in a rapid development of web applications. A wide selection of tools is always a good thing because it allows choosing one most suitable for particular requirements. Aldan3 is a fullstack framework designed primary for creation small applications with codebase around 1000-10000 lines. This guide provides an illustration based on using Eclipse IDE, but it can be transferred to another IDE of your choice or just a simple Java code editor.

Requirements

Create a web application for publishing polls with less than 20 items, allowing custom items, and immune against SPAM and flood robots. Polls have to be created using a simple XML definition, like below:

 

<?xml version=”1.0”?>

<poll>

   <title>poll title is here</title>

   <type>single or multiselect</type>

   <show-result>yes/no</show-result>

  <vote>

       <title>a vote title is here</title>

       <fill-required> yes/no  /<fill-required>

  </vote>

  ….

<vote …>

……

</poll>

Result of polls can be viewable and stored in XML file as well in the following format:

<?xml version=”1.0”?>

   <poll-result>

       <vote>

            <title>a vote title is here</vote>

            <chosen>number</chosen>

            <entered>a user input</entered>

                …..

            <entered>a user input</entered>

      </vote>

  </poll-result>

An administration of a poll is possible by using config files.

A poll can be added to any web site in a simple <div> or <iframe> block.

Design

Since this illustration was created for Aldan3 framework, it assumes that a usage of the framework is mandatory and it is a part of requirements. Certainly Ruby, PHP, or Python based realization can be simpler, however less portable.

Aldan3 dictates the following design patterns for implementing any web applications:

  • Front Controller
  • MVC based services

The Front Controller redirects requests to a respective service, which is represented basically by a MVC pattern. There is no any reason to touch Front Controller, so a design any application is just a matter of defining services and splitting them further on model, view and controller parts. The following decisions have to be made while doing the design:

  1. One or multiple services have to be involved
  2. How to track voters
  3. Ajax or traditional HTML approach
  4. How to parse and generate XML

Any UI service can be multifunctional, however a usage of separate services is much clearer even for a very similar tasks. An inheritance can also help in taking an extra work of implementation every new similar service. So the following two services are introduced:

  1. Poll display and processing
  2. Poll result display

A service class can include or not a model implementation.  Although a model can be included in a service implementation,  it is reasonable to separate a model in a dedicated class as for illustration purpose, as for a consideration of future extensions, The model can take care of a persistency as well. Since a poll has quite simplistic UI, a traditional HTML generation is preferable to cover wider specter of browsers. Xpath seems to be suitable for parsing a poll definition. When XML generation can be done just using a direct tag/data writing. Reading poll data back can utilize SAX parsing. Tracking voters can be done using cookies. Flood and SPAM control can be organized on a black list approach.

Implementation

Aldan3 UI services class takes care of processing user input and preparing a model used by a view.  Every service has to extend BasePageService abstract class.  Eclipse will provide all required methods with an empty body. It is recommended to create an intermediate base class taking care of common required methods especially for big projects. However this possibility will be illustrated even for this small one.  Let’s name Poll display and processing service as Voter. The following code was generated by Eclipse:

 

import org.aldan3.servlet.BasePageService;

 

public class Voter extends BasePageService {

 

      @Override

      protected Object doControl() {

            // TODO Auto-generated method stub

            return null;

      }

 

      @Override

      protected Object getModel() {

            // TODO Auto-generated method stub

            return null;

      }

 

      @Override

      protected String getSubmitPage() {

            // TODO Auto-generated method stub

            return null;

      }

 

      @Override

      protected String getUnauthorizedPage() {

            // TODO Auto-generated method stub

            return null;

      }

 

      @Override

      protected String getViewName() {

            // TODO Auto-generated method stub

            return null;

      }

 

      public boolean isThreadFriendly() {

            // TODO Auto-generated method stub

            return false;

      }

 

      public boolean isThreadSafe() {

            // TODO Auto-generated method stub

            return false;

      }

 

      public String getPreferredServiceName() {

            // TODO Auto-generated method stub

            return null;

      }

 

      public Object getServiceProvider() {

            // TODO Auto-generated method stub

            return null;

      }

 

}

Let's start from defenition of  information and configuration methods first. There are two required service methods   getPreferredServiceName  and getServiceProvider . Name of service used for registration it and accessibility. It doesn’t play much role for UI services, so it can be just a name of service class. Aldan3 Front Controller uses a class name anyway to find a service. Service provider makes sense when an actual service is delegated, so returning this is an appropriate implementation.

 

      public String getPreferredServiceName() {

            return "Voter"; // usually name of class

      }

 

      public Object getServiceProvider() {

            return this; // no delegated services so return ourselves

      }

 

A group of methods telling about multithreading abilities of a service is the next target of implementation. If one instance of a service can serve concurrent requests, then method isThreadFriendly may return true. Here it needs to be careful.Generally the base service class isn't thread friendly since uses class members, however a service can be thread friendly when it doesn't deal with http request parameters. Method isThreadSafe tells a possibility to make concurrent calls when only one instances of a service is instantiated. Services have to be implemented to be at least thread safe, so this methods returns true in most implementations.

 

      public boolean isThreadFriendly() {

            return false; // one instance of service class can manage concurrent requests

      }

 

      public boolean isThreadSafe() {

            return true;  // services can run concurrently

      }

 

Aldan3 services are access controllable; it means that every service authorizes accesses. By default all services are not accessible unless a user is authenticated. A selective authorization can be added as well. Several methods provide a flexibility of this mechanism. In our case, all services have to be public accessible, it is specified by overriding

 

      @Override

      public boolean isPublic() {

            return true; // allow public access, no authorization

      }

 

Non-authorized requests can be redirected to a specific service, which can be defined in a method: getUnauthorizedPage. Implementing the method for public accessible services doesn’t make much sense, however an attempt of returning the same service name can issue infinitive loop at page access and should be avoided.

 

      @Override

      protected String getUnauthorizedPage() {

            throw new IllegalAccessError ("Check authorization mechanism settings");

      }

 

Aldan3 services usually use external views which are a template, JSP, or JavaScript based. Template based views are assumed by default. A view dispatching mechanism can be defined by overriding certain methods. For example JSP and different template engine driven views can be combined in the same application. A view name has to be retuned in method getViewName, unless views are not used. It is convenient to select view name the same as a service name, so an implementation of this method can look like this:

 

      @Override

      protected String getViewName() {

            return getPreferredServiceName().toLowerCase()+".htm";

      }

So we are mostly done with common methods, which can be shared by all UI services of the web application.  One more method has to be taken care for this illustration. Aldan3 provides automatic multi lingual UI based on resource files keeping labels in different languages. Since this example doesn’t utilize this capability, Aldan3 machinery has to be notified about that, so the following method needs to be overridden:

 

      @Override

      protected boolean useLabels() {

            return false;

      }

 

Service specific methods implementation can be started now . getModel passes model to a view. Generally this method will delegate a call to a model. Sometimes it can pre-process model data for easy use by a view. For example for client side views (Ajax), a model can be converted in XML(unless it is already in this format) or JSON data payload. Aldan3 template engine accepts model data represented in a map, so the method does such job.

 

      @Override

      protected Object getModel() {

            HashMap<String,PollModel> hm = new HashMap<String,PollModel>(1);

            hm.put("model", getPollModel());

            return hm;

      }

 

 

getPollModel is a custom method, which implementation will be discussed below. Finally let’s discuss two control functionality related methods.  Method getSubmitPage provides a general inter services navigation. It returns a service name when the current service finishes all own UI tasks and a user has to deal with another UI service. This is only a convenience method, since a user has other possibilities to navigate to other services of an application. Aldan3 concept is one UI service per one functional unit of an application. Since we defined a two services application, this method has to return a name of a service to show a poll result.

 

      @Override

      protected String getSubmitPage() {

            return "Result"; // service reused

      }

 

Processing of a user input (an actual control) happens in method: doControl. This method validates a user input, and then changes the model state.  Generally it can keep a user on the same page or move a navigation to another service. doControl can return data which are considered as data for the service view. In this case the view will be invoked again. It gives a chance to notify users about incorrect data or just ask for input confirmation. If the method returns null, then it is considered as service finishes its work and navigation will be moved to a service defined by method: getSubmitPage. 

 

      @Override

      protected Object doControl() {

            PollModel model = getPollModel();

            String title;

            model.addVote(title = getStringParameterValue("vote", null, 0));

            if (title != null) {

                  model.addVoteChoice(title, getStringParameterValue(title, null, 0));

            }

            return null;

      }

 

The implementation uses some convenient method to access user input data from a servlet request. Data validation can be done on client side in JavaScript. The control code is immune to invalid data, so no additional validation happens here.

Let’s move on implementation of a model. All decisions about model format and parsing/generation XML data were made in design parts, so model implementation code is a straightforward.

public class PollModel implements ServletContextListener {

      public static final String MODEL_ATTR_NAME = "PollModel";

 

      // TODO collect init params constants here

 

      private String pollTitle;

 

      private List<Vote> votes;

 

      private HashMap<String, Vote> voteResults;

 

      private HashMap<String, Set<String>> choiceResults;

 

      private boolean canDisplay;

 

      private boolean multiSelect;

 

      private int pollDuration;

 

      private int maxChoices = 1000;

 

      private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

 

      public String getTitle() {

            return pollTitle;

      }

 

      public List<Vote> getVotes() {

            return votes;

      }

 

      public boolean isShowable() {

            return canDisplay;

      }

 

      public boolean isMultiSelectable() {

            return multiSelect;

      }

 

      public int getPollDuration() {

            return pollDuration;

      }

 

      public void contextDestroyed(ServletContextEvent sce) {

            // store result

            ServletContext sc = sce.getServletContext();

            String pollFile = sc.getInitParameter("poll_result_file");

            OutputStreamWriter osw = null;

            try {

                  saveXMLResult(osw = new OutputStreamWriter(new FileOutputStream(pollFile), "utf-8"));

            } catch (UnsupportedEncodingException e) {

                  e.printStackTrace();

            } catch (FileNotFoundException e) {

                  e.printStackTrace();

            } catch (IOException e) {

                  e.printStackTrace();

            } finally {

                  if (osw != null)

                        try {

                              osw.close();

                        } catch (IOException e) {

                        }

            }

      }

 

      public void contextInitialized(ServletContextEvent sce) {

            ServletContext sc = sce.getServletContext();

            // read a poll definition

            XPath xp = XPathFactory.newInstance().newXPath();

            try {

                  String pollLoc = sc.getInitParameter("poll_url");

                  URL pollUrl = sc.getResource(pollLoc);

                  if (pollUrl == null)

                        pollUrl = new URL(pollLoc);

                  Node document = (Node) xp.evaluate("/*", new InputSource(pollUrl.openStream()), XPathConstants.NODE);

                  // TODO in first evaluate /poll

                  document = (Node) xp.evaluate("//poll", document, XPathConstants.NODE);

                  pollTitle = (String) xp.evaluate("title", document, XPathConstants.STRING);

                  String pollDureationStr = (String) xp.evaluate("duration", document, XPathConstants.STRING);

                  if (pollDureationStr != null && pollDureationStr.length() > 1) {

                        pollDuration = 1;

                        switch(pollDureationStr.charAt(pollDureationStr.length()-1)) {                     

                        case 'w':

                              pollDuration *= 7;

                        case 'd':

                              pollDuration *= 24;

                        case 'h':

                              pollDuration *= 60;

                        case 'm':

                              pollDuration *= 60;

                              pollDureationStr = pollDureationStr.substring(0, pollDureationStr.length()-1);

                        }

                  }

                  try {

                        pollDuration = pollDuration * Integer.parseInt(pollDureationStr);

                  } catch (NumberFormatException e) {

                  }

                  canDisplay = "yess".equals(xp.evaluate("show-result", document, XPathConstants.STRING));

                  multiSelect = "mulitselect".equals(xp.evaluate("type", document, XPathConstants.STRING));

                  NodeList nodes = (NodeList) xp.evaluate("vote", document, XPathConstants.NODESET);

                  int nodesLen = nodes.getLength();

                  votes = new ArrayList<Vote>(nodesLen);

                  voteResults = new HashMap<String, Vote>(nodesLen);

                  choiceResults = new HashMap<String, Set<String>>(nodesLen);

                  for (int p = 0; p < nodesLen; p++) {

                        Vote v = new Vote();

                        v.title = (String) xp.evaluate("title", nodes.item(p), XPathConstants.STRING);

                        v.fillable = "yes".equals(xp.evaluate("fill-required", nodes.item(p), XPathConstants.STRING));

                        votes.add(v);

                        voteResults.put(v.title, v);

                        if (v.fillable)

                              choiceResults.put(v.title, new HashSet<String>());

                  }

                  pollLoc = sc.getInitParameter("poll_result_file");

                  if (pollLoc != null) {

                        FileInputStream fis = null;

                        File resultFile = new File(pollLoc);

                        if (resultFile.exists())

                              try {

                                    readXMLResult(fis = new FileInputStream(resultFile));

                              } finally {

                                    if (fis != null)

                                          fis.close();

                              }

                  }

                  // TODO read maxChoices

                  sc.setAttribute(MODEL_ATTR_NAME, this);

            } catch (XPathExpressionException e) {

                  e.printStackTrace();

            } catch (MalformedURLException e) {

                  e.printStackTrace();

            } catch (IOException e) {

                  e.printStackTrace();

            }

            // read already accumulated result to show

      }

 

      public void addVote(String voteName) {

            if (validate(voteName, null) == false)

                  return;

            rwl.writeLock().lock();

            try {

                  voteResults.get(voteName).count++;

            } finally {

                  rwl.writeLock().unlock();

            }

      }

 

      public void addVoteChoice(String voteName, String choice) {

            if (validate(voteName, choice) == false || choice == null)

                  return;

            rwl.writeLock().lock();

            try {

                  if (maxChoices > 0 && maxChoices > choiceResults.get(voteName).size())

                        choiceResults.get(voteName).add(choice.toLowerCase());

            } finally {

                  rwl.writeLock().unlock();

            }

      }

 

      public static final List<Vote> calculatePercentage(List<Vote> absVotes) {

            List<Vote> pr = new ArrayList<Vote>(absVotes.size());

            int sum = 0;

            for (Vote v : absVotes) {

                  Vote nv = new Vote();

                  nv.title = v.title;

                  nv.count = v.count;

                  sum += nv.count;

                  pr.add(nv);

            }

            if (sum > 0)

            for (Vote v : pr) {

                  v.count = v.count * 100 / sum;

            }

            // TODO do sum of % and last item calculate as 100% - sum%

            return pr;

      }

 

      private void saveXMLResult(Writer w) throws IOException {

            w.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");

            w.write("<poll-result>");

            rwl.readLock().lock();

            try {

                  for (Vote v : voteResults.values()) {

                        w.write("<vote>");

                        w.write("<title>");

                        w.write(HttpUtils.htmlEncode(v.title));

                        w.write("</title>");

                        w.write("<chosen>");

                        w.write(String.valueOf(v.count));

                        w.write("</chosen>");

                        if (v.fillable) {

                              for (String s : choiceResults.get(v.title)) {

                                    w.write("<entered>");

                                    w.write(HttpUtils.htmlEncode(s));

                                    w.write("</entered>");

                              }

                        }

                        w.write("</vote>");

                  }

            } finally {

                  rwl.readLock().unlock();

            }

            w.write("</poll-result>");

      }

 

      private void readXMLResult(InputStream is) throws IOException {

            try {

                  SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

                  parser.parse(is, new DefaultHandler() {

                        private Vote cv;

 

                        private StringBuffer sb = new StringBuffer();

 

                        @Override

                        public void characters(char[] chars, int start, int length) throws SAXException {

                              sb.append(chars, start, length);

                        }

 

                        @Override

                        public void endElement(String uri, String localName, String qName) throws SAXException {

                              if ("title".equals(qName)) {

                                    cv = voteResults.get(sb.toString());

                              } else if ("chosen".equals(qName)) {

                                    if (cv != null)

                                          cv.count = Integer.parseInt(sb.toString());

                              } else if ("entered".equals(qName)) {

                                    if (cv != null)

                                          choiceResults.get(cv.title).add(sb.toString());

                              }

                        }

 

                        @Override

                        public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {

                        }

 

                        @Override

                        public void startElement(String uri, String localName, String qName, Attributes attrs)

                                    throws SAXException {

                              sb.setLength(0);

                        }

                  });

            } catch (ParserConfigurationException e) {

                  e.printStackTrace();

            } catch (SAXException e) {

                  e.printStackTrace();

            }

      }

 

      private boolean validate(String voteName, String choice) {

            rwl.readLock().lock();

            try {

                  if (voteResults.get(voteName) == null)

                        return false;

                  if (choice != null)

                        if (choiceResults.get(voteName) == null)

                              return false;

            } finally {

                  rwl.readLock().unlock();

            }

            return true;

      }

 

      static public class Vote {

            public String title;

 

            public boolean fillable;

 

            public int count;

 

            @Override

            public boolean equals(Object arg0) {

                  if (arg0 == this)

                        return true;

                  if (arg0 instanceof Vote == false)

                        return false;

                  Vote anotherVote = (Vote) arg0;

                  return title != null && title.equals(anotherVote.title) && fillable == anotherVote.fillable;

            }

 

            @Override

            public int hashCode() {

                  if (title != null)

                        return title.hashCode() ^ (fillable ? 1 : 0);

                  return super.hashCode();

            }

 

            @Override

            public String toString() {

                  return "Vote:" + title + " (" + fillable;

            }

      }

}

 

Note that the model is wrapped in servlet context listener interface, that gives benefits like being easy sharable between servlets of the same application, be prepared and ready to use prior servlet initialization. Aldan3 doesn’t dictate any particular approaches of models or other sharable code initialization; it can be done as in different servlet listeners, as in an overriding initialization part of Aldan3 servlet framework. Every approach has own benefits and decision should be done based on particular requirements or personal tastes. The model is thread friendly and uses readwrite locker offered by concurrency library of JDK 1.5 and above. Processing a poll definition data isn’t separated in a method, however it can be done in future.

An implementation of second service is a way simpler. It is reasonable to inherit it from Voter and override just few methods:

 

public class Result extends Voter {

 

      @Override

      protected Object doControl() {

            throw new IllegalAccessError ("No control functionality provided by the service");

      }

 

      @Override

      public boolean isPublic() {

            return true; // allow public access, no authorization

      }

 

      @Override

      protected String getSubmitPage() {

            return getUnauthorizedPage();

      }

 

      @Override

      protected String getUnauthorizedPage() {

            throw new IllegalAccessError ("Check authorization mechanism settings");

      }

 

      public boolean isThreadFriendly() {

            return false;

      }

 

      public String getPreferredServiceName() {

            return "Result";

      }

 

}

 

No voter control mechanism was discussed above in the design part so any voter can do a poll as many times as he/she/it wants. A cookie based state of voting can help here and it requires very small code changes. Aldan3 authorization mechanism can be used here. It can tell that Voter service isn’t accessible for users already performed a poll, here the changes are:

 

      @Override

      public boolean isPublic() {

            return getCookie(COOKIE_NAME) == null;

      }

 

      @Override
     
protected String getUnauthorizedPage() {
           
return getSubmitPage();
     
}

 

A code to set cookie has to be added in doControl in case of a successful poll:

 

      @Override

      protected Object doControl() {

            PollModel model = getPollModel();

            String title;

            model.addVote(title = getStringParameterValue("vote", null, 0));

            if (title != null) {

                  model.addVoteChoice(title, getStringParameterValue(title, null, 0));

            // setting a cookie to keep state of made poll

                  Cookie vc = new Cookie(COOKIE_NAME, title);

                  vc.setMaxAge(model.getPollDuration());

                  resp.addCookie(vc);

            }

            return null;

      }

 

Note that there is no reason to keep a vote state forever, so cookie lifetime is set.

Now we can move to views implementation.  Aldan3 template engine is very simplistic and allows iterative operations, switches, access to object public fields and method calls.

 

<div>

<div id="title">@model.getTitle*()*@</div>

<div id="error_ban"></div>

<form name="poll-@model.getTitle*()*@">

@model.isMultiSelectable*()*{

  @true(@model.getVotes*()*(

     <div id="vote_option"><input type="checkbox" name="vote" value="@element.title@">@element.title@

         @element.fillable{

            @true(<input type="text" name="@element.title@">)@

         }@

     </div>

    )@

  )@

  @(@model.getVotes*()*(

     <div id="vote_option"><input type="radio" name="vote" value="@element.title@">@element.title@

         @element.fillable{

            @true(<input type="text" name="@element.title@">)@

         }@

     </div>

    )@

  )@

}@

<input type=hidden name="submit.x" value="1">

<input type=button value="Vote" onclick="submitForm()">&nbsp;&nbsp;<a href="Result">View result</a>

</form>

</div>

 

Constructions like @name(..)@ are used against collection model objects.  A model object can be used in switches like @name{ @case1(..)@ @case2(..)@ @(..default..)@}@. Member’s access uses a dot-separated notation. Method calls uses ‘*(‘, ‘*)’ parenthesizes for inclosing a list of parameters. Aldan3 separates view and control requests based on “submit.x” request parameter. If it is presented, then the request is considered as control one, otherwise view one. The final implementation adds a JavaScript code for a poll data validation and styles. The view code of Result service is even simpler:

 

<div id="title">@model.getTitle*()*@</div>

<div>Poll result</div>

@javaarchitect.servlet.examples.poll.PollModel.calculatePercentage*(java.util.List^@model.getVotes*()*@)*(

     <div id="vote_option">@element.title@ : @element.count@%

     </div>

  )@

 

Now time is to work on deployment descriptors and configuration data.

Deployment

Web.xml includes the following context init parameters used by the model:

  

    <context-param>

       <param-name>poll_url</param-name>

       <param-value>/WEB-INF/classes/javaarchitect/servlet/examples/poll/resource/photo-feature-poll.xml</param-value>

       <description>Defines poll definition XML file location</description>

    </context-param>

    <context-param>

       <param-name>poll_result_file</param-name>

       <param-value>./poll_result.xml</param-value>

       <description>Defines poll result XML file location</description>

    </context-param>

 

The parameter poll_url specifies a poll definition file location and generally it can be just a context related path. Another parameter poll_result_file specifies where a poll result is stored.

 

Servlet definition is straightforward:

 

  <servlet>

    <servlet-name>Poll</servlet-name>

    <servlet-class>org.aldan3.servlet.Main</servlet-class>

    <init-param>

        <param-name>properties</param-name>

        <param-value>/WEB-INF/config/poll.properties</param-value>

    </init-param>

  </servlet>

 

  <servlet-mapping>

    <servlet-name>Poll</servlet-name>

    <url-pattern>/poll/*</url-pattern>

  </servlet-mapping>

 

Aldan3 fullstack framework main class has to be specified as a servlet class. The class can be extended though. It takes one mandatory parameter properties. This parameter specifies location of Aldan3 configuration file.

Finally a model class has to be specified as a listener:

 

<listener>

  <listener-class>javaarchitect.servlet.examples.poll.PollModel</listener-class>

</listener>

 

Aldan3 configuration file looks as below:

 

# UI services package prefix for Front Controller

WorkerPrefix=javaarchitect.servlet.examples.poll.

## Views HTML template path 

TEMPLATEROOT=/javaarchitect/servlet/examples/poll/resource

# Default UI service name when not specified

DefaultServant=Voter

# Page generation char set

charset=KOI8-r

# Debug flag

DEBUG=0

 

It is a standard Java properties file. The property WorkerPrefix specifies a package prefix of UI services, so Front Controller can automatically resolve a full class name and call a respective service. The property TEMPLATEROOT  makes sense only when Aldan3 template engine is used and specifies location of view template based implementations. The property DefaultServant gives a name of a default UI service. Properties charset and DEBUG give a char set name and a debug mode respectively.

All components of the poll web application have to be properly packaged in a .war file which can be deployed under any J2EE application server. An example of poll definition file can look like:

 

<?xml version="1.0"?>

<poll>

   <title>Which is most important next feature of MediaCChest for iPod?</title>

   <duration>20w</duration>

   <type>singleselect</type>

   <show-result>yes</show-result>

  <vote>

       <title>Photo library</title>

       <fill-required>no</fill-required>

  </vote>

  <vote>

       <title>Ripping music</title>

       <fill-required>no</fill-required>

  </vote>

  <vote>

       <title>Another feature, please specify</title>

       <fill-required>yes</fill-required>

  </vote>

</poll>

 

A complete source code of the illustration with 7Bee building script is supplied as the zip file.

Problems, Open Issues, and Limitations

There are several problems of this implementation. Most important that poll data are stored only at time of closing web application, so there is a risk of losing poll result in case of hardware failures. Another risk is of overflowing web container memory with too many choices entering for polls with custom entries. The current implementation just hardcoded no accept trigger at 1000 entries. And finally a robot not setting cookies can compromise poll result. As an idea of solution can be in maintaining a black list of IPs generating too many votes. Among of limitations you can notice a fixed cookie name, so no multiple polls can be run from the same host/URI. However one poll servlet can't provide multiple polls due the design. So every new poll has to use a different servlet with a distinctive access path that automatically resolves the cookie problem (no domain scope of a cookie).

 

 




Add a comment Send a TrackBack