normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

今更 Java8 を使ってみる ~その1 Lambda 編~

あまりにも Java8 を追っていなかったので、こんな大晦日の晩にちょっくら Lambda をいじってみた。

開発環境

NetBeansIntelliJ さんでは気軽にいじれるようだが、Eclipse さんではつい最近までいろいろしないと動かなかったらしい。昨今は以下の記事を見ればわかる通り、Eclipse IDE with JDK 8 support が(正式ではないが)リリースされているため、こちらを利用することで Lambda を利用できる。

上記の記事を参考に、Eclipse IDE with JDK 8 support と JDK8 をインストールすることで Eclipse を利用した開発環境の構築が可能だ。

簡単な構文で確認

以下のコードを実行して実行結果を確認してみた。今まで長々と匿名インターフェースを実装していたものがだいぶシンプルになっている。

import java.util.Arrays;
import java.util.List;

public class Hello {

	interface HelloService {
		String say(String first, String second);
	}

	public static void main(String[] args) {

		final MyCounter counter = new MyCounter();

		// Enumerable.Range() が無いのでそれっぽいのにはしてみた
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
		for (int n : list) {
			HelloService service = (first, second) -> first + "#" + second
					+ " No." + counter.count();
			System.out.println("service = " + service.say("aaa", "bbb"));
		}

		System.out.println(//
				Arrays.stream(new int[] { 1, 2, 3, 4, 5 }).average()
						.getAsDouble());
	}
}

class MyCounter {
	int value = 0;

	public int count() {
		return ++value;
	}
}
  • 実行結果
service = aaa#bbb No.1
service = aaa#bbb No.2
service = aaa#bbb No.3
service = aaa#bbb No.4
service = aaa#bbb No.5

「String HelloService#say(String, String)」シグネチャのインターフェースに対し、main()メソッド内でラムダ式を利用してメソッド本体を定義して利用している。
実行結果は MyCounter インスタンスの値を利用している。この際、MyCounter インスタンスは final 定義であることに注意していただきたい(なので、final int counter =0; とかすると counter++ でコンパイルエラーになる)。

Stream API を試す

今度は Stream API を試してみる。こちらは map-filter-reduce という関数型チックなコレクション処理を容易に実現できるようになっている。以下にサンプルを記載する。

package org.mydomain.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class HelloStream {
	public static void main(String[] args) {

		List<Person> persons = new ArrayList<>();
		Person person;

		person = new Person();
		person.setName("幼女");
		person.setAge(11);
		persons.add(person);

		person = new Person();
		person.setName("女子");
		person.setAge(21);
		persons.add(person);

		person = new Person();
		person.setName("なかなか幼女");
		person.setAge(9);
		persons.add(person);

		person = new Person();
		person.setName("すごく幼女");
		person.setAge(6);
		persons.add(person);

		// 私はロリコンではありません ①
		persons.stream()
				.filter(p -> p.getAge() <= 12)
				.forEach(
						p -> System.out.println("この幼女は " + p.getAge()
								+ "歳。(*´Д`)ハァハァ"));

		// 私はロリコンではありません ②
		// なぜか p.setName(""); return p; で変数解決できなかったので変更
		persons.stream().map(p -> p.setName("これは幼女 (*´Д`)ハァハァ"))
				.forEach(System.out::println);

		System.out.println(//
				Arrays.stream(new int[] { 1, 2, 3, 4, 5 }).average()
						.getAsDouble());

		// なぜか toString() が必要な辺りに Eclipse のインテリセンスがいまいちなことを感じる…
		Arrays.asList("卑猥", "淫猥", "淫乱", "淫靡", "普通").stream()
				.map(s -> "割と" + s.toString() + "!!")
				.forEach(System.out::println);
	}
}

class Person {
	String name;
	int age;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

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

	@Override
	public String toString() {
		return name + " は " + age + "歳";
	}
}

上記の実行結果は以下になる。

この幼女は 11歳。(*´Д`)ハァハァ
この幼女は 9歳。(*´Д`)ハァハァ
この幼女は 6歳。(*´Д`)ハァハァ
これは幼女 (*´Д`)ハァハァ は 11歳
これは幼女 (*´Д`)ハァハァ は 21歳
これは幼女 (*´Д`)ハァハァ は 9歳
これは幼女 (*´Д`)ハァハァ は 6歳
3.0
割と卑猥!!
割と淫猥!!
割と淫乱!!
割と淫靡!!
割と普通!!

ご覧のとおり、コレクション処理が容易になっている。が、上記をご覧になっていただければわかると思うが、一部 Eclipse のインテリセンスがうまく動作していないと思われる個所もある。

バージョンアップ した Windows Azure Plugin for Eclipse with Java を使ってみる

今回は、Windows Azure での Java 開発について簡単に紹介したい。

はじめに

Windows Azure 上で Java の Web アプリケーションを動作させる場合、大枠で以下の分類があることをご存じだろうか(現時点で Web サイトは Java アプリケーションの動作には向かない)。

  • クラウドサービス:war/ear を送り込むだけ&環境構築が楽
  • 仮想マシン:自前で APサーバを設定する必要がある(もともとテンプレートがある WebLogic Server を除く)けど、設定含めてやりたい放題。WebSphere とかもこっちじゃないとインストールできない

上記の二環境について、Windows Azure Plugin for Eclipse with Java がデプロイ機能を支援しているのはクラウドサービス側であり、仮想マシン側は自前で好き勝手設定できるので自前でデプロイ方法を設定(Maven 等でもOK)する使い分けとなる。
Windows Azure Plugin for Eclipse with Java のインストール方法は Installing the Windows Azure Plugin for Eclipse with Java (by Microsoft Open Technologies) に詳細な記載が存在するため、まずはこちらを参照して Eclipse Plugin をインストールしてほしい。

Windows Azure Plugin for Eclipse with Java を使ってデプロイ

まずは新規プロジェクトの作成から Windows Azure Deployment Project を選択する。選択後、以下の画面が表示される。JDK に Microsoft Open Technologies が作成した OpenJDK ベースの JVM を選択可能になっている(もちろん自分でカスタマイズすることも可能だ)。
f:id:waritohutsu:20131215160515p:plain

Server ではどの APサーバ上で稼働させるかを選択可能になっている。更に、「Deploy my local server(auto-upload to cloud storage)」では、初回のみクラウドストレージにアップロード&デプロイし、二回目以降はクラウドストレージからデプロイするオプションが設定可能だ。
f:id:waritohutsu:20131215160522p:plain

Applications は通常と変わらずに war や ear を選択・追加することが可能になっている。
f:id:waritohutsu:20131215160533p:plain

Windows Azure のクラウドサービスにデプロイするためにはアカウント情報を取得する必要があり、Eclipse プロジェクトを右クリックして Properties を選択し、左メニューから Windows Azure > Subscriptions を選択し、GUI のメニューに従ってアカウント情報をインポートする。この際、以下の画面が出たらおとなしく .NET Framework 3.5 をインストールすること(白目
f:id:waritohutsu:20131215160542p:plain

アカウント情報の設定後、Eclipse プロジェクトを右クリックして Windows Azure > Deploy to Windows Azure Cloud を選択し、以下の画面からデプロイ情報を入力してデプロイを開始する。
f:id:waritohutsu:20131215161118p:plain

以下は二回目以降の Eclipse プロジェクトのデプロイログだ。APサーバが再度アップロードされず、クラウドストレージから取得されていることが確認できる。

Buildfile: C:\XXXXXXXXXXXXXXXX\HelloAzure\package.xml

checkResetScript:

resetEmulator:

createwapackage:
       [mkdir] Created dir: C:\XXXXXXXXXXXXXXXX\HelloAzure\deploy
      [delete] Deleting directory C:\XXXXXXXXXXXXXXXX\HelloAzure\deploy.old
[windowsazurepackage] Verified attributes.
[windowsazurepackage] Role "WorkerRole1": Verifying the approot "C:\XXXXXXXXXXXXXXXX\HelloAzure\WorkerRole1\approot"
[windowsazurepackage] Role "WorkerRole1": Importing components...
[windowsazurepackage]  Not importing component 'jdk1.7.0_40' because it will be downloaded during deployment in the cloud
[windowsazurepackage]  Not importing component 'apache-tomcat-7.0.47' because it will be downloaded during deployment in the cloud
[windowsazurepackage] Role "WorkerRole1": Finished importing components
[windowsazurepackage] Role "WorkerRole1": Generating component deployment script...
[windowsazurepackage] Role "WorkerRole1": Created internal startup script
[windowsazurepackage] Starting package generation...
[windowsazurepackage] Executing '[C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\v2.2\bin\cspack.exe, C:\XXXXXXXXXXXXXXXX\HelloAzure\ServiceDefinition.csdef, /role:WorkerRole1;C:\XXXXXXXXXXXXXXXX\HelloAzure\WorkerRole1\approot, /rolePropertiesFile:WorkerRole1;C:\XXXXXXXXXXXXXXXX\HelloAzure\.rolePropertiesOS3, /out:C:\XXXXXXXXXXXXXXXX\HelloAzure\deploy\WindowsAzurePackage.cspkg]'...
[windowsazurepackage] Process started
[windowsazurepackage] Windows(R) Azure(TM) Packaging Tool version 2.2.0.0
[windowsazurepackage] for Microsoft(R) .NET Framework 4.0
[windowsazurepackage] Copyright c Microsoft Corporation. All rights reserved.
[windowsazurepackage] C:\XXXXXXXXXXXXXXXX\HelloAzure\ServiceDefinition.csdef: Warning  CloudServices040 : The 'schemaVersion' attribute is unspecified. Please set the attribute to avoid this warning.
[windowsazurepackage] Waiting for process to exit...
[windowsazurepackage] Completed package generation.
[windowsazurepackage] Copying 1 file to C:\XXXXXXXXXXXXXXXX\HelloAzure\deploy
[windowsazurepackage] Verifying blob availability (http://portalvhdsmtkkx7225509q.blob.core.windows.net/eclipsedeploy/zulu1.7.0_40-7.1.0.0-win64.zip)...

デプロイが無事完了すると以下の画面が表示される。
f:id:waritohutsu:20131215160558p:plain

閑話休題(JDKの種類について)

ところで、皆様は JDK がどの位の種類存在しているかご存じだろうか?Oracle に買収された Sun が開発していた Sun JDK を主体として、主な JDK ですら以下が存在する。

色々と JDK が増えて厳しい世の中だが、元 Sun JDKOracle JDK と Open JDK ベースの JDK に種類が収束しそうな気配だ。

Windows Azure 上の WebLogic Server から JPA で SQL Database に接続する

本日は Windows Azure Advent Calendar9日目の記事だ。
WebLogic Server を Windows Azure 上で利用するための情報をまとめて来たが、今回は WebLogic Server から SQL Database に JPAJavaEE 標準の O/R マッパー)を利用して接続する手順もまとめた。以下の記事を未参照の方は、まずは以下の記事を通読して欲しい。

今回紹介する内容は、上記の記事を利用して構築した環境が必須となる点に注意頂きたい。

WebLogic Server で SQL Database を設定するためのアレコレ

ざっくりとは以下の手順が必要となる。

上記をスライドにまとめたので参照して欲しい。

EAR アプリの開発

上記だけではソースコード部分が分かりにくいところもあると思うので、以下に実際に利用したソースコードを記載する。

WAR側のコード
<!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:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">

<f:loadBundle basename="resources.application" var="msg" />

<head>
<title><h:outputText value="#{msg.welcomeTitle}" /></title>
</head>

<body>
	<h3>
		<h:outputText value="#{msg.welcomeHeading}" />
	</h3>
	<p>
		<h:outputText value="#{msg.welcomeMessage}" />
	</p>
	<h:form>
		<h:dataTable value="#{viewDto.erogeList}" var="eroge" border="1">
			<h:column>
				<f:facet name="タイトル"></f:facet>
				<h:outputText value="#{eroge.name}" />
			</h:column>
			<h:column>
				<f:facet name="価格"></f:facet>
				<h:outputText value="#{eroge.price}円" />
			</h:column>
			<h:column>
				<f:facet name="コメント"></f:facet>
				<h:outputText value="#{eroge.comment}" />
			</h:column>
		</h:dataTable>
		<h:commandButton action="#{indexAction.doAction()}" value="アクション実行" />
	</h:form>
</body>
</html>
package org.mydomain.action;

import javax.inject.Inject;
import javax.inject.Named;

import org.mydomain.dto.ViewDto;
import org.mydomain.logic.IndexLogic;

@Named
public class IndexAction {

	@Inject
	IndexLogic indexLogic;

	@Inject
	ViewDto viewDto;

	public String doAction() {
		String result = "0";
		System.out.println("!!!! do action");
		viewDto.setErogeList(indexLogic.doLogic());
		return result;
	}
}
package org.mydomain.dto;

import java.io.Serializable;
import java.util.List;

import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;

import org.mydomain.entity.Eroge;

@Named
@ConversationScoped
public class ViewDto implements Serializable {
	private List<Eroge> erogeList;

	public List<Eroge> getErogeList() {
		return erogeList;
	}

	public void setErogeList(List<Eroge> erogeList) {
		this.erogeList = erogeList;
	}
}
EJB側のコード
  • META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
   http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">
	<persistence-unit name="em" transaction-type="JTA">
		<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
		<jta-data-source>JDBC_Data_Source</jta-data-source>
	</persistence-unit>
</persistence>
package org.mydomain.logic;

import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.mydomain.entity.Eroge;

@Named
@Stateless
public class IndexLogic {

	@PersistenceContext
	private EntityManager em;

	public List<Eroge> doLogic() {
		System.out.println("!!!! do logic");
		List<Eroge> resultList = em.createQuery("select k from Eroge k",
				Eroge.class).getResultList();
		return resultList;
	}
}
package org.mydomain.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(schema = "dbo", name = "Eroge")
public class Eroge implements Serializable {
	@Id
	@Column(name = "Id")
	private int id;

	@Column(name = "Price")
	private int price;

	@Column(name = "Name")
	private String name;

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	@Column(name = "Comment")
	private String comment;
}

上記を追記したアプリケーションを実際に実行させると以下の実行結果となる。

Classic ASP アプリを2013年らしい環境で動かすには?

今回は、One ASP.NET Advent Calendar 2013 8日目の記事だ。すでに様々な記事が One ASP.NET Advent Calendar に登録されているため、是非こちらも参照して頂きたい。
さて、今回のテーマは最新とは程遠い Classic ASP (なんなら ASP".NET" ですらない。。。)であり、名前の通りかなり Classic な技術ではあるが、2013年時点でも様々なところで利用されている技術だ。既存の Classic ASP を動作させる環境としては Windows Server 2003 R2(32bit)+IIS 6.0 以下の環境が多いと思う。

Web サイトで Classic ASP を稼働させる

IIS 8.0 & Windows Server 2012 の環境(2013年12月現在)である Windows Azure Webサイトで Classic ASP を動作させるのは難しくない。管理ポータルから Web サイトを作成した後、以下の画像を参考に「構成」タブから「マネージ パイプライン モード」を「クラシック」にして設定を保存することで、簡易的な実行環境は構築できる*1

マネージ パイプラインモードについては以下のリンクを参照してほしい。

次に、「ダッシュボード」タブから画面下部の WebMatrix ボタンを押下して WebMatrix を起動してサンプルを作成する。WebMatrixASP.NET Web Page, PHP, Node.js 等も編集可能だが、もちろん Classic ASP のアプリケーションを編集することも可能だ。管理ポータル越しに Web サイトを WebMatrix から起動し、以下の画面を参考に Default.asp という名前の Classic ASP ファイルと Global.asa ファイルをトップに追加する。

更に、以下の様にコードを追記する。

<%@ Language="VBScript" %>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>Classic ASP on Web サイト</title>
    </head>
    <body>
        <h2>セッション上の値</h2>
        <%= Session("MyMessage") %>
        <br />
        <h2>環境変数一覧</h2>
        <%
            For Each name In Request.ServerVariables
                 Response.Write name & " = " & Request.ServerVariables(name) & "<br />"
             Next
        %>
    </body>
</html>
  • Global.asa
<SCRIPT LANGUAGE="VBScript" RUNAT="Server">
    Sub Application_OnStart 
    End Sub 

    Sub Application_OnEnd 
    End Sub 

    Sub Session_OnStart 
        Session("MyMessage") = "メッセージ at Global.asa"
    End Sub 

    Sub Session_OnEnd 
    End Sub 
</SCRIPT>

上記のソースコードを Web サイト上に配置することで、以下の様に Web サイト上の環境変数一覧を表示できる。

一見簡単に動作できる様に見えるが、細かな挙動が過去のIIS(主に IIS 6.0以下になるだろうが・・・)とは異なるため既存の Classic ASP アプリケーションがうまく動作しないこともある。その場合は次の仮想マシン編を参照して頂きたい。

仮想マシン上(Windows Server 2012 R2)で Classic ASP を稼働させる

今までは Web サイト上で動作させていたが、IIS 7.0 以降で Classic ASP を動作させる場合には以下の様な考慮が必要になる。参考になる情報がIIS 7.0 および IIS 7.5 上で Classic ASP アプリケーションを実行するに記載されているが、主要な留意点を以下に記載する。

  • Classic ASPIIS 7.0 以降では既定ではインストールされないため、役割を追加時に設定が必要(設定は以下の画像を参考のこと)


  • 失敗した要求トレースを使用して Classic ASP エラーをブラウザ側でトラブルシューティングができないため、IIS Managerで設定が必要(設定は以下の画像を参考のこと)

  • Classic ASP の親パスは既定で無効であるため、IIS Managerで設定が必要(設定は以下の画像を参考のこと)

  • 仮想ディレクトリがアプリケーションとして認識されない(Global.asa がディレクトリ毎に実行されない)ため、IIS Managerで設定が必要(設定は以下の画像を参考のこと)

上記を実施すれば、仮想マシン上で Classic ASP を実行する環境が整うはずだ。他にも以下の情報が参考になるので、必要な場合に参照頂きたい。

*1:デフォルトの設定である「統合」のままでも Classic ASP は動作するが、以下の記事でも記載されている様に、IIS がアプリケーションを動作させる機構が異なる点に注意が必要だ(「統合」モードでアプリケーションがそのまま動作する場合、「統合」モードにしていただいても問題ない)

Eclipse を使った JavaEE6 環境(主にWebLogic Server)を簡単に用意して開発してみる on Windows Azure

本エントリーは「Java EE Advent Calendar 2013」の6日目だ。
既に JavaEE7 がリリースされているが、今回は Windows Azure + WebLogic Server + Eclipse を利用した JavaEE6 の開発環境を用意し、CDI を利用した簡単なアプリケーションを実行する。
環境構築対象である Winodws Azure を利用するためには契約が必要(契約したとしても Windows Azure 標準の設定では無料枠内でしか利用できないようになっているため安心)であるが、EclipseWebLogic Server の環境構築情報については任意のJavaEE環境で参考になるはずだ。

WebLogic Server 12c on Windows Azure の環境構築

こちらについては、以下のスライドと共に私の直近エントリである 「WebLogic Server 12c を Windows Azure の Windows Server 2012 R2 で動かしてみる id:waritohutsu:20131130:1385804228」を参照して欲しい。

WebLogic Server 12c を Windows Azure の Windows Server 2012 R2 で動かす

Windows Azure仮想マシンを利用すれば、WebLogic Server 12c がインストール済のインスタンスを利用することができる。
今回は開発環境のみ構築しているが、必要に応じて WebLogic Server 12c がインストール済の Oracle Linux を利用して本番環境相当のものも容易に構築可能だ。

Eclipse を利用した JavaEE6 開発環境を整え、EARプロジェクトを作る

EclipseJavaEE 開発環境を整える際、以下の点に留意が必要だ。

  • Eclipse は Classic でなく、for JavaEE Developers を利用する
  • JSFCDI に対する IDE サポートを利用するには JBoss Tools を追加でインストールする必要がある
  • デフォルトでは WebLogic Server 用の Server Adapter が無いので、別途でインストールする
  • WAR プロジェクト、EJB プロジェクト、EAR プロジェクトの関係に注意

これらのインストール手順を以下のスライドにまとめたので、こちらを参考にして欲しい。

Eclipse for JavaEE Developers + JBoss Tools でコーディング

手順をまとめているスライドでは WAR, EJB プロジェクトに対して以下の *.java と *.xhtml を追加している。スライドのパッケージエクスプローラの情報を参考に、以下のコードを作成する。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; URL=faces/index.xhtml">
</head>
</html>
  • index.xhtml では EL 式を利用して resources.application.properties からメッセージを参照しているほか、EL式からアクションを実行している。
<!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:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">

<f:loadBundle basename="resources.application" var="msg" />

<head>
<title><h:outputText value="#{msg.welcomeTitle}" /></title>
</head>

<body>
	<h3>
		<h:outputText value="#{msg.welcomeHeading}" />
	</h3>
	<p>
		<h:outputText value="#{msg.welcomeMessage}" />
	</p>
	<h:form>
	<h:commandButton action="#{indexAction.doAction()}" value="アクション実行" />
	</h:form>
</body>
</html>
  • IndexAction.java は index.xhtml から呼ばれるアクションであり、 CDI を利用しているため javax.faces.bean.ManagedBean を利用せずに @Named のみ利用している
package org.mydomain.action;

import javax.inject.Inject;
import javax.inject.Named;

import org.mydomain.logic.IndexLogic;

@Named
public class IndexAction {

	@Inject
	IndexLogic indexLogic;

	public String doAction() {
		System.out.println("アクション実行");
		String result  = indexLogic.doLogic();
		return result;
	}
}
  • IndexLogic.javaEJB 側に格納される。CDI に認識させるために @Named を付与し、Stateless Session Bean とするため @Stateless を付与している
package org.mydomain.logic;

import javax.ejb.Stateless;
import javax.inject.Named;

@Named
@Stateless
public class IndexLogic {
	public String doLogic() {
		System.out.println("!!!! do logic");
		return "0";
	}
}

以上のコードを作成した後、WebLogic Server 12c に管理ポータルからデプロイ*1し、アクションを実行すると以下の様に WebLogic Server の標準コンソールにメッセージが出力される。

*1:何故か Eclipse 経由でアプリケーションを実行した場合、CDI が上手く動作しなかった。なぜだ…

WebLogic Server 12c を Windows Azure の Windows Server 2012 R2 で動かしてみる

既に既知の情報かと思うが、Microsoft と Oracle が連携したことで Windows Azure 上で WebLogicOracle DB を利用することが可能となった。これらの一連の情報については、SATO NAOKI氏が以下にまとめている。
Oracle OpenWorld 2013: Oracleのミッション クリティカル ソフトウェアと、Microsoftのエンタープライズ級クラウド

JDK6, JDK7 がインストールされただけの Windows Server 2012 インスタンスを作成することも可能であり、以下のような組み合わせでも Oracle ラインセンスのプロダクトを利用可能だ。

WebLogic Server 12c on Windows Server 2012 R2&Windows Azure な手順メモ

SATO NAOKI 氏の記事に記載されたリンクから、Windows Azure 上で WebLogicOracle Linux 上で動作させる手順が記載されたPDFは入手することが可能だ。しかし、Windows Server 上で WebLogic を動作させる

WebLogic Server 12c を Windows Azure の Windows Server 2012 R2 で動かす

今後は SQL Database への接続やクラスタリング構成を試してみたい。

AWS SDK for Java を使って Elastic Transcoder を弄ってみた

Windows Azure メディアサービスを弄っていたが、ふと http://aws.amazon.com/jp/elastictranscoder/title=AWS Elatic Transcode] を弄る用事があった。せっかくサンプルを書いてみたので公開してみる。
AWS Elastic Transcoder は、AWS が提供する動画変換サービスであり、Windows Azure メディアサービスと同様にクラウドリソースを利用して動画を変換することができる。また、動画等の大容量ファイルは S3 に格納することができるため、これまたクラウドリソースを有効活用することができる。

ちなみに以下のブログを大変参考にさせて頂いたので、本稿を読み進める前に是非参考にしていただきたい

AWS SDK for Java のセットアップ

まずは以下の pom.xml を作成する。

<?xml version="1.0" encoding="UTF-8"?>
<project
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mydomain</groupId>
	<artifactId>elastic-transcoder-sample</artifactId>
	<version>0.9.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>AWS Elastic Transcoder Sample</name>
	<description>AWS Elastic Transcoder用のサンプル</description>

	<dependencies>
		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>aws-java-sdk</artifactId>
			<version>1.5.4</version>
		</dependency>
	</dependencies>

</project>

上記の xml 作成後、「mvn eclipse:eclipse」コマンドを実行することで AWS SDK for Java を利用可能な Eclipse プロジェクトを初期化できる。
今回利用するのは Elastic Transcoder の機能のみだが、AWS SDK for Java には Elastic Transcoder 以外の AWS サービスを利用可能な機能も含まれている。

Elastic Transcoder を利用する Java コード

次に 依存 jar の参照を解決後、以下のコードを利用して Elastic Transcoder に対して処理を行う。今回作成した処理は「パイプラインの一覧表示」「プリセットの一覧表示」「エンコーディングのジョブ生成」「カスタムプリセットの登録」だ。

package com.mydomain;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import com.amazonaws.services.elastictranscoder.AmazonElasticTranscoder;
import com.amazonaws.services.elastictranscoder.AmazonElasticTranscoderClient;
import com.amazonaws.services.elastictranscoder.model.AudioParameters;
import com.amazonaws.services.elastictranscoder.model.CreateJobOutput;
import com.amazonaws.services.elastictranscoder.model.CreateJobRequest;
import com.amazonaws.services.elastictranscoder.model.CreateJobResult;
import com.amazonaws.services.elastictranscoder.model.CreatePresetRequest;
import com.amazonaws.services.elastictranscoder.model.Job;
import com.amazonaws.services.elastictranscoder.model.JobInput;
import com.amazonaws.services.elastictranscoder.model.JobOutput;
import com.amazonaws.services.elastictranscoder.model.ListJobsByPipelineRequest;
import com.amazonaws.services.elastictranscoder.model.ListJobsByPipelineResult;
import com.amazonaws.services.elastictranscoder.model.ListPipelinesRequest;
import com.amazonaws.services.elastictranscoder.model.ListPipelinesResult;
import com.amazonaws.services.elastictranscoder.model.ListPresetsRequest;
import com.amazonaws.services.elastictranscoder.model.ListPresetsResult;
import com.amazonaws.services.elastictranscoder.model.Pipeline;
import com.amazonaws.services.elastictranscoder.model.Preset;
import com.amazonaws.services.elastictranscoder.model.PresetWatermark;
import com.amazonaws.services.elastictranscoder.model.Thumbnails;
import com.amazonaws.services.elastictranscoder.model.VideoParameters;

// Elastic Transcoder の AWS 上公式ドキュメント
// http://docs.aws.amazon.com/elastictranscoder/latest/developerguide/introduction.html
public class AmazonElasticTranscoderSample {

	public static void main(String[] args) throws Exception {
		AmazonElasticTranscoder transCoder = init();
		//dispPresetList(transCoder);
		//createCustomPreset(transCoder);
		createJobEncodeMp4ToMp4(transCoder);
		System.out.println("終了");
	}

	// Elastic TransCoder を制御するインスタンスを生成
	static AmazonElasticTranscoder init() throws Exception {
		AWSCredentials credentials = new ClasspathPropertiesFileCredentialsProvider()
				.getCredentials();

		ClientConfiguration conf = new ClientConfiguration();
		try {
			// プロパティ情報から認証プロキシ情報を読み取る
			Properties prop = new Properties();
			prop.load(new InputStreamReader(AmazonElasticTranscoderSample.class
					.getResourceAsStream("/ProxySetting.properties")));
			conf.setProxyHost(prop.getProperty("PROXY_HOST"));
			conf.setProxyPort(Integer.valueOf(prop.getProperty("PROXY_PORT")));
			conf.setProxyUsername(prop.getProperty("PROXY_USERNAME"));
			conf.setProxyPassword(prop.getProperty("PROXY_PASSWORD"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		AmazonElasticTranscoder transCoder = new AmazonElasticTranscoderClient(
				credentials, conf);

		// 利用しているリージョンのエンドポイントを設定
		// ここを間違えると何も表示されない
		transCoder
				.setEndpoint("https://elastictranscoder.ap-northeast-1.amazonaws.com");
		return transCoder;
	}

	// 指定したエンドポイントに登録されたプリセットを表示する
	public static void dispPresetList(AmazonElasticTranscoder transCoder) {
		System.out.println("----------Preset List----------");
		ListPresetsRequest listPresetsRequest = new ListPresetsRequest();
		ListPresetsResult listPresetsResult = transCoder
				.listPresets(listPresetsRequest);
		List<Preset> presets = listPresetsResult.getPresets();
		for (Preset preset : presets) {
			System.out.println("Id : " + preset.getId());
			System.out.println("  Name : " + preset.getName());
			System.out.println("  Description : " + preset.getDescription());
			System.out.println("  Type : " + preset.getType());
			System.out.println("  Thumbnails : " + preset.getThumbnails());
			System.out.println("  Video : " + preset.getVideo());
			System.out.println("  Audio : " + preset.getAudio());
		}
	}

	// パイプラインのリストを表示
	public static void dispPiplineList(AmazonElasticTranscoder transCoder) {
		ListPipelinesRequest listPipelineRequest = new ListPipelinesRequest();
		ListPipelinesResult listPipelineResult = transCoder
				.listPipelines(listPipelineRequest);
		List<Pipeline> pipelines = listPipelineResult.getPipelines();
		System.out.println("----------Pipeline List----------");
		for (Pipeline pipeline : pipelines) {
			System.out.println("Id : " + pipeline.getId());
			System.out.println("  Name : " + pipeline.getName());
			System.out.println("  Role : " + pipeline.getRole());
			ListJobsByPipelineRequest listJobsByPipelineRequest = new ListJobsByPipelineRequest();
			listJobsByPipelineRequest.setPipelineId(pipeline.getId());
			ListJobsByPipelineResult listJobsByPipelineResult = transCoder
					.listJobsByPipeline(listJobsByPipelineRequest);
			List<Job> jobs = listJobsByPipelineResult.getJobs();
			for (Job job : jobs) {
				System.out.println("  Id : " + job.getId());
				JobInput jobInput = job.getInput();
				System.out.println("    JobInput");
				System.out.println("      AspectRatio : "
						+ jobInput.getAspectRatio());
				System.out.println("      Container : "
						+ jobInput.getContainer());
				System.out.println("      FrameRate : "
						+ jobInput.getFrameRate());
				System.out.println("      Interlaced : "
						+ jobInput.getInterlaced());
				System.out.println("      Key : " + jobInput.getKey());
				System.out.println("      Resolution : "
						+ jobInput.getResolution());
				JobOutput jobOutput = job.getOutput();
				System.out.println("    JobOutput");
				System.out.println("      Key : " + jobOutput.getKey());
				System.out.println("      PresetId : "
						+ jobOutput.getPresetId());
				System.out.println("      Rotate : " + jobOutput.getRotate());
				System.out.println("      Status : " + jobOutput.getStatus());
				System.out.println("      StatusDetail : "
						+ jobOutput.getStatusDetail());
				System.out.println("      ThumbnailPattern : "
						+ jobOutput.getThumbnailPattern());
			}
		}
	}

	// mp4 から mp4 に変換する
	public static void createJobEncodeMp4ToMp4(
			AmazonElasticTranscoder transCoder) {
		String piplineId = "";
		String presetId = "";
		try {
			// プロパティ情報から認証プロキシ情報を読み取る
			Properties prop = new Properties();
			prop.load(new InputStreamReader(AmazonElasticTranscoderSample.class
					.getResourceAsStream("/AppSetting.properties")));
			piplineId = prop.getProperty("PIPLINE_ID");
			presetId = prop.getProperty("PRESET_ID");
		} catch (IOException e) {
			e.printStackTrace();
		}

		System.out.println("---------- Create Job ----------");
		CreateJobRequest createJobRequest = new CreateJobRequest();
		createJobRequest.setPipelineId(piplineId);
		JobInput input = new JobInput();
		input.setKey("WP_20130222_011.mp4");
		input.setAspectRatio("auto");
		input.setContainer("auto");
		input.setFrameRate("auto");
		input.setInterlaced("auto");
		input.setResolution("auto");
		createJobRequest.setInput(input);
		createJobRequest.setOutputKeyPrefix("p-transoder/");

		List<CreateJobOutput> outputs = new ArrayList<CreateJobOutput>();
		CreateJobOutput output1 = new CreateJobOutput();
		output1.setKey("mp4/sample.mp4");
		output1.setPresetId(presetId);
		output1.setThumbnailPattern("mp4/thumb-{count}");
		output1.setRotate("auto");

		outputs.add(output1);

		createJobRequest.setOutputs(outputs);

		CreateJobResult createJobResult = transCoder
				.createJob(createJobRequest);
		com.amazonaws.services.elastictranscoder.model.Job job = createJobResult
				.getJob();
		System.out.println("Name : " + job.getStatus());
		System.out.println("Id : " + job.getId());
		System.out.println("Output : " + job.getOutput());
	}

	// カスタムプリセットの作成
	public static void createCustomPreset(AmazonElasticTranscoder transCoder) {
		CreatePresetRequest request = new CreatePresetRequest();

		request.setContainer("mp4");

		AudioParameters audio = new AudioParameters();
		audio.setBitRate("128");
		audio.setChannels("2");
		audio.setCodec("AAC");
		audio.setSampleRate("44100");
		request.setAudio(audio);
		request.setDescription("preset created from sdk");
		request.setName("Preset created from AWS SDK");

		Thumbnails thumbnails = new Thumbnails();
		thumbnails.setFormat("png");
		thumbnails.setInterval("300");
		thumbnails.setMaxWidth("192");
		thumbnails.setMaxHeight("108");
		thumbnails.setSizingPolicy("ShrinkToFit");
		thumbnails.setPaddingPolicy("NoPad");
		request.setThumbnails(thumbnails);

		VideoParameters video = new VideoParameters();
		video.setCodec("H.264");
		Map<String, String> codecOptions = new HashMap<>();
		codecOptions.put("MaxReferenceFrames", "1");
		codecOptions.put("Profile", "baseline");
		codecOptions.put("Level", "3");
		video.setCodecOptions(codecOptions);
		video.setKeyframesMaxDist("90");
		video.setFixedGOP("true");
		video.setBitRate("272");
		video.setFrameRate("auto");
		video.setMaxWidth("400");
		video.setMaxHeight("288");
		video.setDisplayAspectRatio("auto");
		video.setSizingPolicy("ShrinkToFit");
		video.setPaddingPolicy("NoPad");

		//この辺は手抜き(汗
		{
			Collection<PresetWatermark> watermarks = new ArrayList<>();

			PresetWatermark watermark;
			watermark = new PresetWatermark();
			watermark.setId("TopLeft");
			watermark.setMaxWidth("10%");
			watermark.setMaxHeight("10%");
			watermarks.add(watermark);

			video.setWatermarks(watermarks);
		}

		request.setVideo(video);

		transCoder.createPreset(request);
	}
}

別途で以下 3つのプロパティファイルを作成している点に注意してほしい。

  • AwsCredential.properties(AWS のサービスを利用する場合に必須)
  • AppSetting.properties(Elastic Transcoder の piplieID とかを外だししたので、実際作成する場合は必須でない)
  • ProperSetting.properties(認証プロキシ情報の外だし用、実際作成する場合は必須でない)