Dankwansere Dankwansere - 7 months ago 33
Java Question

My action class is being mapped without Struts.xml mappins or java annotations

I have a JSP login page that would call the login action class. I was using Struts annotations rather than the

struts.xml
to the mapping and everything was working fine. I was randomly trying something new so I removed all the annotations from my action class and my
struts.xml
has no mapping yet somehow my
login.jsp
is able to call my action class. How is it possible that my action class is being called without
Struts.xml
configuration or struts annotations being used?

login.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!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=ISO-8859-1">
<title>Welcome to Popular Movies</title>

<style type="text/css">
.errors {
background-color:#FFCCCC;
border:1px solid #CC0000;
width:400px;
margin-bottom:8px;
}
.errors li{
list-style: none;
}
</style>

</head>
<body>

<h2>Please Log in</h2>
<div id="errorMessage" >
<s:if test="hasActionErrors()">
<div class="errors" >
<s:actionerror/>
</div>
</s:if>
</div>
<s:form action="login" method="post" name="myForm" onSubmit="return preValidate(errorMessage, userName, userPassword)" >
<s:textfield name="userName" label="Name" size="20" id="userName" />
<s:password name="password" label="Password" size="20" id="userPassword" />
<s:hidden name="registration" value="false" />
<s:submit value="Submit" align="center" />
</s:form>

<hr>

<a href="/PopularMovies/registration.jsp">Sign up free</a>
</body>

<script type="text/javascript" src="validation.js">

</script>
</html>


Login.java

package com.esi.actions;

import com.opensymphony.xwork2.ActionSupport;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.sans.model.Movie;
import com.sans.model.Movies;
import com.sans.model.user_account;
import org.hibernate.HibernateException;
import org.hibernate.Query;

@SuppressWarnings("serial")
public class Login extends ActionSupport {
private String password;
private String userName;
private String email;
private String firstName;
private String lastName;
private String registration;
private static SessionFactory sessionFactory;

@SuppressWarnings("deprecation")
public String execute() {
boolean isRegistration = Boolean.parseBoolean(registration);

System.out.println("Action called from struts.xml");
try {
//Setting up Hibernate configuration
System.out.println("Attempting Database connection...");
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch(Exception ex){
System.out.println("Failed to create sessionFactory object. " + ex.toString());
return INPUT;
}

// If Registration flag is set to true then it means user is trying to register
// else authenticate user
if(isRegistration) {
if(this.addUser()) {
return SUCCESS;
}
else {
return INPUT;
}
}
else {
if(this.authenticateUser(this.getUserName())) {
return SUCCESS;
}
else {
return INPUT;
}
}
}

public boolean addUser() {
boolean result = false;
user_account user = new user_account();
user.setUser_Name(this.getUserName());
user.setFirstName(this.getFirstName());
user.setLastName(this.getLastName());
user.setEmail(this.getEmail());
user.setUser_Password(this.getPassword());

Session session = sessionFactory.openSession();
try{
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
result = true;
addActionMessage("Welcome " + user.getUser_Name());
}
catch(HibernateException e){
if(session.getTransaction() != null)
session.getTransaction().rollback();
System.out.println("Error trying to insert user to database.. " + e.getMessage() + "\nStack Trace: ");
e.printStackTrace();
}

finally {
session.close();
}
return result;
}

public boolean authenticateUser(String userName) {
boolean result = false;

Session session = sessionFactory.openSession();
try{
session.beginTransaction();
String hql = "FROM user_account U WHERE U.User_Name = :userName";
Query query = session.createQuery(hql);
query.setParameter("userName", userName);
List results = query.list();

//If the query result size is 0, then it means user does not exist in database
if(results.size() != 0) {
user_account user = (user_account)results.get(0);

if(this.getUserName().equals(user.getUser_Name()) && this.getPassword().equals(user.getUser_Password())) {
addActionMessage("Welcome " + user.getUser_Name());
result = true;
}
else {
addActionError("Invalid User");
result = false;
}
}
else {
addActionError("Invalid User");
result = false;
}
session.getTransaction().commit();
}
catch(HibernateException e){
if(session.getTransaction() != null)
session.getTransaction().rollback();
System.out.println("Error trying to insert user to database.. " + e.toString());
}
catch(IndexOutOfBoundsException e) {

}
catch(Exception ex) {
System.out.println("Something went wrong: " + ex.toString());
}

finally {
session.close();
}
return result;
}


public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getRegistration() {
return registration;
}

public void setRegistration(String registration) {
this.registration = registration;
}



}


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>

<action name="login" class="com.esi.actions.Login">
<result name="success">/success.jsp</result>
</action>

</struts>


Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>PopularMovies</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>


<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>


From what I know from struts framework, an action class can only be called if it has been mapped in a struts.xml or using annotations.

Answer

In Struts2, the annotations are provided by the Convention Plugin.

The name says it all: Convention over Configuration.

It means that you can configure every aspect of it, but that it will also work by default if you follow certain conventions; this way you have to spend time configuring only those cases that diverge from the standard behaviour.

Specifically:

By default, the Convention plugin will find all action classes that implement com.opensymphony.xwork2.Action or whose name ends with the word Action in specific packages.

These packages are located by the Convention plugin using a search methodology. First the Convention plugin finds packages named struts, struts2, action or actions. Any packages that match those names are considered the root packages for the Convention plugin. Next, the plugin looks at all of the classes in those packages as well as sub-packages and determines if the classes implement com.opensymphony.xwork2.Action or if their name ends with Action (i.e. FooAction). Here's an example of a few classes that the Convention plugin will find:

Classes 

com.example.actions.MainAction
com.example.actions.products.Display (implements com.opensymphony.xwork2.Action)
com.example.struts.company.details.ShowCompanyDetailsAction

You extend ActionSupport, that implements the Action interface... hence, even if not configured, a login action will be found.


EDIT

I forgot to mention that there is a plugin that will help you debugging action / interceptor / result configurations: the Struts2 Config Browser Plugin. Simply include the JAR in your WAR/EAR, and call

http://YOUR_DOMAIN/YOUR_WEBAPP/config-browser/index.action

You will have a clear sight of every action configured (implicitly through convention or explicitly through configuration) in your webapp.

Remember to REMOVE THIS PLUGIN before deploying in Production, or attackers will have (way) more chances to hit you.

Comments