ITお絵かき修行

3歩歩いても忘れないために

JavaでTelnetクライアント作成・RHELにTelnetサーバ導入

JavaのSocketクラスを使ってTelnet接続してみる。
ついでにTelnetサーバを立てる手順も備忘録として残す。

【すること】
Telnet接続する。
※セッションモードで端末に接続し、コマンドが打てるところまで確認する。
 NVTとしての仕様は網羅しない。

【環境】
●OS
クライアント側:Windows7 64bit
サーバ側   :Red Hat Enterprise Linux Server release 7.1 (Maipo)

Java
1.8.0_60-b27


Telnetサーバ導入】
0.はじめに
・23番ポートを空ける。
Telnetを実行するユーザはスーパーユーザである必要があるため、rootユーザで接続確認する。
 Telnetプロトコルで使用するユーザ名とパスワードは平文で流れるので注意する。
[参考] ITエンジニアの「やってはいけない」 - [セキュリティ編]運用管理にtelnetを使ってはいけない:ITpro
・スーパーサーバであるxinetdを利用してTelnetサーバを管理する。
[参考] xinetd - Wikipedia


1.インストール(※rootユーザで行う)

yum update
yum install telnet -y
yum install telnet-server -y
yum install xinetd -y ※RHELには入ってないので入れておく

2.xinetdよりTelnetサーバ有効化

vi /etc/xinetd.d/telnet

●/etc/xinetd.d/telnet

service telnet
{
        flags           = REUSE
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/sbin/in.telnetd
        log_on_failure  += USERID
        disable         = no ←「yes」から「no」へ変更し、有効化
}

3.xinetd起動および自動起動の設定
(※xinetdを起動することによりTelnetサーバも起動する)

service xinetd start
chkconfig --add xinetd
chkconfig xinetd on

インストール手順はここまで。


Telnet接続】
Telnet接続用クラス

package hhhhhskw.com;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * Telnet接続用クラス
 */
public class TelnetConnector {

	private final static int DEFAULT_TELNET_PORT = 23;

	private final static byte IAC = (byte) 255;
	private final static byte DONT = (byte) 254;
	private final static byte DO = (byte) 253;
	private final static byte WONT = (byte) 252;
	private final static byte WILL = (byte) 251;

	/**
	 * NVTとしてのネゴシエーションを行う
	 * @param os
	 * @param is
	 * @throws IOException
	 */
	private void negotiation(OutputStream os, BufferedInputStream is) throws IOException {
		byte[] buff = new byte[3];
		while (true) {
			is.mark(buff.length);
			if (is.available() >= buff.length) {
				is.read(buff);
				// ネゴシエーション完了
				if (buff[0] != IAC) {
					is.reset();
					return;
				// DOコマンドに対してWONTで返答する
				} else if (buff[1] == DO) {
					buff[1] = WONT;
					os.write(buff);
				}
			}
		}
	}

	/**
	 * 実行メソッド
	 * @param host
	 * @param port
	 */
	public void execute(String host, int port) {

		Socket socket = null;
		OutputStream os = null;
		BufferedInputStream is = null;

		try {
			socket = new Socket(host, port);
			os = socket.getOutputStream();
			is = new BufferedInputStream(socket.getInputStream());

			// 23番ポートへの接続の場合ネゴシエーションを行う
			if (port == DEFAULT_TELNET_PORT) {
				negotiation(os, is);
			}

			StreamConnector stdinToSocket = new StreamConnector(System.in, os);
			StreamConnector socketToStdout = new StreamConnector(is, System.out);

			Thread input = new Thread(stdinToSocket);
			Thread output = new Thread(socketToStdout);

			input.start();
			output.start();

		} catch (IOException e) {
			e.printStackTrace(System.out);
			System.err.println("接続中に例外が発生しました");
			System.exit(1);
			// コネクションはクローズしない
			// } finally {
			// try {
			// socket.close();
			// os.close();
			// is.close();
			// } catch (IOException e) {
			// }
		}
	}

	/**
	 * ストリームの読み書きクラス
	 */
	class StreamConnector implements Runnable {

		private InputStream is = null;
		private OutputStream os = null;

		public StreamConnector(InputStream in, OutputStream out) {
			this.is = in;
			this.os = out;

		}

		@Override
		public void run() {
			byte[] buff = new byte[1024];
			try {
				while (true) {
					int n = is.read(buff);
					if (n > 0) {
						os.write(buff, 0, n);
					}
				}
			} catch (IOException e) {
				e.printStackTrace(System.out);
				System.out.println("入出力の書き込み中に例外が発生しました");
				System.exit(1);
			}
		}
	}
}

● 実行クラス

package hhhhhskw.com;

public class TelnetMain {

	/**
	 * mainメソッド
	 * @param args 第一引数:ホスト名 第二引数:ポート番号
	 */
	public static void main(String[] args) {
		String host = args[0];
		int port = Integer.parseInt(args[1]);

		TelnetConnector tc = new TelnetConnector();
		tc.execute(host, port);
	}
}

●実行方法
第一引数にサーバのホスト名(IPアドレス)、第二引数にポート番号を指定し、実行する。

java hhhhhskw.com.TelnetMain (ホスト名orIPアドレス) 23

●実行結果

Kernel 3.10.0-229.el7.x86_64 on an x86_64
(サーバ名) login: root
root
Password: (パスワード)

Last login: Sat Sep  5 07:17:35 on pts/2
[root@ip-xxx-xxx-xxx-xxx ~]# ls -al
total 104
dr-xr-x---.  4 root root  4096 Aug 31 04:23 .
dr-xr-xr-x. 18 root root  4096 Aug 30 07:59 ..
-rw-------.  1 root root  9813 Feb 25  2015 anaconda-ks.cfg
-rw-------.  1 root root  1974 Aug 31 05:34 .bash_history
-rw-r--r--.  1 root root    18 Dec 28  2013 .bash_logout
-rw-r--r--.  1 root root   176 Dec 28  2013 .bash_profile
-rw-r--r--.  1 root root   176 Dec 28  2013 .bashrc
-rw-r--r--.  1 root root   100 Dec 28  2013 .cshrc
-rw-------   1 root root 45711 Sep  5 07:18 nohup.out
drwxr-----   3 root root    18 Aug 30 08:00 .pki
-rw-r--r--   1 root root  7154 Aug 31 02:27 ps.txt
drwx------.  2 root root    28 Aug 30 07:55 .ssh
-rw-r--r--.  1 root root   129 Dec 28  2013 .tcshrc
-rw-------   1 root root   759 Aug 31 04:23 .viminfo
[root@ip-xxx-xxx-xxx-xxx ~]#


【参考URL】
http://www.kg-group.com/JavaContent/Menu/TreeView5150_16/reference/j-network/Telnet.java
Telnetプログラム IT EngineerによるIT生活/ウェブリブログ
CentOSでtelnetできるようにするまでの手順(クライアント・サーバ) | Futurismo
UJP - 技術情報(dotforward.jp)

AWS Summit Tokyo 2015 2日目【EA-08】

AWS Summit Tokyo 2015の2日目に参加した。
下記セミナーに参加したので、まとめ・感想文を書いておく。
※正確な情報・見解は後日公開される(であろう)公式資料や資料作成者のスライド等を参照のこと。

[EA-08: Enterprise Advance]AWSNGN(フレッツ・キャスト)によるハイブリット・ネットワークのメリット ~高速映像配信・教育 PF 事例~

http://www.awssummit.tokyo/session.html#EA-08

【講演概要】

AWS上のサービスをNGN(フレッツ・キャスト)という専用線を使用し、インターネットを介さずに利用するサービスの内容紹介および事例紹介
※6/5付けでサービス試用開始されている。
「プレスリリース」
「フレッツ・キャスト」経由のAWSダイレクト接続検証環境の提供開始について〜パブリッククラウドとの連携強化に向けた取り組み〜 | お知らせ・報道発表 | 企業情報 | NTT東日本

【講演内容】

0.導入

NTT東日本では、伝統的に自前のWebサービスを使用して各種コンテンツ・サービスを提供してきた。
・今回初めてAWSを利用する。

1.何故、AWSを選んだのか

・新サービス立ち上げにあたって、重視したのは「コスト」「サービス」「フレキシビリティ」の3点
→以前は「サーバ設備」や「ネットワーク」という点を重視していた。
・増え続ける動画トラフィックに柔軟に対応したい、という思いがあった。
 インターネットに占める動画トラフィック量は、2013年~2018年で倍になる、と予測されている。
 動画トラフィックの大部分はIPビデオ*1が占めており、今後もこの傾向は続くとみられる。
AWSに限らず、パブリッククラウドの選定に当たって最も重要視したのは「セキュリティ」だった。
→NTT東のお客様と社内の双方から求められた為。結果としては各種AWSのサービスの組み合わせにより担保できた。

2.フレッツ・キャストの紹介

ISPを介さずに、AWSのNWからNTT東日本のNWを通って利用者がサービスを利用することを可能とする。
・全FTTHユーザのうち、7割がNTT西・東を利用している。
・以下の3点を売りとして挙げていた。
①速度    :通信キャリアのVPNより安価で高速
②セキュリティ:回線認証(※IPSecでない)
③品質    :クローズドなNW。24時間365日対応可能。
・4KのVoDが可能となる。
→「ひかりTV 4K」等

3.サービス事例

・サービス適用例として「映像配信」「ICT教育」を挙げていた。
 ①映像配信
 ・会場と配信サーバをNGNで結び、配信はNGNかインターネット経由で行う。
 →現在は映像データをバイク便で運んでいるが、回線に置き換えられる。
 ・課題:IPv6
  AmazonVPC内ではインターナルでIPv6のアドレスを割り当てることができない点が課題。
  →リバプロを立てることで解決したとのこと。IPv6のアドレスの割り当て先としてELBを使用しなかった理由としては、セキュリティ面の都合??
  [参考]http://srad.jp/~ozuma/journal/591374/
     http://d.hatena.ne.jp/rx7/20130925/p1
 ②ICT教育
 ・ICTパッケージとして提供する。学校・学習塾がターゲット。
 →インフラから学習アプリの導入~運用まで提供する。
  「反転学習」*2にも使用できるとのこと。
 ・公教育の分野のため、個人情報の取り扱いに対する要望が強い。

●苦労した点
有識者が少なかったこと
→書籍の情報や「ドコモ・クラウドパッケージ」を活用した。

●良かった点
・IaaS部のセキュリティは外部に協力を仰いだ。(ex.スカイアーチ社)
・サーバ構成を頻繁に変更することができた。
→50~60?回は構成を変更したとのこと。

*1:IP(インターネットプロトコル)ネットワーク経由で配信する動画コンテンツを指す

*2:反転授業 - Wikipedia

AWS Summit Tokyo 2015 2日目【Day2 キーノート】

AWS Summit Tokyo 2015の2日目に参加した。
いくつかセミナーに参加したので、まとめ・感想文を書いておく。
※正確な情報・見解は後日公開される(であろう)公式資料や資料作成者のスライド等を参照のこと。
※長くなったので基調講演のまとめを前に置いた。

Day2 キーノート【同時通訳】

セッションスケジュール - AWS Summit Tokyo 2015 - クラウドで、未来を「今」に。- 2015年6月2~3日 グランドプリンスホテル新高輪にて開催

【基調講演まとめ】

1.AWSはモバイル分野・エンタープライズ分野に注力する。
AWSが言うエンタープライズ分野への注力、というのはIaaS、PaaSレイヤの提供・運用だけでなく、
 VDI環境のフルマネージメントサポート*1や、ファイル共有サービスとメールやカレンダーアプリを業務利用できるレベルでの提供、といったDaaS(※Desktop as a Service)、SaaSなどの上位レイヤのサービスも提供・運用することを指す。
 
2.AWSの各マネージドサービスは「世界で使用されているサービス」という実績がある。
AWSとしての顧客は全世界・多業種にわたる。その顧客ごとのユースケースに対して、AWSは各マネージドサービスを適用してきた実績がある。
 そのため、十分な適用・運用実績を持って顧客にアプローチをかけることが可能となっている。

【基調講演内容】

1.AWSの歴史

[内容]
・2006年からサービス開始。
・サービスの数が飛躍的に増えた。またサポートできるレイヤも増えた。
→2006年:24 2015年:516
・現在、一ヶ月におけるアクティブユーザが100万ユーザに達している。
→ここでいうアクティブユーザというのは、AWSのサービスを実運用しているレベルのユーザの数を指すらしい。

2.AWSのモバイルサービス

[概要]
AWSはモバイルデベロッパー向けのサービスに注力する。
→モバイルビジネスのモデルに沿うようなサービスを提供する、ということ。
 →インフラ*2の抽象化等。本来モバイルデベロッパーが時間をかけるべき箇所はアプリ開発であるので、その他の部分をAWSがサービスとして提供する。

[内容]
AWS LambdaとAmazon Cognitoが6/3より東京リージョンで使用可能となった。(※途中で発表された。)
AWSが提供するモバイルサービスについて、4つの観点から紹介していた。
(1)ユーザ管理
・BtoB, BtoC, MtoMなどの認証情報を扱う認証基盤として、「Amazon Cognito」を紹介した。
→「Amazon Cognito」はID認証およびデータ同期、セキュリティに関する機能を提供する認証基盤サービス。
・イベント(データ)駆動が可能となっており、データ同期を契機としてAWS Lambdaのサービスを実行する、といった使い方ができる。また、他デバイスでの認証状況をCognitoにて参照できるため、デバイス毎のアプリの使用状況の把握が可能となる。

[利用事例]
 ATARI Inc. : フィットネスアプリの認証基盤


(2)ユーザ行動分析
・「Amazon Mobile Analytics」を紹介した。
→デバイスから送られるデータをAnalyticsで受け取り、Amazon S3(データストレージ)とAmazon Redshift(分析基盤サービス)を使用し、過去分のデータとマージした結果をAnalyticsのコンソールに表示するという使い方ができる。

[利用事例]
 (※企業名は失念した) : メッセージのプッシュ基盤と組み合わせ、メッセージにおけるデバイス毎のA-Bテストに使用


(3)SNS Push
Amazon Simple Notification Service (Amazon SNS) を紹介した。
→メッセージのプッシュ基盤サービス。データ駆動が可能となっており、AWS Lambdaと組み合わせることができる。

[利用事例]
 Path Inc. : 1日5億件のメッセージを送信するプッシュ基盤サービスを構築


(4)AWS Lambda
・「AWS Lambda」では、ステートレスなイベント駆動型の処理を作成することができる。
→イベント駆動型のバックエンドサービスを作成することができるため、「インフラの抽象化」が可能となる。
Javaでの記述に対応予定。

3.Deep Dive DynamoDB

[概要]
AWSの川本さんの発表。Amazon DynamoDB紹介

[内容]
Amazon DynamoDBAmazonが社内利用するために作ったNoSQLデータベースシステム。
AWSのIaaSレイヤのサービスを扱いやすいように作られている。
・DynamoDB内に格納したデータを「DynamoDB Streams」として取り出すことができ、StreamデータをAWS Lambdaに読み込ませ、Redshift、Amazon Machine Learning(機械学習サービス)等に出力することで、サーバレスで目的のデータを取り出すことが可能となっている。

4.デベロッパ(コミュニティ)向けサービス

[概要]
AWS Community Heroes 横田さんのマネージドサービスの利用事例紹介。

[内容]
(1)スシロー
予約用モバイルアプリから待ち時間情報を収集・加工し、Amazon Machine Learningへ連携するという事例。他にも回転寿司の皿にセンサーをつけて、売れ方や廃棄を管理しているらしい。*3

(2)ガリバーインターナショナル
ユーザ認証毎?のイベント駆動でモバイルアプリにメッセージをプッシュ配信するという事例

[まとめ]
・両サービスの特徴としては、全てAWSのマネージドサービスで構築されており、EC2を一切使っていないこと。
→プログラミングが不要となる、とのこと。*4
・開発および運用をスピーディーに実行することができる。
→1ヶ月かかっていた作業が3日で完了するようになった。

5.エンタープライズモビリティ

[概要]
Gene Farrellさん(TE-10セッションの発表者)によるマネージドサービスのモバイル端末対応について
※紹介程度だった。

[内容]
・全マネージドサービスについて、モバイル端末に対応する。
→業務システムにおけるモバイルファースト
Amazon WorkDocsが東京リージョンで使用可能となるとのこと。(※途中で発表された。)
→「Amazon WorkDocs」はクラウド上でのファイル共有サービス。


基調講演はここで終了。ランチセッションに移行した。

・協賛企業一覧
f:id:hhhhhskw:20150603232559j:plain

・企業ブース(入り口入ってすぐの所)
f:id:hhhhhskw:20150603232617j:plain

*1:例としては、VDI越しに利用者が使用するユーザ企業やベンダー等が作成した業務アプリの保守を行うためのサービスを提供する等。

*2:ここではBaaSレイヤにあたる

*3:今度見に行こう・・・

*4:業務アプリを作る必要が無くなる、という意であると思われる

Oracle Certified Expert, Java Platform, EE 6 Web Component Developerうかった

Oracle Certified Expert, Java Platform, EE 6 Web Component Developer(OCJ-WC)に合格した。
受験動機としては、生のサーブレットを触ったり、アプリケーションサーバにデプロイする資産構成をまとめたりする機会があり、体系的な知識を得るために勉強しておきたかった為。


【試験名】
Java EE 6 Web Component Developer Certified Expert Exam
Java EE 6 Web Component Developer Certified Expert | Oracle Certification Exam


【勉強方法】
JavaEE6の資格に対応した日本語の問題集がないので、英語の問題集をメインに使用し、その他書籍やWeb上の情報を使いながら勉強した。
問題集を1~2周する中で、都度JavaDocを確認したり、サンプルアプリを作ったりし、問題集の各単元ごとに出てくるコードや設定を確認する、という学習方法をとった。
また不安に感じた部分は検証し、ブログの記事として書いた。
http-method-omissionを試す - ITお絵かき修行
サーブレット3.0のセキュリティ系アノテーションを試す - ITお絵かき修行


【使用した教材 書籍】
1.『Ocejwcd Study Companion: Certified Expert Java Ee 6 Web Component Developer (Oracle Exam 1z0-899)』

Ocejwcd Study Companion: Certified Expert Java Ee 6 Web Component Developer (Oracle Exam 1z0-899)

Ocejwcd Study Companion: Certified Expert Java Ee 6 Web Component Developer (Oracle Exam 1z0-899)

■使用感
ゴツくて重い。確実に力がつく*1
全22章、666ページあるので1周するのが非常にしんどい。
ただこの1冊を理解できたらサーブレット3.0の仕様はマスターした、といっても過言ではないと思う。そのくらい仕様を細かく解説している。
問題の精度はそこそこ。若干細かすぎる感がある。レベル感は少し難しめ(と感じた)。
章ごとの構成は「解説+問題」というオーソドックスな構成。自分は問題のみ使用して、解説は都度読んだ。
誤植が少なからず存在するので、詰まったときは早めに回答を見たほうが精神衛生上良い。

■総評
後で紹介するSJC-WC用の紫本のほうがオススメ。ただし、万全を期したいのであれば買ったほうがいいかも。
自分はこの機会に英語の問題文に慣れる、という気持ち込みで買った。今となっては買ってよかったと思っている。


2.『SUN教科書 Webコンポーネントディベロッパ(SJC-WC) 試験番号:310-083』

SUN教科書 Webコンポーネントディベロッパ(SJC-WC) 試験番号:310-083

SUN教科書 Webコンポーネントディベロッパ(SJC-WC) 試験番号:310-083

■使用感
JavaGoldの紫本より少し薄く、少し重い。
内容自体はJavaEE5時代の試験に対応した内容だが、十分対応できる。
ただしサーブレット2.5と3.0とで仕様が変わってる部分がたくさんあるので、サーブレット3.0に関する情報は別途自分で集めなければならない。
本番の試験でも「ここ紫本でやったところだ…!」的な問題が少なからずあった。レベル感は本番の試験と似ている。(問題によっては若干易しいかも)

■総評
合格したいなら絶対に買うべき。書籍はこれ一冊でもいいかも。


3.『Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)』

Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)

Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)

■使用感
重い。詰まってる感がある。1.の問題集と同時に持ち運ぶのはかなり厳しい。

■総評
試験勉強ではほとんど使わなかった。ただしRESTful Webサービスに関する説明は参考になった。


【使用した教材 Web】
1.サーブレット仕様書、第 3.0 最終版
http://www.cresc.co.jp/tech/java/Servlet_Specifications/Servlet_Specification_3_0_Japanese.pdf

2.Java EE | TECHSCORE(テックスコア)
Java EE | TECHSCORE(テックスコア)

3.サーブレット3.0の新機能(1/5):CodeZine
サーブレット3.0の新機能 (1/5):CodeZine

4.Servlet 3.0 の新機能概要 | 寺田 佳央 - Yoshio Terada
Servlet 3.0 の新機能概要 | 寺田 佳央 - Yoshio Terada

1.2.は初めて勉強する際に一通り目を通した。
3.4.は都度確認する際に使用した。受験直前までお世話になった。


【試験の感想】
・JavaSEの試験のようにコードを追いかけていく試験ではなく、基本的に暗記モノの試験なので時間が足りないと感じることは無かった。
JSP、カスタムタグ、JSTL、EL式、デプロイメント記述子(web.xml)のタグ情報や組み合わせ、設定可能な属性を執拗に聞かれる。なので、その他の部分の割とわかりやすい部分(「Java サーブレットの概要」など)で点を落とさないようにするべき。
・合格ライン61%に対して、正解率71%だった。自分はセキュリティに関するアノテーションが苦手だった。

*1:B5サイズで厚みが3.9cmある。Amazonの配送情報によると1.2kgらしい・・・

RedmineのREST APIを使ってチケット登録・参照

RedmineにはRESTのAPIが用意されており、HTTPボディ部にJSON or xml形式のデータを指定してリクエストを送信することで、チケットに対するCRUD操作ができる。
今回はRedmine公式が配布している「Redmine Java API library」*1というライブラリを使ってチケット登録してみた。


【すること】
RedmineREST APIを使ってチケット登録・参照
→登録は1件単位で、参照は1件単位およびプロジェクトに含まれる全てのチケット情報単位で実行できるようにする。

【環境】
●OS
Windows7 64bit

Redmine
- Redmine 2.6.2
- Apache 2.4.12
- MySQL 5.5.42
- Ruby 2.0.0-p594-i386-mingw32
- Rails 4.2.0
- RubyGems 1.8.12
Redmine Cloud Hosting, Redmine Hosting - Installers and VM
※インストールが面倒だったので上記のオールインワンパッケージを入れた。
ソースコード管理は別立てのGitで行うので入れなかった。

Redmine操作用ライブラリ
Redmine Java API library 2.1.0
taskadapter/redmine-java-api · GitHub

Java
jdk1.8.0_25


Redmine Java API libraryのコンパイルに必要なライブラリ】
ソースをGithubから取得するため、依存関係にあるライブラリは別途取得する必要がある。
Gradleのビルドスクリプトが同梱されているので、実行することで実行資産を作成可能(のはず)。
# Gradleの使い方がわからなかったので、スクリプトを読んでググって人力で集めたorz

1.HttpClient
httpclient-4.4.jar
httpcore-4.4.jar

2.CommonsCodec
commons-codec-1.10.jar

3.fest ※テスティングフレームワーク
fest-assert-1.4.jar

4.SLF4J
slf4j-api-1.6.6.jar
slf4j-log4j12-1.6.6.jar

5.Log4J
log4j-1.2.17.jar

6.org-json-java ※jsonパーサ
org.json-20120521.jar


【事前準備】
1.Redmine側で、管理 → 設定 → 認証 の順で設定ページを表示した後、
[REST による Web サービスを有効にする] にチェックを入れる必要がある。
2.自分のユーザのアクセスキー(APIキー)を確認する。
⇒ 個人設定 ページの右側(デフォルトテーマの場合)に表示されている。


【処理】
1.TicketClient クラス
⇒チケット発行&取得する。
⇒チケット発行においては「題名」が重複する場合はチケットを発行しない仕様としている。発行が目的なので・・・

package sample.redmine;

import java.util.List;
import java.util.Optional;

import com.taskadapter.redmineapi.IssueManager;
import com.taskadapter.redmineapi.RedmineException;
import com.taskadapter.redmineapi.RedmineManager;
import com.taskadapter.redmineapi.RedmineManagerFactory;
import com.taskadapter.redmineapi.bean.Issue;
import com.taskadapter.redmineapi.bean.IssueFactory;
import com.taskadapter.redmineapi.bean.Project;
import com.taskadapter.redmineapi.bean.ProjectFactory;
import com.taskadapter.redmineapi.bean.Tracker;
import com.taskadapter.redmineapi.bean.TrackerFactory;
import com.taskadapter.redmineapi.bean.User;

public class TicketClient {

	private static final String BUG = "バグ";
	private static final String FUNCTION = "機能";
	private static final String SUPPORT = "サポート";

	private RedmineManager redmineMgr = null;
	private IssueManager issueMgr = null;
	private List<User> userList = null;

	private boolean flg = false;

	public void setup(String url, String apiAccessKey) throws Throwable {
		System.out.println("【接続先】" + url);
		redmineMgr = RedmineManagerFactory.createWithApiKey(url, apiAccessKey);
		issueMgr = redmineMgr.getIssueManager();
		userList = redmineMgr.getUserManager().getUsers();
		flg = true;
	}

	private void flgChk() {
		if (!flg) {
			System.err.println("TicketClient#setup(String, String)を先に実行してください。");
			System.exit(1);
		}
	}

	public void putIssue(String projectKey, String trackerName, String subject,
			String description) throws RedmineException {
		flgChk();
		System.out.println("■■■チケットを1件登録します。■■■");
		// チケット名重複チェック
		if(chkSubject(projectKey, subject)){
			System.out.println("題名が重複する場合、チケットは発行しません。題名 :" + subject);
		}else {
			
			// Issue(1チケット)生成
			Issue issue = IssueFactory.create(null);
			// プロジェクト
			setProjectInfo(issue, projectKey);
			
			// 題名
			issue.setSubject(subject);
			// 説明
			issue.setDescription(description);
			// トラッカー
			setTrackerInfo(issue, trackerName);

			// 1チケット登録
			Issue newIssue = issueMgr.createIssue(issue);
			issueMgr.update(newIssue);
			System.out.println("チケットを登録しました。題名 :" + subject);
		}

	}

	private void setProjectInfo(Issue issue, String projectKey) throws RedmineException {
		int intProjectKey = redmineMgr.getProjectManager().getProjectByKey(projectKey).getId();
		Project project = ProjectFactory.create(intProjectKey);
		issue.setProject(project);		
	}

	private void setTrackerInfo(Issue issue, String trackerName) {
		int trackerId = getTrackerId(trackerName);
		Tracker tracker = TrackerFactory.create(trackerId, null); // 第二引数はなくてもよい
		issue.setTracker(tracker);		
	}

	private boolean chkSubject(String projectKey, String subject) throws RedmineException {
		
		List<Issue> issues = issueMgr.getIssues(projectKey, null); // 第二引数はなくてもよい?
		boolean contains = issues.stream().anyMatch(a -> {
			if(subject.equals(a.getSubject())){
				return true;
			}
			return false;
		});
		return contains;
	}

	private int getTrackerId(String trackerName) {
		switch (trackerName) {
		case BUG:
			return 1;
		case FUNCTION:
			return 2;
		case SUPPORT:
			return 3;
		default:
			return 1;
		}
	}

	public List<Issue> getAllIssues(String projectKey) throws RedmineException {
		flgChk();

		System.out.println("■■■チケットを全件取得します。■■■");
		List<Issue> issues = issueMgr.getIssues(projectKey, null); // 第二引数はなくてもよい?
		
		System.out.println("チケットの件数 : " + issues.size());
		issues.stream().forEach(is -> {
			System.out.println("【題名】" + is.getSubject());
		});
		return issues;
	}
	
	public Issue getOneIssue(String projectKey, String subject) throws RedmineException {
		flgChk();
		System.out.println("■■■チケットを1件取得します。■■■");
		List<Issue> issues = issueMgr.getIssues(projectKey, null); // 第二引数はなくてもよい?

		// チケットの探索(無かったらnull)
		Optional<Issue> is = issues.stream().filter(i ->(i.getSubject().equals(subject))).findAny();
		if(is.isPresent()){
			System.out.println("【題名】" + is.get().getSubject());
			return is.get();		
		}
		System.out.println("チケットは取得できませんでした。" );
		return null;	 
	}
}


2.TicketClientTest クラス
⇒TicketClientクラスの各操作メソッドを実行する。

package sample.redmine;

import java.util.List;

import com.taskadapter.redmineapi.RedmineException;
import com.taskadapter.redmineapi.bean.Issue;

public class TicketClientTest {

	/**
	 * mainメソッド
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		TicketClient client = new TicketClient();
		try {
			client.setup("(RedmineのトップページURL)", "(アクセスキー)");

			// 全件取得
			List<Issue> allIssues = client.getAllIssues("(プロジェクトの識別名)");
			// 1件登録
			client.putIssue("(プロジェクトの識別名)", "バグ", "題名題名題名題名題名題名", "説明説明説明説明説明説明説明説明説明説明");
			// 1件取得
			Issue issue = client.getOneIssue("(プロジェクトの識別名)", "題名題名題名題名題名題名");

		} catch (RedmineException e) {
			e.printStackTrace();
		} catch (Throwable th) {
			th.printStackTrace();
		}
	}
}


【実行結果】
●実行前
f:id:hhhhhskw:20150411180844p:plain
※実行前に「前もってきっておいたチケット」というチケットを作成済。

●実行後
f:id:hhhhhskw:20150411180851p:plain


【実行ログ ※標準出力】

【接続先】(RedmineのトップページURL)
■■■チケットを全件取得します。■■■
チケットの件数 : 1
【題名】前もってきっておいたチケット
■■■チケットを1件登録します。■■■
チケットを登録しました。題名 :題名題名題名題名題名題名
■■■チケットを1件取得します。■■■
【題名】題名題名題名題名題名題名


【感想】
Redmine Java API libraryはJSON or XMLデータの取り回しやHttp通信時の例外処理を隠蔽し、1チケット分の情報を取り回すBeanと操作メソッドを用意しているため、かゆいところにだいたい手が届くライブラリだった。
⇒カスタムフィールドの操作も簡単だった。履歴情報が操作できるかは要検証。
 ※JSON or XMLデータをいじって実装していたときは、名前空間やタグに関するドキュメントが少なくよく迷ったので、かなり楽になった。
・チケットを削除する場合はIssueManager#deleteIssueを実行する。
・今回作ったクラスでは、操作側にRedmine Java API libraryのクラスが見えてるので、見えないようにする必要がある。


【参考文献】
Rest api - Redmine
Rest api with java - Redmine
いぬこいのこや Redmineのチケット登録をRedmine REST APIを使ってやってみる
RedmineのREST APIを使ってみる | 世界はどこまでもシンプルである
Redmine(オールインワンパッケージ)のWindowsへの導入メモ - Qiita


Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

*1:Apache License 2.0らしい。

StreamAPIのおさらい - java.util.stream.Collector

JavaSE8で導入されたStreamAPIを使用してできる処理のうち、java.util.stream.Collectorクラスの機能を利用してできる処理を書いてみた。

【やること】
java.util.stream.Collector系クラスのメソッドを使ってできる処理をさらう。
Java Collectorメモ(Hishidama's Java8 Collector Memo) で紹介されているメソッドを全部書いてみる。
・分類分けに関しては蒐集してやんよ java.util.stream.Collectors クラス (1) - Map 以外を返す Collector - - 倭マン's BLOG を参考にさせていただいた。


【環境】
Java
・jdk1.8.0_25
IDE
Eclipse Luna SR1 (4.4.1)


【資産】
具体的な使い方はソースを参照。

1.汎用的なCollector

(1)Collectors#mapping・・・値の変換
(2)Collectors#reducing・・・値の集約
StreamSample/GeneralCollector.java at master · hhhhhskw/StreamSample · GitHub

2.文字列に関するCollector

(1)Collectors#joining・・・文字列の結合
StreamSample/StringCollector.java at master · hhhhhskw/StreamSample · GitHub

3.StreamからCollectionへ変換するCollector

(1)Collectors#toCollection・・・Collectionクラスへ変換
(2)Collection#toList・・・Listクラスへ変換
(3)Collection#toSet・・・Setクラスへ変換
StreamSample/StreamToCollectionCollector.java at master · hhhhhskw/StreamSample · GitHub

4.StreamからMapへ変換するCollectior

(1)Collections#toMap・・・Mapクラスへ変換
(2)Collection#toConcurrentHashMap・・・ConcurrentHashMapへ変換
StreamSample/StreamToMapCollector.java at master · hhhhhskw/StreamSample · GitHub

5.Streamの統計に関するCollector

(1)Collectors#counting・・・Streamの要素数
(2)Collection#min・・・Stream内の最小値
(3)Collectors#max・・・Stream内の最大値
(4)Collectors#summingInt・・・Stream(int)の総和
(5)Collectors#summingLong・・・Stream(long)の総和
(6)Collectors#summingDouble・・・Stream(double)の総和
(7)Collectors#averagingInt・・・Stream(int)の平均
(8)Collectors#averagingLong・・・Stream(long)の平均
(9)Collectors#averagingDouble・・・Stream(double)の平均
(10)Collectors#summarizingInt・・・Stream(int)の統計情報
(11)Collectors#summarizingLong・・・Stream(long)の統計情報
(12)Collectors#summarizingDouble・・・Stream(double)の統計情報
StreamSample/SummaryCollector.java at master · hhhhhskw/StreamSample · GitHub

6.Streamに対して条件判定を適用するCollector

(1)Collectors#partitioningBy・・・指定した条件判定を適用する
(2)Collectors#partitioningByRule・・・指定した条件判定を適用する(返却する値の形式は別に指定する)
StreamSample/PartitioningCollector.java at master · hhhhhskw/StreamSample · GitHub

7.Streamに対してグループ分け

(1)Collectors#groupingBy()・・・グルーピング
(2)Collectors#groupingByConcurrent()・・・グルーピング(戻り値がConcurrentHashMap)
StreamSample/GroupingCollector.java at master · hhhhhskw/StreamSample · GitHub


全てをまとめたクラスは以下
StreamSample/CollectorSample.java at master · hhhhhskw/StreamSample · GitHub


【感想】
・数値系の操作はsummarizingInt等で大体こと足りそう。
→StreamAPI側の実装で詳しく書きすぎると可読性が落ちるので、機能が足りないときは別メソッドに委譲するべき?オレオレCollectorで対応する?
・CollectionsクラスのJavaDoc無いと実装できない・・・
ラムダ式内での型変換で何回かハマった。
ラムダ式で使用する関数インタフェースクラス(Predicateクラス、Functionクラス等)のメソッド仕様を理解してないとハマる。


【参考文献・URL】
Collectors (Java Platform SE 8 )
Collector (Java Platform SE 8 )
Java Collectorメモ(Hishidama's Java8 Collector Memo)
蒐集してやんよ java.util.stream.Collectors クラス (1) - Map 以外を返す Collector - - 倭マン's BLOG

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

StreamAPIのおさらい - java.util.stream.Stream

JavaSE8で導入されたStreamAPIを使用してできる処理のうち、java.util.stream.Streamクラスの機能を利用してできる処理を書いてみた。

【やること】
java.util.stream.Stream系クラスのメソッドを使ってできる処理をさらう。
Java Streamメモ(Hishidama's Java8 Stream Memo) で紹介されているメソッド+αを書いてみる。


【環境】
Java
・jdk1.8.0_25
IDE
Eclipse Luna SR1 (4.4.1)


【資産】
具体的な使い方はソースを参照。

1.Stream内の単一の値に対する順次処理

(1)Stream#map・・・Streamの要素を取り出して変換を行う
(2)Stream#mapToDouble・・・Streamの要素を取り出してdoubleへの変換を行う
(3)Stream#mapToInt・・・Streamの要素を取り出してintへの変換を行う
(4)Stream#mapToLong・・・Streamの要素を取り出してlongへの変換を行う
(5)Stream#floatMap・・・Streamの要素を取り出してStreamへの変換を行う
(6)Stream#flatMapToDouble・・・Streamの要素を取り出してDoubleStreamへの変換を行う
(7)Stream#flatMapToInt・・・Streamの要素を取り出してIntStreamへの変換を行う
(8)Stream#flatMapToLong・・・Streamの要素を取り出してLongStreamへの変換を行う
(9)Stream#forEach・・・Stream内各要素に対して処理を適用する(※処理順の指定なし)
(10)Stream#forEachOrdered・・・Stream内各要素に対して処理を適用する(※処理順の指定あり)
StreamSample/OperateSingleElementStreams.java at master · hhhhhskw/StreamSample · GitHub

2.Streamの内の複数の値に対する順次処理

(1)Stream#reduce・・・Stream内の複数の要素を取り出して処理を行い、単一の結果(戻り値は入力値による)として出力する
(2)Stream#collect・・・Stream内の複数の要素を取り出して処理を行い、コレクション(CollectionやStringBuilderなど)として出力する
(3)Stream#concat・・・Stream同士の連結を行う
StreamSample/OpetrateMultiElementStreams.java at master · hhhhhskw/StreamSample · GitHub

3.Stream内の値に対するソート処理

(1)Stream#sort・・・Streamに含まれる値をソートする
StreamSample/SortStreams.java at master · hhhhhskw/StreamSample · GitHub

4.Stream内の値に対するフィルタリング処理

(1)Stream#filter・・・Streamに含まれる値のフィルタリング
(2)Stream#min・・・Streamに含まれる値のうち、最小値を返却する
(3)Stream#max・・・Streamに含まれる値のうち、最大値を返却する
(4)Stream#count・・・Streamに含まれる要素の数を返却する
(5)Stream#anyMatch・・・Streamに含まれる要素に対して、条件検索(条件に1要素でも合致すればtrue)を行う
(6)Stream#allMatch・・・Streamに含まれる要素に対して、条件検索(条件に完全に一致する場合はtrue)を行う
(7)Stream#noneMatch・・・Streamに含まれる要素に対して、条件検索(条件一致する値がなけれはtrue)を行う
(8)Stream#findFirst・・・Streamに含まれる要素の先頭の値を返却する
(9)Stream#findAny・・・Streamに含まれる要素のいずれかの値を返却する(※並列処理用)
(10)Stream#distinct・・・Streamに含まれる値の重複を排除する
StreamSample/FilteringStreams.java at master · hhhhhskw/StreamSample · GitHub

5.Streamに対する各種操作

(1)Stream#limit・・・Streamに含まれる値の個数を制限(limit)し、Streamを返却する
(2)Stream#skip・・・Streamに含まれる値の開始位置を変更(skip)し、Streamを返却する
StreamSample/OperateStreams.java at master · hhhhhskw/StreamSample · GitHub

6.Streamを生成する

(1)Stream#of・・・指定された値を含むStreamを返却する。
(2)Stream#empty・・・空のStreamを返却する
(3)Stream#generate・・・第一引数で渡された要素を含むStreamを返却する
(4)Stream#iterate・・・第一引数で渡された初期要素に対し、第二引数で指定されたを関数を適用した要素の集合を含むStreamを返却する
StreamSample/CreateStreams.java at master · hhhhhskw/StreamSample · GitHub

7.Streamの形式変更操作

(1)Stream#toArray・・・Straemを配列に変換する ※終端操作
(2)Stream#builder・・・Streamのbuilderを取得する
StreamSample/ChangeTypeStream.java at master · hhhhhskw/StreamSample · GitHub