Over a million developers have joined DZone.

"Bootiful" Java EE Support in Spring Boot 1.2

· Java Zone

Navigate the Maze of the End-User Experience and pick up this APM Essential guide, brought to you in partnership with CA Technologies

Originally written by Josh Long  on the SpringSource blog.

In this blog, I want to look at - and demonstrate - some of the many new features in Spring Boot 1.2 that make the lives of those coming from, or otherwise building on, Java EE easier.

It's worth mentioning that a lot of this support has been possible with Spring before, of course, but now with Spring Boot 1.2, it's just so darned easy!

First, here's an example program with notes after.

package demo;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsTemplate;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSException;
import javax.persistence.*;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.Serializable;
import java.util.Collection;
import java.util.logging.Logger;

@SpringBootApplication
public class Application {

  @Named
  public static class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
      this.register(GreetingEndpoint.class);
      this.register(JacksonFeature.class);
    }
  }

  @Named
  @Transactional
  public static class GreetingService {

    @Inject
    private JmsTemplate jmsTemplate;

    @PersistenceContext
    private EntityManager entityManager;

    public void createGreeting(String name, boolean fail) {
      Greeting greeting = new Greeting(name);
      this.entityManager.persist(greeting);
      this.jmsTemplate.convertAndSend("greetings", greeting);
      if (fail) {
        throw new RuntimeException("simulated error");
      }
    }

    public void createGreeting(String name) {
      this.createGreeting(name, false);
    }

    public Collection<Greeting> findAll() {
      return this.entityManager
      .createQuery("select g from " + Greeting.class.getName() + " g", Greeting.class)
      .getResultList();
    }

    public Greeting find(Long id) {
      return this.entityManager.find(Greeting.class, id);
    }
  }

  @Named
  @Path("/hello")
  @Produces({MediaType.APPLICATION_JSON})
  public static class GreetingEndpoint {

    @Inject
    private GreetingService greetingService;

    @POST
    public void post(@QueryParam("name") String name) {
      this.greetingService.createGreeting(name);
    }

    @GET
    @Path("/{id}")
    public Greeting get(@PathParam("id") Long id) {
      return this.greetingService.find(id);
    }
  }

  @Entity
  public static class Greeting implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Override
    public String toString() {
      return "Greeting{" +
      "id=" + id +
      ", message='" + message + '\'' +
      '}';
    }

    private String message;

    public String getMessage() {
      return message;
    }

    public Greeting(String name) {
      this.message = "Hi, " + name + "!";
    }

    Greeting() {
    }
  }

  @Named
  public static class TestCommandLineRunner {

    @Inject
    private GreetingService greetingService;

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
      greetingService.createGreeting("Phil");
      greetingService.createGreeting("Dave");
      try {
        greetingService.createGreeting("Josh", true);
        } catch (RuntimeException re) {
          Logger.getLogger(Application.class.getName()).info("caught exception...");
        }
        greetingService.findAll().forEach(System.out::println);
      }
    }

    @Named
    public static class MessageProcessor {

      @JmsListener(destination = "greetings")
      public void processGreeting(Greeting greeting) throws JMSException {
        System.out.println("received message: " + greeting);
      }
    }

    public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
    }
  }

The rest of the code, application.properties and the Maven build, are available online.

JAX-RS with Jersey

The example demonstrates JAX-RS integration (in this case, using Jersey 2.x) in thedemo.Application.GreetingEndpoint. Note how convenient it is to get it all working! The only thing to be weary of is that you need to specify a ResourceConfig subclass to let Jersey know which components to register.

Global Transactions with JTA

It demonstrates global transactions (a.k.a., XA) with the new auto-configured JTA support. To do this, we've used the Atomikos standalone JTA provider. We could have as easily used Bitronix, as well; both are auto-configured if you bring the appropriate starter along. In this example, in the GreetingService, JMS and JPA work is done as part of a global transaction. We demonstrate this by creating 3 transactions and simulating a rollback on the third one. You should see printed to the console that there are two records that come back from the JDBCjavax.sql.DataSource data source and two records that are received from the embedded JMS javax.jms.Destination destination.

The Undertow embedded web-server

This example also uses the Wildfly (from RedHat) application server's awesome Undertow embedded HTTP server instead of (the default) Apache Tomcat. It's as easy to use Undertow as it is to use Jetty or Tomcat - just exclude org.springframework.boot:spring-boot-starter-tomcat and add org.springframework.boot:spring-boot-starter-undertow! This contribution originated as a third-party PR - thanks Ivan Sopov! It's awesome.

Odds and Ends

Just for consistency, the example also uses JSR 330. JSR 330 describes a set of annotations that you can use in proprietary application servers like WebLogic as well as in a portable manner in dependency injection containers like Google Guice or Spring. I also use a JSR 250 annotation (defined as part of Java EE 5) to demonstrate lifecycle hooks.

This example relies on a Spring Boot auto-configured embedded, in-memory H2javax.sql.DataSource and - a Spring Boot auto-configured embedded, in-memoryHornetQ javax.jms.ConnectionFactory. If you wanted to connect to non-embedded instances, it's straightforward to define beans that will be picked up instead.

This example also uses the new @SpringBootApplication annotation which combines@Configuration@EnableAutoConfiguration and @ComponentScan. Nice!

Deployment

Though I'm using a lot of fairly familiar Java EE APIs, this is still just typical Spring Boot, so by default you can run this application using java -jar ee.jar or easily deploy it to process-centric platforms-as-a-service offerings like - Heroku or Cloud Foundry. If you want to deploy it to a standalone application server like (like Apache Tomcat, or Websphere, or anything in between), it's straightforward to convert the build into a .war and deploy it accordingly to any Servlet 3 container.

If you deploy the application to a more classic application server, Spring Boot can take advantage of the AS's facilities, instead. For example, it's dead-simple to consume a JNDI-bound JMS ConnectionFactoryJDBC DataSource or JTA UserTransaction.

Spring Boot 1.2: Choice and Power

I, personally, would question a lot of these APIs. Do you really need distributed, multi-resource transactions? In today's distributed world, consider global transaction managers an architecture smell. Do you really need to use JAX-RS when Spring offers a richer, integrated Spring MVC-based stack complete with MVC, REST, HATEOAS, OAuth and websockets support? It might well be that you do, and - as always - the choice is yours. That's why this release is so cool! More power, more choice.

What Else?

lot, actually. There are a slew of new features. I couldn't even begin to cover them all here. So I won't try. Check out the release notes for the full scoop!

Spring Boot 1.2 is fast approaching GA, and now's a very good time to try the bits, kick the tiresfile issues and ask questions!

Thrive in the application economy with an APM model that is strategic. Be E.P.I.C. with CA APM.  Brought to you in partnership with CA Technologies.

Topics:

Published at DZone with permission of Pieter Humphrey, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}