7 min read

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


LEAVE A REPLY

Please enter your comment!
Please enter your name here