Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>A bit decent web application consists of a mix of design patterns. I'll mention only the most important ones.</p> <hr> <h2><a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" rel="noreferrer">Model View Controller pattern</a></h2> <p>The core (architectural) design pattern you'd like to use is the <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" rel="noreferrer">Model-View-Controller pattern</a>. The <em>Controller</em> is to be represented by a Servlet which (in)directly creates/uses a specific <em>Model</em> and <em>View</em> based on the request. The <em>Model</em> is to be represented by Javabean classes. This is often further dividable in <em>Business Model</em> which contains the actions (behaviour) and <em>Data Model</em> which contains the data (information). The <em>View</em> is to be represented by JSP files which have direct access to the (<em>Data</em>) <em>Model</em> by EL (Expression Language). </p> <p>Then, there are variations based on how actions and events are handled. The popular ones are:</p> <ul> <li><p><strong>Request (action) based MVC</strong>: this is the simplest to implement. The (<em>Business</em>) <em>Model</em> works directly with <code>HttpServletRequest</code> and <code>HttpServletResponse</code> objects. You have to gather, convert and validate the request parameters (mostly) yourself. The <em>View</em> can be represented by plain vanilla HTML/CSS/JS and it does not maintain state across requests. This is how among others <a href="https://stackoverflow.com/tags/spring-mvc/info">Spring MVC</a>, <a href="https://stackoverflow.com/tags/struts/info">Struts</a> and <a href="https://stackoverflow.com/tags/stripes/info">Stripes</a> works.</p></li> <li><p><strong>Component based MVC</strong>: this is harder to implement. But you end up with a simpler model and view wherein all the "raw" Servlet API is abstracted completely away. You shouldn't have the need to gather, convert and validate the request parameters yourself. The <em>Controller</em> does this task and sets the gathered, converted and validated request parameters in the <em>Model</em>. All you need to do is to define action methods which works directly with the model properties. The <em>View</em> is represented by "components" in flavor of JSP taglibs or XML elements which in turn generates HTML/CSS/JS. The state of the <em>View</em> for the subsequent requests is maintained in the session. This is particularly helpful for server-side conversion, validation and value change events. This is how among others <a href="https://stackoverflow.com/tags/jsf/info">JSF</a>, <a href="https://stackoverflow.com/tags/wicket/info">Wicket</a> and <a href="https://stackoverflow.com/tags/playframework/info">Play!</a> works.</p></li> </ul> <p>As a side note, hobbying around with a homegrown MVC framework is a very nice learning exercise, and I do recommend it as long as you keep it for personal/private purposes. But once you go professional, then it's strongly recommended to pick an existing framework rather than reinventing your own. Learning an existing and well-developed framework takes in long term less time than developing and maintaining a robust framework yourself.</p> <p>In the below detailed explanation I'll restrict myself to request based MVC since that's easier to implement.</p> <hr> <h2><a href="http://en.wikipedia.org/wiki/Front_Controller_pattern" rel="noreferrer">Front Controller pattern</a> (<a href="http://en.wikipedia.org/wiki/Mediator_pattern" rel="noreferrer">Mediator pattern</a>)</h2> <p>First, the <em>Controller</em> part should implement the <a href="http://en.wikipedia.org/wiki/Front_Controller_pattern" rel="noreferrer">Front Controller pattern</a> (which is a specialized kind of <a href="http://en.wikipedia.org/wiki/Mediator_pattern" rel="noreferrer">Mediator pattern</a>). It should consist of only a single servlet which provides a centralized entry point of all requests. It should create the <em>Model</em> based on information available by the request, such as the pathinfo or servletpath, the method and/or specific parameters. The <em>Business Model</em> is called <code>Action</code> in the below <a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html" rel="noreferrer"><code>HttpServlet</code></a> example.</p> <pre><code>protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Action action = ActionFactory.getAction(request); String view = action.execute(request, response); if (view.equals(request.getPathInfo().substring(1)) { request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response); } else { response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern). } } catch (Exception e) { throw new ServletException("Executing action failed.", e); } } </code></pre> <p>Executing the action should return some identifier to locate the view. Simplest would be to use it as filename of the JSP. Map this servlet on a specific <code>url-pattern</code> in <code>web.xml</code>, e.g. <code>/pages/*</code>, <code>*.do</code> or even just <code>*.html</code>. </p> <p>In case of prefix-patterns as for example <code>/pages/*</code> you could then invoke URL's like <a href="http://example.com/pages/register" rel="noreferrer">http://example.com/pages/register</a>, <a href="http://example.com/pages/login" rel="noreferrer">http://example.com/pages/login</a>, etc and provide <code>/WEB-INF/register.jsp</code>, <code>/WEB-INF/login.jsp</code> with the appropriate GET and POST actions. The parts <code>register</code>, <code>login</code>, etc are then available by <a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getPathInfo--" rel="noreferrer"><code>request.getPathInfo()</code></a> as in above example. </p> <p>When you're using suffix-patterns like <code>*.do</code>, <code>*.html</code>, etc, then you could then invoke URL's like <a href="http://example.com/register.do" rel="noreferrer">http://example.com/register.do</a>, <a href="http://example.com/login.do" rel="noreferrer">http://example.com/login.do</a>, etc and you should change the code examples in this answer (also the <code>ActionFactory</code>) to extract the <code>register</code> and <code>login</code> parts by <a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getServletPath--" rel="noreferrer"><code>request.getServletPath()</code></a> instead.</p> <hr> <h2><a href="http://en.wikipedia.org/wiki/Strategy_pattern" rel="noreferrer">Strategy pattern</a></h2> <p>The <code>Action</code> should follow the <a href="http://en.wikipedia.org/wiki/Strategy_pattern" rel="noreferrer">Strategy pattern</a>. It needs to be defined as an abstract/interface type which should do the work based on the <em>passed-in</em> arguments of the abstract method (this is the difference with the <a href="http://en.wikipedia.org/wiki/Command_pattern" rel="noreferrer">Command pattern</a>, wherein the abstract/interface type should do the work based on the arguments which are been passed-in during the <em>creation</em> of the implementation).</p> <pre><code>public interface Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception; } </code></pre> <p>You may want to make the <code>Exception</code> more specific with a custom exception like <code>ActionException</code>. It's just a basic kickoff example, the rest is all up to you.</p> <p>Here's an example of a <code>LoginAction</code> which (as its name says) logs in the user. The <code>User</code> itself is in turn a <em>Data Model</em>. The <em>View</em> is aware of the presence of the <code>User</code>.</p> <pre><code>public class LoginAction implements Action { public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userDAO.find(username, password); if (user != null) { request.getSession().setAttribute("user", user); // Login user. return "home"; // Redirect to home page. } else { request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope. return "login"; // Go back to redisplay login form with error. } } } </code></pre> <hr> <h2><a href="http://en.wikipedia.org/wiki/Abstract_factory_pattern" rel="noreferrer">Factory method pattern</a></h2> <p>The <code>ActionFactory</code> should follow the <a href="http://en.wikipedia.org/wiki/Factory_method" rel="noreferrer">Factory method pattern</a>. Basically, it should provide a creational method which returns a concrete implementation of an abstract/interface type. In this case, it should return an implementation of the <code>Action</code> interface based on the information provided by the request. For example, the <a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getMethod--" rel="noreferrer">method</a> and <a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getPathInfo--" rel="noreferrer">pathinfo</a> (the pathinfo is the part after the context and servlet path in the request URL, excluding the query string).</p> <pre><code>public static Action getAction(HttpServletRequest request) { return actions.get(request.getMethod() + request.getPathInfo()); } </code></pre> <p>The <code>actions</code> in turn should be some static/applicationwide <code>Map&lt;String, Action&gt;</code> which holds all known actions. It's up to you how to fill this map. Hardcoding:</p> <pre><code>actions.put("POST/register", new RegisterAction()); actions.put("POST/login", new LoginAction()); actions.put("GET/logout", new LogoutAction()); // ... </code></pre> <p>Or configurable based on a properties/XML configuration file in the classpath: (pseudo)</p> <pre><code>for (Entry entry : configuration) { actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance()); } </code></pre> <p>Or dynamically based on a scan in the classpath for classes implementing a certain interface and/or annotation: (pseudo)</p> <pre><code>for (ClassFile classFile : classpath) { if (classFile.isInstanceOf(Action.class)) { actions.put(classFile.getAnnotation("mapping"), classFile.newInstance()); } } </code></pre> <p>Keep in mind to create a "do nothing" <code>Action</code> for the case there's no mapping. Let it for example return directly the <code>request.getPathInfo().substring(1)</code> then.</p> <hr> <h3>Other patterns</h3> <p>Those were the important patterns so far. </p> <p>To get a step further, you could use the <a href="http://en.wikipedia.org/wiki/Facade_pattern" rel="noreferrer">Facade pattern</a> to create a <code>Context</code> class which in turn wraps the request and response objects and offers several convenience methods delegating to the request and response objects and pass that as argument into the <code>Action#execute()</code> method instead. This adds an extra abstract layer to hide the raw Servlet API away. You should then basically end up with <strong>zero</strong> <code>import javax.servlet.*</code> declarations in every <code>Action</code> implementation. In JSF terms, this is what the <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/FacesContext.html" rel="noreferrer"><code>FacesContext</code></a> and <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/ExternalContext.html" rel="noreferrer"><code>ExternalContext</code></a> classes are doing. You can find a concrete example in <a href="https://stackoverflow.com/questions/4764285/retrieving-web-session-from-a-pojo-outside-the-web-container/4764894#4764894">this answer</a>.</p> <p>Then there's the <a href="http://en.wikipedia.org/wiki/State_pattern" rel="noreferrer">State pattern</a> for the case that you'd like to add an extra abstraction layer to split the tasks of gathering the request parameters, converting them, validating them, updating the model values and execute the actions. In JSF terms, this is what the <a href="http://download.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html" rel="noreferrer"><code>LifeCycle</code></a> is doing.</p> <p>Then there's the <a href="http://en.wikipedia.org/wiki/Composite_pattern" rel="noreferrer">Composite pattern</a> for the case that you'd like to create a component based view which can be attached with the model and whose behaviour depends on the state of the request based lifecycle. In JSF terms, this is what the <a href="http://docs.oracle.com/javaee/7/api/javax/faces/component/UIComponent.html" rel="noreferrer"><code>UIComponent</code></a> represent. </p> <p>This way you can evolve bit by bit towards a component based framework.</p> <hr> <h3>See also:</h3> <ul> <li><a href="https://stackoverflow.com/questions/1673841">Examples of GoF Design Patterns in Java&#39;s core libraries</a></li> <li><a href="https://stackoverflow.com/questions/4801891">Difference between Request MVC and Component MVC</a></li> <li><a href="https://stackoverflow.com/questions/5003142">Show JDBC ResultSet in HTML in JSP page using MVC and DAO pattern</a></li> <li><a href="https://stackoverflow.com/questions/5104094">What components are MVC in JSF MVC framework?</a></li> <li><a href="https://stackoverflow.com/questions/30639785">JSF Controller, Service and DAO</a></li> </ul>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload