Name: microprofile-meeting
Owner: International Business Machines
Description: null
Created: 2017-09-22 15:24:33.0
Updated: 2017-09-22 17:32:50.0
Pushed: 2017-10-24 21:33:15.0
Homepage: null
Size: 197
Language: JavaScript
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
This sample application solves a real problem that the Liberty development team had. The globally-distributed Liberty development team has a lot of online meetings using IBM Connections Cloud Meetings. IBM Connections Cloud provides meeting rooms to individual employees, which is a problem for team meetings if the person who initially set up the meeting room can?t make it (e.g. they were called into another meeting, are on vacation, or sick). The sample application provides a single URL for a meeting, which can then be ?started? by one person and everyone else gets redirected.
The purpose of this lab is to go over using MicroProfile 1.0, so it will just cover the backend Java logic, and not the user interface, which is provided in GitHub.
Adapted from the blog post: Writing a simple MicroProfile application
Run the following commands:
git clone https://github.com/IBM/microprofile-meeting.git
https://github.com/IBM/microprofile-meeting.git
The first part of this application is a CDI-managed bean that manages the meetings. This bean is application-scoped, meaning there is only one instance. At this point the MicroProfile 1.0 release has not stated a preference for a persistence mechanism, so this example simply stores information in memory. This means that we need shared state, and an application-scoped bean ensures that state is shared by all clients.
meetings
project, then click New > Class?MeetingManager
, then click Finish.Eclipse brings up the MeetingManager
class in the Java editor. The first thing to do is make it a managed bean.
Above the class type definition add @ApplicationScoped
, which is in the package javax.enterprise.context
:
rt javax.enterprise.context.ApplicationScoped;
licationScoped
ic class MeetingManager {
Save the file.
The next step is to create a map to store the meeting information. There could be concurrent requests so we need to ensure a thread-safe data construct is used:
ate ConcurrentMap<String, JsonObject> meetings = new ConcurrentHashMap<>();
rt java.util.concurrent.ConcurrentHashMap;
rt java.util.concurrent.ConcurrentMap;
rt javax.json.JsonObject;
In this example a JsonObject
is being stored; this is defined by the JSON-Processing standard Java API. Many CRUD-style applications just need to take JSON input and store it away. While you might expect to convert the JSON to some form of object data type, in this application, that would be overkill so we just pass JsonObjects around. Taking this approach somewhat breaks the separation of concerns between business logic and protocol handling so do not take this as a best practice ? it?s just a convenient approach for this application.
The bean needs to have four different operations: Add a meeting, get a specific meeting, get all the meetings, and start a meeting. Some will be very simple. To add the operations:
ic void add(JsonObject meeting) {
meetings.putIfAbsent(meeting.getString("id"), meeting);
ic JsonObject get(String id) {
return meetings.get(id);
JsonArrayBuilder
to be returned:ic JsonArray list() {
JsonArrayBuilder results = Json.createArrayBuilder();
for (JsonObject meeting : meetings.values()) {
results.add(meeting);
}
return results.build();
JsonArray
, a JsonArrayBuilder
, and the Json
class. A JsonArray
represents an array of JsonObjects
. A JsonArrayBuilder
is used to create a JsonArray
. The Json
class provides utility methods for constructing the Json builder classes. Add the following imports:rt javax.json.JsonArray;
rt javax.json.JsonArrayBuilder;
rt javax.json.Json;
JsonObjectBuilder
and copies the entries across. In some cases (not used in this article) the clone may want to not have a field copied across; in that case a list of keys to ignore can be provided.ic static JsonObjectBuilder createJsonFrom(JsonObject user, String ... ignoreKeys) {
JsonObjectBuilder builder = Json.createObjectBuilder();
List<String> doNotCopy = Arrays.asList(ignoreKeys);
for (Map.Entry<String, JsonValue> entry : user.entrySet()) {
if (!!!doNotCopy.contains(entry.getKey())) {
builder.add(entry.getKey(), entry.getValue());
}
}
return builder;
JsonValue
and a JsonObjectBuilder
. A JsonValue
is the superclass of all the Json types. A JsonObjectBuilder
is used to create a JsonObject
. The code also uses the standard Java Collections API. Add the following imports:rt javax.json.JsonValue;
rt javax.json.JsonObjectBuilder;
rt java.util.Arrays;
rt java.util.List;
rt java.util.Map;
JsonObject
with the meeting ID and a URL for joining the meeting. To ensure the meeting is started, the meeting ID and URL are fetched from the input parameter meeting
. The JsonObject
for the existing meeting is fetched from memory as existingMeeting
. The existing meeting is then cloned using the helper method above and the meeting URL is added by calling add on the JsonObjectBuilder
returned from the helper method. A JsonObject
is then built from the builder. Finally, the meetings map has the meeting replaced assuming that the existing meeting is still bound. This ensures thread-safe updates so if two calls to startMeeting
run at once only one will win.ic void startMeeting(JsonObject meeting) {
String id = meeting.getString("id");
String url = meeting.getString("meetingURL");
JsonObject existingMeeting = meetings.get(id);
JsonObject updatedMeeting = MeetingsUtil.createJsonFrom(existingMeeting).add("meetingURL", url).build();
meetings.replace(id, existingMeeting, updatedMeeting);
The second part of this example is the JAX-RS service endpoint. This makes a REST API available externally via HTTP.
MeetingService
.Eclipse brings up the MeetingService
class in the Java editor. Most Java EE beans are automatically considered CDI-managed beans by default, but not JAX-RS beans. JAX-RS beans need to be annotated with a CDI scope to become CDI-managed. In this case we want the normal JAX-RS behaviour of a bean instance per request but we need it to be CDI managed. This can be done using the CDI request scope:
@RequestScoped
, which is in the package javax.enterprise.context
:rt javax.enterprise.context.RequestScoped;
uestScoped
public class MeetingService {
ave the file.
next step is to make the bean a JAX-RS bean. This is done using the JAX-RS Path annotation:
bove the class type definition add `@Path`, which is in the package `javax.ws.rs`. This takes a single value which is the default path used to access the JAX-RS resource that this bean will manage:
@Path(“meetings”) public class MeetingService {
his introduces the new type `Path` which needs to be imported:
import javax.ws.rs.Path;
ave the file.
meeting service needs two objects injected to perform its actual behaviour. The first is the `MeetingManager` class and the second is a JAX-RS class for managing URI processing:
ust after the class definition, add the code below. The `Inject` annotation tells CDI to inject the MeetingManager CDI bean. The `Inject` annotation is from the package `javax.inject`:
@Inject private MeetingManager manager;
mport the `Inject` annotation:
import javax.inject.Inject;
ext, add the code below. The `Context` annotation tells the JAX-RS runtime to inject the `UriInfo` object:
@Context private UriInfo info;
he `Context` annotation and `UriInfo` interface are in the `javax.ws.rs.core package` so import the `Context` and `UriInfo`:
import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo;
ave the file.
JAX-RS bean needs four methods to respond to the resource requests. JAX-RS uses annotations to work out which methods map to which operations. Although it is common for JAX-RS beans to receive information by annotated content, the JAX-RS specification only allows this when using XML via JAX-B. Every JAX-RS provider supports JSON binding to Java beans so, in general, this isn?t an issue but since this is using MicroProfile we are sticking to JSON-P which is the only required mapping of JSON to Java.
he method to add the operation is shown below. The `PUT` annotation says to call this method when the HTTP PUT method is called. The `Consumes` annotation tells it that the method expects JSON to be received. The method takes a `JsonObject`. It calls the `MeetingManager` service to add the meeting and then returns a 201 Created response with a link to the created resource.
@PUT @Consumes(MediaType.APPLICATION_JSON) public Response add(JsonObject m) {
manager.add(m);
UriBuilder builder = info.getBaseUriBuilder();
builder.path(MeetingService.class).path(m.getString("id"));
return Response.created(builder.build()).build();
}
he method introduces several new classes that need to be imported: `PUT` and `Consumes` are in package `javax.ws.rs`; `Response`, `MediaType`, and `UriBuilder` are all in `javax.ws.rs.core`; `JsonObject` is in the `javax.json package`. Take care when importing `MediaType` and `Response` as Java contains multiple classes with these names:
import javax.ws.rs.Consumes; import javax.ws.rs.PUT; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.json.JsonObject;
method to list all the meetings that exist is very simple. The `GET` annotation is used to indicate this will be called on an HTTP GET request and the `Produces` method indicates that JSON will be returned to the client:
@GET @Produces(MediaType.APPLICATION_JSON) public JsonArray list() {
return manager.list();
}
his method introduces two new classes that need to be imported: Both `GET` and `Produces` are in the `javax.ws.rs` package. Take care when importing `Produces` as Java EE contains multiple classes with this name:
import javax.ws.rs.GET; import javax.ws.rs.Produces; import javax.json.JsonArray;
o get the details of a single meeting we need a method that responds to a child path of the path provided on the class definition. This can be done using the `Path` annotation on the method. The `Path` value can contain either a literal or, in this case, a named entity that can then be passed in as a method parameter. The `PathParam` annotation is used on the method parameter to indicate which part of the path this parameter should be provided from.
@GET @Path(“{id}“) @Produces(MediaType.APPLICATION_JSON) public JsonObject get(@PathParam(“id”) String id) {
return manager.get(id);
}
his method introduces one new class that needs to be imported: `PathParam` is in the `javax.ws.rs` package. Take care when importing `PathParam` as Java EE contains multiple classes with this name:
import javax.ws.rs.PathParam;
inally, you need to write a method that starts the meeting. This method responds to an HTTP post which is indicated using the `POST` annotation. In this case it responds to a specific resource instance and, to ensure that the `JsonObject` and the path don?t have conflicting information, the JsonObject?s ID in the `JsonObject` is overwritten by the one from the path. In this application it is not important but if there were a security constraint on the URL, it could be crucial.
@POST @Path(“{id}“) @Consumes(MediaType.APPLICATION_JSON) public void startMeeting(@PathParam(“id”) String id, JsonObject m){
JsonObjectBuilder builder = MeetingsUtil.createJsonFrom(m);
builder.add("id", id);
manager.startMeeting(builder.build());
}
his method introduces one new class which needs to be imported: `POST` is in the `javax.ws.rs.package`:
import javax.ws.rs.POST; import javax.json.JsonObjectBuilder;
ave the file.
Step 4. Creating the MeetingApplication class
last step is to tell JAX-RS that this module should be treated as a JAX-RS application. There are a few ways to do this but the simplest is with a class:
reate a new class called `MeetingApplication` with the superclass `javax.ws.rs.core.Application`.
the class opens in the editor, add an annotation to tell the JAX-RS annotation where to dispatch requests to REST endpoints from:
efore the class definition add the `@ApplicationPath` annotation with a value of `"/rest/"`:
import javax.ws.rs.ApplicationPath;
@ApplicationPath(“/rest/“)
ave the file.
application is done and you are ready to run it.
can check that you?ve copied the code correctly by comparing the classes against the code in GitHub on the Master branch of the repository.
clipse, you might see some warnings, which you can ignore. For example, the HTML problems are because Eclipse doesn?t understand the AngularJS tags which are used to define the application?s UI.
Step 5. Running the application
Eclipse WDT
e are two ways to get the application running from within WDT:
he first is to use Maven to build and run the project:
Run the Maven `install` goal to build and test the project: Right-click **pom.xml** in the `meetings` project, click **Run As? > Maven Build?**, then in the **Goals** field type `install` and click **Run**. The first time you run this goal, it might take a few minutes to download the Liberty dependencies.
Run a Maven build for the `liberty:start-server goal`: Right-click **pom.xml**, click **Run As? > Maven Build...**, then in the **Goals** field, type `liberty:start-server` and click **Run**. This starts the server in the background.
Open the application, which is available at `http://localhost:9080/meetings/`.
To stop the server again, run the `liberty:stop-server` build goal.
he second way is to right-click the `meetings` project and select **Run As? > Run on Server** but there are a few things to note if you do this. WDT doesn?t automatically add the MicroProfile features as you would expect so you need to manually add those. Also, any changes to the configuration in `src/main/liberty/config` won?t be picked up unless you add an include.
out more about [MicroProfile and WebSphere Liberty](https://developer.ibm.com/wasdev/docs/microprofile/).
Bluemix
can run your application on Bluemix using Cloud Foundry.
Login to your Bluemix account
cf login
Push your application to Bluemix
$ cf push
your app has finished deploying, click the assigned route/url and make sure to add `/meetings` to the end to hit the home page your application.
ext Steps
2: [MicroProfile Meeting Application - Adding Persistence](https://github.com/IBM/microprofile-meeting-persistence)