LoginSignup
1
0

More than 1 year has passed since last update.

Open Libertyを使用して、JAX-RSとWeb componentsとCarbon Designで、モダンなUIとマイクロ・サービス・アプリケーションを簡単に実装する

Last updated at Posted at 2022-01-12

はじめに

この記事は、Open LibertyをRuntimeとしたJava(JAX-RS)とWeb ComponentsとCarbon Design Systemによるマイクロサービス・アプリケーションの実装サンプルの紹介です。

Open LibertyとWeb Componentsによって小さいアプリケーションを作成し、
OpenShift上のオペレータを使用してデプロイし、コンテナとして稼働させるということを経験しました。
その経験の共有のために記事にします。

試したサンプルのアーキテクチャの特徴は、下記です。

  • Open LibertyをRuntimeとしたJAX-RSによるサービスの実装
  • Web ComponentsによるWeb UI実装。JavaScriptライブラリにLitを使用
  • デザイン・システム(Carbon Design)を使用して、見栄えを確保、

このアーキテクチャは、バックエンドは、Javaによる既存資産を流用しつつ、フロント・エンドは、Web Componentsによって、いまどきのデザインを適用したい、というような状況にお勧めです。

また、上記の組み合わせは、Node.jsによるトランスパイルを必要せず、既存のシステムにも容易に統合可能です。

都合上、特定企業に関連した技術が目につきますが、まったく宣伝の意図はございません。

用語紹介

Open Libertyとは

Open Libertyは、OSSのJakarta EE(旧Java EE/J2EE) Runtimeです。
商用バージョンとしてIBM Websphere Libertyがあります。
Libertyのコードは、OSGiによってモジュール化されているため、最小限の機能(プロファイル)での運用が可能という特徴があります。
今回の例では、Eclipse Micro Profileを使用します。

JAX-RSとは

AX-RS(Java API for RESTful Web Services)は、JavaでRESTサービスを実装するためのAPI仕様です。
アノテーションを使用して、Restful WebサービスのURLを定義できます。

以下の例では、GET /api/files というリクエストで、ファイルのリストを取得し、
DELETE /api/files/filenameというリクエストで、ファイルを削除します。

JAX-RSのサンプル
@Path("/files")
public class FilesController {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<FileInfo> getFiles() {
        return fileService.getFileList();
    }
    @DELETE
    @Path("/{filename}")
    @Produces(MediaType.APPLICATION_JSON)
    public boolean deleteFile(@PathParam("filename")String filename) {
        return fileService.deleteFile(filename);
    }

Web Componentsとは

Web Components | MDNによると、Web Components は、再利用可能なカスタム要素を作成し、ウェブアプリの中で利用するための、一連のテクノロジーです。Web Components自体は、HTMLやJavaScript,CSSというテクノロジーの標準の組み合わせのため、すぐに始めることができます。

LITとは

Web Componentsでの実装を支援するJavaScriptのフレームワークです。シンプルかつ軽量であり、React等の他のフレームワークと一緒に使用することができます。Node.jsを使わず、CDNから使用可能です。

デザイン・システムとは

デザイン・システムは、再利用可能なコンポーネントや、コンポーネントに関するガイド・ドキュメント等を含めたセットです。
デザイン・システムを使用することで、一貫したUI経験を提供できます。


- Material Design https://material.io/design
- Carbon Design System https://www.carbondesignsystem.com/

当記事の例では、Carbon Design System のWeb Components版を使用しています。Node.jsを使わず、CDNから使用可能です。

image.png

サンプル

サンプル・アプリケーションとして、
特定のディレクトリ上のファイルの一覧を表示し、ダウンロード、および、削除をできるようなアプリケーションを実装します。

Open Libertyを使用したJAX-RSの実装

Open Libertyをダウンロード

https://openliberty.io/start/ からOpen Libertyをダウンロードします。

例として、以下のような設定とします。
image.png

適切なディレクトリに展開し、下記コマンドを実行します。このコマンドにより、マイクロ・プロファイルに必要なライブラリがダウンロードされ、Javaのコンパイル、および、Open Libertyが実行されます。

cd hello-app
./mvnw liberty:run

ブラウザを開き、http://localhsot:9080/にアクセスすると、Open Libetyの画面が表示されます。

src/main/java配下に、パッケージおよび、JAX-RSのクラスを定義します。

RestApplication.java
package com.example.filelist.rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
public class RestApplication extends Application {
}

FilesController.javaでは、downloadBaseDirという変数をもとに、ファイルを表示するディレクトリを取得します。
downloadBaseDirは、MicroProfile Configの変数として定義されていることを期待しています。
実稼働時には、Open Shiftおよび、Kubernetesから値が渡ります。

FilesController.java
package com.example.filelist.rest;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
@Path("/files")
public class FilesController {

    private final String MNT_PATH;
    public FilesController() {
        Config config = ConfigProvider.getConfig();
        MNT_PATH = config.getValue("downloadBaseDir", String.class);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<FileInfo> getFiles() {
        return this.getFileList();
    }

    @GET
    @Path("/download/{filename}")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public Response downloadFile(@PathParam("filename")String filename) {
        File file = new File(MNT_PATH,filename);
        ResponseBuilder response = Response.ok((Object) file);
        response.header("Content-Disposition",
            "attachment; filename=\""+filename+"\"");
        return response.build();
    }

    @DELETE
    @Path("/{filename}")
    @Produces(MediaType.APPLICATION_JSON)
    public boolean delete(@PathParam("filename")String filename) {
        return this.deleteFile(filename);
    }
}

フロントエンドとのデータ通信に使用されるクラス

FileInfo.java
package com.example.filelist.rest;
import java.io.File;
import java.util.Date;

public class FileInfo {
    private String fileName;
    private long size;
    private long lastModified;
    private Date lastModifiedDate;


    public long getLastModified() {
        return lastModified;
    }

    public void setLastModified(long lastModified) {
        this.lastModified = lastModified;
    }

    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    public FileInfo(File file) {
        this.fileName = file.getName();
        this.lastModified = file.lastModified();
        this.lastModifiedDate = new Date(file.lastModified());
        this.size = file.length();
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

}

WebComponents/Carbon Design System/Litを使用したフロントエンド実装

この例では、WebComponents/Carbon Design System/Litは、CDNを使用しています。

WebComponents/Carbon Design System/Litの詳しい説明は、省きますが、
これらの仕様・フレームワークを使用することで、容易にコンポーネントベースのWeb UI部品を実装・利用ができます。

import { LitElement, html } from 'https://unpkg.com/lit-element/lit-element.js?module';
import 'https://1.www.s81c.com/common/carbon/web-components/tag/latest/button.min.js';
import 'https://1.www.s81c.com/common/carbon/web-components/tag/latest/data-table.min.js';
import 'https://1.www.s81c.com/common/carbon/web-components/tag/latest/modal.min.js';
import 'https://1.www.s81c.com/common/carbon/web-components/tag/latest/input.min.js';
import 'https://1.www.s81c.com/common/carbon/web-components/tag/latest/form.min.js';

import './my-deleteFileButton.js';

class MyFileList extends LitElement {

    static get properties() {
        return {
            fileLists: { type: Array },
            fileName: { type: String }
        };
    }

    constructor() {
        super();
        this.fileLists = [];
        this.fileName = "";
        this.addEventListener('fetchFileList', this._fetchFileList);
    }

    _fetchFileList(){
        fetch('./api/files').then(function(resp) {
            return resp.json();
        }).then((resp) => {
            console.log(resp);
            this.fileLists = resp;
        });
    }

    firstUpdated() {
        this._fetchFileList();
    }

    humanFileSize(bytes, dp=1) {
      const thresh = 1024;

      if (Math.abs(bytes) < thresh) {
        return bytes + ' B';
      }

      const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] ;
      let u = -1;
      const r = 10**dp;

      do {
        bytes /= thresh;
        ++u;
      } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);


      return bytes.toFixed(dp) + ' ' + units[u];
    }

    formatDate(longDateTime) {
        let date = new Date();
        date.setTime(longDateTime);
        return date.toLocaleString();
    }

    render() {
        const { fileLists } = this;
        const linkpath = "./api/files/download/";
        return html`
    <bx-data-table>
        <bx-table>
            <bx-table-head>
                <bx-table-header-row>
                    <bx-table-header-cell>File
                        Name</bx-table-header-cell>
                    <bx-table-header-cell>Size</bx-table-header-cell>
                    <bx-table-header-cell>Last
                        Modified</bx-table-header-cell>
                    <bx-table-header-cell>Option</bx-table-header-cell>
                </bx-table-header-row>
            </bx-table-head>
            <bx-table-body>
                ${fileLists.map((item) => html`
                <bx-table-row>
                    <bx-table-cell><a href=${linkpath}${item.fileName}>${item.fileName}</a></bx-table-cell>
                    <bx-table-cell>${this.humanFileSize(item.size)}</bx-table-cell>
                    <bx-table-cell>${this.formatDate(item.lastModified)}</bx-table-cell>
                    <bx-table-cell>
                    <my-delfilebtn fileName="${item.fileName}"></my-delfilebtn>
                    </bx-table-cell>
                </bx-table-row>
                `)}
            </bx-table-body>
        </bx-table>
    </bx-data-table>

        `;
    }
}

customElements.define('my-filelist', MyFileList);

画面の例です。
image.png

まとめ

この記事では、以下についての簡単な実装例を紹介致しました。
- Open LibertyをRuntimeとしたJAX-RSによるサービスの実装
- Web ComponentsによるWeb UI実装。JavaScriptライブラリにLitを使用
- デザイン・システム(Carbon Design)を使用して、見栄えを確保

繰り返しとなりますが、このアーキテクチャは、バックエンドは、Javaによる既存資産を流用しつつ、フロント・エンドは、Web Componentsによって、いまどきのデザインを適用したい、というような状況にお勧めです。

当記事が誰かの助けになれば幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0