INTSAMPLES-77 - Fix Travel Example

For reference see: https://jira.springsource.org/browse/INTSAMPLES-77

* Use MapQuest Traffic Web Service
* Improve User Experience + Polishing
* Add support for multiple cities
* Refactor the *XML prettification* of the results into its own transformer step
This commit is contained in:
Gunnar Hillert
2012-10-30 14:56:02 -04:00
parent 015c337969
commit ead4e94f70
13 changed files with 480 additions and 126 deletions

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
/**
* Enumeration that contains relevant information for various American cities,
* such as the postal code (ZIP Code) and the Latitude/Longitude information. Using
* this information, traffic and weather information can be retrieved.
*
* @author Gunnar Hillert
* @since 2.2
*
*/
public enum City {
ATLANTA(1, "Atlanta", "30334", "34.026784,-85.010794,33.471015,-83.765405"),
BOSTON(2, "Boston", "02201", "42.636182,-71.651862,42.080413,-70.467446"),
SAN_FRANCISCO(3, "San Francisco", "94102", "38.052886,-123.009856,37.497117,-121.82544");
private Integer id;
private String name;
private String boundingBox;
private String postalCode;
private City(Integer id, String name, String postalCode, String boundingBox) {
this.id = id;
this.name = name;
this.boundingBox = boundingBox;
this.postalCode = postalCode;
}
public String getBoundingBox() {
return boundingBox;
}
public String getPostalCode() {
return postalCode;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public static City getCityForId(Integer id) {
for (City city : City.values()) {
if (city.id.equals(id)) {
return city;
}
}
return null;
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
import java.util.Scanner;
import org.apache.log4j.Logger;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public final class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class);
/**
* Prevent instantiation.
*/
private Main() {}
/**
* @param args Not used.
*/
public static void main(String... args) throws Exception{
final GenericXmlApplicationContext context = new GenericXmlApplicationContext();
final ConfigurableEnvironment env = context.getEnvironment();
boolean mapQuestApiKeyDefined = env.containsProperty("mapquest.apikey");
if (mapQuestApiKeyDefined) {
env.setActiveProfiles("mapquest");
}
context.load("classpath:META-INF/spring/*.xml");
context.refresh();
final TravelGateway travelGateway = context.getBean("travelGateway", TravelGateway.class);
final Scanner scanner = new Scanner(System.in);
System.out.println("\n========================================================="
+ "\n "
+ "\n Welcome to the Spring Integration Travel App! "
+ "\n "
+ "\n For more information please visit: "
+ "\n http://www.springintegration.org/ "
+ "\n "
+ "\n=========================================================" );
System.out.println("Please select the city, for which you would like to get traffic and weather information:");
for (City city : City.values()) {
System.out.println(String.format("\t%s. %s", city.getId(), city.getName()));
}
System.out.println("\tq. Quit the application");
System.out.print("Enter your choice: ");
while (true) {
final String input = scanner.nextLine();
if("q".equals(input.trim())) {
System.out.println("Exiting application...bye.");
System.exit(0);
} else {
final Integer cityId = Integer.valueOf(input);
final City city = City.getCityForId(cityId);
final String weatherReply = travelGateway.getWeatherByCity(city);
System.out.println("\n========================================================="
+ "\n Weather:"
+ "\n=========================================================" );
System.out.println(weatherReply);
if (mapQuestApiKeyDefined) {
final String trafficReply = travelGateway.getTrafficByCity(city);
System.out.println("\n========================================================="
+ "\n Traffic:"
+ "\n=========================================================" );
System.out.println(trafficReply);
} else {
LOGGER.warn("Skipping Traffic Information call. Did you setup your MapQuest API Key? " +
"e.g. by calling:\n\n $ mvn exec:java -Dmapquest.apikey=\"your_mapquest_api_key_url_decoded\"");
}
System.out.println("\n========================================================="
+ "\n Done."
+ "\n=========================================================" );
System.out.print("Enter your choice: ");
}
}
}
}

View File

@@ -1,9 +1,32 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
/**
* Central business interface which is used to retrieve weather and traffic
* information for the provided {@link City}.
*
* @author Gunnar Hillert
* @since 2.2
*
*/
public interface TravelGateway {
public String getWeatherByZip(String zip);
public String getTrafficByZip(String zip);
public String getWeatherByCity(City city);
public String getTrafficByCity(City city);
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
import java.io.StringWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.springframework.xml.transform.StringSource;
/**
* Provides utility methods for the Travel application.
*
* @author Gunnar Hillert
* @since 2.2
*
*/
public final class TravelUtils {
/**
* Private constructor to prevent instantiation.
*/
private TravelUtils() {
}
/**
* Prettifies XML data, e.g. add indentation.
*
* @param result The XML to prettify
* @return Prettified XML
* @throws Exception
*/
public static String formatXml(String result) throws Exception{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(2));
StreamResult streamResult = new StreamResult(new StringWriter());
Source source = new StringSource(result);
transformer.transform(source, streamResult);
String xmlString = streamResult.getWriter().toString();
return xmlString;
}
}

View File

@@ -1,10 +1,25 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
public class WeatherRequestTransformer {
public String transform(String zipCode){
public String transform(City city){
return "<weat:GetCityWeatherByZIP xmlns:weat=\"http://ws.cdyne.com/WeatherWS/\">" +
" <weat:ZIP>" + zipCode + "</weat:ZIP>" +
" <weat:ZIP>" + city.getPostalCode() + "</weat:ZIP>" +
"</weat:GetCityWeatherByZIP>";
}
}

View File

@@ -1,36 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<gateway id="travelGateway"
service-interface="org.springframework.integration.samples.travel.TravelGateway"
default-request-channel="routingChannel">
<method name="getWeatherByZip">
<header name="REQUEST_TYPE" value="weather"/>
</method>
<method name="getTrafficByZip">
<header name="REQUEST_TYPE" value="traffic"/>
</method>
</gateway>
<publish-subscribe-channel id="routingChannel"/>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<header-value-router input-channel="routingChannel" header-name="REQUEST_TYPE">
<mapping value="weather" channel="weatherPreProcessChannel"/>
<mapping value="traffic" channel="trafficChannel"/>
</header-value-router>
<channel id="weatherPreProcessChannel"/>
<transformer input-channel="weatherPreProcessChannel" output-channel="weatherChannel">
<beans:bean class="org.springframework.integration.samples.travel.WeatherRequestTransformer"/>
</transformer>
<channel id="weatherChannel"/>
<channel id="trafficChannel"/>
</beans:beans>
<int:gateway id="travelGateway"
service-interface="org.springframework.integration.samples.travel.TravelGateway"
default-request-channel="routingChannel">
<int:method name="getWeatherByCity">
<int:header name="REQUEST_TYPE" value="weather"/>
</int:method>
<int:method name="getTrafficByCity">
<int:header name="REQUEST_TYPE" value="traffic"/>
</int:method>
</int:gateway>
<int:publish-subscribe-channel id="routingChannel"/>
<int:header-value-router input-channel="routingChannel" header-name="REQUEST_TYPE">
<int:mapping value="weather" channel="weatherPreProcessChannel"/>
<int:mapping value="traffic" channel="trafficChannel"/>
</int:header-value-router>
<int:channel id="weatherPreProcessChannel"/>
<int:transformer input-channel="weatherPreProcessChannel" output-channel="weatherChannel">
<bean class="org.springframework.integration.samples.travel.WeatherRequestTransformer"/>
</int:transformer>
<int:channel id="weatherChannel"/>
<int:channel id="trafficChannel"/>
<int:transformer input-channel="prettifyXml" expression="T(org.springframework.integration.samples.travel.TravelUtils).formatXml(payload)"/>
</beans>

View File

@@ -1,18 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<outbound-gateway id="trafficGateway"
url="http://local.yahooapis.com/MapsService/V1/trafficData?appid=YdnDemo&amp;zip={zipCode}"
request-channel="trafficChannel"
http-method="GET"
expected-response-type="java.lang.String">
<uri-variable name="zipCode" expression="payload"/>
</outbound-gateway>
</beans:beans>
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans profile="mapquest">
<int:chain id="trafficGatewayChain" input-channel="trafficChannel" output-channel="prettifyXml">
<int:header-enricher>
<int:header name="mapquestapikey" value="#{environment['mapquest.apikey']}"/>
</int:header-enricher>
<int-http:outbound-gateway
url="http://localhost:8084/traffic/v1/incidents?key={apikey}&amp;boundingBox={boundingBox}&amp;filters=construction,incidents&amp;inFormat=kvp&amp;outFormat=xml"
http-method="GET"
expected-response-type="java.lang.String">
<int-http:uri-variable name="apikey" expression="headers.mapquestapikey"/>
<int-http:uri-variable name="boundingBox" expression="payload.boundingBox"/>
</int-http:outbound-gateway>
</int:chain>
</beans>
</beans>

View File

@@ -1,18 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/ws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<header-enricher input-channel="weatherChannel" output-channel="weatherServiceChannel">
<soap-action value="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" />
</header-enricher>
<int-ws:header-enricher input-channel="weatherChannel" output-channel="weatherServiceChannel">
<int-ws:soap-action value="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" />
</int-ws:header-enricher>
<int:channel id="weatherServiceChannel" />
<outbound-gateway request-channel="weatherServiceChannel"
uri="http://ws.cdyne.com/WeatherWS/Weather.asmx" />
<int-ws:outbound-gateway request-channel="weatherServiceChannel" reply-channel="prettifyXml"
uri="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
</beans:beans>
</beans>

View File

@@ -1,45 +0,0 @@
package org.springframework.integration.samples.travel;
import java.io.StringWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.xml.transform.StringSource;
public class TravelDemo {
private static Logger logger = Logger.getLogger(TravelDemo.class);
/**
* @param args
*/
public static void main(String[] args) throws Exception{
ApplicationContext ac = new FileSystemXmlApplicationContext("src/main/resources/META-INF/spring/*.xml");
TravelGateway travelGateway = ac.getBean("travelGateway", TravelGateway.class);
String weatherReply = travelGateway.getWeatherByZip("10035");
logger.info("### Weather: \n" + formatResult(weatherReply));
String trafficReply = travelGateway.getTrafficByZip("10035");
logger.info("### Traffic: \n" + formatResult(trafficReply));
}
private static String formatResult(String result) throws Exception{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(2));
StreamResult streamResult = new StreamResult(new StringWriter());
Source source = new StringSource(result);
transformer.transform(source, streamResult);
String xmlString = streamResult.getWriter().toString();
return xmlString;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.travel;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* This test will test the weather service (does not require an API key).
*
* @author Gunnar Hillert
* @since 2.2
*
*/
@ContextConfiguration({
"classpath:META-INF/spring/integration-context.xml",
"classpath:META-INF/spring/integration-ws-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TravelGatewayTest {
private static final Logger LOGGER = Logger.getLogger(TravelGatewayTest.class);
@Autowired
private TravelGateway travelGateway;
@Test
public void testGetWeatherByCity() {
final String weatherInformation = travelGateway.getWeatherByCity(City.ATLANTA);
LOGGER.info("Weather information for Atlanta:\n\n" + weatherInformation + "\n\n");
Assert.assertNotNull(weatherInformation);
}
}