Unable to get query string params in struts 2 action class - java

Here is my action class:
public class ForgotPassword extends ActionSupport{
private String j_username;
public String execute() throws Exception {
System.out.println("FORGOT PASSWORD ACTION CALLED");
return SUCCESS;
}
public String getUsername() {
return j_username;
}
public void setUsername(String j_username) {
System.out.println("Username received is " + j_username);
this.j_username = j_username;
}
}
I have declared default interceptors in struts.xml which includes:
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
From html page I am sending params to action class like this:
<body>
<input type="text" id="j_username" name="j_username"/>
<div style="margin-top:20px">
<a class="forgot_password"
onclick="forgotPassword()" id="resetAnchor">Forgot Password</a>
</div>
<script type="text/javascript">
function forgotPassword() {
var ctx = '${pageContext.request.contextPath}';
var username = document.getElementById('j_username').value;
if (username !== null && username !== "") {
var username = document.getElementById('j_username').value;
console.log('username is ', username);
document.getElementById('resetAnchor').href = ctx + "/forgotPassword?j_username=" + username;
} else {
alert('please provider username');
}
}
</script>
</body>
Everything looks fine but I am unable to print this line inside setter method:
System.out.println("Username received is " + j_username);
It prints only this line: System.out.println("FORGOT PASSWORD ACTION CALLED");
Here is the action defined in struts.xml:
<action name="forgotPassword" class="com.actions.ForgotPassword">
<interceptor-ref name="params"/>
<result name="success">/reset.jsp</result>
</action>
what I'm I missing?

public String getJ_username() {
return j_username;
}
public void setJ_username(String j_username) {
System.out.println("Username received is " + j_username);
this.j_username = j_username;
}
Just change your setter and getter method. because it reads the method. not the variable name.

I think the issue is with the setter/getter methods.
Could you try this.
public String getJ_username() {
return j_username;
}
public void setJ_username( String j_username ) {
this.j_username = j_username;
}
To avoid such mistakes I suggest to use IDE help rather creating setter/getter methods on your own.
If you are using Eclipse you can follow below steps.
RightCLick on the variable you want to generate setter/getter methods.
Source ==> Generate Getters and Setters...
Check Seter and Getter check boxes click OK.
Step1
Step2

Related

Can I use multiple methods in the same action?

I am using Struts 2 (2.5.10.1) with struts2-json-plugin-2.5.10.1. On my page I have a grid using ExtJs 4.2.2 that will be populated with projects, each row will have two actions: edit and delete.
The problem is that I have encountered: if I try to use multiple methods from the same action to do all the above, when I try to populate the grid I get no JSON response. Is what i am trying to do even possible ? (The edit and delete functionalities are not yet implemented)
Here is my struts.xml:
<package name="admin" extends="json-default">
<action name="requestProjectData"
class="administrator.ACTION_ProjectsGrid"
method="requestProjectData">
<result type="json" >
<param name="root">projectData</param>
</result>
</action>
<action name="requestDeleteProject"
class="administrator.ACTION_ProjectsGrid"
method="deleteProject">
<result type="json" >
<param name="root">success</param>
</result>
</action>
<action name="requestEditProject"
class="administrator.ACTION_ProjectsGrid"
method="editProject">
<result type="json" >
<param name="root">success</param>
</result>
</action>
Here is my Action class:
public class ACTION_ProjectsGrid extends ActionSupport {
private static final long serialVersionUID = 1L;
private List<Project> projectData;
private boolean success;
public void requestProjectData()
{
ProjectManager pm = new ProjectManager();
List<Project> listOfProjects = pm.getAllProjects();
projectData = listOfProjects;
}
public void deleteProject()
{
success = true;
}
public void editProject()
{
success = true;
}
public List<Project> getProjectData() {
return projectData;
}
public void setProjectData(List<Project> projectData) {
this.projectData = projectData;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
And my JSP page:
<body>
<script type="text/javascript">
Ext.define('Project', {
extend: 'Ext.data.Model',
fields: [ 'projectId', 'projectName' ]
});
var projStore = Ext.create('Ext.data.Store', {
model: 'Project',
proxy: {
type: 'ajax',
pageParam: false,
startParam: false,
limitParam: false,
noCache: false,
url: 'requestProjectData',
reader: {
type: 'json'
}
},
autoLoad: true
});
Ext.create('Ext.grid.Panel', {
renderTo: Ext.getBody(),
store: projStore,
width: 641,
height: 300,
title: 'Projects',
columns: [
{
text: 'ID',
width: 80,
dataIndex: 'projectId'
},
{
text: 'Project Name',
width: 80,
dataIndex: 'projectName'
},
{
xtype: 'actioncolumn',
width: 80,
items: [{
icon: '${pageContext.request.contextPath}/JavaScript/extjs/resources/user_edit.png',
tooltip: 'Edit',
handler: function(grid, rowIndex, colIndex) {
var rec = grid.getStore().getAt(rowIndex);
Ext.Ajax.request({
url: 'requestDeleteProject',
disableCaching: false,
params: {
projectId: rec.get("projectId").toString(),
},
success: function(response)
{
grid.getStore().load();
}
});
}
}]
}
]
});
</script>
</body>
Ext.Ajax.request({
url: 'requestDeleteProject',
disableCaching: false,
params: {
projectId: rec.get("projectId").toString(),
},
success: function(response){
grid.getStore().load();
}
});}
Inside that part you making your request... You should decode the "response" inside the sucess and after it load the data to your store; making grid.getStore().load()
You are making another Request you want is something like
var json = Ext.decode(response.wtv..);
and then you want to load the grid's store like
grid.getStore().loadData(json);
soo like
success: function(response){
var json = Ext.decode(response.wtv..);
grid.getStore().loadData(json);
}
I found the problem. For this solution to work, all methods have to be created like the execute method. After I changed the return type to String and returned ActionSupport.SUCCESS, the json response appeared. But I still do not know if this solution is correct or I am breaking some rules, can anyone explain this to me ?
If you define an action that is mapped to the action class' method, the method should return either String or Result. The ActionSupport class provides you the default implementation for the action class.
Beside that, it implements a lot of interfaces that involve additional functionality via interceptors. One of this interfaces is Action. It is a functional interface that has only one method execute() which is invoked by default if no method is mapped to the action.
The value that is returned from the action method is a result code. This code or Result is necessary for Struts to proceed with the response after the action was executed. Thus the result code is returned to the invoker (ActionInvocation) which finds and executes a result with the same name defined in the action configuration. The default result name is Action.SUCCESS or ActionSupport.SUCCESS that you can return from the action. There should be result in the action configuration that implicitly or explicitly define a result with the name "success".
The "success" result is defined by default in your configuration, because it's missing name attribute. So to execute this result you should return "success", or Action.SUCCESS, or ActionSupport.SUCCESS that are the same string.
See more in the Result Configuration.
When an action class method completes, it returns a String. The
value of the String is used to select a result element. An action
mapping will often have a set of results representing different
possible outcomes. A standard set of result tokens are defined by the
ActionSupport base class. Predefined result names
String SUCCESS = "success";
String NONE = "none";
String ERROR = "error";
String INPUT = "input";
String LOGIN = "login";
Of course, applications can define other result tokens to match
specific cases.

How to call action after selecting values from dynamically created dropdown list using struts2

Here I have created dynamic dropdown list using this link, but when I select some value from available list it should be called in action class.
The dropdown list which can be seen in the image ,here the values are loaded dynamically from the database and now what I want is when I select any value from that two dropdown list that values (I mean text value) should be sent to the action class and there I will execute one JDBC select query on the basis of this two values and will display in the table shown in the image but everything should be on load.Action should be on selecting values from dropdown list not on any button click .With static values I am able to call value from dropdown list into action class with name attribute.But in this case I cannot :(
I hope I am clear now .
I have tried calling select tag using listkey,name and id but none of them worked .
Below is my JSP code:
<div>
<div class="invoicetext1">Event Name :</div>
<s:select name="dp.eventState"
list="%{state}"
class="billlistbox1"
id="eventName" />
<div>
<s:select name="dp.companyState"
class="billlistbox2"
listKey="companyState"
list="%{status}">
</s:select>
</div>
<div class="invoicetext2">Company Name :</div>
<div class="clear"></div>
</div>
<s:form action="ActionSelect">
<s:submit value=" Click Here"/>
</s:form>
<div>
Action class for loading dynamic dropdown list :
package com.ca.actions;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.ca.database.Database;
import com.ca.pojo.Event;
import java.sql.PreparedStatement;
import com.opensymphony.xwork2.ActionSupport;
public class RetrieveEvNaCoNaAction extends ActionSupport {
private static final long serialVersionUID = -5418233715172672477L;
List<Event> dataForBillsJspList;
private List state = new ArrayList();
private List status = new ArrayList();
String eventName;
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public RetrieveEvNaCoNaAction() {
// TODO Auto-generated constructor stub
}
public List<Event> getDataForBillsJspList() {
return dataForBillsJspList;
}
public void setDataForBillsJspList(List<Event> dataForBillsJspList) {
this.dataForBillsJspList = dataForBillsJspList;
}
public List getStatus() {
return status;
}
public void setStatus(List status) {
try {
Database database = new Database();
Connection con = database.Get_Connection();
PreparedStatement ps = con
.prepareStatement("SELECT EVENT_NAME,COMPANY_NAME,date_format(FROM_DATE,'%d/%m/%Y') as dateAsFrom,date_format(TO_DATE,'%d/%m/%Y') as dateAsTo FROM EVENT");
ResultSet rs = ps.executeQuery();
//dataForBillsJspList = new ArrayList<Event>();
while (rs.next()) {
/*dataForBillsJspList.add(new Event(rs.getString("EVENT_NAME"),
rs.getString("COMPANY_NAME"), rs
.getString("dateAsFrom"), rs
.getString("dateAsTo")));
System.out.println(rs.getString("EVENT_NAME"));*/
status.add(rs.getString("COMPANY_NAME"));
}
System.out.println("Data Collected ...");
}catch(Exception e)
{
e.printStackTrace();
}
}
public List getState() {
return state;
}
#Override
public String execute() throws Exception {
// TODO Auto-generated method stub
setState(this.state);
setStatus(this.status);
return "success";
}
public String showEventDetails(){
System.out.println("Hi.."+eventName);
return SUCCESS;
}
public void setState(List state) {
//implement the application specific logic to
try {
Database database = new Database();
Connection con = database.Get_Connection();
PreparedStatement ps = con
.prepareStatement("SELECT EVENT_ID,EVENT_NAME,COMPANY_NAME,CONTACT_PERSON,CONTACT_NO,EMAIL_ID,EVENT_VENUE,date_format(FROM_DATE,'%d/%m/%Y') as dateAsFrom,date_format(TO_DATE,'%d/%m/%Y') as dateAsTo ,EVENT_TIME FROM EVENT");
ResultSet rs = ps.executeQuery();
dataForBillsJspList = new ArrayList<Event>();
while (rs.next()) {
dataForBillsJspList.add(new Event(rs.getString("EVENT_ID"),rs.getString("EVENT_NAME"),
rs.getString("COMPANY_NAME"),rs.getString("CONTACT_PERSON"),rs.getString("CONTACT_NO"),rs.getString("EMAIL_ID"),rs.getString("EVENT_VENUE"), rs
.getString("dateAsFrom"), rs
.getString("dateAsTo"),rs.getString("EVENT_TIME")));
//System.out.println(rs.getString("EVENT_NAME"));
state.add(rs.getString("EVENT_NAME"));
System.out.println(rs.getString("EVENT_ID"));
}
System.out.println("Data Collected ...");
}catch(Exception e)
{
e.printStackTrace();
}
//Here for displaying the data on UI, we are using few hardcoded values//
}
}
After loading dynamic dropdown list now i am trying to call selected value in action class by S.O.P but it gives null pointer exception. Below is my POJO class:
package com.ca.pojo;
public class Dropdown
{
private String eventState;
private String companyState;
public Dropdown() {
// TODO Auto-generated constructor stub
}
public String getEventState() {
return eventState;
}
public void setEventState(String eventState) {
this.eventState = eventState;
}
public String getCompanyState() {
return companyState;
}
public void setCompanyState(String companyState) {
this.companyState = companyState;
}
}
and below is action class where I am trying to call that selected value by using name attribute :
package com.ca.actions;
import com.ca.pojo.Dropdown;
import com.opensymphony.xwork2.ActionSupport;
public class DropdownAction extends ActionSupport
{
Dropdown dp;
public DropdownAction() {
// TODO Auto-generated constructor stub
}
public Dropdown getDp() {
return dp;
}
public void setDp(Dropdown dp) {
this.dp = dp;
}
#Override
public String execute() throws Exception {
// TODO Auto-generated method stub
System.out.println(dp.getEventState());
return "success";
}
}
struts.xml is properly configured. Now after selecting two values I want to display data in the below table accordingly without any button click but in jsp i have created button just to see whether i am getting the selected value in action class but in actual i want it without any button click.
Well, there is a huge mess here :D
First of all, the NullPointerException is thrown because the values are not sent, and the values are not sent because they're not in the form.
You should enclose them in the form like this for them to be sent to the ActionSelect action:
<s:form action="ActionSelect">
<div class="invoicetext1">Event Name :</div>
<s:select name="dp.eventState"
list="%{state}"
class="billlistbox1"
id="eventName" />
<div>
<s:select name="dp.companyState"
class="billlistbox2"
listKey="companyState"
list="%{status}">
</s:select>
</div>
<div class="invoicetext2">Company Name :</div>
<div class="clear"></div>
</div>
<s:submit value=" Click Here"/>
</s:form>
Solved the mistery, this doesn't solve your problem, though.
You have two main ways to contact actions from a page:
Using a standard submit (as you're doing):
you either submit a form with its content, or call a link by eventually passing parameters in the querystring. This creates a Request, that will contact an action, that will return an entire JSP, that will be loaded in place of the page you're on now.
Using AJAX:
you POST or GET to an action without changing the current page, and the action can return anything, like a JSP snippet, a JSON result, a binary result (through the Struts2 Stream result), etc...
You then can choose what to do with the returned data, for example load it inside a <div> that before was empty, or had different content.
Now your problem is that you're contacting an action that is not the one you're coming from (is not able to re-render the entire JSP you're on) and you're calling it without using AJAX, then whatever the object mapped to the "success" result is (the whole JSP, or a JSP snippet), it will be loaded in place of the JSP you're on, and it will fail.
Since you seem to be quite new to this, I suggest you start with the easy solution (without AJAX), and after being expert with it, the next time try with AJAX.
That said,
avoid putting logic in getters and setters;
avoid calling methods that are not setter as setters (setState, setStatus...);
always make your attributes private;
try giving speaking names to variables: state and status for event states and company states are really confusing; and what about "state" instead of "name" (in jsp and on DB is "name");
consider loading informations like selectbox content in a prepare() method, so they will be available also in case of errors;
you're not closing the connections (and BTW it would be better to use something more evoluted, like Spring JDBC, or better Hibernate, or even better JPA, but for now keep going with the raw queries)
The following is a refactoring of your code to make it achieve the goal. I'll use #Getter and #Setter only for syntactic sugar (they're Lombok annotations, but you keep using your getters and setters, it's just for clarity):
<head>
<script>
$(function(){
$("#event, #company").on('change',function(){
$("#myForm").submit();
});
});
</script>
</head>
<body>
<form id="myForm">
<div>
...
<s:select id="event" name="event" list="events" />
...
<s:select id="company" name="company" list="companies" />
...
</div>
</form>
<div>
...
Table - iterate **dataForBillsJspList** here
...
</div>
</body>
public class RetrieveEvNaCoNaAction extends ActionSupport {
private static final long serialVersionUID = -5418233715172672477L;
#Getter private List<Event> dataForBillsJspList = new ArrayList<Event>();
#Getter private List<String> events = new ArrayList<String>();
#Getter private List<String> companies = new ArrayList<String>();
#Getter #Setter private String event = null;
#Getter #Setter private String company = null;
#Override
public void prepare() throws Exception {
Connection con;
try {
con = new Database().Get_Connection();
// load companies
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT company_name FROM event");
ResultSet rs = ps.executeQuery();
while (rs.next()) { companies.add(rs.getString("company_name")); }
// load events
ps = con.prepareStatement("SELECT DISTINCT event_name FROM event");
rs = ps.executeQuery();
while (rs.next()) { events.add(rs.getString("event_name")); }
} catch(Exception e) {
e.printStackTrace();
} finally {
con.close();
}
}
#Override
public String execute() {
Connection con;
try {
con = new Database().Get_Connection();
// load the table. The first time the table is loaded completely
String sql = "SELECT EVENT_ID, EVENT_NAME, COMPANY_NAME, CONTACT_PERSON, CONTACT_NO, EMAIL_ID, EVENT_VENUE, " +
"date_format(FROM_DATE,'%d/%m/%Y') as dateAsFrom, date_format(TO_DATE,'%d/%m/%Y') as dateAsTo ,EVENT_TIME " +
"FROM event";
String where = "";
// if instead this action has been called from the JSP page,
// the result is filtered on event and company:
if (event!=null && company!=null) {
where = " WHERE event_name = ? AND company_name = ?";
}
// load companies
PreparedStatement ps = con.prepareStatement(sql + where);
if (where.length()>0) {
ps.setString(1,event);
ps.setString(2,company);
}
ResultSet rs = ps.executeQuery();
while (rs.next()) {
dataForBillsJspList.add(new Event(rs.getString("EVENT_ID"),rs.getString("EVENT_NAME"),rs.getString("COMPANY_NAME"),
rs.getString("CONTACT_PERSON"),rs.getString("CONTACT_NO"),rs.getString("EMAIL_ID"),
rs.getString("EVENT_VENUE"), rs.getString("dateAsFrom"), rs.getString("dateAsTo"),
rs.getString("EVENT_TIME")));
}
} catch(Exception e) {
e.printStackTrace();
} finally {
con.close();
}
return SUCCESS;
}
}
It is a kickoff example, but it should work.
The next steps are:
create a POJO with id and description, show the description in the select boxes, but send the id
use header values ("please choose an event"...) and handle in action conditional WHERE (only company, only event, both)
PAGINATION
Good luck
Using Javascript/jQuery you can do this, it depends on what you want to do after reached action class.
If you want to navigate to another page use the code below.
Add onchange event as an attribute to your dropdown,
onchange="customFunction(this.value)"
create customFunction in header part,
function customFunction(selectedValue){
window.location="Action_URL?myValue="+selectedValue;
}
Or if you want to return back the same page use jQuery ajax,
$("#eventName").change(function(e){
var selectedValue = $(this).val();
$.ajax({
type : 'post',
url : 'Action_URL',
data: { myValue: selectedValue},
success : function(data) {
alert(data);
console.log(data);
}
});
});
Hope this helps.

Adding #RequiredStringValidator causing result not to be found

I have an Apache Struts web application. All of my actions are defined in my struts.xml file. I am trying to add support to my action classes to have validation annotations added to my project.
To try and get this working I added the struts2-convention-plugin.jar and also commons-lang.jar.
I then added an annotation on top of my action method.
jsp
<s:form id="tradingOutageFrm" action="addTradingOutageDo.action"
validate="true" theme="simple">
struts.xml
<action name="addTradingOutageDo" class="com.myproject.action.TradingOutageAction"
method="addDo">
<result name="success" type="tiles">page.tradingOutageAdd</result>
</action>
My action class
public class TradingOutageAction extends ActionSupport {
#RequiredStringValidator(type = ValidatorType.SIMPLE,
fieldName = "storeNo",
message = "Please enter a store.")
public String addDo() throws Exception {
try{
Map<String, Object> session = ActionContext.getContext().getSession();
String userId = session.get("userid").toString();
tradingOutage.setStoreNo(storeNo);
tradingOutage.setStoreServer(storeServer);
tradingOutageDAO.insertOutage(userId, startDate, startTime,
endDate, endTime, tradingOutage);
addActionMessage(MessageConstants.RECORD_INSERTED);
}catch(Exception e){
addActionError(MessageConstants.UPDATE_FAILED + " " + e.getMessage());
}
return SUCCESS;
}
Without the annotation everything works fine but with the annotation I get a
No result defined for action com.myproject.action.TradingOutageAction and result input.
Can anyone help me with what might be wrong here.

Passing id value to action class

I want to pass textfield with id value pat to the getautocomplete.action in Struts 2. Here I am using TINY.box to pop up the next page.
<s:textfield name="pat" id="pat"/>
<script type="text/javascript">
T$('tiny_patient').onkeypress = function(){
TINY.box.show('getautocomplete.action',1,0,0,1)
}
</script>
You need to append the id pat and its value to the url that you pass to the show function. For example
var url = 'getautocomplete.action?pat=' + $("#pat").val();
You can then use the variable url in your show function.
You also need to add the following in your action class. This also depends on the java type of pat. I am using String,
private String pat;
public String getPat()
{
return pat;
}
public void setPat(final String value)
{
this.pat = value;
}
Note
It is recommended to get your url using the following instead of hard-coding the extension
<s:url id="url_variable" namespace="/namespace_of_action" action="action_name" />
var url = '<s:property value="url_variable" />?pat=' + $("#pat").val();
If you are trying to populate the box based on previous box selection or any server side process you have to use ajax.
In your action class , write a getter-setter for variable named "pat" like this:
private string pat;
public getPat()
{
.........
}
public setPat(String pat)
{
this.pat=pat;
}
and change
TINY.box.show('getautocomplete.action',1,0,0,1)
to
TINY.box.show('getautocomplete.action?pat="xyz"',1,0,0,1)
Hope this will solve your problem unless you have an idea about ajax.
Try
<s:textfield name="pat" id="pat"/>
<script type="text/javascript">
document.getElementById("tiny_patient").onkeypress = function(e){
TINY.box.show("<s:url action='getautocomplete'/>"+"?pat="+document.getElementById("pat").value,1,0,0,1)
}
</script>

Struts2 if tag not working

I am writing a login page that when a invalid user tries to login I redirect to the login action with a error parameter equal to 1.
private String username;
private String password;
private int error;
#Override
public String execute()
{
//validate user input
if (username == null || password == null || username.isEmpty() || password.isEmpty())
{
error = 2;
return LOGIN;
}
LoginModel loginModel = new LoginModel(username, password);
HttpBuilder<LoginModel, User> builder = new HttpBuilder<LoginModel, User>(User.class);
builder.setPath("service/user/authenticate");
builder.setModel(loginModel);
IHttpRequest<LoginModel, User> request = builder.buildHttpPost();
User user = request.execute(URL.BASE_LOCAL);
//redirects to login page
if (user == null)
{
error = 1;
return LOGIN;
}
else
{
return SUCCESS;
}
}
//Getters/Setters
If a invalid user trys to login it redirects to localhost:8080/app/login.action?error=1. I am trying to display a error message to user by access the error parameter by using the if tag but its not working the message is not displaying.
<s:if test="error == 1">
<center>
<h4 style="color:red">Username or Password is invalid!!</h4>
</center>
What am I doing wrong?
As far as I'm concerned what you're doing wrong is completely ignoring the framework.
Roughly, IMO this should look more like this:
public class LoginAction extends ActionSupport {
private String username;
private String password;
#Override
public String validate() {
if (isBlank(username) || isBlank(password)) {
addActionError("Username or Password is invalid");
}
User user = loginUser(username, password);
if (user == null) {
addActionError("Invalid login");
}
}
public User loginUser(String username, String password) {
LoginModel loginModel = new LoginModel(username, password);
HttpBuilder<LoginModel, User> builder = new HttpBuilder<LoginModel, User>(User.class);
builder.setPath("service/user/authenticate");
builder.setModel(loginModel);
IHttpRequest<LoginModel, User> request = builder.buildHttpPost();
return request.execute(URL.BASE_LOCAL);
}
}
You would have an "input" result containing the form, and display any action errors present using whatever style you wanted. If it's critical to change the styling based on which type of login error it is you'd have to play a few more games, but that seems excessive.
Unrelated, but I'd move that loginUser code completely out of the action and into a utility/service class, but at least with it wrapped up in a separate method you can mock it more easily. It certainly does not belong in the execute method.
You need to provide the getter and setter of field 'error' to access it from value stack.
public int getError()
{
return error;
}
public void setError(int error)
{
this.error = error;
}
And try to access it in OGNL:
<s:if test="%{#error==1}"></s:if>
Or using JSTL:
<c:if test="${error==1}"></c:if>

Categories