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 (&)