移行しました。
Glowroot で Web サービスの負荷を監視する
Java で作成した Web サービスの CPU 使用率などをブラウザ上で確認できたらいいなと思い、さくっと調べたところ Glowroot がとても簡単に導入できそうでした。
導入
導入は 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 にも対応しているので、レンダリング処理と呼び出し処理などそれぞれの時間が分けて集計されています。
サイトの方に対応しているフレームワークが記載されていますが、プラグインを作成すれば他のフレームワークでも監視できるようになるようです。
Groovy で JSF を使ってみる その2
前回からの続き
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 を使うチュートリアルが公開されているため、こちらを参考にしていきます。
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 アプリを作成するのにとても便利なプラグインです。
Groovy は groovy-all から groovy に変更します。これは Java と Jetty のバージョンの兼ね合いでサーバの起動に失敗してしまうみたいです。今回は簡単な表示確認までしか行わないので動作確認できたこの設定で一旦よしとします。
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 にアクセスすると確認できました。以前は重量級のアプリケーションサーバでないと使えないイメージがあったのですが、かなり簡単に使えました。
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 にアクセスして、「ユーザ名/プロジェクト名」を入力して Look Up を押します。
Log の箇所がぐるぐる回っているときはビルド中です。しばらくするとビルド結果にアクセスでき、ビルドが成功したか失敗したかが確認できます。
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製ライブラリを公開することができました。
【Godot Engine】iOS 版テンプレートは Godot Engine 3.1 で改善されるっぽい
Godot Engine 3.1 の開発状況
3.1 に向けて開発が進んでいますが、現時点で進捗率 50% くらい。リリースはもう少し先になりそうです。
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ファイル
動きました。かなり重いです。アプリを最小化しても音楽が流れ続けるのはバグかな? 何はともあれ安定してきているようなので、これからも楽しみです。
【Godot Engine】画面遷移時のアニメーションのやり方を検討してみる
前回
読み込みスクリプト
前回とほぼ同じですが、今回の用途から少しだけ修正。読み込み完了の状態を保持しておくように 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つを用意しておきます。
簡単にフェードイン・フェードアウトアニメーションのみです。
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 を追加します。
FromScene.gd
extends Sprite
func _on_Button_pressed():
$Transition.change_scene('res://ToScene.tscn')
遷移先画面
こちらも同様
ToScene.gd
extends Sprite
func _on_Button_pressed():
$Transition.change_scene('res://FromScene.tscn')
動作確認