hatenob

プログラムって分からないことだらけ

一人Web開発~第8夜 共通ライブラリ

ようやくプログラミングに入ります。
Eclipseを使いますが、細かい設定についてはここでは触れません。
Mavenが使えるようになっていれば最低限ことは足りると思います。
ターゲットはWildflyに固定していることもあり、面倒なのでJBoss Toolsを入れてしまうのが楽だと思います。

せっかくのWebアプリなのでいきなり画面とか書きたくなってしまうかもしれませんが、とりあえずプロジェクト構成を下のように決めました。

  • oneman-utils:共通ライブラリ
  • oneman-ejbEJBライブラリ
  • oneman-web:Webアプリケーション

utilsとejbは他のアプリケーションでも使いまわすことを念頭に分けてあります。
utilsは部品、ejbトランザクション処理で共通的に使えそうなものを入れ込んでいくことにします。
ただし、このシリーズの最初に書いた通り、作るアプリケーション自体に意味はなく、適当に作るのでそこのところご注意ください。
セキュリティとか開発効率とかは全く無視であり、責任ももちません。

Producer

CDIを使うので、Loggerとかいちいち各クラスに書きたくないよね、ということでLoggerProducerなるものを用意してあげます。Loggerにはslf4jを使うことにします。Wildflyが有しているロギングフレームワークを使ってもよいのですが、個人的にはslf4jのAPIのほうが使いやすいので。Wildflyはslf4jの実装として自身のロギングフレームワークを使ってくれるので、logbackのような実装は使用しないことにします。

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Named;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
@Dependent
public class LoggerProducer {
	@Produces
	public Logger getLogger(InjectionPoint ip) {
		return LoggerFactory.getLogger(ip.getMember().getDeclaringClass());
	}
}

getLoggerメソッドに@Producesを付けておいて、Loggerを使用するアプリケーション側では、

@Inject
private Logger log;

みたいにしておけばOKです。
同じように、EntityManagerもInjectできるようにしておきます。

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Named
@Dependent
public class EntityManagerProducer {
	@PersistenceContext
	@Produces
	private EntityManager em;
}

こちらはメソッドですらなし。
CDIの組みを理解していないので、なんでこれで各クラスのフィールドにあるEntityManagerにセットされるのか、おじさんには不思議です。

テスト

コードを書いたらテストしなきゃいかん。いや、本当はコードを書く前にテストを書かないと、TDD界隈の方から叱責を受けてしまいかねません。とにかく、テストを書きましょう。
ここでは、「正しくInjectされるか」をテストしたいかもしれませんが、最初は一番粒度の細かい、「メソッドが正しく動作するか」に観点を向けてテストしましょう。となると、EntityManagerProducerはメソッドないのでテストできませんので、LoggerProducerだけテストします。
LoggerProducerの役割は、Injectされた時の自身のクラスでLoggerを作成するというものですが、つまりはInjectionPointから取得できたものでLoggerを作るだけです。InjectionPointのインスタンスを作ってどうこうするのは面倒なので、jMockitを使ってモック化し、手っ取り早く処理内容のテストだけすることにします。

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.lang.reflect.Member;

import javax.enterprise.inject.spi.InjectionPoint;

import mockit.Expectations;
import mockit.Mocked;

import org.junit.Test;
import org.slf4j.Logger;

public class LoggerProducerTest {
	private LoggerProducer producer = new LoggerProducer();
	@Mocked
	private InjectionPoint mockInjectionPoint;
	@Mocked
	private Member mockMember;

	@Test
	public void should_produce_logger() {
		new Expectations() {
			{
				mockInjectionPoint.getMember();
				result = mockMember;

				mockMember.getDeclaringClass();
				result = LoggerProducerTest.class;
			}
		};

		Logger logger = producer.getLogger(mockInjectionPoint);

		assertThat(LoggerProducerTest.class.getName(), is(logger.getName()));
	}
}

InjectionPointとそこから取得されるMemberをモック化しておいて対象のメソッドを呼び出しテストします。
member.getDeclaringClass()のresultにセットしたクラスがLoggerクラスとして正しくセットされているかをテストしています。
まぁこれだけならあまり意味のないテストかもしれませんが、内容は無視して説明に事足りるだけのものを作って突き進むことにします。
一応、pom.xmlはこんな感じになります。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.oneman</groupId>
	<artifactId>oneman-utils</artifactId>
	<version>0.0.1</version>
	<name>oneman utilities</name>
	<description>oneman utilities</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<jdk.version>1.7</jdk.version>
		<repository.url>http://${repository.host}/nexus/content/repositories</repository.url>
	</properties>

	<distributionManagement>
		<repository>
			<id>nexus</id>
			<name>Releases</name>
			<url>${repository.url}/releases/</url>
		</repository>
		<snapshotRepository>
			<id>nexus</id>
			<name>Snapshots</name>
			<url>${repository.url}/snapshots/</url>
		</snapshotRepository>
	</distributionManagement>

	<dependencies>
		<!-- test -->
		<dependency>
			<groupId>com.googlecode.jmockit</groupId>
			<artifactId>jmockit</artifactId>
			<version>1.6</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.5</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.0.9</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>7.0</version>
			<scope>provided</scope>
		</dependency>

	</dependencies>

	<reporting>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-report-plugin</artifactId>
			</plugin>
		</plugins>
	</reporting>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.1</version>
					<configuration>
						<source>${jdk.version}</source>
						<target>${jdk.version}</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

</project>

これであとはEclipseからでもコマンドプロンプトからでも、

$ mvn deploy

とすれば前回設定したNexusにライブラリを公開することができます。

次はEJBJPAです。