Providing Website search friendly pages using Java and Servlet
is the main objective of this writing.

If you have explored opertunity to provide URL of the application
much different than what actually the file path is.

Those who have already worked using Struts, may say that this feature
is already present in Struts, where by the URL is much different than
the file that really has the content, be it JSP, Servlet, HTML, TXT etc.

But Struts 1.x has these URL or relative path in URL are statically
defined in struts-config XML file. But if any application has a 
requirement as to having the URL dynamically generated from user
inputs. Can it be possible to implement?

I have really thought about, how to approach this specific problem at hand,
and come up with some dummy implementation/application , so as to provide
URL that is dynamically decided at runtime, but point to some 
of the predefined JSP pages.

For this dummy search engine friendly application using Java and Servlet
Technology, there would be a Controller and a Filter in place, to accept
initial request from the User and convert it to any other internal
content holder file. So some sort of URL mapping to be defined while
defining the controller and Filter in web.xml file.

Let us start with a web.xml file as follows:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Java-Forum</display-name>
    <description>Java Forum</description>
    <filter>
        <filter-name>jf-filter</filter-name>
        <filter-class>
            com.forum.filter.FrontFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>jf-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- Java Forum Controller -->
    <servlet>
        <servlet-name>jf</servlet-name>
        <servlet-class>
            com.forum.controller.ForumController
        </servlet-class>
    </servlet>
    <!-- Mapping -->
    <servlet-mapping>
        <servlet-name>jf</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.htm</welcome-file>
    </welcome-file-list>
</web-app>
Idea is to receive all sorts of HTTP request from User browsers in the FrontFilter only, and then checking the URI value and then parsing it with StringTokenizer and slipting path/URI value to many values, such as follows: for a dummy URL as http://localhost:8080/JF/Hibernate/How%20to%20create%20SessionFactory with the URI as /JF/Hibernate/How%20to%20create%20SessionFactory, can be split to
  • page=JF
  • category=Hibernate
  • topic=How%20to%20create%20SessionFactory
  • These key and value pair are set as attribute to the HttpServletRequest and the the same request and response are forwarded to Controller by forwarding to appropriate URL with the appropriate extension, in this example it will be *.do. Please have a look at the FrontFilter code as shown below; FrontFilter.java
    package com.forum.filter;
    
    import java.io.IOException;
    import java.util.StringTokenizer;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    
    public class FrontFilter implements Filter {
    
    	private String category;
    	private String article;
    	public void doFilter(ServletRequest request, 
    	                     ServletResponse response,
    			FilterChain filterChain) 
    			throws IOException, ServletException {
    		
            String path = ((HttpServletRequest)request).getRequestURI();
    		
            //Parsing code to split URI to String array.
    
            StringTokenizer st = new StringTokenizer( path,"/");
            int count = st.countTokens(); 
            int i = 0;
            String[] arrayStr = new String[count];
            while(st.hasMoreTokens()) {
            	arrayStr[i] = st.nextToken();
            	i++;
            }
    		
            for(int j=0;j<arrayStr.length;j++)
            System.out.println(arrayStr[j]);
            if(arrayStr[1].equalsIgnoreCase("pages")) {
              category = arrayStr[count-2];
              article = arrayStr[count-1];
    		
              //Setting the category and article values to Request
              request.setAttribute("category", category);
              request.setAttribute("article", article);
    		
              ((HttpServletRequest)request).
    		      getRequestDispatcher("/index.do").
    			  forward(request, response);
            }
            if(arrayStr[1].equalsIgnoreCase("manageuser")) {
              ((HttpServletRequest)request).
    		      getRequestDispatcher("/manageuser.do").
    			  forward(request, response);
            }     
    	}
    }
    
    Once request is forwarded to either "index.do" or "manageuser.do" there could be a check in FrontController to act differently. FrontController will be receiving the URI and forward to appropriate processor such as for this example, for index.do, the request processor is ForumProcessor and for manageuser.do, the request processor is ManageUserProcessor. Have a look at the code of FrontController to match the above statement mapped to code: ForumController.java
    package com.forum.controller;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.forum.process.ForumProcessor;
    import com.forum.process.ManageUserProcessor;
    
    public class ForumController extends HttpServlet {
    
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response) 
                          throws ServletException, IOException {
            String path = request.getRequestURI();
            if(path.endsWith("index.do")) {
                String forwardPath = new ForumProcessor().
    			                 execute(request, response);
                request.getRequestDispatcher(forwardPath).
    			                 forward(request, response);
            }
            if(path.endsWith("manageuser.do")) {
                String forwardPath = new ManageUserProcessor().
    			                 execute(request, response);
                request.getRequestDispatcher(forwardPath).
    			                 forward(request, response);
            }
        }
        public void doPost(HttpServletRequest request,
                HttpServletResponse response) 
                throws ServletException, IOException {
            doGet(request,response);
        }   
    }
    
    ForumProcessor and ManageUserProcessor are plain POJO classes with a execute method for forwarding the incomming/original request and response to appropriate JSP files/pages. such as follows: ForumProcessor.java
    package com.forum.process;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class ForumProcessor {
        public ForumProcessor() {
            
        }
        public String execute(HttpServletRequest request, 
    	                      HttpServletResponse response)
        {
            return "WEB-INF/jsp/index.jsp";
        }
    }
    
    And the ManageUserProcessor file as follows: ManageUserProcessor.java
    package com.forum.process;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.forum.error.ForumErrorHandler;
    
    public class ManageUserProcessor {
        public ManageUserProcessor() {
            
        }
        public String execute(HttpServletRequest request, 
    	                      HttpServletResponse response)
        {
            String mode = request.getParameter("mode");
            ForumErrorHandler errHandle = new ForumErrorHandler();
            errHandle.clear();
            String userName = request.getParameter("txtUserName");
            String password = request.getParameter("txtUserPass");
            String confirmPass = request.
                                getParameter("txtConfirmPass");
            String secretQuestion = request.
                                getParameter("txtSecretQuestion");
            String secretAnswer = request.
                                getParameter("txtSecretAnswer");
    
            if(mode != null && mode.equalsIgnoreCase("create_user")) {
                if(userName.equals("") && userName.length() != -1){
                    errHandle.setErrorMessage("txtUserName", 
                                       "Please enter User Name.");
                }
                if(password.equals("") && password.length() != -1){
                    errHandle.setErrorMessage("txtUserPass", 
                                       "Please enter your Password.");
                }
                if(confirmPass.equals("") && confirmPass.length() != -1){
                    errHandle.setErrorMessage("txtConfirmPass", 
                                       "Please re-enter your Password.");
                }
                if(secretQuestion.equals("") && secretQuestion.length() != -1){
                    errHandle.setErrorMessage("txtSecretQuestion", 
                                      "Please enter your Secret Question.");
                }
                if(secretAnswer.equals("") && secretAnswer.length() != -1){
                    errHandle.setErrorMessage("txtSecretAnswer", 
                                     "Please enter your Secret Answer.");
                }
                
                if(errHandle.hasError() > 0) {
                    request.setAttribute("errors", errHandle);
                    request.setAttribute("txtUserName", userName);
                    request.setAttribute("txtUserPass", password);  
                    request.setAttribute("txtConfirmPass", confirmPass);  
                    request.setAttribute("txtSecretQuestion", secretQuestion);  
                    request.setAttribute("txtSecretAnswer", secretAnswer);  
                    return "WEB-INF/jsp/manage.jsp";
                } else {
                    
                }
                
            }
            if(mode != null && mode.equalsIgnoreCase("login")) {
                return "WEB-INF/jsp/index.jsp";         
            }
            if(mode != null && mode.equalsIgnoreCase("manage_user")) {
                return "WEB-INF/jsp/manage.jsp";
            }
            return "WEB-INF/jsp/error.jsp";
        }
    }
    
    This ManageUserProcessor POJO class is having some of the Validation code, that checks for appropriate user input from the Form. It accumulates all the Form Errors in an ErrorHandler and pass this as request attribute to the JSP file. The final JSP file , such as here it is manage.jsp file, has ways to handle either the Form erros from the ErrorHandler or move towards next screen on success. manage.jsp
    <%@ page import="com.forum.error.ForumErrorHandler" %>
    <html>
    <head>
    <script language="JavaScript">
    function submitSignup() {
        window.document.f.mode.value="create_user";
        window.document.f.submit();
    }
    </script>
    </head>
    <body>
    <%
    
        ForumErrorHandler ferror = (ForumErrorHandler)request.
                                     getAttribute("errors");
        if(ferror != null)
        out.println(ferror.getErrorHTML());
    
    %>
    <form name="f" method="POST" action="/JF/manageuser">
    <table>
    <tr><td>Name :</td><td>
    <input type="text" name="txtUserName" 
           value="<%=(
           request.getAttribute("txtUserName")==null)?"":
           request.getAttribute("txtUserName")%>">
    </td></tr>
    <tr><td>Password :</td><td>
    <input type="password" name="txtUserPass" 
           value="<%=(
           request.getAttribute("txtUserPass")==null)?"":
           request.getAttribute("txtUserPass")%>">
    </td></tr>
    <tr><td>Cofirm Password :</td><td>
    <input type="password" name="txtConfirmPass" 
           value="<%=(
           request.getAttribute("txtConfirmPass")==null)?"":
           request.getAttribute("txtConfirmPass")%>">
    </td></tr>
    <tr><td>Secret question :</td><td>
    <input type="text" name="txtSecretQuestion" 
           value="<%=(
        request.getAttribute("txtSecretQuestion")==null)?"":
        request.getAttribute("txtSecretQuestion")%>">
    </td></tr>
    <tr><td>Secret Answer :</td><td>
    <input type="text" name="txtSecretAnswer" value="<%=(
        request.getAttribute("txtSecretAnswer")==null)?"":
        request.getAttribute("txtSecretAnswer")%>">
    <%if(((String)request.getAttribute("txtSecretAnswer")) == null ||
    ((String)request.getAttribute("txtSecretAnswer")).equals("") )
     out.println("<font color=\"red\">*</font>");%>
    </td></tr>
    <tr><td>
    <input type="button" value="signup" onClick="javascript:submitSignup();"></td>
    <td><input type="button" value="clear" ></td></tr>
    </table>
    <input type="hidden" name="mode" value="manage_user">
    </form>
    <!-- page: /WEB-INF/jsp/manage.jsp -->
    </body>
    </html>
    
    The ForumErrorHandler as mentioned in bold red above, is a plain POJO with a Hashtable as container for holding all the Form error and show appropriate Error Message by getErrorHTML method call from JSP pages. ForumErrorHandler.java
    package com.forum.error;
    
    import java.util.Enumeration;
    import java.util.Hashtable;
    
    public class ForumErrorHandler {
        private Hashtable errors = new Hashtable();
        public void clear() {
            errors.clear();
        }
        public boolean hasError(String key) {
            return errors.containsKey(key);
        }
        public String getErrorMessage(String key) {
            return (String) errors.get(key);
        }
        public void setErrorMessage(String key, String message) {
            errors.put(key, message);
        }
        public int hasError(){
            return errors.size();
        }
        public String getErrorHTML() {
            StringBuffer sb = new StringBuffer();
            Enumeration em = errors.elements();
            while(em.hasMoreElements()) {
                sb.append("<font color=\"red\">"+(String)em.nextElement()
                                                      +"</font><br>");
            }
            return sb.toString();
        }
    }
    
    Appropriate JSP files , such as index.jsp and error.jsp files are as follows: index.jsp
    <html>
    <head>
    <title>Java Forum</title>
    </head>
    <body>
    <%
    out.println("This is the sample page and category :" 
                    + request.getAttribute("category"));
    out.println("<br>");
    out.println("This is the sample page and category :" 
                    + request.getAttribute("article"));
    %>
    <form method="POST" action="/JF/manageuser">
    <input type="hidden" name="mode" value="manage_user">
    <input type="submit" value="Signup">
    </form>
    <!-- page: /WEB-INF/jsp/index.jsp -->
    </body>
    </html>
    
    Of course puting all these files at appropriate places in the WAR file, and running it on Tomcat 5.5.x, I found the screen as shown below, These pages are meant to demonstrate capability/POC for achieving search engine friendly URL using Java Technology. I might have left some of the files from showing those here, due to lack of time and not to increase this page size/length. If any one interested in receiving the complete working code, please contact Author at "usingframeworks at gmail dot com"
    
    
    
    
    Copyright © 2009-2010, Interview-Questions-Tips-Forum, All Rights Reserved.
    Java, J2EE are trademark or registered trademark of Sun Microsystems, Inc. in the United States
    and other countries. these pages are independent of Sun Microsystems, Inc.
    
    These Pages are NOT affiliated in any form with Weebly.com
    Author don't claim any of the material found on these pages is true /fit for any purpose. If you have
    any complaint about content/material found on these pages, you can contact me by sending details
    about the same to my email id "opensourceutilityhelp at gmail.com".
    
    Hibernate is registered trademarks and servicemarks of Red Hat, Inc.