We're working on our first JSF project, and we have some problems. We are making the login functionality, and when we hit the login button, it throws:
an IllegalArgumentException - null
source
a NullPointerException -
Servlet.service() for servlet [Faces
Servlet] in context with path
[/jsf-blank] threw exception
Do you have any idea why it's not working? Please enlighten us.
Here is out User bean:
public class User
{
private long id;
private String username;
private String password;
public User()
{
}
public User(String username, String password)
{
this.username = username;
this.password = password;
}
public User(long id, String username, String password)
{
super();
this.id = id;
this.username = username;
this.password = password;
}
public String getNextPage()
{
return "failure"; //or "admin" or "client"
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getUsername()
{
return username;
}
public String getPassword()
{
return password;
}
public void setUsername(String username)
{
this.username = username;
}
public void setPassword(String password)
{
this.password = password;
}
}
Our login.jsp page:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%# taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!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>Login</title>
</head>
<body>
<f:view>
<h:form>
Username: <h:inputText value="#{userBean.username}"></h:inputText>
<br />
Password: <h:inputText value="#{userBean.password}"></h:inputText>
<br />
<h:commandButton value="Login" action="#{userBean.getNextPage}" ></h:commandButton>
</h:form>
</f:view>
</body>
</html>
Our faces-config.xml:
<?xml version="1.0"?>
<faces-config 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-facesconfig_2_0.xsd"
version="2.0">
<managed-bean>
<managed-bean-name>userBean</managed-bean-name>
<managed-bean-class>data_layer.model.User</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/login.jsp</from-view-id>
<navigation-case>
<from-action>#{userBean.getNextPage}</from-action>
<from-outcome>admin</from-outcome>
<to-view-id>/admin.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{userBean.getNextPage}</from-action>
<from-outcome>failure</from-outcome>
<to-view-id>/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{userBean.getNextPage}</from-action>
<from-outcome>client</from-outcome>
<to-view-id>/client.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
Our 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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<!-- <welcome-file>index.html</welcome-file> -->
</welcome-file-list>
</web-app>
And our directory structure is:
You're intermixing JSF 1.x and JSF 2.x approaches. Maybe you're reading from an outdated book/tutorial which is targeted on JSF 1.x while the IDE is autogenerating JSF 2.x stuff?
JSF 2.0 doesn't support JSP by default anymore. The legacy JSP has been succeeded by Facelets which offers far more superior templating capabilities. Although you can configure JSF 2.0 to use the vintage JSP again, I strongly recommend to not do so. Rather rewrite login.jsp to login.xhtml:
<!DOCTYPE html>
<html lang="en"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Login</title>
</head>
<body>
<h:form>
Username: <h:inputText value="#{userBean.username}" />
<br />
Password: <h:inputText value="#{userBean.password}" />
<br />
<h:commandButton value="Login" action="#{userBean.getNextPage}" />
</h:form>
</body>
Unrelated to the problem, with JSF 2.0 you can also get rid of the whole faces-config.xml as you currently have. Add the following javax.faces.bean annotations to the User class:
#ManagedBean(name="userBean")
#RequestScoped
public class User {
// ...
}
With the new implicit navigation, the outcome will be by default treated as filename of the view. You've already done it fine, so you don't need those navigation rules at all.
Related
I am using Spring Security 3.1.3.RELEASE in my maven pom because the book am reading is 2013 and that is what they used and have the following code snippets:
// AdminController
#Controller
#RequestMapping("/admin")
public class AdminController {
#RequestMapping(method=RequestMethod.POST, value="/movies")
#ResponseBody
public String createMovie(#RequestBody String movie) {
System.out.println("Adding movie!! "+movie);
return "created";
}
}
// LoginController
#Controller
#RequestMapping("")
public class LoginController {
#RequestMapping(method= {RequestMethod.GET, RequestMethod.POST}, value="/custom_login")
public String showLogin() {
return "login";
}
}
// web.xml
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-security.xml</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>terrormovies</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>terrormovies</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
// Spring security Config :: applicationContext-security.xml
<security:http auto-config="true">
<security:intercept-url pattern="/admin/**/*" access="ROLE_ADMIN" />
<security:form-login login-page="/custom_login" username-parameter="user_param" password-parameter="pass_param"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user authorities="ROLE_ADMIN" name="admin" password="admin" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
//login.jsp
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!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>Terror movies</title>
</head>
<body>
<form action="/j_spring_security_check" method="POST">
Username<input type="text" name="user_param"/><br/>
Password<input type="password" name="pass_param"/><br/>
<input type="submit" value="Login"/>
</form>
</body>
<% if(request.getParameter("error") != null){
out.println("ERROR LOGIN");
}
%>
</html>
When I start my application I get the login page with the form alright. I enter admin/admin as username/password respectively. When i click on the login button I get this error page saying:
Problem accessing /admin/movies. Reason:
Request method 'GET' not supported
Powered by Jetty://
instead of going to the method createMovie(#RequestBody String movie) in the
AdminController.
The address of this error page is :: http://localhost:8080/admin/movies
The LoginController and AdminController are in the same package.
What am I missing here?
Updated
In the form action:: <form action="/j_spring_security_check" method="POST">,
where does "/j_spring_security_check"` leads to? I think that is where the problem is. Am beginner in Spring Security so I can't figure it out now. I did a search but not any good answer.
The error response message you are receiving tells you exactly what the problem is:
When i click on the login button i get this error page saying::
Problem accessing /admin/movies. Reason:
Request method 'GET' not supported
Powered by Jetty://
And in your controller you have set this method:
#Controller
#RequestMapping("/admin")
public class AdminController {
#RequestMapping(method=RequestMethod.POST, value="/movies")
#ResponseBody
public String createMovie(#RequestBody String movie) {
System.out.println("Adding movie!! "+movie);
return "created";
}
}
And just as the message says, the /admin/movies method is mapped just for POST requests, so a GET request which is what is generated on redirection from the login success cannot be handled.
So here the trouble is not really the spring-security config, the problem is just that after login you are making a request to a request-mapping annotated method which does not support GET requests.
To solve it you could just configure this method into the existing AdminController:
#RequestMapping(method=RequestMethod.GET, value="/movies")
public String createMovieForm() {
return "createMovieForm";
}
And create a jsp with a form which points to the POST mapped controller method:
<form action="/admin/movies" method="POST">
Movie<input type="text" name="movie"/><br/>
<input type="submit" value="Login"/>
</form>
I would be easier too if you delete the #RequestBody annotation in the POST method, so finally the AdminController should end like this:
#Controller
#RequestMapping("/admin")
public class AdminController {
#RequestMapping(method=RequestMethod.POST, value="/movies")
#ResponseBody
public String createMovie(String movie) {
System.out.println("Adding movie!! "+movie);
return "created";
}
#RequestMapping(method=RequestMethod.GET, value="/movies")
public String createMovieForm() {
return "createMovieForm";
}
}
I am doing a simple program , the task is to Get (user id) from html form over Rest and username associated with should be displayed. I am
getting a 404 error when I load the index.html page.
But when i give the values(userid) over the url it works fine.
here's the code
<!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>Index page</title>
</head>
<body>
<form action="http://localhost:8080/rest/my/first/rest/users" method="GET">
<label for="uid">ID</label>
<input name="uid" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
and here is the resource class
#Path("users") //attach client request to resource: .../users
public class RestResource {
Map<String,User> listUsers;
//initialize some resources
public RestResource(){
listUsers = new HashMap();
listUsers.put("1",new User("1", "John"));
listUsers.put("2",new User("2", "Peter"));
}
//return list of users
#GET
#Produces(MediaType.TEXT_PLAIN)
public String listOfUsersInText(){
String list="";
for (Entry<String,User> u:listUsers.entrySet()){
list += u.getValue().getName() + "\n";
}
return list;
}
//return user information corresponding to the requested uid.
#GET
#Path("{uid}") //attach client request to resource: .../users/<uid>
#Produces(MediaType.TEXT_PLAIN)
public String getUID(#PathParam("uid") String uid){
if (!listUsers.containsKey(uid))
return "User not exist!";
return listUsers.get(uid).getID()+":"+listUsers.get(uid).getName();
}
}
WEB.xml
<display-name>rest</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.first.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
The approach you have followed is wrong we have several rest client api's you can use any of those and can send the request to rest resource
The response data you can display in jsp pages.
I'm currently working on a school project. We have to do a small market website in Java.
I've a small problem. I have an Index.jsp where i want to include a servlet (RandomArticle.jsp). This servlet have a .jsp and a .java and just shuffle a collection and return names.
When I display index.jsp, the html of RandomArticle.jsp is well displayed (so the include is correct) but the returned name are "null".
Here is some code:
Index.jsp (in WebContent of eclipse)
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Market</title>
</head>
<body>
<header>
<h1>market 2013</h1>
<h2>Bievenue </h2>
</header>
<nav>
<% String include = "/WEB-INF/RandomArticle.jsp"; %>
<jsp:include page='<%=include%>' />
</nav>
</body>
</html>
RandomArticle.jsp (in WEB-INF):
<h1>Liste des produits EpiMarket</h1>
<%
String productName1 = (String) request.getAttribute("productName1");
String productName2 = (String) request.getAttribute("productName2");
String productName3 = (String) request.getAttribute("productName3");
%>
<p>Acheter <% out.println(productName1); %></p>
<p>Acheter <% out.println(productName2); %></p>
<p>Acheter <% out.println(productName3); %></p>
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>Market</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>fr.market.servlets.Test</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>RandomArticle</servlet-name>
<servlet-class>fr.market.servlets.RandomArticle</servlet-class>
</servlet>
</web-app>
and RandomARticle.java
public class RandomArticle extends HttpServlet {
private ArrayList<Object> allProducts;
public String getProductName(int index){
return (((AbstractProduct) allProducts.get(index)).getName());
}
public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{
ADAO<AbstractProduct> dao = new DAOProduit();
allProducts = ((DAOProduit) dao).getAll();
Collections.shuffle(allProducts);
request.setAttribute("productName1", getProductName(0));
request.setAttribute("productName2", getProductName(1));
request.setAttribute("productName3", getProductName(2));
this.getServletContext().getRequestDispatcher( "/WEB-INF/RandomArticle.jsp" ).forward( request, response );
}
}
I think that the .java is never called, but I don't understand why.
Thanks for your time.
Gilles
I have a sample project configured with JAAS, JSF-2 and Primefaces. Here is my 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>JAASProject</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>start</param-value>
</context-param>
<context-param>
<param-name>primefaces.SUBMIT</param-name>
<param-value>partial</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/app/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>adbADRealm</realm-name>
<form-login-config>
<form-login-page>/login.xhtml</form-login-page>
<form-error-page>/login.xhtml</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>*</role-name>
</security-role>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
</web-app>
I have set <session-timeout> for testing purpose. The welcome file index.jsp is:
<%
response.sendRedirect("app/home.xhtml");
%>
So that when I am browsing localhost:8080/JAASProject I am navigating to the login page, but the URL remains localhost:8080/JAASProject/app/home.xhtml. I guess it is desired. In login.xhtml I have:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Login</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2" cellpadding="5">
<p:outputLabel value="Username" />
<p:inputText value="#{authenticationController.username}" required="true"/>
<p:outputLabel value="Password" />
<p:password value="#{authenticationController.password}" required="true"/>
<f:facet name="footer">
<p:commandButton id="loginButton" actionListener="#{authenticationController.login}" value="Login"/>
</f:facet>
</h:panelGrid>
</h:form>
</h:body>
</html>
And the AuthenticationController is:
package com.myself.jassproject.controller;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
#ManagedBean(name = "authenticationController")
#ViewScoped
public class AuthenticationController implements Serializable{
private static final long serialVersionUID = 7083052321396088714L;
private String originalURL;
private String username;
private String password;
public AuthenticationController() {
}
#PostConstruct
public void initialize() {
ExternalContext externalContext = getExternalContext();
originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
if (originalURL == null) {
originalURL = externalContext.getRequestContextPath() + "/app/home.xhtml";
}
System.out.println(originalURL);
}
public void login(ActionEvent event){
System.out.println("==");
try {
getServletRequest().login(username, password);
getExternalContext().redirect(originalURL);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private ExternalContext getExternalContext() {
return getFacesContext().getExternalContext();
}
private FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
private HttpServletRequest getServletRequest() {
return (HttpServletRequest) getExternalContext().getRequest();
}
}
The authentication is working and I am getting navigated to the home. In home I have three buttons: one Primesfaces', one JSF's (non-ajax) and one JSF's (ajax-enabled).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Home</title>
</h:head>
<h:body>
<h:form>
<p:commandButton value="Click me (Primefaces)" actionListener="#{homeController.doSomething}"/>
<h:commandButton value="Click me (JSF)" actionListener="#{homeController.doSomething}" />
<h:commandButton value="Click me (JSF - AJAX)" actionListener="#{homeController.doSomething}">
<f:ajax execute="#form"/>
</h:commandButton>
</h:form>
</h:body>
</html>
So far everything goes well untill now. When the session timeout occur and if I click on the Primefaces's button or the ajax enabled JSF's button it does nothing, neither any exception nor the actionListener get called; that is okay; but I am excepting that I would get navigated to the login page.
I can see a POST get fired if I click on the ajax enabled buttons by Firebugging but that is it. If I click on the JSF's button (non-ajax) I am navigating to the login page but not if I click on the other two.
Does JAAS works with AJAX POST? Is it an issue of my code? Am I missing something?
My configuration is:
Server - JBoss AS 7.1
Mojara - 2.1.7
Primefaces - 3.4.2
I'm following "Struts 2 in Action" book and under the first chapter of that book, there is a sample basic application to be developed. I tried to follow it and the action class is not calling properly. But I can view the success result JSP page. When I use the debug mode and try to debug the execute method of the action class, it is not calling. I'm using struts-2.3.15.1 libraries to develop this application.
My Action class is below :
package manning.chapterTwo;
import org.apache.struts2.config.Result;
import com.opensymphony.xwork2.ActionSupport;
#Result(name="SUCCESS", value="/chapterTwo/HelloWorld.jsp" )
public class AnnotatedHelloWorldAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private static final String GREETING = "Hello ";
public String execute() {
setCustomGreeting( GREETING + getName() );
return "SUCCESS";
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String customGreeting;
public String getCustomGreeting()
{
return customGreeting;
}
public void setCustomGreeting( String customGreeting ){
this.customGreeting = customGreeting;
}
}
JSP Page to get the Input looks like below (NameCollector.jsp) :
<%# page contentType="text/html; charset=UTF-8" %>
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Name Collector</title>
</head>
<body>
<hr>
<h4>Enter your name so that we can customize a greeting just for you!</h4>
<s:form action="annotatedHelloWorld">
<s:textfield name="name" label="Your name"/>
<s:submit/>
</s:form>
<hr>
</body>
</html>
Output JSP :
<%# page contentType="text/html; charset=UTF-8" %>
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>HelloWorld</title>
</head>
<body>
<hr>
<h3>Custom Greeting Page</h3>
<h4><s:property value="customGreeting"/></h4>
<hr>
</body>
</html>
web.xml file :
<?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>Struts2HelloWorldXML</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>
<init-param>
<param-name>actionPackages</param-name>
<param-value>manning</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
How to resolve it?
You should add an action annotation to the action class or method
#ParentPackage("chapterTwo");
#Action(value="HelloWorld", results=#Result(name="SUCCESS", value="/chapterTwo/HelloWorld.jsp" ))
public class AnnotatedHelloWorldAction extends ActionSupport {
Supposed the author either using conventions to map your action or added the action annotation in the errata. Also added the parent package annotation to which the action belongs to. And don't forget to add struts2-convention-plugin to the library set, because it's missed in the examples.