Camel on Google App Engine Tutorial
...
title | Work in progress |
---|
Overview
Goal of this tutorial is
...
to get a non-trivial Camel application running on Google App Engine (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 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.
). For developing that application the Camel Components for Google App Engine are used. The example web application generates a weather report for a city that is entered by the user into a form and sends the weather report via email to either the currently logged-in user or a user-defined receiver. The following figure gives an overview. Users of this application need to login with their Google account.
POSTed form data are dispatched to the Camel application via the ghttp component. The application transforms the report request and enqueues it with the gtask component for further background processing. It then generates an immediate HTML response containing information about the city and the receiver of the report. The response also contains a link logging out from the application. In the background, the application retrieves weather data from the Google Weather Service, transforms the data to generate a simple weather report and sends the report by email via the gmail component.
Note | ||
---|---|---|
| ||
Clients other than web browsers may also interact with this application by POSTing the form data and an authentication cookie. At the moment the example application doesn't include a standalone Java client that demonstrates how to do that. This is work in progress. In the meantime, refer to the glogin component documentation for instructions how to access security-enabled GAE services. |
Tip | ||
---|---|---|
| ||
The next two sections describe how to checkout, build and deploy the example application. If you want to skip these steps, go directly to the #Usage section and use the pre-deployed example application at http://camelcloud.appspot.com. |
Prerequisites
- Sign up for a Google App Engine account if you don't have one.
- Create a new application via the admin console or reuse an existing one for uploading the example.
- Install the Google App Engine SDK for Java. This tutorial has been tested with version 1.3.6.
Deployment
First, checkout the sources from the Camel code repository.
No Format |
---|
svn co http://svn.apache.org/repos/asf/camel/trunk camel
|
Navigate to the camel-example-gae and install from here.
Open the camel-example-gae/src/main/webapp/WEB-INF/application-web.xml
file and replace the template application name replaceme
with the name of the application that you created in the previous section. Optionally, adjust the version number if needed.
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<?xml version="1.0" encoding="utf-8"?> <web<appengine-web-app xmlns="http://javaappengine.sungoogle.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>1.0"> </init-param> </servlet> <servlet-mapping> <servlet-name>CamelServlet</servlet-name><!-- <url-pattern>/camel/*</url-pattern> </servlet-mapping> Set your application name and version here <servlet-mapping> <servlet-name>CamelServlet</servlet-name>> <url-pattern>/worker/*</url-pattern><application>replaceme</application> <<version>1</servlet-mapping> version> <welcome<static-file-list>files> <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.
Code Block | |
---|---|
xml | xml |
title | appengine-web.xml | <?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"<exclude path="/index.html" /> <application>replaceme<</application> <version>1</version> <!-- Configure java.util.logging -->static-files> <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>
|
Then go to the camel-example-gae
directory and enter
No Format |
---|
mvn install
|
This will create the application war
file in the target directory. Finally use the appcfg
command-line tool of the App Engine SDK to deploy the application.
No Format |
---|
appcfg update target/camel-example-gae-<version>
|
where version
needs to be replaced with the version of Camel you're using. You will be prompted for the email address and password of your Google App Engine account. After deployment the example application is ready to use.
Usage
In the following, the application name camelcloud
will be used as an example. If you deployed the example application somewhere else, use your application name instead. Go to http://camelcloud.appspot.com
. The application will redirect you to a login page (see also Security for Camel GAE Applications).
After login, the application displays the tutorial's main page where you can enter the name of a city and optionally enter an email address where to send the waether report. If you check Send report to me
the report will be send to the email address that you used for login.
After pressing Submit
the response is
The home
link brings you back to the main page, the logout link is to logout from the application (which brings you back to the login page). In the background, the current weather conditions for the user-entered city will be retrieved from the Google weather service and a formatted weather report will be send by email. Submitting the form the first time initializes the application on Google App Engine which can take several seconds. Subsequent submissions (within a certain time frame) 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:
No Format |
---|
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.
Code walkthrough
The TutorialRouteBuilder
implements the message processing routes shown in the #Overview section. Input form data are received via the ghttp component. After receiving the request a RequestProcessor
adds the form data and information about the current user to a ReportData
POJO. The ReportData
object is then serialized and queued for background processing. Queueing messages on GAE is done with the gtask component. After adding the ReportData
object to the queue an HTML response is generated with the ResponseProcessor
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.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
package org.apache.camel.example.gae; import org.w3c.dom.Document; import org.apache.camel.Exchange; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.gae.mail.GMailBinding; import org.apache.camel.processor.aggregate.AggregationStrategy; public class TutorialRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from("ghttp:///weather") .to("gtask://default"process(new RequestProcessor()) .setHeadermarshal(Exchange.CONTENT_TYPE, constant("text/plain")).serialization() .to("gtask://default") .transformunmarshal(constant("Weather report will be sent to ").append(header("mailto")))).serialization() .process(new ResponseProcessor()); from("gtask://default") .unmarshal().serialization() .setHeader(Exchange.HTTP_QUERY, constant("weather=").append(header("city"ReportData.city())) .toenrich("ghttp://www.google.com/ig/api") .process(new WeatherProcessor()) , reportDataAggregator()) .setHeader(GMailBinding.GMAIL_SUBJECT, constant("Weather report")) .setHeader(GMailBinding.GMAIL_TOSENDER, header("mailto"ReportData.requestor()) .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
:
Code Block | |
---|---|
java | java |
title | WeatherProcessor.java | 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(); setHeader(GMailBinding.GMAIL_TO, ReportData.recipient()) .process(new ReportGenerator()) XPath xpath = xpfactory.newXPath(); // Extract result values via XPath String city = xpath.evaluateto("gmail://forecast_information/city/@data", docdefault"); String cond = xpath.evaluate("//current_conditions/condition/@data", doc);} private static String temp = xpath.evaluate("//current_conditions/temp_c/@data", doc); String msg = null;AggregationStrategy reportDataAggregator() { ifreturn (city != null && city.length() > 0new AggregationStrategy() { msgpublic =Exchange new StringBuffer() .append("\n").append("Weather report for: ").append(city) aggregate(Exchange reportExchange, Exchange weatherExchange) { ReportData reportData = reportExchange.appendgetIn("\n").append("Current condition: ").append(cond)getBody(ReportData.class); reportData.append("\n").append("Current temperature: ").append(temp).append(" (Celsius)").toString(setWeather(weatherExchange.getIn().getBody(Document.class)); } else { // create an error messagereturn reportExchange; 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.
Note | ||
---|---|---|
| ||
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:
No Format |
---|
Weather report for: London, England
Current condition: Klar
Current temperature: 12 (Celsius)
|
The report is partly internationalized, depending on the language settings of your browserBackground processing of the queued messages starts from("gtask://default")
. The first step is to enrich the previously generated ReportData
object with data from the Google weather service. The service URL is dynamically constructed by setting the Exchange.HTTP_QUERY
header to weather=<city>
. For example, if the user entered London
in the city field of the form the resulting URL is ghttp://www.google.com/ig/api?weather=London. The weather service is accessed with the ghttp component and the weather service response is aggregated into the ReportData
object using a custom aggregator returned by the reportDataAggregator()
method. Generating a simple weather report from ReportData
is done by the ReportGenerator
. The report is then sent by email with the gmail component.