- アクセスログ https://github.com/briannesbitt/play-accesslog
- Coffee自動コンパイル https://github.com/robfig/play-coffee
- クラス操作ログ(クラス拡張あり) https://github.com/sgodbillon/BetterLogs
src
├ play.plugins
└ play
└ modules
└ MODULE_NAME
├ FooPlugin.java
└ FooEnhancer.java
play-accesslog https://github.com/briannesbitt/play-accesslog より
public class AccessLogPlugin extends PlayPlugin
{
//vhost remoteAddress - requestUser [time] "requestUrl" status bytes "referrer" "userAgent" requestTime "POST"
private static final String FORMAT = "%v %h - %u [%t] \"%r\" %s %b \"%ref\" \"%ua\" %rt \"%post\"";
private static final String CONFIG_PREFIX = "accesslog.";
private static final String DEFAULT_PATH = "logs/access.log";
private static boolean _canLog = false;
private static PrintWriter _writer;
private static File _logFile;
private boolean _shouldLog2Play;
private boolean _shouldLogPost;
@Override
//初期化処理
public void onConfigurationRead() {
_shouldLog2Play = Boolean.parseBoolean(Play.configuration.getProperty(CONFIG_PREFIX+"log2play", "false"));
_shouldLogPost = Boolean.parseBoolean(Play.configuration.getProperty(CONFIG_PREFIX+"logpost", "false"));
_logFile = new File(Play.configuration.getProperty(CONFIG_PREFIX+"path", DEFAULT_PATH));
if (!_logFile.isAbsolute()) {
_logFile = new File(play.Play.applicationPath, _logFile.getPath());
}
}
@Override
//アプリケーション起動時
public void onApplicationStart() {
//ファイル書き込みハンドラ生成
_canLog = createWriter();
}
@Override
//アプリケーション終了時
public void onApplicationStop() {
if (_writer != null) {
//ファイル書き込みハンドラクローズ
_writer.close();
}
}
@Override
//リクエスト処理終了時
public void invocationFinally() {
//ログ書き込み
log();
}
//ログ書き込み
private synchronized void log() {
//リクエスト・レスポンスオブジェクト取得
Http.Request request = Http.Request.current();
Http.Response response = Http.Response.current();
if (request == null || response == null) {
return;
}
long requestProcessingTime = System.currentTimeMillis() - request.date.getTime(); //処理時間
Http.Header referrer = request.headers.get(HttpHeaders.Names.REFERER.toLowerCase()); //リファラ
Http.Header userAgent = request.headers.get(HttpHeaders.Names.USER_AGENT.toLowerCase()); //userAgent
String bytes = "-";
String status = "-";
/* It seems as though the Response.current() is only valid when the request is handled by a controller
Serving static files, static 404's and 500's etc don't populate the same Response.current()
This prevents us from getting the bytes sent and response status all of the time
*/
if (request.action != null && response.out.size() > 0) {
bytes = String.valueOf(response.out.size());
status = response.status.toString();
}
//アクセスログ文字列作成
String line = FORMAT;
line = StringUtils.replaceOnce(line, "%v", request.host);
line = StringUtils.replaceOnce(line, "%h", request.remoteAddress);
line = StringUtils.replaceOnce(line, "%u", (StringUtils.isEmpty(request.user)) ? "-" : request.user);
line = StringUtils.replaceOnce(line, "%t", request.date.toString());
line = StringUtils.replaceOnce(line, "%r", request.url);
line = StringUtils.replaceOnce(line, "%s", status );
line = StringUtils.replaceOnce(line, "%b", bytes);
line = StringUtils.replaceOnce(line, "%ref", (referrer != null) ? referrer.value() : "");
line = StringUtils.replaceOnce(line, "%ua", (userAgent != null) ? userAgent.value() : "");
line = StringUtils.replaceOnce(line, "%rt", String.valueOf(requestProcessingTime));
if (_shouldLogPost && request.method.equals("POST")) {
String body = request.params.get("body");
if (StringUtils.isNotEmpty(body)) {
line = StringUtils.replaceOnce(line, "%post", body);
} else {
// leave quotes in the logged string to show it was an empty POST request
line = StringUtils.remove(line, "%post");
}
} else {
line = StringUtils.remove(line, "\"%post\"");
}
line = StringUtils.trim(line);
if (_canLog) {
_writer.println(line);
}
if (_shouldLog2Play) {
play.Logger.info(line);
}
}
}
BetterLogs https://github.com/sgodbillon/BetterLogs より
public class BetterLogsEnhancer extends Enhancer {
@Override
public void enhanceThisClass(final ApplicationClass applicationClass) throws Exception {
final CtClass ctClass = makeClass(applicationClass);
//javassist: getDeclaredBehaviros() からコンストラクタとメソッド一覧を取得
for(final CtBehavior behavior : ctClass.getDeclaredBehaviors()) {
behavior.instrument(new ExprEditor() {
@Override
public void edit(MethodCall m) throws CannotCompileException {
try {
//play.Loggerメソッドであったら
if("play.Logger".equals(m.getClassName())) {
String name = m.getMethodName();
if("trace".equals(name) || "debug".equals(name) || "info".equals(name) || "warn".equals(name) || "error".equals(name) || "fatal".equals(name)) {
String code = String.format("{play.modules.betterlogs.BetterLogsPlugin.log(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %s, %s);}",
name,
ctClass.getName(), // canonical name
ctClass.getSimpleName(), // simple name
ctClass.getPackageName(), // package
behavior.getName(),
behavior.getSignature(),
m.getFileName(),
applicationClass.javaFile.relativePath(),
m.getLineNumber(),
"$args" // original args
);
//メソッド書き換え
m.replace(code);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
//クラス書き換え
applicationClass.enhancedByteCode = ctClass.toBytecode();
ctClass.defrost();
}
}
すみません。全部網羅してません。
- Called at plugin loading
- Called at application start (and at each reloading) Time to start stateful things.
- 拡張ログプラグインでの使用例:ログファイル生成、あるいはオープン
- Called at application start (and at each reloading) Time to start stateful things.
- 拡張ログプラグインでの使用例:ログファイルハンドラの生成
- Called after the application start.
- Called at application stop (and before each reloading) Time to shutdown stateful things.
- 拡張ログプラグインでの使用例:ログファイルハンドラのクローズ
- Called after routes loading.
- Facebook認証プラグインでの使用例:OAuthコールバックURLの追加
- Called before a Play! invocation. Time to prepare request specific things.
- Called after an invocation. (unless an excetion has been thrown). Time to close request specific things.
- Called if an exception occured during the invocation.
- Called at the end of the invocation. (even if an exception occured). Time to close request specific things.
- 拡張ログプラグインでの使用例:ログ書き込み
- Called before an 'action' invocation, ie an HTTP request processing.
- Called when the action method has thrown a result.
- Called when the request has been routed.
- Called at the end of the action invocation.
- Event may be sent by plugins or other components
- Enhance this class
- クラス拡張(バイトコード修正)する場合は、ここから
- Let a chance to this plugin to manage a static resource
- Coffeeスクリプト対応プラグインでの使用例:拡張子coffeeファイルであったらコンパイルしてhttpレスポンスを返す
- テンプレートファイル呼び出しイベント
- It's time for the plugin to detect changes. Throw an exception is the application must be reloaded.
- route拡張プラグインでの使用例:onRouteLoaded()イベントで拡張したroute情報を、detectChange()時に再度拡張する
- void onLoad() まだポートも開かれていない
-
void enhance(ApplicationClass applicationClass)
-
Template loadTemplate(final VirtualFile file) enhanceの次
-
List addTemplateExtensions() loadTemplateとセットで呼ばれる?
-
void onConfigurationRead() 設定ファイル読み込み
-
void onRoutesLoaded()
-
void onApplicationStart()
-
void afterApplicationStart()
-
void beforeInvocation()
-
void afterInvocation()
-
void invocationFinally()
- void onApplicationReady() この後、ポートが開かれる
-
boolean rawInvocation(final Request request, final Response response)
-
void routeRequest(final Request request)
-
void onRequestRouting(Route route)
-
void beforeInvocation()
-
void beforeActionInvocation(Method actionMethod)
-
void onActionInvocationResult(Result result)
-
Map<String, String> addMimeTypes()
-
Template loadTemplate(final VirtualFile file)
-
List addTemplateExtensions()
-
void afterActionInvocation()
-
void afterInvocation()
-
void onInvocationSuccess()
-
void invocationFinally()
- void onLoad() まだポートも開かれていない
- void onApplicationReady() この後、ポートが開かれる
-
boolean rawInvocation(final Request request, final Response response)
-
void onRoutesLoaded()
-
void beforeDetectingChanges()
-
boolean detectClassesChange()
-
void onConfigurationRead() 設定ファイル読み込み
-
void onApplicationStart()
-
void afterApplicationStart()
-
void enhance(ApplicationClass applicationClass)
-
void beforeDetectingChanges()
-
boolean detectClassesChange()
-
void detectChange()
-
void beforeInvocation()
-
void afterInvocation()
-
void invocationFinally()
-
void routeRequest(final Request request)
-
void onRequestRouting(Route route)
-
void beforeInvocation()
-
void beforeActionInvocation(Method actionMethod)
-
void onActionInvocationResult(Result result)
-
Map<String, String> addMimeTypes()
-
Template loadTemplate(final VirtualFile file)
-
List addTemplateExtensions()
-
void afterActionInvocation()
-
void afterInvocation()
-
void onInvocationSuccess()
-
void invocationFinally()
-
boolean rawInvocation(final Request request, final Response response)
-
void beforeDetectingChanges()
-
boolean detectClassesChange()
-
void detectChange()
-
void routeRequest(final Request request)
-
void onRequestRouting(Route route)
-
void beforeInvocation()
-
void beforeActionInvocation(Method actionMethod)
-
void onActionInvocationResult(Result result)
-
Template loadTemplate(final VirtualFile file)
-
void afterActionInvocation()
-
void afterInvocation()
-
void onInvocationSuccess()
-
void invocationFinally()
- Google検索: "extends PlayPlugin" detectChange site:github.com
モジュール名はパッケージに引用されるので、すべて小文字で登録する。
# play new-module sampleplugin
# tree
.
├── app
│ ├── controllers
│ │ └── sampleplugin
│ ├── models
│ │ └── sampleplugin
│ └── views
│ ├── sampleplugin
│ └── tags
│ └── sampleplugin
├── build.xml
├── commands.py
├── conf
│ ├── dependencies.yml
│ ├── messages
│ └── routes
├── documentation
│ └── manual
│ └── home.textile
└── src
├── play
│ └── modules
│ └── sampleplugin
└── play.plugins
# cd sampleplugin
# touch .gitignore
# vim .gitignore
/.classpath
/.project
/.settings
/eclipse/
/logs/
/test-result/
/tmp/
ビルド時のjarファイルを配置するため
# mkdir lib
# rm -rf app/* documentation/ conf/messages
# echo /app/ >> .gitignore
# vim conf/dependencies.yml
self: play -> sampleplugin 1.0
require:
- play
play ec
してから、EclipseのImport機能でインポート
srcフォルダのパッケージ play.modules.sampleplugin に追加(appフォルダではないので注意)
public class SamplePlugin extends PlayPlugin {
@Override
public void onLoad() {
System.out.println("Hello, SamplePlugin!");
}
}
上で作成したプラグインクラス名を指定する。頭の数字はプラグインの呼び出し優先順位値 (play statusコマンドで実行中プロジェクトのプラグイン優先順が確認できる)
# echo 100:play.modules.sampleplugin.SamplePlugin >> src/play.plugins
Eclipse上でプロジェクト名(sampleplugin)を選択し、NEW > Other > Java > Source Folder からフォルダ名 test を追加。
application.conf ファイルがないと UnitTest が動作しないため、追加しておく
# touch conf/application.conf
testフォルダのパッケージ play.modules.sampleplugin に追加
public class SamplePluginTest extends UnitTest {
@Test
public void test() {
assertThat(new SamplePlugin(), is(not(nullValue())));
}
}
- conf/routes と conf/application.conf が存在すること
- URLのルートが1つ以上あること
- GET /public/ staticDir:public などをconf/routesに追加しておけばOK
プラグインにソースを含むよう、build.xmlを修正しておく
<jar destfile="lib/play-ddd.jar" basedir="tmp/classes">
<!-- ソースを含める -->
<fileset dir="src" includes="**/*.java" />
<manifest>
<section name="Play-module">
<attribute name="Specification-Title" value="ddd"/>
</section>
</manifest>
</jar>
モジュール作成
# play build-module --require 1.2.5
Error: JAVA_HOME is not defined correctly. と言われたら、 JAVA_HOME 環境変数を設定する(下記はOSXの場合)
# export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
BUILD FAILED ... sampleplugin/lib does not exist. と言われたら、libフォルダを作成する
# mkdir lib
新しいアプリケーションを作成して、作成したプラグインを利用してみる。
# play new SamplePluginTest
# cd SamplePluginTest
# vim conf/dependencies.yml
require:
- play
- sampleplugin -> sampleplugin
repositories:
- local_modules:
type: local
artifact: ${application.path}/../[module]
contains:
- sampleplugin
# play deps --clearcache
# play run
~ _ _
~ _ __ | | __ _ _ _| |
~ | '_ \| |/ _' | || |_|
~ | __/|_|\____|\__ (_)
~ |_| |__/
~
~ play! 1.2.5, http://www.playframework.org
~
~ Ctrl+C to stop
~
JPDA port 8000 is already used. Will try to use any free port for debugging
CompilerOracle: exclude jregex/Pretokenizer.next
Listening for transport dt_socket at address: 55646
14:54:07,179 INFO ~ Starting /SamplePluginTest
14:54:07,182 INFO ~ Module sampleplugin is available (/SamplePluginTest/../sampleplugin)
14:54:07,530 WARN ~ You're running Play! in DEV mode
14:54:07,580 INFO ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...
Hello, SamplePlugin!
14:54:18,071 INFO ~ Application 'SamplePluginTest' is now started !
Hello, SamplePlugin! が表示されたらプラグイン読み込みOK
Maven Central Repository に登録しなくとも、Github上にビルドしたZipファイルを配置することでプラグイン公開が可能
Github上で PlaySamplePlugin リポジトリを作成。ソースとビルド済みファイルをプッシュする
# cd sampleplugin
# git init
# git add .
# git status
new file: .gitignore
new file: build.xml
new file: commands.py
new file: commands.pyc
new file: conf/application.conf
new file: conf/dependencies.yml
new file: conf/routes
new file: dist/sampleplugin-1.0.zip
new file: lib/play-sampleplugin.jar
new file: src/play.plugins
new file: src/play/modules/sampleplugin/SamplePlugin.java
new file: test/play/modules/sampleplugin/SamplePluginTest.java
# git commit -m "first commit"
# git remote add origin https://github.com/{YOUR_NAME}/PlaySamplePlugin.git
# git push -u origin master
dependencies.yml を修正する。
# cd SamplePluginTest
# vim conf/dependencies.yml
require:
- play
- asufana -> sampleplugin 1.0
repositories:
- asufana_github_repo:
type: http
artifact: https://github.com/{YOUR_NAME}/PlaySamplePlugin/raw/master/dist/[module]-[revision].zip
contains:
- asufana -> *
# play deps --clearcache
WARNING: These dependencies are missing, your application may not work properly (use --verbose for details), と言われた場合には、
# play deps --clearcache --verbose
として、エラー詳細を確認する。
Server access Error: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty url=https://github.com... となっていたら、たぶん使っているJDKが古いため、SSL通信エラーになっていると思われる。JDKを更新すること。
- OSX: NG: 1.6.0_45-b06-451.jdk
- OSX: OK: 1.6.0_65-b14-466.1-11M4716