Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

The Java Trove: Part I

unknown-1175939Aug 30 2016 — edited Oct 21 2016

by Andres Almiray

Part 1 in a series that explores libraries for improving Java developers' productivity and enhancing the development experience

Table of Contents
The Setup
Foundation
Handling REST Calls
Turbocharged POJOs
Consuming the Data
Event Handling
Tying Up Loose Ends
Conclusion
See Also
About the Author

Hello! Welcome to the first part of "The Java Trove" series. The Java landscape is covered with thousands of libraries and tools that make working in the Java space a much better experience. Some of these libraries are well known and used by many in their daily work; others have yet to break into the spotlight. The goal of this series is to have a positive impact on Java developers' productivity and enhance the development experience by presenting these libraries in the context of useful examples and shedding some light on the not-so-well-known libraries that abound.

It's worth noting that, like with many things in life, there are no silver bullets, nor is there a single, perfect way to reach a specific goal. The code examples presented in this series strive to be small enough to be comprehensible while still showing the power of the chosen libraries. That being said, the code examples should be seen as guides rather than canonical implementations that must be followed to the letter.

The Setup

We'll begin this adventure by attempting to solve the following problem: query a REST-based API and present the results in a JavaFX application. I've chosen JavaFX as the UI technology because it's very easy to get started with it, and also doing that keeps the whole application centered in Java-based technologies. However JavaFX is not the main concern for this article; thus, it will be kept to a minimum for now. Please let me know through your comments if you want to know more about the JavaFX internals. The full source code is also available under the GPL3 license at javatrove/github-api-01.

There are many openly available REST APIs to be found on the web these days. We'll pick GitHub's API because it's well documented and straightforward. There are many operations we can choose from. Some of them require authentication using either basic authentication or an OAuth2 token. We'll leave the authentication concern for another time, which means we'll pick a simple operation, for example, querying the available repositories for a particular organization. This API is explained here; basically, what you do is issue a GET call such as this:


And you get a JSON payload as the result, which looks similar to Listing 1:

[

{

"id": 1296269,

"owner": { /* omitted for brevity */ },

"name": "Hello-World",

"full_name": "octocat/Hello-World",

"description": "This your first repo!",

"html_url": "https://github.com/octocat/Hello-World",

/* many other properties follow */

},

/* additional repositories if they exist */

]

Listing 1. JSON payload

Results may be paginated if the queried organization contains more than 30 repositories. GitHub's API will automatically paginate the results, providing enough information in the response headers so they can be used to determine how to navigate to subsequent pages. For now, we'll parse the first page only, leaving pagination to the next installment in the series.

The application looks like Figure 1 and Figure 2:

figure-1.png

Figure 1. Freshly launched application

figure-2.png

Figure 2. Querying the "griffon" organization

The application can present a dialog box if for any reason an error occurs during the execution of the REST call, as shown in Figure 3.

figure-3.png

Figure 3. Error due to an unknown organization

In order to build this application, we have to tackle the following tasks at the very least:

  • Build an HTTP client that can query GitHub's API.
  • Parse JSON results into POJOs.
  • Issue the REST call on a background thread.
  • Notify the UI of new results, inside the UI thread.
  • Notify the user if an error occurs.

Let's get down to business.

Foundation

In this day and age, it's difficult to think of building an application without using dependency injection. Java has its own standard in this regard: JSR 330.There are many implementations available for this JSR, but we'll stick with Google Guice because it's the reference implementation. Guice provides an API that enables you to define bindings between types. Think of a binding as the basic unit of information that's required by the dependency injection (DI) container to determine whether it can resolve a dependency. Guice exposes five types of bindings:

  • Source to instance

    This binding is used when the value to be bound is already known. The binding is an effective singleton.

    
    
  • Source to target

    This binding is used when the type to be bound differs form the source type. You may define any scope.

bind(GitHub.class)
.to(GitHubImpl.class)
.in(Singleton.class);

   This binding is functionally equivalent to the instance binding shown earlier
  • Source to provider type

    You typically use providers to lazily initialize instances that may require additional dependencies. Providers must implement the javax.inject.Provider interface.

bind(GitHub.class)
.toProvider(GitHubProvider.class)
.in(Singleton.class);

This binding is also functionally equivalent to the instance binding shown earlier.

  • Source to provider

    You may also instantiate the provider before it's bound to the injector, for example:

bind(GitHub.class)

   .toProvider(new GitHubProvider())

   .in(Singleton.class);     
  • Constant value

    The last type of binding allows you to bind a constant value. This option is typically used to avoid relying on system properties, for example:

bindConstant()

 .annotatedWith(named(GithubAPI.GITHUB\_API\_URL\_KEY)) 

 .to("[https://api.github.com](https://api.github.com/)"); 

We'll use all these bindings in this application. But before we continue, we should have a word on how injection points can be defined. There are three ways to perform injection:

  • Define a constructor annotated with @javax.inject.Inject.
  • Define a private field annotated with @javax.inject.Inject.
  • Define a setter method annotated with @javax.inject.Inject.

Some prefer option #1, because it leads to immutable design. Others (like myself) prefer option #2, because it fits my coding style. Finally, others prefer option #3. Regardless of the chosen option, be aware that reflection is used by the DI container in order to inject dependencies into the target instance. It's up to you to pick an option that matches your programming style.

The last aspect we need to cover is proper lifecycle handlers for when instances are created and destroyed. Java defines the @PostConstruct and @PreDestroy annotations as part of JSR 250, which have been merged into JDK 7. The role of these annotations is to signal a DI container to perform further customizations when an instance has been fully initialized, for example, when its dependencies have been resolved and injected, and also when the instance is about to be discarded from the container. Unfortunately, Guice does not support these annotations out of the box. It's possible to add support for them if you know how the binding listeners API work. Fortunately for us, there's a library that extends Guice capabilities to deliver this behavior: [guice-ext-annotations](https://github.com/xvik/guice-ext-annotations).

Handling REST Calls

It's safe to say that, given the prevalence of REST APIs, many of us have implemented a Java-based client on top of a REST API at some point in our developer's career. If you look at the implementation from afar, it most likely behaves in the following manner:

  • Configure input parameters.
  • Configure request headers (if there are any).
  • Issue an HTTP call.
  • Parse response headers (was the call successful?).
  • Parse response body if the status message was successful (that is, the message status is equal to 200).
  • Parse errors, if there are any.

Given that those operations are repeated over and over, it would be great if there was a way to define a Java API on top of any REST API, wouldn't it? This is precisely what Retrofit delivers, plus a few more goodies, of course. In order to parse our chosen REST endpoint, we need to define a Java interface and create a Retrofit proxy, as shown in Listing 2:

public interface GithubAPI {

String GITHUB_API_URL_KEY = "GITHUB_API_URL";

@GET("/orgs/{organization}/repos")

Call<List<Repository>> repositories(@Path("organization") String organization);

}

Listing 2. GitHubAPI.java

In Listing 2, notice how concise the Java call turns out to be. We send in a parameterized URL and we get a java.util.List of results, mapped by a POJO of our own. We'll see how that POJO is defined shortly. The Retrofit proxy can be created as shown in Listing 3:

public class GithubAPIProvider implements Provider<GithubAPI> {

@Inject

@Named(GithubAPI.GITHUB_API_URL_KEY)

private String githubApiUrl;

@Inject

private ObjectMapper objectMapper;

@Override

public GithubAPI get() {

return new Retrofit.Builder()

.baseUrl(githubApiUrl)

.addConverterFactory(JacksonConverterFactory.create(objectMapper))

.build()

.create(GithubAPI.class);

}

}

Listing 3. GitHubAPIProvider.java

We use the provider approach to resolve dependencies before creating the proxy. Note that providers may also have dependencies injected into them. Retrofit can produce and consume data in many formats; I've selected Jackson as the JSON producer/consumer, because it's one of the most well-known JSON parsing libraries out there. The next piece of the puzzle is how the POJO is defined.

Turbocharged POJOs

You may already be thinking of a way to implement the Repository POJO based on the type of JSON response we saw earlier. POJOs follow a well-known structure and naming convention, which in our case is not fully compatible with the JSON properties that are returned in the results. We have to somehow find a way to map one world to another. Fortunately, Jackson provides a set of annotations to do just that.

But it's also true that we must generate getters/setters for each property, making sure the mapping annotations are placed at the right location. Given that we're building a simple example, for now it would be overkill to generate every single property that the JSON results deliver; it would be great if we can omit unmapped properties. It would also be great if we could let a tool write the boilerplate code for use (getters/setters). We could, of course, rely on the templating features of our favorite IDE; however, in this case I believe Lombok is better suited to take care of these concerns. Have a look at the Repository POJO when both Lombok and Jackson are combined, as shown in Listing 4:

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Repository implements Comparable<Repository> {
private String name;
private String description;
@Setter(onMethod = @__({@JsonProperty("full_name")}))
private String fullName;
@Setter(onMethod = @__({@JsonProperty("html_url")}))
private String htmlUrl;

 @Builder   
 public static Repository build(String name, String fullName, String description, String htmlUrl) {   
      Repository repository = new Repository();   
      repository.name = name;   
      repository.fullName = fullName;   
      repository.description = description;   
      repository.htmlUrl = htmlUrl; return repositor;   
 }    
   
 @Override   
 public String toString() {   
      return fullName;   
 }    
 @Override   
 public int compareTo(Repository other) {   
      if (other == null) { return 1; }   
      return name.compareTo(other.name);   
 }   

}

Listing 4. Repository.java

On the one hand, the Lombok @Data annotation is used as a hint to the compiler in order to generate getters/setters for each property, as well as suitable implementations for equals, hashCode, and toString. We use two Jackson annotations to instruct the JSON mapper that we want to ignore unmapped properties (those without getters/setters) and that some properties have a different name in their JSON format (full_name and html_url, respectively). The source code stays clean and concise, while at the same time the generated bytecode contains all the required behavior. Remember to enable annotation processing on your IDE when using Lombok in your source code.

Consuming the Data

We can consume data now that we know how it can be queried. Given that we're setting up a JavaFX application, we must make sure that the network call is invoked on a thread that's not the UI thread. There are many ways we can achieve this goal. Let's try a concept that has been very popular in the JavaScript world for the past few years: promises. A promise can be seen as the encapsulation of a series of callbacks that can act when data becomes available or if the operation encountered an error. JDeferred is a JQuery-inspired implementation of promises for Java. It's very popular in the Android community and delivers just enough of the behavior that we require to fulfill the task at hand.

We'll start by defining an interface that specifies the contract to be used by the data consumer, as shown in Listing 5.


Listing 5. Github.java

Let's inspect the parameterized types of the Promise returned by this API. The first type is the computed results: in this case, a List of Repository instances. The second type is the error that may result when a failure is encountered; you may use any type here such as non-Exception types. The third type is the intermediate result that the data handling code may produce. We'll make use of this type in a future installment of the series (when we revisit pagination) for now we just keep it simply and post no intermediate results, thus we use Void.

The implementation of this API is a simple as the code shown in Listing 6.

public class GithubImpl implements Github {

@Inject private GithubAPI api;

@Inject private DeferredManager deferredManager;

@Override

public Promise<Collection<Repository>, Throwable, Void> repositories(final String name) {

return deferredManager.when(() -> {

Response<List<Repository>> response = api.repositories(name).execute();

if (response.isSuccess()) { return response.body(); }

throw new IllegalStateException(response.message());

});

}

}

Listing 6. GitHubImpl.java

There are plenty of ways to obtain and configure a Promise instance. We make use of JDeferred's DeferredManager to create such an instance. Notice the usage of Java 8–style lambdas; the design of JDeferred is such that it supports functional interfaces with ease. This particular implementation returns the parse list of results if the REST call was successful, or it throws an exception if there was a failure.

The consumer of this API looks like Listing 7.

public class AppController {

@Inject private AppModel model;

@Inject private Github github;

@Inject private ApplicationEventBus eventBus;

public void loadRepositories() {

model.setState(RUNNING);

github.repositories(model.getOrganization())

.done(model.getRepositories()::addAll)

.fail(throwable -> eventBus.publishAsync(new ThrowableEvent(throwable)))

.always((state, resolved, rejected) -> model.setState(READY));

}

}

Listing 7. AppController.java

The AppController class is responsible for invoking the REST call and passing the parsed the results to the UI. It performs this task by relying on a couple of collaborators:

  • The model is used as an intermediate between the logic and the UI. This keeps the controller completely free from any UI concerns.
  • The github collaborator is the actual implementation of the REST call.
  • The eventBus is used to notify other components when a failure occurs.

First, the UI disables the Load button due to the change of state set in the model. Notice how easy is to chain behavior to the resulting Promise. The list of results is added to the model once the data is ready (note the usage of a method reference instead of a lambda expression). An event is posted on the eventBus in an asynchronous fashion in the case of a failure; the controller is free from handling the error and does not have to wait for the error handlers to finish processing the event. Finally, regardless of success of failure, the state of the application is changed back. This should signal the UI to re-enable the Load button.

Event Handling

The latest version of Guice has a dependency on Google Guava, which includes an EventBus implementation. We could have used that implementation, but I chose to go with MBassador instead, because it delivers better performance and a much friendlier API.

Basically what you do is define event classes for every event you need to publish/handle. We can use Lombok again, as shown in Listing 8 and Listing 9, given that these event classes can be implemented as simple POJOs:


Listing 8. ApplicationEvent.java


Listing 9. ThrowableEvent.java

Next we define our own EventBus subtype, as shown in Listing 10.


Listing 10. ApplicationEventBus.java

This is exactly the same type we used back in the AppController in order to publish an event. The final piece of the puzzle is how we handle events. One way to do it is to create a type that defines methods annotated with net.engio.mbassy.listener.Handler. Event handlers will be matched by their argument types.

public class ApplicationEventHandler {
@Inject private ApplicationEventBus eventBus;

 @PostConstruct   
 private void init() {   
      eventBus.subscribe(this);   

}
@PreDestroy
private void destroy() {
eventBus.unsubscribe(this);
}
@Handler
public void handleThrowable(ThrowableEvent event) {
Platform.runLater(() -> {
TitledPane pane = new TitledPane();
pane.setCollapsible(false);
pane.setText("Stacktrace");
TextArea textArea = new TextArea();
textArea.setEditable(false);
pane.setContent(textArea);

      ByteArrayOutputStream baos = new ByteArrayOutputStream();   
      event.getThrowable().printStackTrace(new PrintStream(baos));   
      textArea.setText(baos.toString());    

      Alert alert = new Alert(Alert.AlertType.ERROR);   
      alert.setTitle("Error");   
      alert.setHeaderText("An unexpected error occurred");   
      alert.getDialogPane().setContent(pane);   
      alert.showAndWait();   
 });   

}
}

Listing 11. ApplicationEventHandler.java

Now we see why we needed to include support for @PostConstruct and @PreDestroy. The ApplicationEventHandler class is smart enough to subscribe itself with the event bus once an instance has been fully initialized. It can also unsubscribe itself from the event bus when the time comes, typically during shutdown or when the injector is destroyed.

Tying Up Loose Ends

We've got the bulk of the application written. As mentioned earlier, the JavaFX particulars will not be covered for now, but we still need to explore how the application can be assembled and launched. We need to subclass javafx.application.Application and create an instance of a Guice Injector. The application class is quite trivial, given a good separation of concerns between the view, the model, and the logic.


Listing 12. Launcher.java

The Injector is created based on binding definitions provided by an AppModule class. Guice groups bindings in modules; many modules can be supplied to the injector factory. In our case, we make sure to complement the module with the necessary behavior to handle lifecycle handlers.

public class AppModule extends ExtAnnotationsModule {

public AppModule() {

super(AppModule.class.getPackage().getName());

}

@Override

protected final void configure() {

   super.configure();

   bindConstant()

        .annotatedWith(named(GithubAPI.GITHUB\_API\_URL\_KEY))

        .to("[https://api.github.com](https://api.github.com/)");

   bind(ExecutorService.class)

        .toInstance(Executors.newFixedThreadPool(1));

   bind(DeferredManager.class)

        .toProvider(DeferredManagerProvider.class)

        .in(Singleton.class);

   bind(Github.class)

        .to(GithubImpl.class)

        .in(Singleton.class);

   bind(ObjectMapper.class)

        .toProvider(ObjectMapperProvider.class)

        .in(Singleton.class);

   bind(GithubAPI.class)

        .toProvider(GithubAPIProvider.class)

        .in(Singleton.class);

   bind(ApplicationEventBus.class)

        .in(Singleton.class);

   bind(ApplicationEventHandler.class)

        .asEagerSingleton();

   bind(AppModel.class).in(Singleton.class);

   bind(AppController.class).in(Singleton.class);

   bind(AppView.class).in(Singleton.class);

}

}

Listing 13. AppModule.java

Of particular note is the fact that the binding for ApplicationEventHandler is marked as an eager singleton. This instructs the injector to create the instance as immediately as possible, even if no component defines a dependency on it. By declaring it as an eager singleton, the event handler will be automatically subscribed to the event bus once the application is launched. Remember that the full source code is available at javatrove/github-api-01.

Conclusion

And there we have it folks: a simple JavaFX application that can issue REST calls while honoring the proper UI threading rules of the UI toolkit. It might look like overkill to include so many dependencies for such a trivial application, but believe me when I say that once the number of features increases, you'll realize that putting all elements together using DI is a good thing.

Also, once the application starts to consume more and more REST endpoints, you'll need more POJOs, which means more boilerplate code that can be eliminated by leveraging Lombok and Jackson. Building HTTP clients is a repetitive and error-prone task; Retrofit makes it easy to get the task done. Finally, JDeferred and MBassador enable the execution of background threads, chaining behavior, and notifying data consumers at the appropriate time.

The next installment in this series will cover how we make sure we have implemented the code correctly. We'll discover some options to build unit, integration, and functional tests.

See Also

About the Author

Andres Almiray is a Java/Groovy developer and a Java Champion with more than 16 years of experience in software design and development. He has been involved in web and desktop application development since the early days of Java. Andres is a true believer in open source and has participated on popular projects such as Groovy, Griffon, and DbUnit, as well as starting his own projects (Json-lib, EZMorph, GraphicsBuilder, JideBuilder). He is a founding member of the Griffon framework and Hackergarten community event. Andres maintains a blog and you can reach him at @aalmiray.

Join the Conversation

Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!

Comments
Post Details
Added on Aug 30 2016
0 comments
29,638 views