-
log4jの取得
Javaでlog4jを利用します。
log4jも1.xと2.xでバージョンがありますが
2015年8月で1.xの開発は終わっており、
今は2.xの利用が推奨されています。log4j2.xの最新版は以下のサイトの(binary (zip))から入手します。
http://logging.apache.org/log4j/2.x/download.htmlzipファイルを解凍後、以下のjarをクラスパスに通します。
log4j-api-2.x.jarとlog4j-core-2.x.jar -
log4jの設定ファイル
log4j1.xではlog4j.propertiesだったようですが、
log4j2.xではlog4j.xmlを利用します。<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE project> <Configuration status="off"> <Properties> <Property name="format1">%d{yyyy/MM/dd HH:mm:ss.SSS} [%t] [%p] [%c] %m%n</Property> <Property name="logfile">D:/test.log</Property> <Property name="logfile-archive">D:/test_%d{yyyy-MM-dd}-%i.log</Property> <!-- %dは日時。{}に日時の形式を指定 --> <!-- %tはスレッド名 --> <!-- %pはログレベル名(TRACE, ERROR) --> <!-- %cはロガーを生成したクラスフルパス --> <!-- %mはログメッセージ --> <!-- %nは改行 --> <!-- 出力イメージ 2017/06/25 14:54:20.068 [pool-1-thread-1] [TRACE] [thread.Log] 10:処理開始 2017/06/25 14:54:20.070 [pool-1-thread-1] [TRACE] [thread.Log] 10:処理終了 --> </Properties> <Appenders> <!-- コンソール出力設定 --> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout> <pattern>${format1}</pattern> </PatternLayout> </Console> <!-- ファイル出力設定 --> <RollingFile name="logfile001" append="true" fileName="${logfile}" filePattern="${logfile-archive}"> <PatternLayout> <pattern>${format1}</pattern> </PatternLayout> <Policies> <!-- 1KBを超えたらローテーション --> <SizeBasedTriggeringPolicy size="1KB"/> <!-- 日付でローテーション --> <TimeBasedTriggeringPolicy /> </Policies> <!-- ローテーションファイルの圧縮率や削除タイミング --> <DefaultRolloverStrategy max="3"/> </RollingFile> </Appenders> <Loggers> <!-- nameは任意の名前かパッケージ名(hoge.fuga.sample)を指定する --> <!-- 任意の名前の場合、JavaでLoggerを生成する際にコンストラクタで指定する --> <!-- additivityは上位のロガーにもログを出力するかどうか。デフォルトはtrue --> <Logger name="WEB" level="info" additivity="false"> <AppenderRef ref="logfile001" /> </Logger> <Root level="trace"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
-
log4jの利用
package thread; public class Log { private Logger logger = null; private massage = ""; public Log() { logger = LogManager.getLogger(); } public void writeInfoLog(String message) { // messageIDを引数として、メッセージ内容を取得する方法が良い。 logger.trace(Thread.currentThread().getId() + ":" + message); } }
package thread; public class CallableTest implements Callable<Integer> { private Log logger = new Log(); private Log loggerWeb = new Log("WEB"); public Integer call() throws Exception { logger.writeInfoLog("ログ"); loggerWeb.writeInfoLog("ログ"); } }
【Java】Javaで外部プロセスを起動する
Javaで外部プロセス(外部のexeファイル)を起動します。
外部プロセスとはxcopyコマンドやffmpegコマンド等です。
例として、ffmpegでmp4をmp3に変換します。
ffmpegは最新版zipを解凍後のフォルダ(D:/DownloadFiles/freesoft/ffmpeg.exe)に存在し、
パスは通していないものとします。
-
外部プロセス起動設定
// 外部プロセスの起動のためのコマンドを設定する。 // Listに設定するものはスペースを含めないように設定する。 // 今回の例では「D:/DownloadFiles/freesoft/ffmpeg -i D:/test.mp4 -ab 128 D:/test.mp3」となる。 String mp4File = "D:/test.mp4"; String mp3File = "D:/test.mp3"; List<String> cmd = new ArrayList<String>(); // 「cmd /C」は必要に応じて設定する。 // echoのようにコマンドプロントが解釈するコマンドを実行する場合に有効です。 // cmd.add("cmd"); // コマンドプロンプトを起動する。 // cmd.add("/C"); // コマンドを実行した後に終了を指定する。 cmd.add("D:/DownloadFiles/freesoft/ffmpeg"); cmd.add("-i"); cmd.add(mp4File); cmd.add("-ab"); cmd.add("128"); cmd.add(mp3File); ProcessBuilder processBuilder = new ProcessBuilder(cmd); // 標準エラーを標準出力にマージする。 processBuilder.redirectErrorStream(true);
-
外部プロセス起動その1
JDK1.7以上であれば今回の方法を選択すると
外部プロセスの標準エラーを標準出力にマージする設定を活かしつつ、
出力先を呼び出し側のJavaプロセスに設定できます。// 標準出力(標準エラー含む)の内容の出力先をJavaプロセスと同一にする。 processBuilder.inheritIO(); // 外部プロセスを起動する。 Process process = processBuilder.start(); // 外部プロセスの終了を待機する。 // この設定を行わないと、「while ((line = br.readLine()) != null)」がないため、 // Javaプロセスの方が先に処理が進み、「process.exitValue();」で // 「process has not exited」というエラーになります。 process.waitFor(); // 外部プロセスの結果を出力する。 int ret = process.exitValue(); System.out.println("結果:" + ret);
-
外部プロセス起動その2
外部プロセスの標準エラーを標準出力にマージする設定を活かしつつ、
出力先を別ファイルに指定できます。// 標準出力(標準エラー含む)の内容の出力先を別ファイルに指定する。 processBuilder.redirectOutput(new File("D:/process.log")); // 外部プロセスの終了を待機する。 process.waitFor(); // 外部プロセスの結果を出力する。 int ret = process.exitValue(); System.out.println("結果:" + ret);
-
外部プロセス起動(非推奨な方法1)
この方法の欠点として、InputStreamは1024バイトを超える量が保持できないため、
ロック状態になる可能性があるようです。
JavaAPIの仕様にも記載があります。// 外部プロセスを起動する。 Process process = processBuilder.start(); // 標準出力と標準エラーを受け取る。 InputStream inputStream = process.getInputStream(); // 受け取ったInputStreamを出力し、外部プロセスの結果を出力する。 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } int ret = process.exitValue(); System.out.println("結果:" + ret);
-
外部プロセス起動(非推奨な方法2)
JDK1.4まではRuntimeで起動させていたようですが、
JDK1.5からはProcessBuilderが内部的にRuntimeを呼び出しているのと、
標準エラーの内容を標準出力にマージできるようになったりとしているため、
以下の呼び出し方法は非推奨なようです。String cmd = "D:/DownloadFiles/freesoft/ffmpeg -i " + mp4File + " -ab 128 " + mp3File; Process process = Runtime.getRuntime().exec(cmd);
【ニコニコ動画】コメント取得
ログインしたセッション、コメントサーバURL、ユーザID、スレッドIDを利用し、
コメントを取得します。
-
HTTP通信開始
// HTTP通信を開始する。 URL url = new URL(commentServer); HttpURLConnection http = (HttpURLConnection)url.openConnection();
-
リクエストヘッダ部設定
// リクエストヘッダ部を設定する。 http.setRequestMethod("POST"); http.setDoOutput(true); // XML形式を指定する。 http.setRequestProperty("Content-Type", "text/xml"); // セッションと動画用クッキーを設定する。 http.setRequestProperty("Cookie", "user_session=" + userSession);
-
リクエストボディ部設定
// リクエストボディ部を設定する。 PrintStream ps = new PrintStream(http.getOutputStream()); StringBuilder xml = new StringBuilder(); xml.append("<thread "); // スレッドIDを設定する。 xml.append("thread=\"" + threadId + "\" "); // 固定値を設定する。 xml.append("version=\"20061206\" "); // 取得コメント数を設定する。一般会員の場合、最大で1000件となる。 xml.append("res_form=\"-100\" "); // ユーザIDを設定する。 xml.append("user_id=\"" + userId + "\" "); xml.append("/>"); ps.print(xml.toString()); ps.close();
-
レスポンスボディ部取得
// レスポンスボディ部を取得する。 String comment = null; BufferedReader br = new BufferedReader(new InputStreamReader(http.getInputStream())); while((comment = br.readLine()) != null) { // XML形式のため、整形する必要がある。 System.out.println(comment); } br.close();
レスポンスヘッダ部の取得は不要です。
【ニコニコ動画】動画取得
ログインしたセッション、動画サーバURL、動画拡張子、動画用クッキーを利用し、
動画を取得します。
一般会員、プレミアム会員によって動画サイズが異なる事象あり。
プレミアム会員の方が解像度が高そうです。
-
HTTP通信開始
// HTTP通信を開始する。 URL url = new URL(movieServer); HttpURLConnection http = (HttpURLConnection)url.openConnection();
-
リクエストヘッダ部設定
// リクエストヘッダ部を設定する。 http.setRequestMethod("GET"); // セッションと動画用クッキーを設定する。 http.setRequestProperty("Cookie", "user_session=" + userSession + "; nicohistory=" + nicoHistory);
-
レスポンスボディ部取得
// レスポンスボディ部を取得する。 InputStream in= http.getInputStream(); File file = new File("D:/", "sm12345678" + movieExtension); Files.copy(in, file.toPath()); in.close();
GET通信のため、リクエストボディ部の設定は不要です。
レスポンスヘッダ部の取得は不要です。
【ニコニコ動画】動画用クッキー取得
ログインしたセッションを維持して、
「http://www.nicovideo.jp/watch/sm12345678」にGET通信し、
動画用のクッキーを取得します。
-
HTTP通信開始
// HTTP通信を開始する。 URL url = new URL("http://www.nicovideo.jp/watch/sm12345678"); HttpURLConnection http = (HttpURLConnection)url.openConnection();
-
リクエストヘッダ部設定
// リクエストヘッダ部を設定する。 http.setRequestMethod("GET"); // セッションを設定する。 http.setRequestProperty("Cookie", "user_session=" + userSession);
-
レスポンスヘッダ部取得
// レスポンスヘッダ部を取得する。 String nicoHistory = null; Map<String, List<String>> responseHeaders = http.getHeaderFields(); Iterator<String> responseIt = responseHeaders.keySet().iterator(); while (responseIt.hasNext()) { String responseKey = responseIt.next(); List<String> responseList = responseHeaders.get(responseKey); for(String reponseValue: responseList) { System.out.println(responseKey + ":" + reponseValue); if ("Set-Cookie".equals(responseKey)) { String[] cookieAry = reponseValue.split(";"); for (String cookie: cookieAry) { String[] nicoHistoryAry = cookie.split("="); if ("nicohistory".equals(nicoHistoryAry[0])) { nicoHistory = nicoHistoryAry[1]; } } } } }
GET通信のため、リクエストボディ部の設定は不要です。
レスポンスボディ部の取得は不要です。
【ニコニコ動画】動画情報取得
ログインしたセッションを維持して、
「http://flapi.nicovideo.jp/api/getflv/sm12345678」にGET通信し、
以下の動画情報を取得します。
-
動画サーバURL
APIのレスポンスボディ部のうち、
「url=http%3A%2F%2Fsmile-pow64.nicovideo.jp%2Fsmile%3Fm%3D11111111.11111」を取得します。
URLデコードしておく必要があります。
「http://smile-pow64.nicovideo.jp/smile?m=11111111.11111」となります。 -
動画拡張子
動画サーバURLのパラメータから判断します。
パラメータが「m」の場合、動画拡張子が「.mp4」、
パラメータが「s」の場合、動画拡張子が「.swf」、
パラメータが上記以外の場合、動画拡張子が「.flv」になります。 -
コメントサーバURL
APIのレスポンスボディ部のうち、
「ms=http%3A%2F%2Fnmsg.nicovideo.jp%2Fapi%2F」を取得します。
URLデコードしておく必要があります。 -
ユーザID
APIのレスポンスボディ部のうち、
「user_id=11111」を取得します。 -
スレッドID
APIのレスポンスボディ部のうち、
「thread_id=1111111111」を取得します。
-
HTTP通信開始
// HTTP通信を開始する。 URL url = new URL("http://flapi.nicovideo.jp/api/getflv/sm12345678"); HttpURLConnection http = (HttpURLConnection)url.openConnection();
-
リクエストヘッダ部設定
// リクエストヘッダ部を設定する。 http.setRequestMethod("GET"); // セッションを設定する。 http.setRequestProperty("Cookie", "user_session=" + userSession);
-
レスポンスボディ部取得
// レスポンスボディ部を取得する。 String str = null; String movieServer = null; String movieExtension = null; String commentServer = null; String userId = null; String threadId = null; BufferedReader br = new BufferedReader(new InputStreamReader(http.getInputStream())); while((str = br.readLine()) != null) { System.out.println(str); System.out.println(); String[] responseBodyAry = str.split("&"); for (String responseBody: responseBodyAry) { String[] elementAry = responseBody.split("="); if ("url".equals(elementAry[0])) { movieServer = URLDecoder.decode(elementAry[1], "UTF-8"); String extensionChk = movieServer.substring(movieServer.indexOf("?")+1, movieServer.indexOf("?")+2); if ("m".equals(extensionChk)) { movieExtension = ".mp4"; } else if ("s".equals(extensionChk)) { movieExtension = ".swf"; } else { movieExtension = ".flv"; } } if ("ms".equals(elementAry[0])) { commentServer = URLDecoder.decode(elementAry[1], "UTF-8"); } if ("user_id".equals(elementAry[0])) { userId = URLDecoder.decode(elementAry[1], "UTF-8"); } if ("thread_id".equals(elementAry[0])) { threadId = URLDecoder.decode(elementAry[1], "UTF-8"); } } } br.close();
GET通信のため、リクエストボディ部の設定は不要です。
レスポンスヘッダ部の取得は不要です。
【ニコニコ動画】ログイン処理
ニコニコ動画にHTTP通信でログインし、セッションを取得します。
-
HTTP通信開始
// HTTP通信を開始する。 URL url = new URL("https://secure.nicovideo.jp/secure/login?site=niconico"); String params = String.format("mail=%s&password=%s", "niconico@gmail.com", "password"); HttpsURLConnection https = (HttpsURLConnection)url.openConnection();
-
リクエストヘッダ部設定
// リクエストヘッダ部を設定する。 https.setRequestMethod("POST"); https.setDoOutput(true); https.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); https.setRequestProperty("Content-Length", Integer.toString(params.length()));
-
リクエストボディ部設定
// リクエストボディ部を設定する。 PrintStream ps = new PrintStream(https.getOutputStream()); ps.print(params); ps.close();
-
レスポンスヘッダ部取得
// レスポンスヘッダ部を取得する。 String userSession = null; Map<String, List<String>> responseHeaders = https.getHeaderFields(); Iterator<String> responseIt = responseHeaders.keySet().iterator(); while (responseIt.hasNext()) { String responseKey = responseIt.next(); List<String> responseList = responseHeaders.get(responseKey); for(String reponseValue: responseList) { System.out.println(responseKey + ":" + reponseValue); if ("Set-Cookie".equals(responseKey)) { String[] cookieAry = reponseValue.split(";"); for (String cookie: cookieAry) { String[] userSessionAry = cookie.split("="); if ("user_session".equals(userSessionAry[0]) && userSessionAry[1].indexOf("deleted") == -1) { userSession = userSessionAry[1]; } } } } }
レスポンスヘッダは以下のように出力されます。
HTTPステータスコードが302なので、
Locationに指定されたニコニコ動画のトップ画面にリダイレクトしているのが分かります。
null:HTTP/1.1 302 Found x-niconico-authflag:0 x-niconico-authflag:0 Content-Language:ja Date:Fri, 28 Apr 2017 13:39:03 GMT Content-Length:0 Location:http://www.nicovideo.jp/ Set-Cookie:nicosid=1111111111.1111111111; Max-Age=315360000; Expires=Mon, 26 Apr 2027 13:39:03 GMT; Path=/; Domain=.nicovideo.jp Set-Cookie:user_session_secure=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; Max-Age=2591999; Expires=Sun, 28 May 2017 13:39:02 GMT; Path=/; Domain=.nicovideo.jp; Secure; HTTPOnly Set-Cookie:user_session=user_session_22222_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz; Max-Age=2591999; Expires=Sun, 28 May 2017 13:39:02 GMT; Path=/; Domain=.nicovideo.jp Set-Cookie:user_session=deleted; Max-Age=0; Expires=Fri, 28 Apr 2017 13:39:03 GMT; Path=/ Set-Cookie:nicosid=3333333333.3333333333; expires=Mon, 26-Apr-2027 13:39:03 GMT; Max-Age=315360000; path=/; domain=.nicovideo.jp X-niconico-sid:3333333333.3333333333 X-niconico-sid:4444444444.4444444444 Connection:close Content-Type:text/html; charset=UTF-8 Server:Apache
取得したいのは5つある「Set-Cookie」のうち、3つ目の「user_session」の値です。
不要:「nicosid」
不要:「user_session_secure」
必要:「user_session」
不要:「user_session」(deletedのものは不要)
不要:「nicosid」
クッキーからセッションを取得できれば良いので
レスポンスボディ部の取得は行いません。
【ニコニコ動画】JavaでHTTP通信
JavaでHTTP通信をお試しする上で電車検索のAPIやGoogleのAPI等、色々ありますが、
ニコニコ動画のAPIを利用してみることにしました。
まずはHTTP通信についての基礎事項を記載します。
-
HTTP通信開始
HTTP通信を利用するには抽象クラスであるURLConnectionクラスを利用します。// HTTP通信接続 URL url = new URL("http://xxx"); HttpURLConnection httpUrlConnection = (HttpURLConnection)(url.openConnection()); // HTTPS通信接続 URL url = new URL("https://xxx"); HttpsURLConnection httpsUrlConnection = (HttpsURLConnection)(url.openConnection());
上記だけではまだサーバへの接続は開始されておらず、
接続管理オブジェクトが生成された状態で、
後述の「httpUrlConnection.getOutputStream()」か
「httpUrlConnection.getInputStream()」が呼び出されると、
内部的に「httpUrlConnection.connect()」が呼び出されます。
切断メソッドはなく、「httpUrlConnection.getInputStream()」で取得した
「InputStream」がcloseされるとサーバから切断されます。 -
POST通信とGET通信
POST通信、またはGET通信を行う設定は以下の通りです。// POST通信 httpUrlConnection.setRequestMethod("POST"); httpUrlConnection.setDoOutput(true); // GET通信 httpUrlConnection.setRequestMethod("GET");
-
リクエストヘッダ部の設定
リクエストヘッダ部に値を設定する場合は以下の通りです。// リクエストヘッダ部設定 httpUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
-
リクエストボディ部の設定
リクエストボディ部に値を設定する場合は以下の通りです。
しかし、GET通信の場合、リクエストボディ部は設定しないのが正しいです。
パラメタは「http://xxx?mail=xxxx.yyyy.zzzz@gmail.com&password=pass」で渡します。// リクエストボディ部に設定するパラメータ String params = String.format("mail=%s&password=%s", "xxxx.yyyy.zzzz@gmail.com", "pass"); // リクエストボディ部設定 PrintStream out = new PrintStream(httpUrlConnection.getOutputStream()); out.print(params); out.close();
-
レスポンスボディ部の取得
レスポンスボディ部を取得する場合は以下の通りです。BufferedReader responseBody = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream())); String str = null; while((str = responseBody.readLine()) != null) { System.out.println(str); } responseBody.close();
-
レスポンスヘッダ部の取得
レスポンスヘッダ部を取得する場合は以下の通りです。Map<String, List<String>> responseHeaders = https.getHeaderFields(); Iterator<String> responseIt = responseHeaders.keySet().iterator(); while (responseIt.hasNext()) { String responseKey = responseIt.next(); List<String> responseList = responseHeaders.get(responseKey); for(String reponseValue: responseList) { System.out.println(responseKey + ":" + reponseValue); } }
【JSP】ボタンでリンクと同じ動作を行う
ボタン押下時にリンク押下時と同じ動作を行う。
<input type="button" value="メニュー画面へ" onclick="location.href='Menu.jsp'" class="btn btn-default">
class属性はbootstrapの指定方法のひとつ。
ボタンを無地のデフォルトにします。
【JSP】ボタンやリンクからフォームのアクションを動的に変更しSubmit
ボタンやリンク押下時に
フォームのaction先をJavaScriptで設定し、submitする。
-
ボタンの場合
<head> <script type="text/javascript"> function exec(){ document.form1.value = 'ACTION001' document.form1.submit(); } </script> </head>
<form action="" method="POST" name="form1"> <input type="button" value="ボタン1" onClick="exec()" /> </form>
-
リンクの場合
<head> <script type="text/javascript"> function exec(){ document.form1.value = 'ACTION001' document.form1.submit(); } </script> </head>
<form action="" method="POST" name="form1"> <a href="javascript:void(0)" onClick="exec()">リンク1</a> </form>
hrefに「javascript:void(0)」を指定しないと、
ルート(http://localhost:8080/コンテキスト)にアクセスする。