Main Tutorials

Google App Engine + Struts 2 example

In this tutorial, we will show you how to develop Struts 2 web application in Google App Engine (GAE) environment.

Tools and technologies used :

  1. JDK 1.6
  2. Eclipse 3.7 + Google Plugin for Eclipse
  3. Google App Engine Java SDK 1.6.3.1
  4. Struts 2.3.1.2
Note
Before proceed on this tutorial, make sure you read this – GAE + Java example and Struts 2 hello world example.

1. New Web Application Project

In Eclipse, create a new Web Application project, named as “Struts2GoogleAppEngine”.

gae struts2 example new project

Google Plugin for Eclipse will generate a sample of GAE project structure. Later, we will show you how to integrate Struts2 with this generated GAE project.

gae struts2 example sample project

2. Integrate Struts 2 Libraries

Get following Struts 2 dependency libraries, download Struts2 here.

  • asm-3.3.jar
  • asm-commons-3.3.jar
  • asm-tree-3.3.jar
  • commons-fileupload-1.2.2.jar
  • commons-io-2.0.1.jar
  • commons-lang-2.5.jar
  • freemarker-2.3.18.jar
  • javassist-3.11.0.GA.jar
  • ognl-3.0.4.jar
  • struts2-core-2.3.1.2.jar
  • xwork-core-2.3.1.2.jar

Put all in “war/WEB-INF/lib” folder.

gae struts2 example libraries

Right click on the project folder, select “Properties” -> “Java Build Path” -> “Libraries” tab, click “Add Jars” button and select above 11 jars from “war/WEB-INF/lib” folder into the build path.

gae struts2 example java build path

3. Integrate Struts 2 Code

3.1 Delete the generated Struts2GoogleAppEngineServlet.java, you don’t need this.

3.2 Create a new Struts 2 Action class.

File : src/com/mkyong/user/action/WelcomeUserAction.java


package com.mkyong.user.action;

public class WelcomeUserAction {

	private String username;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String execute() {

		return "SUCCESS";

	}
}

3.3 Create a listener class, and set ognl security to null.

Note
Struts 2 need this listener to run in GAE environment. Read this – Issues when deploying Struts 2 on GAE and Error: result ‘null’ not found

File : src/com/mkyong/listener/Struts2ListenerOnGAE.java


package com.mkyong.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import ognl.OgnlRuntime;

public class Struts2ListenerOnGAE implements ServletContextListener,
		HttpSessionListener, HttpSessionAttributeListener {

	public void contextInitialized(ServletContextEvent sce) {
		OgnlRuntime.setSecurityManager(null);
	}

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void sessionCreated(HttpSessionEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void sessionDestroyed(HttpSessionEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void attributeAdded(HttpSessionBindingEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void attributeRemoved(HttpSessionBindingEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void attributeReplaced(HttpSessionBindingEvent arg0) {
		// TODO Auto-generated method stub

	}

}

3.4 To run Struts2 project in local GAE environment, you have to create a TextBlock class and overload the original TextBlok class, otherwise you will hit “javax.swing.tree.TreeNode is a restricted class” error message. Hope Struts2 team can fix this in future released.

TextBlock Source Code
Go this URL to download TextBlock source code.

3.5 Review project directory structure.

gae struts2 example directory

4. Integrate Struts 2 Pages

4.1 Create a login.jsp page, to accept user input.

File : war/User/pages/login.jsp


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head></head>
<body>
<h1>GAE + Struts 2 Example</h1>

<s:form action="Welcome">
	<s:textfield name="username" label="Username"/>
	<s:password name="password" label="Password"/>
	<s:submit/>
</s:form>

</body>
</html>

4.2 Create a welcome_user.jsp page.

File : war/User/pages/welcome_user.jsp


<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head></head>
<body>
<h1>GAE + Struts 2 Example</h1>

<h2>Hello <s:property value="username"/></h2>

</body>
</html>

4.3 Review project directory structure again.

gae struts2 example directory

5. Struts XML configuration

Create a struts.xml file, and put in “src/struts.xml“.

File : 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>

  <package name="user" namespace="/User" extends="struts-default">
	<action name="Login">
		<result>pages/login.jsp</result>
	</action>
	<action name="Welcome" class="com.mkyong.user.action.WelcomeUserAction">
		<result name="SUCCESS">pages/welcome_user.jsp</result>
	</action>
   </package>

</struts>

6. web.xml

Update web.xml, integrate Struts2 and configure ognl security listener.

File : 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">
	<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>

	<listener>
		<listener-class>
                   com.mkyong.listener.Struts2ListenerOnGAE
                </listener-class>
	</listener>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

7. Directory Structure

Review the final directory structure.

gae struts2 example final directory

8. Run on Local

Done, time to perform testing. Right click on the project, run as “Web Application“.

URL : http://localhost:8888/User/Login.action

gae struts2 example run on local
gae struts2 example run on local

9. Deploy on GAE

Update appengine-web.xml, put your App Engine application ID.


<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <application>mkyong-struts2gae</application>
  <version>1</version>

  <!-- Configure java.util.logging -->
  <system-properties>
    <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
  </system-properties>
  
</appengine-web-app>

Select project, click on Google icon, “Deploy to App Engine“. Google Plugin for Eclipse will deploy all necessary files to GAE production automatically.

gae struts2 example deploy to GAE

During deployment, following similar messages will be displayed in Eclipse console view.


------------ Deploying frontend ------------

Preparing to deploy:
	Created staging directory at: 'C:\Users\mkyong\AppData\Local\Temp\appcfg7432687551.tmp'
	Scanning for jsp files.
	Compiling jsp files.
	Scanning files on local disk.
	Initiating update.
	Cloning 2 static files.
	Cloning 46 application files.

Deploying:
	Uploading 12 files.
	Uploaded 3 files.
	Uploaded 6 files.
	Uploaded 9 files.
	Uploaded 12 files.
	Initializing precompilation...
	Sending batch containing 11 file(s) totaling 44KB.
	Sending batch containing 1 blob(s) totaling 1KB.
	Deploying new version.

Verifying availability:
	Will check again in 1 seconds.
	Will check again in 2 seconds.
	Will check again in 4 seconds.
	Closing update: new version is ready to start serving.

Updating datastore:
	Uploading index definitions.

Deployment completed successfully

URL : http://mkyong-struts2gae.appspot.com/User/Login.action

gae struts2 example run on GAE
Note
Finally, finished this long article. The overall integration is not much difficult, just need to fix Struts2 ognl security and TextBlock issues, hope Struts2’s team can fix this in future.

Download Source Code

Due to large file size, all Struts2 jars are excluded, you need to download it manually.

Download – Struts2GoogleAppEngine.zip (23 KB)

References

  1. Struts2 hello world example
  2. Google App Engine + Java hello world example, using Eclipse
  3. Apache Struts
  4. Struts 2 on GAE

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
13 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Pranshu
9 years ago

How to integrate existing struts2 app with google app engine…plz help ([email protected])

Vladimir
10 years ago

Anyone familiar with this exception?
org.apache.jasper.JasperException: File “/struts-tags” not found

vineet
9 years ago
Reply to  Vladimir

yes, this exception occurs when particular file is not found or Mapping of the file is not correct.

Seekend
10 years ago

Hi mkyoung,

Thank you for your tutorial. I like this article but i have a error. Could you help me?

java.security.AccessControlException: access denied (“java.io.FilePermission” “jar:file:\D:\workspace\Guestbook\war\WEB-INF\lib\struts2-core-2.3.15.2.jar” “read”)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:372)
at java.security.AccessController.checkPermission(AccessController.java:559)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:383)
at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
at java.util.zip.ZipFile.(ZipFile.java:206)
at java.util.zip.ZipFile.(ZipFile.java:145)
at java.util.jar.JarFile.(JarFile.java:153)
at java.util.jar.JarFile.(JarFile.java:90)
at com.opensymphony.xwork2.util.fs.JarEntryRevision.needsReloading(JarEntryRevision.java:76)
at com.opensymphony.xwork2.util.fs.DefaultFileManager.fileNeedsReloading(DefaultFileManager.java:66)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.needsReload(XmlConfigurationProvider.java:394)
at org.apache.struts2.config.StrutsXmlConfigurationProvider.needsReload(StrutsXmlConfigurationProvider.java:169)
at com.opensymphony.xwork2.config.ConfigurationManager.needReloadContainerProviders(ConfigurationManager.java:215)
at com.opensymphony.xwork2.config.ConfigurationManager.conditionalReload(ConfigurationManager.java:179)
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:73)
at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:968)
at org.apache.struts2.dispatcher.ng.PrepareOperations.createActionContext(PrepareOperations.java:77)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:86)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:368)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:351)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:97)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:485)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

Sultan
8 years ago
Reply to  Seekend

Did you able to resolve this

Knight Rises
10 years ago
Reply to  Seekend

I also have met this issue! Mkyong, please give me the solutions! Thank so much!

mtr
11 years ago

How about if we have existing struts2 app and want to install it on Google App Engine?

Struts2 App build with: SiteMesh, JDBC with SQLite

How to add it to GAE?
Please help me with some propose steps.

Nahoot
11 years ago

Hello,
I’ve followed this tutorial to create an application (next step: tiles).
It worked OK except that, for some reason, eclipse says my jsp files have errors. The project constantly has the red cross on it, and I have an error notification every time I try to deploy the project.
Do you have any idea why this is happening?
Thanks for the article.

Ian Rae
11 years ago

Worked for me too. Thanks!

Bai_Hui
11 years ago

Doesnt work with GAE 1.6.6

I find it very inconvinent that Google and the Java frameworks are constantly changing something in their architecture which causes incompatibility issues.

To get running a Framework on the GAE you have to be really a magician or jedi. Yoda perhaps can do it.

I am new to webapp devoloping. I choosed the java way, but the dark hacky php way seems to me every day more and more tempting.

JR Galia
11 years ago

Very detailed example. Can be used as a template for making Struts 2 apps in Google App Engine.

Thanks for this example.

zzj
11 years ago

I like this article…Thank you

Deepak
11 years ago

Hi mkyoung,

I am regular vistor of your greate site.Thanks for making such site.
A week ago google has revised its policy for many of its API and services ncluding App engine.what will its affect on google appengine application.whether they are going to drop this feature in near future,or they will make it as a paid service.Please reply me soon..