Spring 4.x MVCで、JSONサービス

JSONサービスに関して、
JerseyかSpring MVCどちらでやるか、かなり悩んでるところなんですが、
出来れば、Spring MVCに集約したと思っているので、
その関連で、調べたことを書こうかと思います。
Spring MVCでのJSONサービスの実装方法についてですが、
Spring 4系に、JSONのサポートが追加されました。
前よりは、簡単に作れる様になってるような気が?します...。
JAXBも使えます。
JAXBに関しては、こちらを参考にして下さい。
JAXBに関して - クロノスの技術系ブログ
@RestControllerというアノテーションが追加されていて、
サービスクラスにそれを付けるとそのクラスがRestのサービスクラスとして扱われます。

@RestController 
public class MyService {
}

ちなみに@Controllerだと、エラーになってしまい駄目です。
Spring 4系からは、@RestControllerを使う様にする必要があります。
次にメソッドは、今まで通り、@RequestMappingで、パス、メソッドなどの設定を書きます。

@RestController 
public class MyService {
	@RequestMapping(value = "/json", method = RequestMethod.GET, produces="application/json")
	public PersonResource json() {
		ObjectFactory factory = new ObjectFactory();
		PersonResource personResource = factory.createPersonResource();
		List<Person> personList = personResource.getPerson();
		PersonResource.Person person = new PersonResource.Person();
		person.setId("100");
		person.setName("AAAAAA");
		personList.add(person);
		return personResource;
		return getResource();
	}
}

JAXBで検証して見ました。
問題なく動作したので、こんな感じで問題ないと思います。
applicationContext.xmlの設定は、Spring MVCの設定のままで問題ありません。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:lang="http://www.springframework.org/schema/lang"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-4.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/jee
		http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
		http://www.springframework.org/schema/lang
		http://www.springframework.org/schema/lang/spring-lang-4.0.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
	<mvc:annotation-driven />
	<!-- Autowire -->
	<context:component-scan base-package="chronos.system" scoped-proxy="targetClass"/>
	<!-- AOP -->
	<aop:aspectj-autoproxy/>
</beans>

web.xmlの設定もSpring MVCと同様の設定になります。

<web-app id="webflow" version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<filter>
		<filter-name>characterEncodingFilter</filter-name>
			<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>characterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/config/spring/applicationContext.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>dispatcher</servlet-name>
			<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
			<init-param>
				<param-name>contextClass</param-name>
				<param-value>
					org.springframework.web.context.support.AnnotationConfigWebApplicationContext
				</param-value>
			</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

しかし、問題は、JSON-Pなんですが、結構がんばらないとまだ駄目ぽいので、
Jerseyの方が楽だなぁと思ったりしてます。

Java SE8 Stream APIに関して

Lambda式ともに、Stream APIJava SE 8に追加されています。
この2つの機能が追加されたことにより、
Collection Libraryの操作に関して、強化されました。
なにが強化されたか?と言うと、
Collectionは、それに含まれる要素を操作する場合、イテレータを使って、処理します。
拡張for文がそれにあたるのですが、これは、外部イテレータと言われるやり方で、
今度の追加されたLambda式とStream APIを使うやり方は、内部イテレータになるのだと思います。
例えば、下の様なPersonクラスのリストを処理する場合

public class Person {
	private String id;
	private String name;
	public Person(String id, String name) {
		this.id = id;
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public String getName() {
		return name;
	}
}

今までは、拡張for文(外部イテレータ)で処理していました。
下の様に拡張for文で、リストから順次取り出して、処理するやりです。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		for (Person person : personList) {
			System.out.println("id : "+person.getId()+" name : "+person.getName());
		}
	}
}

今度の追加されたLambda式とStream APIを使うやり方の場合、
下の様な記述になります。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		Stream<Person> stream = personList.stream();
		stream.forEach(p -> System.out.println("id : "+p.getId()+" name : "+p.getName()));
	}
}

下の様な書き方もできます(恐らく、こちらの方がいいかなぁ)。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		personList.stream().forEach(p -> System.out.println("id : "+p.getId()+" name : "+p.getName()));
	}
}

ちなみにマルチコアの対応も入っていて、
通常、下の場合は、シングルスレッドで動いているので、結果も順次処理された内容になります。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		personList.stream().forEach(
				p -> {
					try {
						Thread.sleep(3000);
						LocalTime time = LocalTime.now();
						System.out.println("time : "+time.toString()+" id : "+p.getId()+" name : "+p.getName());
					} catch (Exception e) {
					} 
				});
	}
}
time : 01:17:45.374 id : 100 name : person1
time : 01:17:48.376 id : 200 name : person2
time : 01:17:51.377 id : 300 name : person3
time : 01:17:54.378 id : 400 name : person4

stream()をparallelStream()に変更するだけで、並列で動くようになります。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		personList.parallelStream().forEach(
				p -> {
					try {
						Thread.sleep(3000);
						LocalTime time = LocalTime.now();
						System.out.println("time : "+time.toString()+" id : "+p.getId()+" name : "+p.getName());
					} catch (Exception e) {
					} 
				});
	}
}
time : 01:19:33.308 id : 400 name : person4
time : 01:19:33.308 id : 100 name : person1
time : 01:19:33.308 id : 200 name : person2
time : 01:19:33.308 id : 300 name : person3

Java SE8以前は、このようなことをする場合、開発がかなりがんばらないいけなかったのですが、
今回の機能追加で、簡単に書けるようになりました。
ただし、スレッドセーフかどうかは、気をつけない駄目ですけど!
例えば、Listに格納されたエンティティをDTOのListに入れ替えるとの場合、
下の様に書くと問題がでます。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		List<PersonDto> personDtoList = new ArrayList<>();
		personList.parallelStream().forEach(
				p -> {
					personDtoList.add(new PersonDto(p.getId(), p.getName()));
				});
	}
}

ArrayListは、スレッドセーフでは、ないので、addメソッドを呼ぶと、内部で、サイズを拡張しているのですが、
この時、並列で動いている場合、indexがおかしなったりして、エラーになったりします。
恐らく、数件だったら大丈夫かもしれませんが、かなりの件数を処理すると現象が出るかもしれません。
なので、下の様に、synchronizedをかます必要があるかと思います。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		List<PersonDto> personDtoList = new ArrayList<>();
		personList.parallelStream().forEach(
				p -> {
					synchronized(personDtoList) {
						personDtoList.add(new PersonDto(p.getId(), p.getName()));	
					}
				});
	}
}

次にOptionalというものがあって、中間処理みないな機能があります。

メソッド 機能
filter(Predicate predicate) 条件にマッチ(true)した場合のみを対象にする
limit(long maxSize) 最初の要素からmaxSizeまでを対象にする
distinct() 重複要素を除外する
map(Function function) 処理により生成された要素をStreamとし戻します
flatMap(Function> function) 処理により生成された複数の要素をStreamとして戻します
sorted(Comparator comparator) Comparatorの結果を元に並び替えして、Streamを戻します
Stream APIのfilterの使い方。

1〜10までの整数を格納したリストの内、5以上のみ表示する処理の例です。

public class Main {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		list.stream().filter(s -> s > 5).forEach(System.out::println);
	}
}
Stream APIのlimitの使い方

1〜10までの整数を格納したリストの内、一番最初要素から3つまで表示する処理の例です。

public class Main {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		list.stream().limit(3).forEach(System.out::println);
	}
}
Stream APIのdistinctの使い方

重複した要素がある場合、重複したもの除いて、結果を表示する処理の例です。

public class Main {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		list.stream().distinct().forEach(System.out::println);
	}
}

このdistinctは、Objectクラスのequalsメソッドで、比較をしているので、
POJOのリストから重複を除外する場合は、equalsメソッドをオーバーライドする必要があります。
例えば、下の様に、Personオブジェクトのリストをdistinctを使って、重複を除外する場合、
equalsメソッドをオーバーライドしないままでは、
重複は、除外されずに、全て表示されてしまいます。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = getPersonList();
		personList.stream().distinct().forEach(System.out::println);
	}
	public static List<Person> getPersonList() {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person(200, "二郎"));
		personList.add(new Person(100, "一郎"));
		personList.add(new Person(400, "四郎"));
		personList.add(new Person(300, "三郎"));
		personList.add(new Person(200, "二郎"));
		personList.add(new Person(100, "一郎"));
		personList.add(new Person(400, "四郎"));
		personList.add(new Person(300, "三郎"));
		return personList;
	}
}

重複を除外する場合は、下の様に、equalsメソッドをオーバーライドする必要があります。

public class Person {
	private Integer id;
	private String name;
	public Person(Integer id, String name) {
		this.id = id;
		this.name = name;
	}
	public Integer getId() {
		return id;
	}
	public String getName() {
		return name;
	}
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Person) {
			return ((Person) obj).getId().equals(id) && 
					((Person) obj).getName().equals(name);
		}
		return false;
	}
}
Stream APIのmapの使い方

Listに格納されたエンティティをDTOのListに入れ替える処理の例です。

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("100", "person1"));
		personList.add(new Person("200", "person2"));
		personList.add(new Person("300", "person3"));
		personList.add(new Person("400", "person4"));
		List<PersonDto> personDtoList = new ArrayList<>();
		personList
			.stream()
			.map(s -> {
				PersonDto personDto = new PersonDto(s.getId(), s.getName());
				return personDto;})
			.forEach(personDto ->  personDtoList.add(personDto));
	}
}

mapの処理結果がListなどの場合は、flatMapを使うぽいです。

Stream APIのsortedの使い方

Listに格納された要素を比較して、並び替える例

public class Main {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("100", "あああ"));
		personList.add(new Person("200", "えええ"));
		personList.add(new Person("300", "いいい"));
		personList.add(new Person("400", "ううう"));
		List<PersonDto> personDtoList = new ArrayList<>();
		personList
			.stream()
			.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
			.forEach(p -> System.out.println("id : "+p.getId()+" name : "+p.getName()));
	}
}

上記以外にも、まだいろいろなメソッドがあるぽいのですが、
それは、まだ調べてないので、今度、調べて、更新することにします。

Java Day Tokyo 2014へ潜入してきた!

f:id:chronos_snow:20140524224454j:plain
毎年開催されている日本のJavaのお祭りイベント、Java Day Tokyoへ行ってきました。
今回は、品川プリンスホテルでの開催です!
品川とか、あんまり行かないから、若干、迷子になったwww。

基調講演

基調講演からの参戦です!
会場は、めちゃ広い!でかいモニターもいっぱいで、凄かった!
基調講演は、Java SE8、JavaEE、組み込み関連とか、ロードマップ的なものが発表されました。
f:id:chronos_snow:20140522105230j:plain
組み込み系の話が多かった気がします。
JavaEEに関しては、さらっと終わって、あんまり印象に残ったものは、なかった気がする。
いろいろなデモが行われたので、結構、良かったかなぁ。
全部、組み込み関連のデモでしたがwww。
ロボット系、多かったなぁ。
NECだったけ?ロボットのデモがありました。
これは、結構、びっくり!!
Javaって、そっち系でもいけるのか!と思った。
組み込み系は、未知の領域だけど。
f:id:chronos_snow:20140522103900j:plain
それと、レゴを使ったロボットのデモとかもありました。
これも組み込みで、レゴのラジコンみたいなぁやつ!
f:id:chronos_snow:20140522111512j:plain
Java FX 8のチェスのデモとか!
このチェスのデモは、画像がめちゃ綺麗だった。
しかし、Java FXとか、どうなんでしょう?いろいろ使われてるのかなぁ?
f:id:chronos_snow:20140522112758j:plain

Java SE8 概要

Java SE8に追加された機能の簡単な説明でした。
下のものに関して、話をさせてたと思います。
Lambda式
StreamAPI
Default Method
Date And Time API
Annotations on Java Types
JavaScriptエンジン(Nashorn)
JavaFX 8
Compact Profiles

それと、Java SE8のAPIドキュメントの日本語版が公開されたみたいです。
Java Platform Standard Edition 8ドキュメント
完全には、日本語化していないようです。
順次、対応していくそうですが...。
f:id:chronos_snow:20140522135239j:plain

Lambda式 概要

Lambda式に関しての概要レベルの説明でした。
ソースとかで、いろいろ説明してましたが、ソースコードが断片的で、Lambda触ったことがない人は、
わかりづらかったかもしれない。
やっぱり、デモとかあるといいかなぁ....と思いました。
主に内部クラスの今までの記述とLambda式で記述した場合の違いに関しての説明でした。
f:id:chronos_snow:20140522145859j:plain

TDD

テスト駆動のセッションを聞いてきました。
Junitとかで、テストしてる人?の質問には、
参加したほとんどの人がテスト書いているぽかったなぁ。
自動化となると、一気に減ったけど。
わからないではないけど。
CIに乗せるが、結構大変なのは、確かだし。
それと、結合テストツールに関しては、いいこと教えてもらったので、
良かったです。
今度、やってみようかと思います。
f:id:chronos_snow:20140522160632j:plain

Lambda式とStream API

主にStream APIに関してのセッションで、コレクション関連の話でした。
これもソースとかで、いろいろ説明してましたが、ソースコードが断片的で、
さわったことがない人は、わかりづらかったかもしれません。
これは、コレクションの操作方法が、かなり変わるので、
実際、移行とかは、めちゃ大変だろうなぁ。
新規プロジェクトとかは、Java 8で組みたいけど、ミドルが、まだ、揃わないし...。
悩みますね。
f:id:chronos_snow:20140522165528j:plain

Java SE8 Lambda式に関して

Lambda式は、Java SE8に追加された新しい機能です。
Lambda式は、1つのメソッドインターフェイスの処理を簡素的な方法で、記述できます。
これが導入されたことにより、一番、影響を受けているのは、
コレクションライブラリからのデータ操作だと思います。
コレクションライブラリの記述が大幅に簡素に記述出来る様になっています。
コレクションライブラリの操作は、以前は、シングルスレッドの処理だけで、
並列処理をしたい場合は、自分で、並列処理出来るように記述する必要がありましたが、
Lambda式で記述すると、簡単に並列処理出来る様に記述することができます。
これにより、マルチコア環境でのパフォーマンスを向上させることができます。
また、コレクションライブラリ以外も内部クラスの記述に関しても
Lambda式で、簡素的に記述することが出来る様になっています。

内部クラスの記述方法から書こうと思います。
内部クラスですが、今までは、内部クラスを書く場合、数行ぐらい記述する必要がありました。
それを単一のステートメントで記述出来るようになり、
内部クラスを使う場合の長い定義から解放されます。
例えば、下の用なインターフェイスがあるとします。

@FunctionalInterface
public interface Hello {
	String getMessage();
}

これを内部クラスとして、実装した場合、
以前は、こんな感じで定義する必要がありました。

public class HelloTest {
	public static void main(String[] args) {
		Hello hello = new Hello() {
			@Override
			public String getMessage() {
				return "Hello Lambda Expressions ";
			}
		};
		System.out.println(hello.getMessage());
	}
}

Lambda式で書くとこうなります。

public class HelloTest {
	public static void main(String[] args) {
		Hello hello = () -> {return "Hello Lambda Expressions";};
		System.out.println(hello.getMessage());
	}
}

引数をとるインターフェイスの場合

@FunctionalInterface
public interface Check {
	boolean isCheck(int a, int b);
}

Lambda式で書くとこうなります。

public class CheckTest {
	public static void main(String[] args) {
		Check check = (a, b) -> {return a == b;};
		System.out.println(check.isCheck(100, 100));
	}
}

ちょっと、今までと結構違いますが、
1つのメソッドインターフェイスの定義を元に、型、引数などを内部で、
自動的に判断されます。
Lambda式で書く場合は、メソッド名などは、必要なく、
そのメソッドの引数とその処理を記述するだけで、すみます。
基本構文は、下の様な定義になっています。

(型 引数名, 型 引数名,...) -> {処理};

また、省略系の場合は、このような定義になります。

(引数名, 引数名,...) -> 処理;

Lambda式のざっくりしたパターンは、こんな感じです。

引数リスト アロー演算子 本文
() -> "hello"
(String s -> {return s;}
(s) -> s
(int a, int b) -> {return a == b;}
(a, b) -> a == b

Lambda式で、再帰処理も可能です。
なんらかの文字にある文字を設定した回数付加するような処理をLambda式で、再帰的に書く場合は、
下の様な感じになります。

@FunctionalInterface
public interface Recursive {
	String append(String str, String addStr, int start, int end);
}
public class Main {
	private Recursive recursive;
	public static void main(String[] args) {
		new Main().run();
	}
	public void run() {
		recursive = (str, addStr, start, end) -> start > end?str:recursive.append(str+addStr, addStr, ++start, end);
		System.out.println(recursive.append("A", "B",0, 10));
		
	}
}

Genericsを使ったインターフェイスにもLambda式を利用することもできます。
下の様なGenericsを使ったインターフェイスを定義します。

@FunctionalInterface
public interface Generics<T, V> {
	T test(V v);
}

Lambda式で、上記のインターフェイスを利用した例は、下記の定義です。

public class GenericsTest {
	public static void main(String[] args) {
		Generics<String, String> generics1 = obj -> obj.toLowerCase();
		System.out.println(generics1.test("BBBB"));
		Generics<String, Integer> generics2 = obj -> obj.intValue() > 100?"UP":"DOWN";
		System.out.println(generics2.test(90));
	}
}

最後に、Lambda式で、いろいろなことが出来ますが、制約がないわけでは、ありません。
Lambda式から外側の変数へのアクセスは、問題ありませんので、下記の様な記述は、できますが、

@FunctionalInterface
public interface Sample {
	String test();
}
public class SampleTest {
	public static void main(String[] args) {
		int arg = 13;
		Sample sample = () -> arg > 100?"up":"down";
		sample.test();
	}
}

下の様な記述は、できません。

public class SampleTest {
	public static void main(String[] args) {
		int arg = 13;
		String msg = "";
		Sample sample = () -> arg > 100?msg = "up":msg = "down";
		sample.test();
	}
}

Lambda式では、Lambda式から外側の変数へのアクセスは、出来ても、
Lambda式から外側の変数への変更は、出来なようになっています。

コレクションライブラリでのLambda式は、次のエントリーで書こうかと思います。

【第2回】スクラムナイトセミナーに潜入

前は、 Javaの技術系のセミナーとか良く行ってたんだけど、
最近は、スクラムで回すことが多くなって、
スクラム関連のセミナーに参加することが多くなってきた!!
それで、今回は、スクラムナイトセミナーに参加してきました。
スクラムセミナーは、毎回、ドキドキする。
自分なんかで、本当に大丈夫かなぁ…とかwww。
でも自分が抱えていることを共有して、他のスクラム実践者の方の意見を
すごく聞きたいし、他の人は、どんな課題があるんだろうと、かなり興味があるし。
それで、セミナーですが、ディスカッション形式でした。
最初に、全員の思っていることを付箋に書いて、発表して、
ホワイトボードに貼って行く作業を行いました。
f:id:chronos_snow:20140321021623j:plain
その中で、4つに絞り込んで、興味がある議題ごとにわかれて、議論する感じでした。
テーマは、下の4つでした。

  • スクラムマスターのキャリアパス
  • チームがプロセスを気にしない時どうする
  • チームやプロダクトのためのメトリクス
  • スプリントのゴール

自分は、チームがプロセスを気にしない時どうするのテーマのチームに参加しました。
プロセス改善に関しては、かなり興味があるし、
前のエントリでもスクラムマスターとは?みたいなことも書いてたりするのでwww
スクラムマスターとは、なんなのか? - クロノスの技術系ブログ
f:id:chronos_snow:20140321021624j:plain
いろんな意見が出てました。
東京と大阪の拠点があって、それぞれ動いている場合のスクラムは?
大阪の方は、スクラムが浸透していない。
離れているからコミュニケーションがとりずらい。

  • 大阪にもSMを置く。
  • バーダウンチャートとかで、成果を見せて、浸透させて行く。
  • うまく行ったベロシティをオーバーアクションで、声に出してく。
  • 共有できるツールを導入してみる。

開発プロセスを改善するには?
そもそもうまく行っているから、あんまり改善しようとは思わない。
振り返りの議論が活発にならない。

  • 振り返りのやり方を変えてみる (KPT以外のフレームワーク使ってみるとか)。
  • スプリントの目標を2倍にしてみる。
  • POにもっとプレッシャーをかけてもらう。
  • 評判がわるいPOを連れてくるwww。
  • 技術でも目標でもいいから挑戦的な目標設定。

などなど、参考になる意見やおもしろ意見が聞けて、楽しかった。
それで、最後に議論の結果を発表して、親睦会で終わりでした。
f:id:chronos_snow:20140321021625j:plain

スクラムマスターとは、なんなのか?

スクラムマスターとは、なんなのか?のエントリーです。
スクラムマスターは、PMでもないし、開発メンバーでもない。
じゃあ何なの?
最近は、あんまりないけど、
初期の頃は、これじゃ駄目じゃんと思ったことが多々あった気がする。
スプリントを回してく中で、
お前は、スクラムマスターじゃない!と自分に突っ込みを入れる時の
トップ3が、多分、これ。
① POと仕様調整したり、仕様の検討をいつのまにかしている。
② 仕様とか開発方法について、メンバーと議論してしまっている。
③ メンバーに指示してしまっている。
まぁ、今は、ないけど。
この事象が出やすい場合、そもそもスクラムマスターじゃなく、
POとか開発リーダーをやるべき!
じゃあ、スクラムマスターとして、
ユーザに短期間で、いいものを提供したと思って、意識していることは?
多分、これ!
① 開発のプロセスで、改善できる箇所があるか?
② 安定的な生産性を確保するには、どうすればいいか?
③ 品質を確保するには、どうすればいいか?
④ チームメンバーが成長できるには、どうすればいいか?
これについては、技術的なサポートが必要な部分が結構多い気がする。
自分の技術的な知識とか経験は、ほとんど Javaでの開発がしめているわけで、
なんとなくスクラムマスタとして、機能するのは、Javaでの開発プロジェクトかもしれない。
多分、実際そうだと思う。
でも世の中には、全然技術よりじゃないスクラムマスタもいるわけなんだけど。
自分では、サポートできない部分は、他の詳しい人に一時的に入ってもらって、
少し手伝ってもらったり、ワークショップ的なものをお願いしたりできる状況なら、
それでもいいと思う。

Jersey 2.x系でのJSONサービスに関して

Jerseyのバージョンアップで、いろいろ大変だったから残しておく。
えっと、なんか2系から結構変わってるwww。
かなり違うのと、設定がよくわからんかったけど、
なんとか動く様には、なった。
厳密には、まだ、完全に調べきれてないけど。
もともとのソースがSpringとの連動とかやってたので、
若干大変だったかなぁ。
それと、公式ドキュメントがかなり読みづらかった気がする。
設定とかが分散していて、Try ant Errorみたい感じになってしまった。
それで、とりあえずのmavenのpomの設定。

<dependency>
	<groupId>org.glassfish.jersey.containers</groupId>
	<artifactId>jersey-container-servlet</artifactId>
	<version>2.6</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.media</groupId>
 	<artifactId>jersey-media-json-jackson</artifactId>
	<version>2.6</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.media</groupId>
 	<artifactId>jersey-media-json-processing</artifactId>
	<version>2.6</version>
</dependency>

Springも使うから、下の設定も追加します。

<dependency>
  <groupId>org.glassfish.jersey.ext</groupId>
   <artifactId>jersey-spring3</artifactId>
   <version>2.6</version>
   <exclusions>
     <exclusion>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
     </exclusion>
     <exclusion>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
     </exclusion>
     <exclusion>
       <groupId>org.springframework</groupId>
       <artifactId>spring-web</artifactId>
     </exclusion>
   </exclusions>
</dependency>

それと、JAXBも使うので、下のリンクの設定も追加します。
JAXBに関して - クロノスの技術系ブログ
これで、とりあえずMavenの設定は、完了。
以前、書いたエントリーをバージョンアップする方法で書こうかと思います。
以前のエントリーは、下にあります。
JerseyでのJSONサービス - クロノスの技術系ブログ
1.x系の時は、こんな感じで、記述していました。

@Path("/service")
public class JsonService {
   @GET
   @Path("/get")
   public JSONWithPadding get(
      @QueryParam("callback") @DefaultValue("callback") String callback)  {
      CategoryResouce resouce = ObjectFactory().createCategoryResouce();
      List<CategoryList.Category> categoryList = resouce.getCategory();
      CategoryList.Category category1 = new CategoryList.Category();
      category1.setKey("1000");
      category1.setValue("AAAAA");
      categoryList.add(category1);
      CategoryList.Category category2 = new CategoryList.Category();
      category2.setKey("1000");
      category2.setValue("AAAAA");
      categoryList.add(category2);
      return new JSONWithPadding(resource, callback);
   }
}

2.x系からJSONを戻す場合、JSONWithPaddingではなく、アノテーションになります。
そもそもJSONWithPaddingクラスは、ないようです。
あと、JSONPのコールバックの設定も若干違います。
なので、下の書き方になります。

@Path("/service")
public class JsonService {
   @GET
   @JSONP
   @Produces({"application/json", "application/javascript"})
   @Path("/get")
   public CategoryResouce get(
      @QueryParam(JSONP.DEFAULT_QUERY) String callback)  {
      CategoryResouce resouce = ObjectFactory().createCategoryResouce();
      List<CategoryList.Category> categoryList = resouce.getCategory();
      CategoryList.Category category1 = new CategoryList.Category();
      category1.setKey("1000");
      category1.setValue("AAAAA");
      categoryList.add(category1);
      CategoryList.Category category2 = new CategoryList.Category();
      category2.setKey("1000");
      category2.setValue("AAAAA");
      categoryList.add(category2);
      return resource;
   }
}

サービス自体は、これで、終わりなんですが、
web.xmlも前と書き方が違うようで、

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/config/spring/applicationContext.xml</param-value>
</context-param>
<servlet>
  <servlet-name>SpringApplication</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>SpringApplication</servlet-name>
  <url-pattern>/service/*</url-pattern>
</servlet-mapping>

こんな定義です。
まぁ...ここまでは、公式ドキュメント読んで、書けたんですけど、
案の定、動かんない...
別の設定がいるようで、コンテナの設定がですね...
設定クラスが必要になるみたいで、

package com.chronos.web.resource;

import javax.json.stream.JsonGenerator;

import org.glassfish.jersey.jsonp.JsonProcessingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;

public class WebApplicationResourceConfig extends ResourceConfig {
	public WebApplicationResource() {
		register(JsonProcessingFeature.class);
		packages("com.chronos.web.resource");
		register(RequestContextFilter.class);
		property(JsonGenerator.PRETTY_PRINTING, true);
	}
}

JAX-RSのプロバイダがどのパッケージ配下にあるか設定する必要があって、
その設定クラスを書かない駄目みたいです。
それで、作成した設定クラスをweb.xmlのコンテナのところに
定義しないといけない仕様になってました。

<servlet>
  <servlet-name>SpringApplication</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>com.chronos.web.resource.WebApplicationResourceConfig</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

これで動くところまでは、確認しましたが、
なぜかXML形式で戻ってしまう?
あら?でも起動とか、呼び出してもエラーにならん?
下のライブラリーも必要なようです。

<dependency>
	<groupId>org.glassfish.jersey.media</groupId>
	<artifactId>jersey-media-moxy</artifactId>
	<version>2.6</version>
</dependency>

おお!JSONで、戻るようになった。
Springは?
これはですね、jersey-spring3のライブラリがあるとですね、
コンテナの起動ログをデバックしてたら
自動的にSpringの設定がよまれて、Jerseyと自動的に連動される仕組みになっている
ようでした。