Connecting to a web service (Should know)

Exclusive offer: get 50% off this eBook here
Instant Spring Tool Suite [Instant]

Instant Spring Tool Suite [Instant] — Save 50%

A practical guide for kick-starting your Spring projects using the Spring Tool Suite IDE with this book and ebook

£8.99    £4.50
by Geoff Chiang | September 2013 | Open Source

This article by Geoff Chiang the author of Instant Spring Tool Suite is aiming to get you up and running developing with various Spring technologies, providing you with enough to give you a head start in Spring development.

The term web service is a nebulous one, encompassing a range of both technologies and approaches. At its heart, a web service is simply a software service which is accessible over a network, designed for machine-to-machine interaction. Early web services were based on the W3C WS-* set of standards, with interfaces described using WSDL and interaction using SOAP messages. This approach seems to have fallen out of favor somewhat, with lightweight RESTful approaches now prevalent.

In this recipe, we're going to retrieve weather data from the Australian Bureau of Meteorology. This service exposes Australian weather data in JSON format. As we're about to see, STS and Spring Integration allow us to interact with web services with very little effort.

(For more resources related to this topic, see here.)

Getting ready

This recipe is going to log a large JSON message to the console, so large that it will actually overflow STS's default console buffer. To ensure that we don't miss anything, we're going to change STS's configuration to have an unlimited console buffer. Open the Preferences dialog by selecting Window | Preferences from the menu if you are running Windows, or by pressing ?+, if you are using a Mac. Open the Run/Debug item and select the Console item. Uncheck the Limit console output checkbox, and then click on the OK button.

How to do it...

Let's start by creating our project and updating its dependencies.

  1. Launch the New Spring Project wizard by navigating to File | New | Spring Project in the menu.

  2. In the dialog box, enter the project name WebServiceProject, expand the Integration folder, and select Spring Integration Project (Standalone) – Simple. Click on the Next button. Allow STS to download the project template if you are prompted to do so.

  3. Enter the groupId as com.example, artifactId as web-service-project, and top-level package as com.example.webserviceproject. Click on the Finish button to create the project.

  4. Open the Maven pom.xml file. Click on the Dependencies tab, and add the following dependencies:

    • org.springframework.integration : spring-integrationhttp : ${spring.integration.version}

    • org.springframework : spring-test : 3.1.3.RELEASE (Spring Integration 2.2.4.RELEASE depends on this version of Spring, and it's a good idea to be consistent)

    • org.codehaus.jackson : jackson-core-asl : 1.9.12

    • org.codehaus.jackson : jackson-mapper-asl : 1.9.12

    Save the file to update the project's dependencies.

Now let's create an integration test.

  1. Expand the src/test/java source folder, right-click on the top-level package under it, and select New | JUnit Test Case from the pop-up menu. Name the test WebServiceTest, and click on the Finish button.

  2. We're creating a Spring context test, so add the following annotations to the top of the class:

    @ContextConfiguration(locations = "classpath:
    META-INF/spring/integration/
    spring-integration-context.xml")
    @RunWith(SpringJUnit4ClassRunner.class)

  3. Our test is going to send a Spring Integration message down one channel and then wait for a response on another channel. We need to inject the following two channels into our test:

    @Autowired private MessageChannel weatherRequest;
    @Autowired private SubscribableChannel weatherResponse;

  4. Create the following test case:

    @Test
    public void shouldRetrieveWeatherDataFromWebService() {
    weatherResponse.subscribe(new MessageHandler() {
    @Override
    public void handleMessage(Message<?> message)
    throws MessagingException {
    Map payload = (Map) message.getPayload();
    Map observations = (Map) payload.get("observations");
    List data = (List) observations.get("data");
    Map latestData = (Map) data.get(0);
    assertNotNull(latestData);
    assertEquals("Sydney - Observatory Hill",
    latestData.get("name"));
    assertNotNull(latestData.get("air_temp"));
    assertNotNull
    (latestData.get("local_date_time_full"));
    }
    });
    weatherRequest.send(MessageBuilder.withPayload
    (new WeatherRequest("N", "94768")).build());
    }

  5. Add all required imports using Quick Fixes. The instantiation of the WeatherRequest object will cause a compilation error because the class does not yet exist. Use a Quick Fix to create the class, taking care to change its source folder to WebServiceProject/src/main/java. Create the class as follows:

    package com.example.webserviceproject;
    public class WeatherRequest {
    private final String stateCode;
    private final String locationId;
    public WeatherRequest
    (String stateCode, String locationId) {
    this.stateCode = stateCode;
    this.locationId = locationId;
    }
    public String getStateCode() {
    return stateCode;
    }
    public String getLocationId() {
    return locationId;
    }
    }

  6. Run the test. It will fail, complaining that no Spring beans could be injected into our test.

We have our failing test. Now it's time to create our integration pipeline.

  1. Open spring-integration-context.xml (found in the src/main/ resources/META-INF/spring/integration folder). Click on the Namespaces tab. You will find that the int namespace is already checked. Check the int-http namespace as well.

  2. Click on the integration-graph tab and maximize the editor to give yourself more space. This template project has already created an integration pipeline for us, but we want to create our own, so select all of the components in the workspace area by dragging a box around them, and then press the Delete key to remove them.

  3. Expand the Channels category and drag a channel component into the workspace. Double-click on it to bring up its properties in the Properties view. Change its id to weatherRequest.

    Remember to close the Properties view after editing the properties of each integration component.

  4. Drag another channel component into the workspace, and use the Properties view to set its id to weatherServiceResponse.

  5. Drag a publish-subscribe-channel component into the workspace. Give it the id weatherResponse.

  6. Drag a logging-channel-adapter component into the workspace. Give it the id logger, set its level to INFO, and its logger-name to com.example. webserviceproject.webserviceresponse.

  7. Now expand the http category and drag an outbound-gateway component into the workspace. Set its id property to weatherService, its url property to http://www.bom.gov.au/fwo/ID{stateCode}60901/ ID{stateCode}60901.{locationId}.json, its http-method to GET, and expected-response-type to java.lang.String.

  8. Expand the Transformation category and drag a json-to-object-transformer component into the workspace. Set its type property to java.util.HashMap.

  9. Now we need to connect everything together. Select the connection tool from the palette and make the following connections:

    • weatherRequest channel to weatherService gateway

    • weatherService gateway to weatherServiceResponse channel

    • weatherServiceResponse channel to json-to-object-transformer

    • json-to-object-transformer to weatherResponse channel

  10. Select the mapping/recipient/wire-tap tool from the palette and create a connection from the weatherServiceResponse channel to the logging-channel-adapter channel. Our pipeline should now look like the following figure:

  11. Select the integration tab. In the Integration Overview panel, expand all of the beans and then right-click on the int-http:outbound-gateway bean. Select Insert <int-http:uri-variable> element from the pop-up menu. This will create a new sub element and show its properties on the right side of editor. Set its name property to stateCode and its expression to payload.stateCode. Create another uri-variable property, setting its name to locationId, and its expression to payload.locationId.

  12. And we're done! Save the file, then re-run the WebServiceTest. If all was configured correctly, the test will take a few seconds to run (it has to contact the web service), and then you should be rewarded with a green bar. Maximize the Console view, and you will find the JSON string returned from the web service.

How it works...

In this recipe, we used a few different Spring Integration components to make a request to a web service and to transform its response to a Map class so that our test could pick out pieces of data. We also used a wire tap component to log messages to the console.

Channels

We have seen a couple of different channel implementations in action in this recipe. The weatherRequest and weatherService channels are examples of the default Spring Integration channel, DirectChannel , which is a simple point-to-point channel (that is, there is one sender on one side of the channel and one receiver on the other).

By contrast, the weatherRequest channel is a PublishSubscribeChannel , which allows for multiple receivers to register for message events (in our test, we only had the one).Spring Integration provides a number of other channel types designed for different requirements.

See the Spring Integration Reference Manual ( http://static.springsource.org/spring-integration/reference/htmlsingle ) for further information.

URI variables

You would have noticed when setting the URL property of the HTTP outbound gateway that the value included two placeholders, namely {stateCode} and {locationId}. We configured a URL variable for each of these, giving them the values payload.stateCode and payload.locationId respectively. These are simply JavaBean property references. In our case, the payload was a WeatherRequest instance, and we had defined both the stateCode and locationId JavaBean properties on that class. The values of those properties are substituted into the placeholders in the configured URL string to determine the actual URL requested.

Wire taps and logging

When we connected the weatherServiceResponse channel with logging-channel-adapter, under the hood, STS configured a special type of channel interceptor called a wire tap. A wire tap takes messages passing through a channel, and sends a copy to another channel. This makes them excellent for logging and auditing purposes.

When we configured our logging-channel-adapter , we told it to log at the INFO level using the logger name com.example.webserviceproject.webserviceresponse .

We could have used any name, of course, but the log4j configuration provided in the project template (the log4j.xml file under src/main/resources ) already had an INFO level logging threshold configured for com.example.webserviceproject loggers, so it was convenient for us to choose the logger name that we did.

Summary

In this article, we retrieved weather data from the Australian Bureau of Meteorology and also learned about how STS and Spring Integration allow us to interact with web services with very little effort.

Resources for Article :


Further resources on this subject:


Instant Spring Tool Suite [Instant] A practical guide for kick-starting your Spring projects using the Spring Tool Suite IDE with this book and ebook
Published: September 2013
eBook Price: £8.99
See more
Select your format and quantity:

About the Author :


Geoff Chiang

Geoff Chiang is a software professional with over a decade of experience. He started his career in consulting with Accenture, before deciding that the glamorous life of a software developer was the path for him. Almost all of his work has been within the Java ecosystem, and he has been using the Spring Framework and many of the Spring technologies since 2004. He has worked in various financial domains, and is currently shifting payments paradigms at Tyro Payments.

In his free time, Geoff enjoys walking around Sydney, listening to classical music, and imbibing good red wine.

Books From Packt


Spring Web Services 2 Cookbook
Spring Web Services 2 Cookbook

Spring Roo 1.1 Cookbook
Spring Roo 1.1 Cookbook

Spring 2.5 Aspect Oriented Programming
Spring 2.5 Aspect Oriented Programming

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Spring Persistence with Hibernate
Spring Persistence with Hibernate

Spring Security 3
Spring Security 3

Instant Spring for Android Starter [Instant]
Instant Spring for Android Starter [Instant]

Spring Security 3.1
Spring Security 3.1


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software