読者です 読者をやめる 読者になる 読者になる

割と普通なブログ

Microsoft Azure や ASP.NET、Java EE 系の話題を記載します

仮想ネットワークに参加した Worker ロールの Java アプリに ILB 経由で通信する

前回記載した Eclipse を利用して Worker ロールの Java アプリを仮想ネットワークに参加させる - 割と普通なブログ
の記事では Worker ロールの Java アプリを仮想ネットワークに配置したが、疎通自体はクラウドサービスの FQDN(xxxx.cloudapp.net)でアクセスするため「閉域」での疎通ではなかった。
今回は前回の記事を一歩進めて、閉域での通信をする Azure 内部負荷分散 (ILB) を利用する方法を紹介する。

設定ファイルの編集

Worker ロール(はもちろん Web ロールを含むクラウドサービス)を仮想マシンに参加させ、ILB の設定をする場合は ServiceConfiguration.cscfg と ServiceDefinition.csdef の作成が必要になる。記載例は以下になる。

  • ServiceConfiguration.cscfg
<?xml version="1.0" encoding="UTF-8"?>
<ServiceConfiguration
	xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"
	osFamily="4" osVersion="*" serviceName="AzureDeploymentProject">
	<Role name="WorkerRole1">
		<Instances count="2" />
		<ConfigurationSettings>
		</ConfigurationSettings>
		<Certificates>
		</Certificates>
	</Role>
	<NetworkConfiguration>
		<VirtualNetworkSite name="java-vnet" />
		<AddressAssignments>
			<InstanceAddress roleName="WorkerRole1">
				<Subnets>
					<Subnet name="Subnet-Tomcat" />
				</Subnets>
			</InstanceAddress>
		</AddressAssignments>
		<LoadBalancers>
			<LoadBalancer name="Tomcat-ILB">
				<FrontendIPConfiguration
					staticVirtualNetworkIPAddress="10.0.0.200" subnet="Subnet-Tomcat"
					type="private" />
			</LoadBalancer>
		</LoadBalancers>
	</NetworkConfiguration>
</ServiceConfiguration>

LoadBalancer タグ内で、ロードバランサ-を定義していることが分かると思う。

  • ServiceDefinition.csdef
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<ServiceDefinition xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" name="AzureDeploymentProject">
	<WorkerRole name="WorkerRole1" vmsize="Small">
		<Startup>
			<!-- Sample startup task calling startup.cmd from the role's approot folder -->
			<Task commandLine="util/.start.cmd .startup.cmd" executionContext="elevated" taskType="simple"/>
		</Startup>
		<Runtime executionContext="elevated">
			<EntryPoint>
				<!-- Sample entry point calling run.cmd from the role's approot folder -->
				<ProgramEntryPoint commandLine="run.cmd" setReadyOnProcessStart="true"/>
			</EntryPoint>
		</Runtime>
		<Imports>
		</Imports>
		<Endpoints>
			<!-- <InputEndpoint localPort="8080" name="http" port="8080" protocol="tcp"/> -->
			<InputEndpoint loadBalancer="Tomcat-ILB" localPort="8080" name="internal-http" port="8080" protocol="tcp"/>
		</Endpoints>
	</WorkerRole>
</ServiceDefinition>

こちらでは、InputEndpoint タグのに ServiceConfiguration.cscfg で定義したロードバランサを割り当てている。

Microsoft Azure(Worker ロール)上にデプロイ

仮想ネットワーク上に Windows Server 2012 R2 のインスタンスを配置し、ILB に対して疎通を確認する。以下の画面が表示されれば対応は完了だ。
f:id:waritohutsu:20150716160840p:plain

Eclipse を利用して Worker ロールの Java アプリを仮想ネットワークに参加させる

Azure Eclipse Plugin を利用した Worker ロールで稼働する Java アプリは単独で動かしている例が多いので、仮想ネットワークにも参加させる方法を記載する。事前準備としては以下が実施済みなことを前提とする。

  • 仮想ネットワークが作成済み
  • Azure Deployment Project を Eclipse 上で作成済み

以下の仮想ネットワークを例として扱う。

f:id:waritohutsu:20150716150110p:plain

設定ファイルの編集

Worker ロール(はもちろん Web ロールを含むクラウドサービス)を仮想マシンに参加させる場合、ServiceConfiguration.cscfg に仮想マシンの構成情報を記載する。以下が記載例となるので、参考にして欲しい。

  • ServiceConfiguration.cscfg
<?xml version="1.0" encoding="UTF-8"?><ServiceConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" serviceName="AzureDeploymentProject">
	<Role name="WorkerRole1">
		<Instances count="2"/>
		<ConfigurationSettings>
		</ConfigurationSettings>
		<Certificates>
		</Certificates>
	</Role>
	<NetworkConfiguration>
		<VirtualNetworkSite name="java-vnet"/>
		<AddressAssignments>
			<InstanceAddress roleName="WorkerRole1">
				<Subnets>
					<Subnet name="Subnet-Tomcat"/>
				</Subnets>
			</InstanceAddress>
		</AddressAssignments>
	</NetworkConfiguration>
</ServiceConfiguration>

上記で NetworkConfiguration タグ内に記載している内容が追記内容となる。NetworkConfiguration タグの name 属性に仮想ネットワーク名、Subnet タグの name 属性にサブネット名を記載することで構成可能だ。

デプロイと確認

ServiceConfiguration.cscfg 設定ファイルを作成後、右クリックから Azure > Deploy to Azure Cloud を選択してウィザードに従ってデプロイする。設定がうまくいっていれば、以下の様に仮想マシン内に Worker ロールのインスタンスが配置されていることが確認できる。
f:id:waritohutsu:20150716150131p:plain

Eclipse を利用して Spring Boot アプリを Microsoft Azure にデプロイする

既に id:okazuki さんが Spring Bootを使ってHello world(Thymeleafの使用からwar化してAzureデプロイまで) でまとめている情報と一部重複するが、Spring Boot のアプリケーションを Microsoft Azure にデプロイするまでの手順を紹介する。

まず、前提として Spring Boot の公式ドキュメント にも記載があるが、 Spring Boot には以下 2 パターンの実行形式が存在する。

  • 組み込み Tomcat を含むポータブル実行形式の jar として稼働する
  • war 形式のファイルとして作成し、通常通り Tomcat にデプロイする

id:okazuki さんは両方の形式を試され、Microsoft Azure の WebApps を利用した Spring Boot の稼働方法だ。私の記事では、Spring Boot アプリを Eclipse Plugin を利用して Worker ロールにデプロイする方法を紹介する。

Spring Boot アプリの作成

簡単なサンプルは id:okazuki さんの記事を見ながらが確実だと思うが、Spring Boot の Quick Start サンプルを参考にアプリケーションを作成して Spring Boot の jar を war に変更する手順 を確認すれば対応可能だ。念のため以下にアプリケーションの情報を記載する。

<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>com.mydomain</groupId>
	<artifactId>HelloSpringboot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>HelloSpringboot</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.2.5.RELEASE</version>
	</parent>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>
package com.mydomain.HelloSpringboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(
			SpringApplicationBuilder application) {
		return application.sources(Application.class);
	}

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
	}

}
  • SampleController.java
package com.mydomain.HelloSpringboot;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {
	@RequestMapping("/")
	public String home() {
		return "Spring Boot での日本語文字列返し。(/ω\)イヤン";
	}

}

上記のアプリケーションは Tomcat 7 以上であれば動作可能だ( Spring Boot は Servlet 3 系の API を利用しているため、Tomcat 7 以上が必要)。まずはローカルで動作を確認をお勧めする。

Microsoft Azure(Worker ロール)上にデプロイ

Azure Toolkit for Eclipse を利用して Spring Boot アプリケーションを Worker ロールにデプロイする。新規に Azure Deployment Project を作成する。事前準備として以下を行うこと。

  • Spring Boot のプロジェクトに対して mvn package を辞しして war ファイルを作成し、ROOT.war にリネームする
  • ローカルに Tomcat ( v7 以上)をダウンロードし、%TOMCAT_HOME%\webapps\ROOT フォルダを削除する(削除しないと Tomcat デフォルトの ROOT が表示されてしまう

次にプロジェクトを右クリックしてプロパティを選択し、Azure > Roles から既存の WorkerRole1 を選択して Edit ボタンを押下する。
f:id:waritohutsu:20150715183108p:plain

Server Configuration から Deploy my local server を選択し、ローカルに存在する Tomcat を選択する( ROOT フォルダ削除済みのものを選択すること )。
f:id:waritohutsu:20150715183120p:plain

既存の HelloWorld.war を削除し、事前に用意した Spring Boot の ROOT.war を選択する。この際、ダイアログからエラーメッセージが表示される場合があるが無視しても問題ない。
f:id:waritohutsu:20150715183135p:plain

以上を完了後、Spring Boot のアプリケーションを 右クリックから Azure > Deploy to Azure Cloud を選択してウィザードに従ってデプロイすれば手順は完了だ。以下の画面が表示されれば完了だ。
f:id:waritohutsu:20150715183145p:plain

Application Insights for Java の JavaEE 対応

Microsoft Azure 上でアプリに対して運用監視を行うサービスとして定評のある Application Insights が Java SDK に対応 したことはご存じの方も多いと思う。今回は新たに JavaEE 向けに追加機能が提供されたので紹介する。
また、今回利用したサンプルは https://github.com/normalian/JavaEEAppInsigtsApp に配置しているので参照してほしい。

利用方法

インターセプタとして提供されるため 0.9.6 以上のバージョンの applicationinsights-web.jar を参照する(以下は pom.xml の記載例)。

<dependency>
	<groupId>com.microsoft.azure</groupId>
	<artifactId>applicationinsights-web</artifactId>
	<version>0.9.6</version>
</dependency>

上記の jar に含まれるクラスのうち、以下を利用する。

まずは beans.xml に以下の様な記載をし、インターセプタを有効化する。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
	<interceptors>
		<class>com.microsoft.applicationinsights.web.javaee.RequestNameInterceptor</class>
	</interceptors>
</beans>

次に RequestName アノテーションを以下の様に監視対象のクラスに付与する。

package com.domain.mavenjavaeeapp1.action;

import com.domain.mavenjavaeeapp1.dto.IndexViewDto;
import com.microsoft.applicationinsights.web.javaee.RequestName;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@RequestScoped
@Named
public class IndexAction {

	@Inject
	IndexViewDto indexViewDto;

	@RequestName
	public String do1(String arg) {
		System.out.println("IndexAction#do1(" + arg + ") = "
				+ indexViewDto.getName());
		return "/index.xhtml";
	}

	public String do2() {
		return "0";
	}
}

参考となるが、利用する画面は以下になる。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html">
<h:head>
	<script type="text/javascript">
	//<![CDATA[
    var appInsights=window.appInsights||function(config){
        function s(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},r=document,f=window,e="script",o=r.createElement(e),i,u;for(o.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",r.getElementsByTagName(e)[0].parentNode.appendChild(o),t.cookie=r.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)s("track"+i.pop());return config.disableExceptionTracking||(i="onerror",s("_"+i),u=f[i],f[i]=function(config,r,f,e,o){var s=u&&u(config,r,f,e,o);return s!==!0&&t["_"+i](config,r,f,e,o),s}),t
    }({
        instrumentationKey:"your key"
    });
    
    window.appInsights=appInsights;
    appInsights.trackPageView();
  //]]>
</script>
	<title>Facelet Title</title>
</h:head>
<h:body>
	<span>hello Facelets</span>
	<h:form>
		<div>
			名前を入力して下さい
			<h:inputText value="#{indexViewDto.name}" />
		</div>
		<h:commandButton value="button"
			action="#{indexAction.do1('some argument')}" />
	</h:form>
</h:body>
</html>

結果

上記のアプリケーションに対してブラウザアクセスを行い、"button" のボタンを押下することで以下の様にアクセスされたアクション/HTTPメソッドが確認できる。
f:id:waritohutsu:20150629232426p:plain

Windows 上で WildFly 8.x 実行時に "java.net.BindException: Address already in use" が発生する

WildFly 8.x, "java.net.BindException: Address already in use" on fresh install in windows Vista/7/8 の記事に記載があるが、WindowsVista, 7, 8 )で WildFly 8.x 実行時に "java.net.BindException: Address already in use" が発生する場合がある。

普通は「Tomcat を起動しっぱなしだった」や「JBoss AS や別バージョンの WildFly を起動しっぱなしだった」が原因だが、どうやら NVIDIA Network Service が問題らしい。WindowsVista, 7, 8 )で NVIDIA を利用しており、WildFly 8.x を利用する場合は以下のプロセスを停止することで暫定対策ができる。
f:id:waritohutsu:20150605143726p:plain

DocumentDB の Java SDK で jackson を使う場合の TIPS

随分前に発表された DocumentDB だが、意外にサンプルが少ないので触ってみたところを軽く記載する。DocumentDB は JSON 形式でデータをやり取りするため、Java オブジェクトを JSON 形式に変換する必要がある。

Build a Java web application using DocumentDB に記載されたサンプルを見ると Project Lombok を利用しているが、今回は jackson を使ってみた。

まず、あらかじめ管理ポータルから DocumentDB アカウントを作成し、"TestDB" というデータベースの作成、"TestCollection" というコレクションの作成、以下の様にドキュメントの作成を実施する。
f:id:waritohutsu:20150524151856p:plain

次に、管理ポータルからエンドポイントとキーを取得し、以下のコードを実行する。

package com.mydomain.documentdb;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.microsoft.azure.documentdb.ConnectionPolicy;
import com.microsoft.azure.documentdb.ConsistencyLevel;
import com.microsoft.azure.documentdb.Database;
import com.microsoft.azure.documentdb.Document;
import com.microsoft.azure.documentdb.DocumentClient;
import com.microsoft.azure.documentdb.DocumentClientException;
import com.microsoft.azure.documentdb.DocumentCollection;
import com.microsoft.azure.documentdb.RequestOptions;
import com.mydomain.model.Person;

public class SampleTest {

	// Define an id for your database and collection
	private static final String DATABASE_ID = "TestDB";
	private static final String COLLECTION_ID = "TestCollection";

	// documentdb.properties から情報を取得
	private static String END_POINT;
	private static String MASTER_KEY;

	private static DocumentClient documentClient;
	private static Database databaseCache;
	private static DocumentCollection collectionCache;

	@Before
	public void testInitialize() throws IOException {
		// TODO: 外部設定ファイルを作成すること
		String path = "documentdb.properties";
		InputStream in = SampleTest.class.getClassLoader().getResourceAsStream(
				path);
		if (in == null) {
			throw new IllegalArgumentException(path + " not found.");
		}
		Properties props = new Properties();
		props.load(in);

		END_POINT = props.getProperty("END_POINT");
		MASTER_KEY = props.getProperty("MASTER_KEY");
		documentClient = new DocumentClient(END_POINT, MASTER_KEY,
				ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
	}

	@Test
	public void readTest01() throws DocumentClientException,
			JsonParseException, JsonMappingException, IOException {
		Person expected = new Person();
		Person actual;

		expected.setAge(45);
		expected.setName("割と普通");

		List<Document> documentList = documentClient
				.queryDocuments(getTestCollection().getSelfLink(),
						"SELECT * FROM root r WHERE r.id='" + 1 + "'", null)
				.getQueryIterable().toList();
		ObjectMapper mapper = new ObjectMapper();
		actual = mapper.readValue(documentList.get(0).toString(), Person.class);

		Assert.assertEquals(expected, actual);
	}

	private Database getTestDBDatabase() {
		if (databaseCache == null) {
			// Get the database if it exists
			List<Database> databaseList = documentClient
					.queryDatabases(
							"SELECT * FROM root r WHERE r.id='" + DATABASE_ID
									+ "'", null).getQueryIterable().toList();

			if (databaseList.size() > 0) {
				// Cache the database object so we won't have to query for it
				// later to retrieve the selfLink.
				databaseCache = databaseList.get(0);
			} else {
				// Create the database if it doesn't exist.
				try {
					Database databaseDefinition = new Database();
					databaseDefinition.setId(DATABASE_ID);

					databaseCache = documentClient.createDatabase(
							databaseDefinition, null).getResource();
				} catch (DocumentClientException e) {
					// TODO:
					e.printStackTrace();
				}
			}
		}

		return databaseCache;
	}

	private DocumentCollection getTestCollection() {
		if (collectionCache == null) {
			// Get the collection if it exists.
			List<DocumentCollection> collectionList = documentClient
					.queryCollections(
							getTestDBDatabase().getSelfLink(),
							"SELECT * FROM root r WHERE r.id='" + COLLECTION_ID
									+ "'", null).getQueryIterable().toList();

			if (collectionList.size() > 0) {
				// Cache the collection object so we won't have to query for it
				// later to retrieve the selfLink.
				collectionCache = collectionList.get(0);
			} else {
				// Create the collection if it doesn't exist.
				try {
					DocumentCollection collectionDefinition = new DocumentCollection();
					collectionDefinition.setId(COLLECTION_ID);

					// Configure the new collection performance tier to S1.
					RequestOptions requestOptions = new RequestOptions();
					requestOptions.setOfferType("S1");

					collectionCache = documentClient.createCollection(
							getTestDBDatabase().getSelfLink(),
							collectionDefinition, requestOptions).getResource();
				} catch (DocumentClientException e) {
					// TODO:
					e.printStackTrace();
				}
			}
		}

		return collectionCache;
	}
}
package com.mydomain.model;

public class Person {
	int age;
	String name;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Person == false)
			return false;

		Person p = (Person) obj;
		boolean isAgeSame = age == p.getAge();
		boolean isNameSame = name == null ? p.getName() == null : //
				name.equals(p.getName());
		return isAgeSame && isNameSame;
	}
}

上記を実行すると、以下のエラーが発生する。

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "_attachments" (Class com.mydomain.model.Person), not marked as ignorable
 at [Source: java.io.StringReader@1bb5a082; line: 1, column: 18] (through reference chain: com.mydomain.model.Person["_attachments"])
	at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
	at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:246)
	at org.codehaus.jackson.map.deser.StdDeserializer.reportUnknownProperty(StdDeserializer.java:604)
	at org.codehaus.jackson.map.deser.StdDeserializer.handleUnknownProperty(StdDeserializer.java:590)
	at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:689)
	at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:514)
	at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:350)
	at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2395)
	at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1595)
	at com.mydomain.documentdb.SampleTest.readTest01(SampleTest.java:61)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	(中略)


不明なフィールドである _attachments が存在する様だ。念のためやり取りされる JSON データを見ると以下の様に複数のフィールドが追加されている。

{_attachments=attachments/, _rid=i-ZOANFBawACAAAAAAAAAA==, name=割と普通, id=1, _self=dbs/i-ZOAA==/colls/i-ZOANFBawA=/docs/i-ZOANFBawACAAAAAAAAAA==/, age=45, _etag="00001000-0000-0000-0000-5560769f0000", _ts=1432385183}

こちらの問題に対応するには以下の様に jackson 特有のアノテーションを追加して不明なプロパティの無視を指定する。

package com.mydomain.model;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
	int age;
	String name;

	//(中略)


読み込みの場合は上記で良いのだが、書き込みの場合(特に更新)では今回無視したフィールドが必要なことが懸念される。こちらに対する調査はまた後日で。

Maven でコンパイルするとラムダ式使えないと怒られる件

JDK 1.8 が出てだいぶ経つので、各位もラムダ式を使っていることだと思う。今回はラムダ式を利用したモジュールで Maven コンパイルを実施する場合の軽い TIPS を紹介する。ご存じな方はご存じだと思うが、毎度 pom.xml に記載用の情報を検索するのも面倒なので以下に記載しておきたい。

ハマるポイントとして、ラムダ式を利用したソースで "mvn compile" 等を実行した場合に以下のエラーが発生する点だ。

D:\opt\workspace\Java8CodingProject>mvn compile
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Java8CondigProject 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
<中略>
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /D:/opt/workspace/Java8CodingProject/src/test/java/com/mydomain/lambda/SampleTest.java:[18,33] ラムダ式は-source 1.5でサポートされていません  (ラムダ式を使用可能にするには、-source 8以上を使用してください)
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.798 s
[INFO] Finished at: 2015-05-23T16:58:51+09:00
[INFO] Final Memory: 10M/491M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.
1:testCompile (default-testCompile) on project Java8CondigProject: Compilation failure
[ERROR] /D:/opt/workspace/Java8CodingProject/src/test/java/com/mydomain/lambda/SampleTest.java:[18,33] ラムダ式は-source 1.5でサポートされていません
[ERROR] (ラムダ式を使用可能にするには、-source 8以上を使用してください)
[ERROR] -> [Help 1]

上記のエラーを見れば一目瞭然だが、Maven さん(使っているのは 3.2.5 )は JDK 1.5 ベースでコンパイルしているらしい(流石に古すぎだろうと思うが)。。。。


解決方法は以下の様に 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>com.mydomain</groupId>
	<artifactId>Java8CondigProject</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>
	<dependencies>
		<dependency>
        (中略)

次回以降は 1.8 ベースでのコンパイルが実行される。