Camel on Google App Engine Tutorial
Work in progress
This tutorial is still work in progress but already provides enough information to get a non-trivial Camel application running on GAE.
Overview
Goal of this tutorial is to get a Camel application running on Google App Engine that can send weather reports to user-defined email accounts. The user enters the name of a city and the email address of the report receiver into a form and submits it to the tutorial application. Retrieving weather data for that city and sending the weather report is done in the background.
Download
The packaged tutorial application can be (temporarily) downloaded from here. It is an Eclipse project that requires the Google Eclipse Plugin and the App Engine Java SDK 1.2.6 for running. A Maven-based version of the project should be available soon. The following sections describe how to build that application from scratch.
Create a GAE application
For setting up the Camel-independent parts of a GAE application refer to the Java Getting Started Guide in the GAE documentation. This tutorial uses the Google Plugin for Eclipse and the App Engine Java SDK 1.2.6. After installing the SDK and the plugin, create a new GAE project via File->New->Web Application Project
.
After pressing Finish
the new GAE project appears in the package explorer.
Add required libraries
The created project is not a Maven project so all required dependencies must be added manually to the war/WEB_INF/lib
directory. These are
- camel-core-2.1-SNAPSHOT.jar
- camel-spring-2.1-SNAPSHOT.jar
- camel-http-2.1-SNAPSHOT.jar
- camel-servlet-2.1-SNAPSHOT.jar
- camel-gae-2.1-SNAPSHOT.jar
- commons-logging-1.1.1.jar
- commons-management-1.0.jar
- commons-httpclient-3.1.jar
- commons-codec-1.4.jar
- spring-aop-2.5.6
- spring-beans-2.5.6
- spring-context-2.5.6
- spring-core-2.5.6
Then right-click on the Tutorial
project and add these libraries to the classpath via Properties->Java Build Path->Libraries->Add JARs...
. After adding they should now appear in the Referenced Libraries
container.
Setup deployment descriptors
The deployment descriptors web.xml
and appengine-web.xml
are located in the war/WEB-INF
directory.
web.xml
<?xml version="1.0" encoding="utf-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>CamelServlet</servlet-name> <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>context.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>CamelServlet</servlet-name> <url-pattern>/camel/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>CamelServlet</servlet-name> <url-pattern>/worker/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
appengine-web.xml
The <application>
element in appengine-web.xml
requires a GAE application name. In the following example, replace the value replaceme
with a valid GAE application name.
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>replaceme</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>
Setup the form
Users provide input data to the application by submitting form data. The following form definition should be stored in war/WEB-INF/index.html
.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Weather Report</title> </head> <body> <h1>Weather Report</h1> <form action="camel/weather" method="post"> <table> <tr> <td>City:</td> <td><input type="text" name="city"></td> </tr> <tr> <td>Mailto:</td> <td><input type="text" name="mailto"></td> </tr> <tr> <td></td> <td align="right"><input type="submit" value="Submit" /></td> </tr> </table> </form> </body> </html>
When the user presses the Submit
button the form data are POSTed to http://<appname>.appspot.com/camel/weather
. The input field values will be available as in
message headers city
and mailto
.
Setup the routes
Add the following TutorialRouteBuilder
class to the org.apache.camel.example.gae
package. The previously generated TutorialServlet
in that package can be deleted.
package org.apache.camel.example.gae; import org.apache.camel.Exchange; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.gae.mail.GMailBinding; public class TutorialRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from("ghttp:///weather") .to("gtask://default") .setHeader(Exchange.CONTENT_TYPE, constant("text/plain")) .transform(constant("Weather report will be sent to ").append(header("mailto"))); from("gtask://default") .setHeader(Exchange.HTTP_QUERY, constant("weather=").append(header("city"))) .to("ghttp://www.google.com/ig/api") .process(new WeatherProcessor()) .setHeader(GMailBinding.GMAIL_SUBJECT, constant("Weather report")) .setHeader(GMailBinding.GMAIL_TO, header("mailto")) .to("gmail://<replaceme>@gmail.com"); } }
Form data are received via the ghttp component. After receiving the request it is added to the default
queue for background processing. Queueing messages on GAE is done with the gtask component. After queueing the request a response is generated for being displayed in the browser. The value of the mailto
header is the email address the user entered in the form.
Background processing of the queued messages starts from("gtask://default")
. The first step is the construction of the Google weather service URL followed by the weather service invocation using the ghttp component. For example, if the user entered London
in the city field of the form the resulting URL is http://www.google.com/ig/api?weather=London
. The resulting XML data are processed by the WeatherProcessor
:
package org.apache.camel.example.gae; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.w3c.dom.Document; public class WeatherProcessor implements Processor { @Override public void process(Exchange exchange) throws Exception { // convert XML body to DOM tree Document doc = exchange.getIn().getBody(Document.class); XPathFactory xpfactory = XPathFactory.newInstance(); XPath xpath = xpfactory.newXPath(); // Extract result values via XPath String city = xpath.evaluate("//forecast_information/city/@data", doc); String cond = xpath.evaluate("//current_conditions/condition/@data", doc); String temp = xpath.evaluate("//current_conditions/temp_c/@data", doc); String msg = null; if (city != null && city.length() > 0) { msg = new StringBuffer() .append("\n").append("Weather report for: ").append(city) .append("\n").append("Current condition: ").append(cond) .append("\n").append("Current temperature: ").append(temp).append(" (Celsius)").toString(); } else { // create an error message msg = "Error getting weather report for " + exchange.getIn().getHeader("city", String.class); } exchange.getIn().setBody(msg); } }
This processor extracts data from the XML result with XPath expressions and creates a simple text-based report for being sent by email. Add this class to the org.apache.camel.example.gae
package. For sending the email the gmail component is used. The sender is given by the endpoint URI where <replaceme>
must be replaced with the username of the admin account of the GAE application (usually the account to deploy the GAE application). The recipient is derived from the user-defined mailto
header.
Local development server
Please note that on the local development server sending emails does not work and queued tasks must be executed manually from the developer console.
Setup the application context
Add the following application context XML file to the project's src
directory (needs to be on the classpath).
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="camelContext" class="org.apache.camel.component.gae.context.GaeSpringCamelContext"> <property name="routeBuilder" ref="tutorialRouteBuilder" /> </bean> <bean id="tutorialRouteBuilder" class="org.apache.camel.example.gae.TutorialRouteBuilder"> </bean> </beans>
Deploy the application
To deploy the application right-click on the Tutorial
project and select Google->Deploy to App Engine
. In the dialog enter the project name, your email address and password for logging into App Engine and press Deploy
.
Use the application
Go to http://<appname>.appspot.com
where <appname>
must be replaced with a valid GAE application name. The following form should now appear.
Enter the name of a city and your email address, for example:
Then press Submit
. Submitting the form the first time will initialize the application on Google App Engine which can take more than 10 seconds. Subsequent submissions are served much faster. Check your emails and you should now see a new email with subject Wheather report
and content similar to this one:
Weather report for: London, England Current condition: Klar Current temperature: 12 (Celsius)
The report is partly internationalized, depending on the language settings of your browser.