Glowroot で Web サービスの負荷を監視する

Java で作成した Web サービスの CPU 使用率などをブラウザ上で確認できたらいいなと思い、さくっと調べたところ Glowroot がとても簡単に導入できそうでした。

github.com

導入

導入は Github のページ記載されている Quick Start の通りで、zip をダウンロードして Java エージェントを指定してアプリを起動するだけです。

Gradle + Gretty で Web サービスを起動する場合、まずは適当にプロジェクト直下に lib ディレクトリを作成して、その中に zip を解凍します。

projectName/ ・・・ プロジェクトルート
    lib/ ・・・ ライブラリディレクトリ
        glowroot/ ・・・ 解凍した zip。この下に負荷情報が保存される
            glowroot.jar ・・・ Java エージェント
            lib/ ・・・ OS 毎に必要な jar

次に gretty の設定に Javaエージェントを指定します。

build.gradle

gretty {
    jvmArgs = [
        '-javaagent:lib/glowroot/glowroot.jar'
    ]
}

Web アプリの起動方法は特に変わりません。アプリ起動後 localhost:4000 へアクセスするとサーバの負荷を確認できます。

$ gradle appRun

サンプル

前回の JSF プロジェクトで設定した例になります。JSF にも対応しているので、レンダリング処理と呼び出し処理などそれぞれの時間が分けて集計されています。

f:id:erudoru:20190324222927p:plain

サイトの方に対応しているフレームワークが記載されていますが、プラグインを作成すれば他のフレームワークでも監視できるようになるようです。

https://glowroot.org/features.html

Groovy で JSF を使ってみる その2

前回からの続き

erudoru.hatenablog.com

CDI の設定

JSF に Bean 管理の仕組みは用意されていますが、CDI を使ったほうがいろいろ便利なような気がするので追加していきます。

実装は Weld を使用します。Jetty 上で使用するので weld-servlet-core を追加します。サーバ起動時にエラーが発生したので jetty-servlet を追加しましたが若干微妙です。この辺はもう少し依存関係を調べる必要がありそうです。

build.gradle

dependencies {
    〜
    compile 'org.jboss.weld.servlet:weld-servlet-core:3.1.0.Final'
    providedCompile 'org.eclipse.jetty:jetty-servlet:9.4.15.v20190215'
    〜
}

大体の設定は自動で行ってくれますが、CDI を有効にするために beans.xml を追加する必要があります。

src/main/webapp/WEB-INF/beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

おそらく通常の IDE での開発であればこれで OK っぽいんですが、Gradle メインでビルドする場合もうひと手間必要になります。

CDI の仕様(?)で beans.xml が「WEB-INF」直下に、ビルドされたクラスファイルが「WEB-INF/classes」に配置されないと Bean が認識されないっぽいです。

そのためビルド時のクラスファイルの出力先を WEB-INF 直下に変更します。

build.gradle

sourceSets.main.output.classesDir = file("build/inplaceWebapp/WEB-INF/classes")
sourceSets.main.output.resourcesDir = file("build/inplaceWebapp/WEB-INF/classes")

Bean の設定

アノテーションCDI に変更します。それ以外は特に変更はありません。

src/main/groovy/HelloWorld.groovy

//@javax.faces.bean.ManagedBean // 削除
@javax.inject.Named(value = 'helloWorld') // 追加
public class HelloWorld implements Serializable { // Serializable を実装

  def firstName = "John"
  def lastName = "Doe"

  def showGreeting() {
    return "Hello ${firstName} ${lastName}!";
  }
}

セッションタイムアウトの追加

単位は「分」です。

src/main/webapp/WEB-INF/web.xml

  <session-config>
      <session-timeout>1</session-timeout>
  </session-config>

src/main/groovy/HelloWorld.groovy

@javax.inject.Named(value = 'helloWorld')
@javax.enterprise.context.SessionScoped // 追加
public class HelloWorld {
    〜
}

Groovy で JSF を使ってみる

今更 JSF ? とか思われそうですが、サンプル or 簡易的な Web ページを作りたいけど少しでもリッチな UI にしたいという時に使えるんじゃないかと思って試してみました。

PrimeFaces

JSF の実装は PrimeFaces を使用します。

Jetty 上で PrimeFaces を使うチュートリアルが公開されているため、こちらを参考にしていきます。

codenotfound.com

Groovy プロジェクトを作成

今回も Gradle の type オプションで手っ取り早く雛形を作成してしまいます。

( 一番シンプルな構成なライブラリプロジェクトを作成します )

mkdir groovyJSF
cd groovyJSF
gradle init --type groovy-library

Web アプリケーションの設定

build.gradle に war プラグインと gratty プラグインを追加します。

build.gradle

plugins {
    id 'groovy'
    id 'war' // 追加
}
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin' // 追加

dependencies {
    //compile 'org.codehaus.groovy:groovy-all:2.4.14'
    compile 'org.codehaus.groovy:groovy:2.5.6' // ついでに最新のバージョンにしておく

   〜
}

gretty {  // 追加
    servletContainer = 'jetty9.4'
    contextPath = '/'
}

gratty プラグインは Jetty や Tomcat を起動、アプリのデプロイ、さらにはホットリロードにも対応しているため、エディタ + JVM で Web アプリを作成するのにとても便利なプラグインです。

github.com

Groovy は groovy-all から groovy に変更します。これは Java と Jetty のバージョンの兼ね合いでサーバの起動に失敗してしまうみたいです。今回は簡単な表示確認までしか行わないので動作確認できたこの設定で一旦よしとします。

github.com

JSF の設定

build.gradle に PrimeFaces と必要なAPI を追加します。

build.gradle

dependencies {
    〜
    compile group: 'org.primefaces', name: 'primefaces', version: '7.0'
    compile group: 'com.sun.faces', name: 'jsf-api', version: '2.2.18'
    compile group: 'com.sun.faces', name: 'jsf-impl', version: '2.2.18'
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    〜
}

チュートリアルに従い、web.xml も追加します。内容はそのままコピペ

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">

  <!-- File(s) appended to a request for a URL that is not mapped to a web component -->
  <welcome-file-list>
    <welcome-file>helloworld.xhtml</welcome-file>
  </welcome-file-list>

  <!-- Define the JSF servlet (manages the request processing life cycle for JavaServer Faces) -->
  <servlet>
    <servlet-name>faces-servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- Map following files to the JSF servlet -->
  <servlet-mapping>
    <servlet-name>faces-servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>

ページの追加

XHTML と Bean を追加します。

PrimeFaces のチュートリアルJava のコードなので Groovy に置き換えます。

src/main/groovy/HelloWorld.groovy

@javax.faces.bean.ManagedBean
public class HelloWorld {

  def firstName = "John"
  def lastName = "Doe"

  def showGreeting() {
    return "Hello ${firstName} ${lastName}!";
  }
}

Groovy だとかなりシンプルになりますね。

XHTML はそのままコピペ

src/main/webapp/helloworld.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:p="http://primefaces.org/ui">

<h:head>
  <title>PrimeFaces Hello World Example</title>
</h:head>

<h:body>
  <h:form>

    <p:panel header="PrimeFaces Hello World Example">
      <h:panelGrid columns="2" cellpadding="4">
        <h:outputText value="First Name: " />
        <p:inputText value="#{helloWorld.firstName}" />

        <h:outputText value="Last Name: " />
        <p:inputText value="#{helloWorld.lastName}" />

        <p:commandButton value="Submit" update="greeting"
          oncomplete="PF('greetingDialog').show()" />
      </h:panelGrid>
    </p:panel>

    <p:dialog header="Greeting" widgetVar="greetingDialog"
      modal="true" resizable="false">
      <h:panelGrid id="greeting" columns="1" cellpadding="4">
        <h:outputText value="#{helloWorld.showGreeting()}" />
      </h:panelGrid>
    </p:dialog>

  </h:form>
</h:body>
</html>

実行確認

gretty プラグインを使いアプリを起動します。ホットリロードも対応しているので、build.gradle を変更した場合は再起動が必要ですが、それ以外のコードであれば少し待てば再読込が行われます。

$ gradle appRun

localhost:8080 にアクセスすると確認できました。以前は重量級のアプリケーションサーバでないと使えないイメージがあったのですが、かなり簡単に使えました。

f:id:erudoru:20190321175259p:plain

github.com

JitPack を使って Groovy 製ライブラリを公開してみる

Groovy ライブラリプロジェクトを作成

Gradle の type オプションで手っ取り早く雛形を作成してしまいます。

mkdir groovyLibrary
cd groovyLibrary
gradle init --type groovy-library

必要最低限の Groovy のプロジェクトが作成されるため、JitPack のドキュメントに従い build.gradle に maven プラグインと group を追記します。

build.gradle

plugins {
    id 'groovy'
    id 'maven' // 追加
}

group = 'com.github.tadaedo' // 追加

確認用に処理も追加しておきます。

src/main/groovy/Library.groovy

class Library {
    // 追加
    def foo() {
        println 'test groovy library'
    }
}

後はGithub にプッシュします。ライブラリのバージョンとしてタグを切っておきます。

JitPack でビルド

jitpack.io

JitPack にアクセスして、「ユーザ名/プロジェクト名」を入力して Look Up を押します。

Log の箇所がぐるぐる回っているときはビルド中です。しばらくするとビルド結果にアクセスでき、ビルドが成功したか失敗したかが確認できます。

f:id:erudoru:20190315003737p:plain

Groovy で JitPack のライブラリを使用してみる

GrabResolver で JitPack を登録すれば公開したライブラリを簡単に使用することができます。

test.groovy

@GrabResolver(name='jitpack.io', root='https://jitpack.io')
@Grab('com.github.tadaedo:groovyLibrary:0.0.2')

def obj = new Library()
obj.foo()
$ groovy test.groovy
test groovy library

JitPack のおかげで簡単にGroovy製ライブラリを公開することができました。

github.com

【Godot Engine】iOS 版テンプレートは Godot Engine 3.1 で改善されるっぽい

Godot Engine 3.1 の開発状況

3.1 に向けて開発が進んでいますが、現時点で進捗率 50% くらい。リリースはもう少し先になりそうです。

github.com

iOS 向けの修正が master ブランチに取り込まれたようです。これで iOS 版が安定してくれることを期待します。

iPhone X support and iOS-related fixes · godotengine/godot@1d9a3a9 · GitHub

ちょっとだけ試してみる

Github からソースコードを取得し、公式サイトに従いコンパイルします。(コンパイル環境や諸々は割愛)

Compiling for iOS — Godot Engine latest documentation

Simulator 用なので x86 でビルド

$ scons p=iphone target=debug arch=x86
$ scons p=iphone target=debug arch=x86_64
$ lipo -create bin/libgodot.iphone.debug.x86.a bin/libgodot.iphone.debug.x86_64.a -output bin/libgodot.iphone.debug.x86_universal.a
$ cp bin/libgodot.iphone.debug.x86_universal.a エクスポート先パスのaファイル

動きました。かなり重いです。アプリを最小化しても音楽が流れ続けるのはバグかな? 何はともあれ安定してきているようなので、これからも楽しみです。

f:id:erudoru:20180411224233p:plain

【Godot Engine】画面遷移時のアニメーションのやり方を検討してみる

前回

erudoru.hatenablog.com

読み込みスクリプト

前回とほぼ同じですが、今回の用途から少しだけ修正。読み込み完了の状態を保持しておくように finished を追加しました。

SceneLoader.gd

extends Node

# 読込進捗通知シグナル
signal _scene_loading(percent)
# 読込完了シグナル
signal _loaded_scene(scene)
# 読込失敗シグナル
signal _scene_load_error

# ローディング実行時間
const limit_msec = 100
# ローダー
var loader
# 読み込み完了
var finished = false

# 対象シーンパス設定
func set_target_scene(path):
    # ローダー作成
    loader = ResourceLoader.load_interactive(path)
    if loader == null:
        return FAILED
    return OK

# ローディング処理
func process(delta):
    if loader == null || finished:
        return
    var time = OS.get_ticks_msec()
    while OS.get_ticks_msec() < time + limit_msec:
        var ret = loader.poll()
        if ret == OK:
            # 読み込み実行
            continue
        elif ret == ERR_FILE_EOF:
            # 読み込み完了
            finished = true
            emit_signal('_loaded_scene', loader.get_resource())
            return
        else:
            # 読み込み失敗
            emit_signal('_scene_load_error')
            loader = null
            return
    # 進捗通知
    var p = float(loader.get_stage() + 1) / float(loader.get_stage_count())
    emit_signal('_scene_loading', p)

自動ロード

今回は使用しません。登録済みの SceneLoader.gd は解除します。

サンプルシーン作成

今回も3つのシーンを作成します。

  • FromScene.tscn ・・・ 遷移元画面
  • ToScene.tscn ・・・ 遷移先画面
  • Transition.tscn ・・・ ローディング、アニメーション用Node。FromScene と ToScene に組み込んで使用する。

画面遷移用 Node

ルートは ColorRect でカラーは黒を設定します。子にアニメーション用の Tween を追加します。画面表示時用と画面終了用の2つを用意しておきます。

f:id:erudoru:20180402221408p:plain

簡単にフェードイン・フェードアウトアニメーションのみです。

Transition.gd

extends ColorRect

var scene_loader = preload('res://SceneLoader.gd').new()
var next_scene
var exit_anim_completed

# シーン変更
func change_scene(target_path):
    scene_loader.set_target_scene(target_path)
    _start_exit_anim()

func _enter_tree():
    scene_loader.connect("_scene_loading", self, "_on_scene_loading")
    scene_loader.connect("_loaded_scene", self, "_on_loaded_scene")
    scene_loader.connect("_scene_load_error", self, "_on_scene_load_error")
    # 画面表示アニメーション開始
    _start_enter_anim()

func _exit_tree():
    scene_loader.disconnect("_scene_loading", self, "_on_scene_loading")
    scene_loader.disconnect("_loaded_scene", self, "_on_loaded_scene")
    scene_loader.disconnect("_scene_load_error", self, "_on_scene_load_error")

func _process(delta):
    scene_loader.process(delta)

# Enterアニメーション
func _start_enter_anim():
    # Nodeを表示する
    visible = true
    # アニメーション
    $EnterAnim.interpolate_property(
        self,
        "color",
        Color(1, 1, 1, 1),
        Color(1, 1, 1, 0),
        1,
        Tween.TRANS_LINEAR, Tween.EASE_IN)
    $EnterAnim.start()

# Exitアニメーション
func _start_exit_anim():
    # Nodeを表示する
    visible = true
    # アニメーション
    $ExitAnim.interpolate_property(
        self,
        "color",
        Color(1, 1, 1, 0),
        Color(1, 1, 1, 1),
        1,
        Tween.TRANS_LINEAR, Tween.EASE_IN)
    $ExitAnim.start()

# 画面遷移
func _next_scene():
    # Scene読み込み済み、アニメーション終了済みであれば画面遷移する
    if next_scene and exit_anim_completed:
        visible = false
        get_tree().change_scene_to(next_scene)

# Enterアニメーション完了処理
func _on_EnterAnim_tween_completed(object, key):
    # Nodeを非表示
    visible = false

# Exitアニメーション完了処理
func _on_ExitAnim_tween_completed(object, key):
    exit_anim_completed = true
    # 画面遷移
    _next_scene()

# ローディング中
func _on_scene_loading(p):
    # ローディング中の画面表示を更新
    print("Loading... %d" % [int(p * 100)])

# ロード完了
func _on_loaded_scene(scene):
    next_scene = scene
    # 画面遷移
    _next_scene()

# ロード失敗
func _on_scene_load_error(error_type):
    # エラーの場合の表示
    print('error:' + str(error_type))

遷移元画面

ボタン一つと、一番ツリーの下に Transition を追加します。

f:id:erudoru:20180402221228p:plain

FromScene.gd

extends Sprite

func _on_Button_pressed():
    $Transition.change_scene('res://ToScene.tscn')

遷移先画面

こちらも同様

f:id:erudoru:20180402221742p:plain

ToScene.gd

extends Sprite

func _on_Button_pressed():
    $Transition.change_scene('res://FromScene.tscn')

動作確認

f:id:erudoru:20180402222632g:plain