编写简单的 MicroProfile 应用程序,第 1 部分

在 Open Liberty 上设置 MicroProfile 会议应用程序

使用 MicroProfile 1.0 创建一个简单的会议协调应用程序

Comments

系列内容:

此内容是该系列 4 部分中的第 # 部分: 编写简单的 MicroProfile 应用程序,第 1 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:编写简单的 MicroProfile 应用程序,第 1 部分

敬请期待该系列的后续内容。

此样本应用程序可解决 Liberty 开发团队遇到的实际问题。全球分布式 Liberty 开发团队使用 IBM Connections Cloud Meetings 召开大量在线会议。IBM Connections Cloud 为个别员工提供会议室,如果最初设置会议室的人员无法操作(例如,被邀请参加另一个会议、休假或生病),这对团队会议来说是一个问题。样本应用程序为会议提供单个 URL,然后可以由一个人“启动”,并且让其他所有人都重定向。

本教程的目的是介绍如何使用 MicroProfile 1.0,因此它将只涵盖后端 Java 逻辑,而不涉及 GitHub 中提供的用户界面。

改编自博客文章:“编写简单的 MicroProfile 应用程序”。

前提条件

  • Eclipse IDE for Web Developers:运行安装程序并选择 Eclipse IDE for Java EE Developers。

    注意:这些步骤在 Linux 和 Liberty Developer Tools 18.0.0.3 上运行的 2018-09 版 Eclipse 上进行了测试。

    注意:如果遇到类似于Could not initialize class org.codehaus.plexus.archiver.jar.JarArchiver 的错误消息,可参阅“故障诊断”部分。

  • IBM Liberty Developer Tools (WDT)。
    1. 启动 Eclipse。
    2. 启动 Eclipse Marketplace:Help -> Eclipse Marketplace
    3. 搜索 IBM Liberty Developer Tools,在已选中默认配置的情况下单击 Install
  • Git
  • 安装 IBM Cloud CLI

步骤

1 查看源代码

  1. 从命令行运行以下命令:
    git clone https://github.com/IBM/microprofile-meeting.git
  2. 在 Eclipse 中,将项目作为现有项目导入。
    1. 在 Eclipse 中,切换至 Git 透视图。
    2. 单击 Git Repositories 视图中的 Clone a Git repository
    3. 输入 URI:https://github.com/IBM/microprofile-meeting.git
    4. 单击 Next,然后再次单击 Next,接受默认值。
    5. Initial branch 下拉列表中,单击 master
    6. 选择 Import all existing Eclipse projects after clone finishes,然后单击 Finish
    7. 切换至 Java EE 透视图。
    8. 在 Project Explorer 视图中会自动创建会议项目。

2 创建 MeetingManager 类

此应用程序的第一部分是用于管理会议的 CDI 托管 bean。该 bean 是应用程序范围的,意味着只有一个实例。此时,MicroProfile 1.0 发行版尚未声明对持久性机制的偏好,因此该示例仅将信息存储在内存中。这意味着我们需要共享状态,而应用程序范围的 bean 可确保所有客户共享此状态。

  1. 创建新类:右键单击 meetings 项目,然后单击 New > Class…
  2. 将此类命名为 MeetingManager,然后单击 Finish

注意:将在与 MeetingsUtil.java 相同的包下创建所有新类。

Eclipse 将在 Java 编辑器中显示 MeetingManager 类。首先要做的是使它成为托管 bean。

  1. 在类的类型定义上方添加 @ApplicationScoped,它位于包 javax.enterprise.context 中:
    import javax.enterprise.context.ApplicationScoped;
       @ApplicationScoped
       public class MeetingManager {
  2. 保存该文件。

下一步是创建一个映射来存储会议信息。可能存在并发请求,因此我们需要确保使用线程安全的数据构造:

  1. 只需在类定义之后添加以下代码:
    private ConcurrentMap<String, JsonObject> meetings = new ConcurrentHashMap<>();
  2. 此代码将引入三个新类,所有这些类都必须导入。相关导入为:
    import java.util.concurrent.ConcurrentHashMap;
       import java.util.concurrent.ConcurrentMap;
       import javax.json.JsonObject;
  3. 保存该文件。

在此示例中,将存储 JsonObject;这是通过 JSON-Processing 标准 Java API 来定义的。许多 CRUD 风格的应用程序只需要获取 JSON 输入,并将其存储起来。虽然您可能希望将 JSON 转换为某种形式的对象数据类型,但在此应用程序中,这将是过分之举,所以我们只是传递 JsonObjects。采用这种方法在某种程度上不利于分离业务逻辑和协议处理间的关注点,因此不要将其视为最佳做法,这只是该应用程序的一种便捷方法。

该 bean 需要有四种不同的操作:添加会议、获取特定会议、获取所有会议以及启动会议。一些操作可能非常简单。可如下所述添加操作:

  1. 添加一个方法以添加新会议。这将使用 JSON id 属性存储会议。只存储一次,因此后续调用基本上会被忽略。
    public void add(JsonObject meeting) {
           meetings.putIfAbsent(meeting.getString("id"), meeting);
       }
  2. 添加一个方法以获取会议:
     public JsonObject get(String id) {
           return meetings.get(id);
       }
  3. 添加一个方法以获取所有会议。这是第二个最复杂的操作。基本上会循环先前创建的映射中的所有值,并将其添加到要返回的 JsonArrayBuilder
    public JsonArray list() {
           JsonArrayBuilder results = Json.createArrayBuilder();
    
           for (JsonObject meeting : meetings.values()) {
               results.add(meeting);
           }
    
           return results.build();
       }
  4. 此方法将引入三种新类型:JsonArrayJsonArrayBuilderJson 类。JsonArray 表示 JsonObjects 的数组。JsonArrayBuilder 用于创建 JsonArrayJson 类提供了用于构造 Json 构建器类的实用方法。添加以下导入内容:
    import javax.json.JsonArray;
       import javax.json.JsonArrayBuilder;
       import javax.json.Json;
  5. 最后,您需要创建会议启动方法。对于 JsonObjects,关键要认识到它们是只读的。这意味着一旦创建,就无法更改,因此要启动会议,就必须克隆现有项。此代码隐藏在 Git 存储库内提供的 helper 类中。该方法如下所示。它基本上会创建一个新的 JsonObjectBuilder,并复制条目。在某些情况下(在本文中未使用),克隆时可能不希望复制字段;在此情况下,可以提供要忽略的键列表。
    public 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;
       }

    例如,此类中使用的 JSON 对象具有的值表示用户创建了名为“AfternoonMeeting”的会议,且时长为 1 小时:

    {
         "id" : "01",
         "title" : "AfternoonMeeting",
         "duration" : "60",
         "meetingURL" : "AfternoonMeeting",
         "id" : "01"
       }
  6. 此方法将引入两个新类型:JsonValueJsonObjectBuilderJsonValue 是所有 Json 类型的超类。JsonObjectBuilder 用于创建 JsonObject。该代码还将使用标准的 Java Collections API。添加以下导入内容:
    import javax.json.JsonValue;
       import javax.json.JsonObjectBuilder;
       import java.util.Arrays;
       import java.util.List;
       import java.util.Map;
  7. 当应用程序用户启动新会议时,其操作将提供新的 JsonObject,以及用于加入会议的会议 ID 和 URL。为确保启动会议,将从输入参数 meeting 中访存会议 ID 和 URL。将从内存中访存现有会议的 JsonObject 作为 existingMeeting。然后使用上述 helper 方法克隆现有会议,并且通过对从 helper 方法返回的 JsonObjectBuilder 调用 add 来添加会议 URL。然后,通过构建器来构建 JsonObject。最后,假设仍绑定现有会议,会议映射将替换会议。这可确保线程安全更新,因此如果对 startMeeting 的两个调用同时运行,只有一个调用会成功。
    public 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);
       }
  8. 保存该文件。

3 创建 MeetingService 类

此示例的第二部分是 JAX-RS 服务端点。这使得 REST API 可通过 HTTP 从外部获得。

  1. 创建名为 MeetingService 的新类。

    Eclipse 将在 Java 编辑器中显示 MeetingService 类。默认情况下,大多数 Java EE bean 都自动被视为 CDI 托管 bean,而不是 JAX-RS bean。必须使用 CDI 作用域对 JAX-RS bean 进行注解,使其成为 CDI 托管 bean。在此情况下,我们希望每个请求的 bean 实例的 JAX-RS 行为都正常,但我们需要 bean 为 CDI 托管 bean。可以使用 CDI 请求作用域来完成此操作:

    1. 在类的类型定义上方添加 @RequestScoped,它位于包 javax.enterprise.context 中:
      import javax.enterprise.context.RequestScoped;
            @RequestScoped
      
            public class MeetingService {
    2. 保存该文件
  2. 使该 bean 成为 JAX-RS bean。使用 JAX-RS Path 注解完成此操作:
    1. 在类的类型定义上方添加 @Path,它位于包 javax.ws.rs 中。这将采用单个值,该值是用于访问此 bean 将管理的 JAX-RS 资源的默认路径:
       @Path("meetings")
            public class MeetingService {
    2. 这引入了需要导入的新类型 Path
      import javax.ws.rs.Path;
    3. 保存该文件。

会议服务需要注入两个对象以执行其实际行为。第一个是 MeetingManager 类,第二个是用于管理 URI 处理的 JAX-RS 类:

  1. 只需在类定义之后添加以下代码。Inject 注解将告知 CDI 注入 MeetingManager CDI bean。Inject 注解来自 javax.inject 包:
     @Inject
       private MeetingManager manager;
  2. 导入 Inject 注解:
    import javax.inject.Inject;
  3. 接下来,添加以下代码。Context 注解将告知 JAX-RS 运行时注入 UriInfo 对象:
    @Context
       private UriInfo info;
  4. Context 注解和 UriInfo 接口位于 javax.ws.rs.core 包中,因此将导入 ContextUriInfo
     import javax.ws.rs.core.Context;
       import javax.ws.rs.core.UriInfo;
  5. 保存该文件。

JAX-RS bean 需要四种方法来响应资源请求。JAX-RS 使用注解来确定哪些方法映射到哪些操作。 尽管 JAX-RS bean 根据注解内容接收信息很常见,但 JAX-RS 规范仅在通过 JAX-B 使用 XML 时才允许这样做。每个 JAX-RS 提供程序都支持 JSON 绑定到 Java bean,因此,一般来说,这不是问题,但由于这将使用 MicroProfile,我们坚持使用 JSON-P,JSON-P 是从 JSON 到 Java 的唯一必需映射。

  1. 下面显示了用于添加操作的方法。PUT 注解表示在调用 HTTP PUT 方法时调用此方法。Consumes 注解表明此方法期望接收 JSON。此方法将采用 JsonObject。它将调用 MeetingManager 服务以添加会议,然后返回 201 Created 响应以及已创建资源的链接。
    @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();
       }
    • 此方法会引入多个需要导入的新类:PUTConsumes 位于 javax.ws.rs 包中;ResponseMediaTypeUriBuilde 全都位于 javax.ws.rs.core 中;JsonObject 位于 javax.json 包中。在导入 MediaTypeResponse 时务必小心,因为 Java 包含多个具有这些名称的类:
    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;
  2. 存在的用于列示所有会议的方法非常简单。GET 注解用于指示将对 HTTP GET 请求调用此方法,Produces 方法指示会将 JSON 返回至客户端:
    @GET
       @Produces(MediaType.APPLICATION_JSON)
       public JsonArray list() {
           return manager.list();
       }
    • 此方法会引入两个需要导入的新类:GETProduces 都位于 javax.ws.rs 包中。在导入 Produces 时务必小心,因为 Java EE 包含多个具有此名称的类:
    import javax.ws.rs.GET;
       import javax.ws.rs.Produces;
       import javax.json.JsonArray;
  3. 要获取单个会议的详细信息,我们需要一种方法来响应类定义中所提供路径的子路径。可以对方法使用 Path 注解来完成此操作。Path 值可以包含文字,或者在此情况下,可以包含一个命名实体,该实体随后可作为方法参数传入。在该方法参数上使用 PathParam 注解,以指示应通过哪部分路径提供此参数。
    @GET
       @Path("{id}")
       @Produces(MediaType.APPLICATION_JSON)
       public JsonObject get(@PathParam("id") String id) {
           return manager.get(id);
       }
    • 此方法会引入一个需要导入的新类:PathParam 位于 javax.ws.rs 包中。在导入 PathParam 时务必小心,因为 Java EE 包含多个具有此名称的类:
    import javax.ws.rs.PathParam;
  4. 最后,您需要编写用于启动会议的方法。此方法将响应使用 POST 注解指示的 HTTP Post。在此情况下,它将响应特定的资源实例,并且为确保 JsonObject 和路径没有冲突信息,JsonObject 中的 JsonObject ID 将被路径中的 ID 覆盖。在此应用程序中,这并不重要,但如果 URL 上存在安全约束,这可能就会至关重要。
    @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());
       }
    • 此方法会引入一个需要导入的新类:POST 位于 javax.ws.rs 包中:
    import javax.ws.rs.POST;
       import javax.json.JsonObjectBuilder;
  5. 保存该文件。

4 创建 MeetingApplication 类

最后一步是告知 JAX-RS 应将此模块视为 JAX-RS 应用程序。可以使用几种方法来实现此目的,但最简单的方法就是使用类。

  1. 使用超类 javax.ws.rs.core.Application 创建名为 MeetingApplication 的新类。
  2. 在编辑器中打开类后,添加注解以告知 JAX-RS 注解从何处向 REST 端点分派请求:
    1. 在类定义之前添加 @ApplicationPath 注解以及值 "/rest/"
      import javax.ws.rs.ApplicationPath;
      
            @ApplicationPath("/rest/")
  3. 保存该文件。

应用程序已完成,您可以运行该应用程序。

在 Eclipse 中,您可能会看到一些可以忽略的警告。例如,HTML 问题是由于 Eclipse 不了解用于定义应用程序 UI 的 AngularJS 标记所导致。

5 运行应用程序

Eclipse WDT

可以使用两种方法从 WDT 中运行应用程序:

  • 第一种方法是使用 Maven 来构建和运行项目:
    • 运行 Maven install 目标来构建和测试项目:右键单击 meetings 项目中的 pom.xml,单击 Run As… > Maven Build…,然后在 Goals 字段中输入 install,并单击 Run。第一次运行此目标时,可能会花几分钟时间来下载 Liberty 依赖项。 如果安装了相应的软件包,也可以从命令行运行 mvn install
    • liberty:start-server goal 运行 Maven 构建:右键单击 pom.xml,单击 Run As… > Maven Build...,然后在 Goals 字段中输入 liberty:start-server,并单击 Run。这会在后台启动服务器。或者,运行 mvn liberty:start-server。 您可以通过查看端口 9080 上的 Java 进程来验证服务器是否正在运行。
      $ netstat -pant | grep 9080
            tcp6       0      0 127.0.0.1:9080          :::*                    LISTEN      7140/java
    • 打开应用程序(地址为 http://localhost:9080/meetings/)。
    • 要再次停止服务器,可运行 liberty:stop-server 构建目标。(或者 mvn liberty:stop-server)。
  • 第二种方法是右键单击 meetings 项目,并选择 Run As… > Run on Server,但如果这样做,须注意几点。WDT 不会像您期望的那样自动添加 MicroProfile 功能,因此您需要手动添加这些功能。此外,除非添加 include,否则不会选取对 src/main/liberty/config 中配置的任何更改。

了解有关 MicroProfile 和 Open Liberty 的更多信息。

IBM Cloud

您可以使用 Cloud Foundry 在 IBM Cloud 上运行应用程序。

  1. 登录到您的 IBM Cloud 帐户。
    ibmcloud cf login
  2. 将您的应用程序推送至 CloudFoundry。
    ibmcloud cf push <yourappname> -p wlp/usr/servers/meetingsServer

一旦应用完成部署,即可单击指定的路由/URL,并确保在末尾添加 /meetings 以访问应用程序的主页。

故障排除

如果 Eclipse 弹出以下错误消息:

Could not initialize class org.codehaus.plexus.archiver.jar.JarArchiver

可尝试以下解决步骤:

  1. 从 Maven 存储库中除去这两个目录:
     ~/.m2/repository/org/codehaus/plexus/plexus-archiver
       ~/.m2/repository/org/apache/maven/maven-archiver
  2. 在项目目录中运行 mvn install,以使用当前版本重新填充。

本文翻译自 :Write a simple MicroProfile application, Part 1: Set up the MicroProfile meeting application on Open Liberty(2019-01-29)


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=1065200
ArticleTitle=编写简单的 MicroProfile 应用程序,第 1 部分: 在 Open Liberty 上设置 MicroProfile 会议应用程序
publish-date=03192019