Building Your Next Microservice With Eclipse MicroProfile
This quick tutorial will show you how to build your next microservice with the recently updated Eclipse MicroProfile APIs.
Join the DZone community and get the full member experience.
Join For FreeEclipse MicroProfile has been gaining a lot of attention recently with the latest release, 1.2, as well as recent new additions, including Oracle. This is a quick tutorial for building your next microservice with the MicroProfile APIs.
MicroProfile is built from core JavaEE technologies:
While adding to them a set of specifications that make your microservices ready for the cloud including:
These specifications together make up Eclipse MicroProfile 1.2, the third release of the APIs. So how do you use all of this? Since this is only a specification and not an implementation, you'll need to check vendor-specific requirements, but this is a quick guide to building your first application. In many cases, you'll continue to deploy a WAR file like you would with JavaEE. First, add the MicroProfile dependency to your project.
Maven:
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>1.2</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
Gradle:
dependencies {
compileOnly 'org.eclipse.microprofile:microprofile:1.2'
}
This one dependency brings in all of the needed APIs to build your application, however, you may need to consult with your server's runtime to see if other dependencies are required. So what would a typical service look like?
A JAX-RS Controller. Since we're exposing a REST API, we want a controller to handle the API calls.
A Service of some kind. You need some backing component to generate or consume data. We're going to be using some mock data for now, just to explain the paradigm.
Monitoring. We want to know how often this service is invoked, and how long each request takes.
Configurability. We don't want the client specifying the data volume, we want to do it declaratively.
Security. Need both declarative and business logic driven security to know how to respond to requests.
Fault Tolerance. We care about any services we consume and ensuring we can fail fast or be resilient.
First, we have our rest controller, which should look very familiar.
@Path("/api/books") // just a basic JAX-RS resource
@Counted // track the number of times this endpoint is invoked
public class BooksController {
@Inject //use CDI to inject a service
private BookService bookService;
@GET
@RolesAllowed("read-books")
// uses common annotations to declare a role required
public Books findAll() {
return bookService.getAll();
}
}
If we dive in further to the service, we can start to see how configurability works:
@ApplicationScoped
public class BookService {
@Inject
// JPA is not provided out of the box, but most providers support it at
// some level. worst case, create your own producer for the field
private EntityManager entityManager;
@Inject
// use configuration to control how much data you want to supply at
// a given time
@ConfigProperty(name = "max.books.per.page", defaultValue = "20")
private int maxBooks;
public Books getAll() {
List < Book > bookList = entityManager
.createQuery("select b from Book b", Book.class)
.setMaxResults(maxBooks) // use that configuration to do a paginated look up
.getResultList();
return new Books(bookList);
}
}
Next, let's suppose we also want to handle the creation of books, the publication process. A service to support that may look like this:
@RequestScoped
public class PublishBookService {
@Inject
// we can inject a JsonWebToken, a Principal specific to the JWT specification
private JsonWebToken jsonWebToken;
// we could also inject individual ClaimValue objects.
@Inject
private AuthorService authorService;
@Inject
private EntityManager entityManager;
@Timeout(500)
// we want to limit how long it takes to publish and if it
// exceeds, return an exception to the caller.
public BookId publish(PublishBook publishBook) {
// we check the standard claim of subject
if (!publishBook.getAuthor().equals(jsonWebToken.getSubject())) {
// as well as a custom claim as a boolean
boolean createAny = jsonWebToken.getClaim("create.books.for.other.authors");
if (!createAny) {
throw new NotAuthorizedException("Cannot create book, wrong author");
}
}
Author author = authorService.findAuthor(publishBook.getAuthor());
if (author == null) {
throw new NotAuthorizedException("The list author is not an author");
}
Book book = entityManager.merge(new Book(publishBook.getIsbn(),
publishBook.getAuthor()));
return new BookId(book.getIsbn(), book.getAuthor());
}
}
The last part, if we consider that managing authors is a separate bounded context, we want that to be represented as a discreet service. As a result, we want a client to that remote server to check that the author exists.
@ApplicationScoped
public class AuthorService {
@Inject //inject a configuration property for the URL to the remote service
@ConfigProperty(name = "author.service.url")
private String authorUrl;
private ConcurrentMap < String, Author > authorCache = new ConcurrentHashMap < > ();
@Retry
// indicate that this should trigger a retry in case the remote server
// is unavailable or at capacity
@CircuitBreaker
// We also wrap the invocation in a circuit breaker, it will eventually
@Fallback(fallbackMethod = "getCachedAuthor")
// indicate that we should fallback to the local cache
public Author findAuthor(String id) {
// in MicroProfile 1.3 we'll be including a type safe rest client,
// to make this setup even easier!
Author author = ClientBuilder.newClient()
.target(authorUrl)
.path("/{id}")
.resolveTemplate("id", id)
.request(MediaType.APPLICATION_JSON)
.get(Author.class);
// ideally we want to read from the remote server. However, we can build
// a cache as a fallback when the server is down
authorCache.put(id, author);
return author;
}
public Author getCachedAuthor(String id) {
return authorCache.get(id);
}
}
So there you have it! A couple of rest controllers, services, and you have a microservice built with Eclipse MicroProfile to manage books. You can download this sample code on GitHub.
Published at DZone with permission of John Ament. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Logging Best Practices Revisited [Video]
-
What Is Test Pyramid: Getting Started With Test Automation Pyramid
-
A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage
-
Build a Simple Chat Server With gRPC in .Net Core
Comments