Creating a Pretty URL With Struts Framework and URLRewrite

On this tutorial im trying to create a pretty URL using one of java’s most famous framework, Struts Framework. I want to change a usual struts URL from news.do?id=newsID into a pretty URL such as news/newsID/newsTitle. The main benefits are your links get easily indexed by search engines and make them easy-to-read and easy-to-remember.

Im using urlrewrite library for url rewriting. As for this example, im using old version of struts 1.3.8 and iBatis ORM version 2.3.4.

First as always, a simple MySQL table

CREATE TABLE news
    (
        id bigint NOT NULL AUTO_INCREMENT,
        title VARCHAR(100) NOT NULL,
        content text NOT NULL,
        createddate DATETIME NOT NULL,
        PRIMARY KEY (id)
    )
	
insert into news 
        (id, title, content, createddate) 
        values (1, 'A Tale of Two More Earths?', 'Example of Content', '2011-12-26 19:00:00');
insert into news 
        (id, title, content, createddate) 
        values (2, 'India: Boat Capsizes; 11 Missing', 'Example of Content number 2', '2011-12-27 19:00:00');

and a javabean and xml for database mapping. And please be aware, im injecting Slugify class to getUrl method so i could get a clean url property inside this bean.

package com.edw.bean;

import com.edw.util.Slugify;
import java.util.Date;

public class News {

    private Long id;
    private String title;
    private Date createddate;
    private String content;
    
    // added for pretty URL
    private String url;

    // other getter and setter

    // do title formatting for a clean url
    public String getUrl() {
        return Slugify.slugify(getTitle());
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

And this is my xml queries

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="news" >
    
  <resultMap id="newsBean" class="com.edw.bean.News" >    
    <result column="id" property="id" jdbcType="BIGINT" />
    <result column="title" property="title" jdbcType="VARCHAR" />
    <result column="content" property="content" jdbcType="LONGVARCHAR" />
    <result column="createddate" property="createddate" jdbcType="TIMESTAMP" />
  </resultMap>  
  
  <select id="select" resultMap="newsBean" >    
    select id, title, createddate, content
    from news    
  </select> 
  
  <select id="selectWithId" resultMap="newsBean" parameterClass="java.lang.Integer" >    
    select id, title, createddate, content
    from news    
    where id = #id#
  </select> 
  
</sqlMap>

This is my utility java class, its main purpose is to encode and clean news titles so they can fit into URLs.

package com.edw.util;

import java.net.URLEncoder;
import java.text.Normalizer;
import org.apache.log4j.Logger;

public class Slugify {

    private static final Logger logger = Logger.getLogger(Slugify.class);
    
    /**
     * 
     * modified version of Jozef Ševcík's slugify
     * 
     * @link http://maddemcode.com/java/seo-friendly-urls-using-slugify-in-java/
     * @param input
     * @return formatted URL
     */
    public static String slugify(String input) {
        if (input == null || input.length() == 0) {
            return "";
        }
        
        try {
            String toReturn = normalize(input);            
            toReturn = toReturn.replaceAll("[^\\w\\s\\-]", "");
            toReturn = toReturn.replace(" ", "-");
            toReturn = toReturn.toLowerCase();
            toReturn = URLEncoder.encode(toReturn, "UTF-8");
            return toReturn;
        } catch (Exception e) {
            logger.error(e, e);
        }
        return "";

    }

    private static String normalize(String input) {
        if (input == null || input.length() == 0) {
            return "";
        }
        return Normalizer.normalize(input, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
    }
}

And an xml file to load all my xml queries

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <settings
        useStatementNamespaces="true"
        lazyLoadingEnabled="true"
        enhancementEnabled="true"
        maxSessions="20"
        />

    <transactionManager type="JDBC" commitRequired="false">
        <dataSource type="SIMPLE">

            <property name="SetAutoCommitAllowed" value="false"/>
            <property name="DefaultAutoCommit" value="false"/>
            
            <property name="JDBC.Driver" value="com.mysql.jdbc.Driver"/>
            <!-- my database name = pepe -->
            <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/pepe"/>
            <property name="JDBC.Username" value="root"/>
            <property name="JDBC.Password" value="xxx"/>
   
        </dataSource>
    </transactionManager>

    <sqlMap resource="com/edw/sqlmap/news.xml"/>
    
</sqlMapConfig>

A java class to load my xml configuration

package com.edw.sqlmap.config;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import java.io.Reader;


public class SqlMapConfig {

    protected static final SqlMapClient sqlMap;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("com/edw/sqlmap/sqlmapconfig.xml");
            sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);       
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }

    public static SqlMapClient getSqlMap() {
        return sqlMap;
    }
}

Next is im creating a Struts Action class

package com.edw.action;

import com.edw.bean.News;
import com.edw.sqlmap.config.SqlMapConfig;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class NewsAction extends org.apache.struts.action.Action {

    private static final String SUCCESS = "success";
    
    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        
        List<News> newses = null;
        if(request.getParameter("id") != null)
            newses = SqlMapConfig.getSqlMap().queryForList("news.selectWithId", Integer.parseInt(request.getParameter("id")));       
        else
            newses = SqlMapConfig.getSqlMap().queryForList("news.select");       
        request.setAttribute("newses", newses);
        
        return mapping.findForward(SUCCESS);
    }        
}

dont forget to register NewsAction to strutsconfig.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">

<struts-config>
    <form-beans></form-beans>
    
    <global-exceptions></global-exceptions>

    <global-forwards>
        <forward name="welcome"  path="/Welcome.do"/>
    </global-forwards>

    <action-mappings>
        <action path="/news" type="com.edw.action.NewsAction">
            <forward name="success" path="/WEB-INF/pages/news.jsp" />
        </action>
        <action path="/Welcome" forward="/welcomeStruts.jsp"/>
    </action-mappings>        

    <message-resources parameter="com/edw/res/ApplicationResource"/>    
    
</struts-config>

Next is my presentation layer. Im using a plain JSP and Struts tags, and put my JSP file under WEB-INF folder, so it cant be accessed directly.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>

<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>News Page</title>
    </head>
    <body>
        <h1>News</h1>

        <logic:iterate name="newses" id="news">            
            <bean:write name="news" property="id"/> , 
            <bean:write name="news" property="title"/>, 
            <bean:write name="news" property="createddate"/>, 
            <bean:write name="news" property="content"/>            
            <br />
            <a href="${pageContext.request.contextPath}/news/<bean:write name="news" property="id"/>/<bean:write name="news" property="url"/>">link</a>
            <br />
            <br />
            <br />
            
        </logic:iterate>

    </body>
</html>

Next is where the magic of urlrewrite starts, first i register urlrewrite filter to my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    
    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>   
    </filter>
    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
        
    
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>2</param-value>
        </init-param>
        <init-param>
            <param-name>detail</param-name>
            <param-value>2</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    
</web-app>

and create a urlrewrite xml under WEB-INF

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN"
        "http://tuckey.org/res/dtds/urlrewrite3.2.dtd">

<urlrewrite>       
    
    <rule>
        <note>for news with parameters</note>
        <from>^/news/([0-9]+)/(.*)</from>
        <to>/news.do?id=$1</to>
    </rule> 
    
    <rule>
        <note>display all news</note>
        <from>^/news$</from>
        <to>/news.do</to>
    </rule> 

</urlrewrite>

The final result is my url path will be changed from


http://localhost:8084/StrutsPrettyURL/news.do

and

http://localhost:8084/StrutsPrettyURL/news.do?id=1

to


http://localhost:8084/StrutsPrettyURL/news

and

http://localhost:8084/StrutsPrettyURL/news/1/a-tale-of-two-more-earths

This are my netbeans project structure and libraries.

Have Fun (&amp;)

Google+

No Comments

Leave a Comment

Please be polite. We appreciate that.
Your email address will not be published and required fields are marked


:-[ (B) (^) (P) (@) (O) (D) :-S ;-( (C) (&) :-$ (E) (~) (K) (I) (L) (8) :-O (T) (G) (F) :-( (H) :-) (*) :-D (N) (Y) :-P (U) (W) ;-)