<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Go</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Code language: HTML, XML (xml)
Now create a source folder called resources. Right click on your project in Project explorer -> New -> Source Folder. Create a file hibernate.cfg.xml in the resources folder. This will be the configuration file of Hibernate which contains database connection strings and other related data. resources/hibernate.cfg.xml <?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/shorty
</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<property name="connection.pool_size">1</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="current_session_context_class">thread</property>
<property name="cache.provider_class">
org.hibernate.cache.NoCacheProvider
</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
Code language: HTML, XML (xml)
Also lets create package structure for the source code in our base framework. We will create few packages in src folder. CREATE TABLE LINKS
(
id INT PRIMARY KEY AUTO_INCREMENT,
shortcode VARCHAR(20),
url VARCHAR(255),
clicks INT DEFAULT 0,
created TIMESTAMP DEFAULT NOW()
);
Code language: SQL (Structured Query Language) (sql)
public static String base48Encode(Long no) {
Double num = Double.valueOf(no);
String charSet = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";
Integer length = charSet.length();
String encodeString = new String();
while(num > length) {
encodeString = charSet.charAt(num.intValue() % length)+encodeString;
num = Math.ceil(new Double(num / length) - 1) ;
}
encodeString = charSet.charAt(num.intValue())+encodeString;
return encodeString;
}
Code language: Java (java)
In above method we have passed a long number (which will be auto generated primary key) and get string representation. We will add this logic into a file ShortyUtil.java. Create ShortyUtil class under net.viralpatel.shorty.util package. net.viralpatel.shorty.util.ShortyUtil package net.viralpatel.shorty.util;
public class ShortyUtil {
public static String base48Encode(Long no) {
Double num = Double.valueOf(no);
String charSet = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";
Integer length = charSet.length();
String encodeString = new String();
while(num > length) {
encodeString = charSet.charAt(num.intValue() % length)+encodeString;
num = Math.ceil(new Double(num / length) - 1) ;
}
encodeString = charSet.charAt(num.intValue())+encodeString;
return encodeString;
}
public static String getShortCodeFromURL(String URL) {
int index=0;
for(index=URL.length()-1; index>=0 && URL.charAt(index)!= '/' ;index--);
String shortCode = URL.substring(index+1);
return shortCode;
}
}
Code language: Java (java)
package net.viralpatel.shorty.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new AnnotationConfiguration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Code language: Java (java)
Now create an entity class that will map to LINKS table in database. Create Link.java class under net.viralpatel.shorty.model
package. net.viralpatel.shorty.model.Link package net.viralpatel.shorty.model;
import java.io.Serializable;
import java.sql.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="LINKS")
public class Link implements Serializable{
private static final long serialVersionUID = -8767337896773261247L;
private Long id;
private String shortCode;
private String url;
private Long clicks;
private Date created;
@Id
@GeneratedValue
@Column(name="id")
public Long getId() {
return id;
}
@Column(name = "shortcode")
public String getShortCode() {
return shortCode;
}
@Column(name = "created")
public Date getCreated() {
return created;
}
@Column(name = "url")
public String getUrl() {
return url;
}
@Column(name = "clicks")
public Long getClicks() {
return clicks;
}
public void setClicks(Long clicks) {
this.clicks = clicks;
}
public void setShortCode(String shortCode) {
this.shortCode = shortCode;
}
public void setUrl(String url) {
this.url = url;
}
public void setCreated(Date created) {
this.created = created;
}
public void setId(Long id) {
this.id = id;
}
}
Code language: Java (java)
Now add the mapping for above entity class Link.java in hibernate.cfg.xml file. Add following link in >session-factory< tag: <mapping class="net.viralpatel.shorty.model.Link" />
Code language: Java (java)
Also we will need a controller class that we invoke from Struts action class to do read/write in database. Create LinkController.java under net.viralpatel.shorty.controller package. net.viralpatel.shorty.controller.LinkController package net.viralpatel.shorty.controller;
import org.hibernate.Query;
import org.hibernate.classic.Session;
import net.viralpatel.shorty.model.Link;
import net.viralpatel.shorty.util.HibernateUtil;
import net.viralpatel.shorty.util.ShortyUtil;
public class LinkController extends HibernateUtil {
public Link get(String shortCode) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("from Link where shortcode = :shortcode");
query.setString("shortcode", shortCode);
Link link = (Link) query.uniqueResult();
if(null != link) {
link.setClicks(link.getClicks());
session.save(link);
}
session.getTransaction().commit();
return link;
}
public Link add(Link link) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("from Link where url = :url");
query.setString("url", link.getUrl());
Link oldLink = (Link) query.uniqueResult();
if(null != oldLink)
return oldLink;
session.save(link);
if(null == link.getShortCode()) {
link.setShortCode(ShortyUtil.base48Encode(link.getId()));
session.save(link);
}
session.getTransaction().commit();
return link;
}
}
Code language: Java (java)
LinkAction.java
under net.viralpatel.shorty.view
package. LinkAction.java package net.viralpatel.shorty.view;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware;
import net.viralpatel.shorty.controller.LinkController;
import net.viralpatel.shorty.model.Link;
import net.viralpatel.shorty.util.ShortyUtil;
import com.opensymphony.xwork2.ActionSupport;
public class LinkAction extends ActionSupport implements ServletRequestAware {
private static final long serialVersionUID = 1L;
private final static String DETAIL = "detail";
private String url;
private Link link;
private LinkController linkController;
private HttpServletRequest request;
public LinkAction() {
linkController = new LinkController();
}
public String add() {
link = new Link();
link.setUrl(this.url);
link = linkController.add(link);
return SUCCESS;
}
public String get() {
String uri = request.getRequestURI();
uri = ShortyUtil.getShortCodeFromURL(uri);
if(uri.charAt(uri.length()-1) == '+') {
uri = uri.substring(0, uri.length()-1);
this.link = this.linkController.get(uri);
return DETAIL;
}
this.link = this.linkController.get(uri);
if(null == this.link) {
addActionError(getText("error.url.unavailable"));
return INPUT;
} else {
setUrl(link.getUrl());
return SUCCESS;
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Link getLink() {
return link;
}
public void setLink(Link link) {
this.link = link;
}
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
}
Code language: Java (java)
Also create struts configuration file struts.xml under resources folder and copy following content into it. resources/struts.xml <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation"
value="false" />
<constant name="struts.devMode" value="false" />
<constant name="struts.custom.i18n.resources"
value="MessageResources" />
<package name="default" extends="struts-default" namespace="/">
<action name="add" class="net.viralpatel.shorty.view.LinkAction"
method="add">
<result name="success">index.jsp</result>
</action>
<action name="*" class="net.viralpatel.shorty.view.LinkAction"
method="get">
<result name="success" type="redirect">${url}</result>
<result name="input">index.jsp</result>
<result name="detail">detail.jsp</result>
</action>
</package>
</struts>
Code language: HTML, XML (xml)
Note that we have used wildcard mapping in Struts2 action class. This is to ensure we call LinkAction for any shortcode passed in url shortner service. Create a resource bundle file MessageResources.properties which will hold value for domain name of our URL shortner and an error message. resources/MessageResources.properties You may want to change value of key shorty.base.url to your domain name. Add following JSP files in WebContent folder. WebContent/index.jspCode language: HTML, XML (xml)shorty.base.url=http://shorty/ error.url.unavailable=Couldn't find the site's URL to redirect to.
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Shorty - An Open Source URL Shortner in Struts2/Hibernate | ViralPatel.net</title>
<link href="css/style.css" rel="stylesheet"/>
</head>
<body>
<h1 class="title">Shorty</h1>
<br>
<p>A Simple URL Shortner in Struts2/Hibernate/MySQL</p>
<br><br>
<div id="link-container">
<s:form action="add" method="post">
<s:actionerror/>
<s:textfield name="url" cssClass="link"/>
<s:submit value="Shorten"/>
</s:form>
<s:if test="link.shortCode != null">
<h3><s:text name="shorty.base.url"/><s:property value="link.shortCode"/></h3>
</s:if>
</div>
</body>
</html>
Code language: HTML, XML (xml)
WebContent/detail.jsp <%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Shorty - An Open Source URL Shortner in Struts2/Hibernate | ViralPatel.net</title>
<link href="css/style.css" rel="stylesheet" />
</head>
<body>
<h1 class="title">Shorty</h1>
Short Code: <s:property value="link.shortCode" /> <br />
Original URL: <s:property value="link.url" /> <br />
Clicks: <s:property value="link.clicks" /> <br />
Created On: <s:property value="link.created" /> <br />
</body>
</html>
Code language: HTML, XML (xml)
Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…
Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…
Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…
1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…
GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…
1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…
View Comments
In LinkController.get(...), shouldn't the line
link.setClicks(link.getClicks())
instead read:
link.setClicks(link.getClicks() + 1)
In ShortyUtil.base48Encode(...): the charSet variable is missing 0 and 1—is this intentional or a typo?
@markvgti:
1. The get() method is called everytime user access the short url and we redirect it to long url. Also here we track the number of clicks and hence the link.setClicks(link.getClicks() + 1) is used to increment the number of clicks in get() method.
2. Yes the 0 1 l o O are intentionally dropped from string charSet. Thanks for pointing out this as I forgot to mention this in post. I will update it. This is dropped to avoid any confusion in generated URL as O can be confused with 0 and 1 with l in some fonts. Although you may wish to include this.
I dont understand why you take a Long as the arg in the base64Encode method and then truncate the value to an int using intValue().
Surely this means you cannot generate different strings for numbers greater than Integer.MAX_VALUE?
Cheers
well, I tried but failed, and maybe there is something wrong ..
yes,,, I tried but failed, and maybe there is something wrong ..
http://google.com/abc
http://yahoo.com/abc
above are two different urls, but definitely will return the same value for your service!
So when the user request the shorten URL, There will be a confusion as to which one you will redirect!