normalian blog

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

Knockout MVC を使ってみる

今回は Knockout.js を ASP.NET MVC で活用するためのライブラリである Knockout MVC を利用してみる。同ライブラリを利用することで、ASP.NET MVC でのモデルバインディングが容易になり、ASP.NET 側と JavaScript 側の機能連携が非常に容易になる。

インストール方法

Visual Studio から NuGet の管理コンソールを起動し、検索窓に「knockout」と入力した結果の P.3 辺りに Knockout MVC が発見できる。
f:id:waritohutsu:20140113134146j:plain

次に、Knockout.js を利用するためには ASP.NET MVC プロジェクトに対して以下の修正をする必要がある。

  • App_Start/BundleConfig
public class BundleConfig
{
    // バンドルの詳細については、http://go.microsoft.com/fwlink/?LinkId=301862 を参照してください
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.validate*"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryunobtrusive").Include(
        "~/Scripts/jquery.validate.unobtrusive.js"));

        //knockout.s 用
        bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
        "~/Scripts/knockout-{version}.js"));
        bundles.Add(new ScriptBundle("~/bundles/knockoutmapping").Include(
        "~/Scripts/knockout.mapping-latest.js"));

        // 以下は省略
    }
}
  • Views/Shared/_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
//中略
</head>
<body>
//中略
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - マイ ASP.NET アプリケーション</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/knockout")
    @Scripts.Render("~/bundles/knockoutmapping")
    @RenderSection("scripts", required: false)
</body>
</html>

という感じで、Knockout.js を画面側で読みこめるように設定しておく必要がある。

簡単な使い方

以下にサンプルコードを記載してみる。

  • Person.cs Name, Age プロパティはさておき、Summary プロパティに Computed 属性を付与している点に注目していただきたい。Computed 属性を付与することで、JavaScript 側にロジックを自動生成することができる。
using System.ComponentModel.DataAnnotations;
using DelegateDecompiler;

namespace KnockoutMVCSample.Models
{
    public class Person
    {
        [Display(Name = "名前"), Required]
        public string Name { get; set; }
        [Required, Display(Name = "年齢"), Range(typeof(int), "0", "200")]
        public int Age { get; set; }

        [Computed]
        public string Summary
        {
            get
            {
                var ret = Name + "は";
                if (Age < 20)
                {
                    ret += "まだまだ未成熟な果実(/ω\)イヤン";
                }
                else
                {
                    ret += "熟れた果実(*´Д`)ハァハァ";
                }
                return ret;
            }
        }

        // 後述するが、 ToString を上書きすると死ぬ
        //public override string ToString()
        //{
        //    return string.Format("私は {0}、{1}歳☆(ゝω・)vキャピ", this.Name, this.Age);
        //}
    }
}
  • HomeController.cs 通常の ASP.NET MVC のコントローラであり、特に留意する点はない
using System.Web.Mvc;
using KnockoutMVCSample.Models;
using System.Diagnostics;

namespace KnockoutMVCSample.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new Person()
            {
                Name = "幼女",
                Age = 9
            });
        }

        [HttpPost]
        public ActionResult Index(Person person)
        {
            Trace.WriteLine(string.Format("私は {0}、{1}歳☆(ゝω・)vキャピ", person.Name, person.Age));
            return View(person);
        }

        //中略
    }
}
  • Home/Index.cshtml 画面最初に Html.CreateKnockoutContext() を利用して Knockout MVC 用のインスタンスを作成しており、フォーム部品を作成する際に同インスタンスを利用している点に注目してほしい
@using PerpetuumSoft.Knockout
@model KnockoutMVCSample.Models.Person

@{
    ViewBag.Title = "Home Page";
    var ko = Html.CreateKnockoutContext();
}

@using (Html.BeginForm())
{
    <br /><br />
    <div>
        <h3>knockout.js でのマッピング</h3>
        @Html.LabelFor(m => m.Name) : @ko.Html.TextBox(m => m.Name, new { value = Model.Name, name = "Name" }) @Html.ValidationMessageFor(m => m.Name)<br />
        @Html.LabelFor(m => m.Age) : @ko.Html.TextBox(m => m.Age, new { value = Model.Age, name = "Age" }) @Html.ValidationMessageFor(m => m.Age)<input id="update_button" type="button" value="年齢+1" /> <br />
        <div>
            名前と年齢についてのサマリ:@ko.Html.Span(m => m.Summary, new { id = "summary" })
        </div>
        <br />        
        <input type="submit" name="submit" value="送付" /><br />
    </div>
}
@section scripts
{
    @ko.Apply(Model)
    <script type="text/javascript">
        $(function() {
            $('#update_button').click(function() {
                $('#Age').text(viewModel.Age(viewModel.Age() + 1));
            });
        });
    </script>
}

更に、「注意点」のスクリーンショットを参照して頂ければわかると思うが、Knockout MVC は viewModel という変数名でモデルを生成するため、上記の記載方法で画面上で JavaScript の viewModel を操作することができる。

注意点

Computed 属性を付与しない場合、以下の様に JavaScript 側の計算用ロジックが自動生成されない。
f:id:waritohutsu:20140113134149j:plain

また、モデルクラス(今回は Person.cs) の ToString() メソッドをオーバーライドすると、以下の様に JavaScript 側の自動生成コードがおかしくなってしまう点にも注意が必要だ。
f:id:waritohutsu:20140113134151j:plain

参考

Knockout MVC は以下の様にサンプルが豊富なため、こちらも参照頂きたい。

Windows Azure SDK for .NET の Management Library を利用して Webサイト の構成を変更する

前回の記事である Windows Azure SDK for .NET の Management Library を利用して Webサイト を動的に作成してみる で疎通までの簡単な手順とWebサイトの作成までを紹介したが、今回は Web サイトの設定情報を更新する方法を紹介する。

サンプルコードを実行する

ダラダラと文章を書いても仕方ないので、以下に試したコードと実行結果を記載する。作成済みの Web サイトの情報を取得する際に、GetAync() メソッドと GetConfigurationAsync() メソッドで取得できる情報が異なる点に注意が必要だ*1

  • サンプルコード
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            ChangeWebSiteStatus();
            Console.ReadLine();
        }

        public static async void ChangeWebSiteStatus()
        {
            const string webSiteName = "createfromcode";
            const string webSpaceName = "eastasiawebspace";

            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<パスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (var client = CloudContext.Clients.CreateWebSiteManagementClient(credentials))
            {
                // まず Web サイトの状態を取得
                {
                    var parameters = new WebSiteGetParameters();
                    //以下を試したけど取れなかった。これなんだろ?
                    //{
                    //    PropertiesToInclude = new string[] { "WebSocketsEnabled", "PhpVersion" }
                    //};
                    var response = await client.WebSites.GetAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のサイトモード    = {1} ", webSiteName, response.WebSite.SiteMode);
                    //以下二つは取れない(key がないので例外が出る)
                    //Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSite.SiteProperties.AppSettings["WebSocketsEnabled"]);
                    //Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.WebSite.SiteProperties.AppSettings["PhpVersion"]);
                }

                // Web サイトの構成情報を取るのはこっち
                {
                    var response = await client.WebSites.GetConfigurationAsync(webSpaceName, webSiteName, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSocketsEnabled);
                    Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.PhpVersion);
                }

                // 構成を更新
                {
                    // PHP バージョンを 5.4-> 5.5, WebSocket を Enabled -> Disabled へ
                    var parameters = new WebSiteUpdateConfigurationParameters
                    {
                        PhpVersion = "5.5",
                        WebSocketsEnabled = false
                    };
                    var updateConfigurationResponse = await client.WebSites.UpdateConfigurationAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    //こっちには WebSite プロパティがない
                    Console.WriteLine("RequestId = {0}, StatusCode= {1} ", updateConfigurationResponse.RequestId, updateConfigurationResponse.StatusCode);
                }

                // Web サイトの状態を 無料 -> 共有 へ変更
                {
                    var parameters = new WebSiteUpdateParameters
                    {
                        SiteMode = WebSiteMode.Basic,
                    };
                    var response = await client.WebSites.UpdateAsync(webSpaceName, webSiteName, parameters, CancellationToken.None);
                    Console.WriteLine("変更後, {0} のサイトモード    = {1} ", webSiteName, response.WebSite.SiteMode);
                }
                // もっかい Web サイトの構成情報を取る
                {
                    var response = await client.WebSites.GetConfigurationAsync(webSpaceName, webSiteName, CancellationToken.None);
                    Console.WriteLine("変更前, {0} のWebScoketモード = {1} ", webSiteName, response.WebSocketsEnabled);
                    Console.WriteLine("変更前, {0} のPHPバージョン   = {1} ", webSiteName, response.PhpVersion);
                }
            }
        }
    }
}
  • 実行結果
変更前, createfromcode のサイトモード    = Limited
変更前, createfromcode のWebScoketモード = True
変更前, createfromcode のPHPバージョン   = 5.4
RequestId = a4f9803ff1a75c2db950db5d938b1ec6, StatusCode= OK
変更後, createfromcode のサイトモード    = Basic
変更前, createfromcode のWebScoketモード = False
変更前, createfromcode のPHPバージョン   = 5.5

*1:この辺はパケットキャプチャで確認したいところ

Windows Azure SDK for .NET の Management Library を利用して Webサイト を動的に作成してみる

Windows Azure SDK も日々進化しており、管理ポータルで操作している内容を SDK 越しでも実現可能になるようだ。今回は Preview 機能(2014年1月時点)となるが、Windows Azure SDK for .NET の Management Library 機能を利用して C# で動的に Webサイトを構築するまでの流れを紹介する。

準備と疎通

Windows Azure SDK for .NET の Preview 機能を利用するためには以下を実行する必要がある。

  • Windows SDK for Windows 8.1 をインストールする(makecert, certmgr コマンドを利用するため)
  • 自己証明書(*.cer)を作成し、管理ポータルにアップロードする
  • 新規にC#プロジェクト作成し、自己証明書から作成する秘密鍵(*.pfx)を配置する
  • NuGet を利用して、Microsoft.WindowsAzure.Management.Libraries のプレビューリリースを取得する。
  • 疎通用のコードを書く
自己証明書(*.cer)を作成し、管理ポータルにアップロードする

事前に Windows SDK for Windows 8.1 をインストールしてもらっているとして、さっそく証明書の作成にはいる。

まず、cmd.exe 直でなく VS2012 ARM Cross Tools Command Prompt 等の makecert.exe や certmgr.exe 実行ファイルが格納されたフォルダ(例:C:\Program Files (x86)\Windows Kits\8.1\bin\x64)にパスを通してくれるコマンドプロンプトを実行する。更に、コマンドプロンプトから以下のコマンドを実行し、ManagementTestCert.cer という名前の自己証明書を作成する。

c:\temp>makecert -r -pe -a sha1 -n "CN=NORMALIAN" -ss my -len 2048 -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" -sy 24 ManagementTestCert.cer

自己証明書を作成した後、Windows Azure の管理ポータルにアクセスし、以下の画面を参考に自己証明書をアップロードする。
f:id:waritohutsu:20140103022939p:plain

新規にC#プロジェクト作成し、自己証明書から作成する秘密鍵(*.pfx)を配置する

自己証明書から秘密鍵を作成する。コマンドプロンプトから certmgr.exe を実行し、作成した自己証明書を選択して[エクスポート]ボタンを押下する(名前は makecert.exe の引数で設定した "CN=NORMALIAN" で表示される)。[証明書エクスポートウィザードの開始]が起動するため、[はい、秘密鍵をエクスポートします]にチェックをし、パスワードを設定して *.pfx ファイルを作成する。この際設定したパスワードは後ほど利用するので注意してほしい。

次に、Visual Studio を起動して新規にアプリケーションを作成する(今回はコンソールアプリケーションとする)。作成したソリューションに *.pfx ファイルを以下のように配置し、プロジェクトの含めた後、[ビルドアクション] を「コンテンツ」に、[出力ディレクトリにコピー]を「新しい場合はコピーする」に設定する。
f:id:waritohutsu:20140103022943p:plain

最後に NuGet のコンソールを起動し、以下のコマンドを実行することで Windows Azure SDK の Preview がインストールできる。

Install-Package Microsoft.WindowsAzure.Management.Libraries -IncludePrerelease
疎通用のコードを書く

以下の疎通用コードを作成することで、実際の疎通が取れる。

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            ListLocationServices();
            Console.ReadLine();
        }

        public static async void ListLocationServices()
        {
            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<秘密鍵のパスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (ManagementClient client = CloudContext.Clients.CreateManagementClient(credentials))
            {
                var result = await client.Locations.ListAsync();
                var locations = result.Locations;
                foreach (var location in locations)
                {
                    Console.WriteLine("Location: {0}", location.Name);

                    foreach (var feature in location.AvailableServices)
                    {
                        Console.WriteLine("\t{0}", feature);
                    }
                }
            }
        }
    }
}

実行結果は以下になる。

Location: East Asia
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: Southeast Asia
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: North Europe
        Compute
        Storage
        PersistentVMRole
        HighMemory
Location: West Europe
        Compute
        Storage
        PersistentVMRole
        HighMemory
(中略)

C# のコードで Webサイトを作成してみる

上記の環境まで作成していれば、あとは以下のコードを実行することで East Asia に Web サイトが作成可能だ。

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management;
using Microsoft.WindowsAzure.Management.WebSites.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace AzureManagementConsoleApp
{
    public class Program
    {
        static void Main()
        {
            CreateNewWebSite();
            Console.ReadLine();
        }

        public static async void CreateNewWebSite()
        {
            const string sitename = "createfromcode";
            const string webSpaceName = "eastasiawebspace";
            const string regionName = "East Asia";

            var cert = new X509Certificate2(@"Assets\ManagementTestCert.pfx", "<秘密鍵のパスワード>");
            var credentials = new CertificateCloudCredentials("<サブスクリプションID>", cert);

            using (var client = CloudContext.Clients.CreateWebSiteManagementClient(credentials))
            {
                // WebSiteCreateParameters が null でもエラー
                // WebSpace が null だとエラー
                // eastasiawebspace 等、WebSpace の名前はある程度決まっている
                var parameters = new WebSiteCreateParameters
                {
                    WebSpace = new WebSiteCreateParameters.WebSpaceDetails
                    {
                        Name = webSpaceName,
                        GeoRegion = regionName,
                        Plan = "VirtualDedicatedPlan",
                    },
                    // カスタムドメイン名を指定できる場所だが、無料モードだと
                    // azurewebsite.net じゃないとバリデーションがかかるっぽい
                    HostNames = new List<string>
                    { 
                        sitename + ".azurewebsites.net",
                    },
                    Name = sitename,
                    WebSpaceName = webSpaceName,
                };
                await client.WebSites.CreateAsync(webSpaceName, parameters, CancellationToken.None);
            }
        }
    }
}

Windows Azure SDK for Java を利用して BLOB 上に content-type を変えてアップロードしてみる

Java 8 の話題が続いたが、お次は Windows Azure SDK for Java についての TIPS を記載する。ご存じの方も多いと思うが、Windows Azure SDK for JavaGithub/azure-sdk-for-java の様に GitHub にて公開されている。
GitHub のトップページに記載されているサンプルコードを利用することでファイルのアップロードは可能だが、画像ファイルや動画ファイルをアップロードする場合、別途 content-type を明示的に指定しないとブラウザが正しく動作しない場合がある(画像ファイルなのにダウンロードしようとするとか)。

以下に、BLOB への画像データアップロード時に content-type を image/jpeg に設定するサンプルコードを記載した。何も指定しない場合、content-type は application/octet-stream としてアップロードされる点に注意してほしい*1

package org.mydomain.sample;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;

import com.microsoft.windowsazure.services.blob.client.BlobContainerPermissions;
import com.microsoft.windowsazure.services.blob.client.BlobContainerPublicAccessType;
import com.microsoft.windowsazure.services.blob.client.BlobProperties;
import com.microsoft.windowsazure.services.blob.client.CloudBlobClient;
import com.microsoft.windowsazure.services.blob.client.CloudBlobContainer;
import com.microsoft.windowsazure.services.blob.client.CloudBlockBlob;
import com.microsoft.windowsazure.services.core.storage.CloudStorageAccount;
import com.microsoft.windowsazure.services.core.storage.StorageException;

public class Main {
	public static final String storageConnectionString = 
	"DefaultEndpointsProtocol=https;"
	+ "AccountName=your_account_name;"+ "AccountKey=your_account_key";

	public static void main(String[] args) {
		try {
			CloudStorageAccount account;
			CloudBlobClient serviceClient;
			CloudBlobContainer container;
			CloudBlockBlob blob;

			account = CloudStorageAccount.parse(storageConnectionString);
			serviceClient = account.createCloudBlobClient();

			// コンテナ名を設定し、存在しない場合は作成
			container = serviceClient.getContainerReference("blobsample");
			container.createIfNotExist();

			// コンテナレベルでアクセスを設定
			BlobContainerPermissions containerPermissions;
			containerPermissions = new BlobContainerPermissions();
			containerPermissions
					.setPublicAccess(BlobContainerPublicAccessType.CONTAINER);
			container.uploadPermissions(containerPermissions);

			// ブロブの作成
			blob = container.getBlockBlobReference("image1.jpg");
			File fileReference = new File("c:\\temp\\image1.jpg");

			// ブロブのアップロード
			blob.upload(new FileInputStream(fileReference),
					fileReference.length());

			// content-type の設定。ブロブのアップロード後に実施しないとダメ
			BlobProperties properties = blob.getProperties();
			properties.setContentType("image/jpeg");
			blob.uploadProperties();

			System.out.println(blob.getUri());
		} catch (StorageException | URISyntaxException | InvalidKeyException
				| IOException exception) {
			System.out.print("One Exception encountered: ");
			exception.printStackTrace();
			System.exit(-1);
		}
		System.out.println("end");
	}
}

上記にコードにて、BLOBのアップロード後に content-type を設定している点に注意してほしい(BLOBのアップロード後でないと blob.getProperties() でこける)。

出力結果は以下となる。

https://<your_account_name>.blob.core.windows.net/blobsample/image1.jpg
end

*1:ちなみに、今回のサンプルは JDK8 で動作している

今更 Java8 を使ってみる ~その2 インターフェースの default 実装編~

Java 8 からはインターフェースにデフォルトの実装を持てるようになった。Java でデフォルトの実装を持たせる場合、Java 7 までは必ず抽象クラスを経由する必要があったが、Java 8 からは 本機能を利用することで実現が可能になった。

簡単な default 実装の利用方法

default というキーワードを利用して、以下のようにインターフェースに対して標準の実装を持たせることができる。

public interface HelloService{
     defualt String sayHello(String name){
         return name + "さん、こんにちは";
     }
}

もちろんインターフェースは直接インスタンス化できないが、標準的な処理をあらかじめインターフェースに定義することができるため「これだけはこういう実装をして欲しい」という部分をインターフェースに定義可能になったと言える(標準化というものに携わる人にはちょっとうれしい機能な気がする)。

もうちょっと突っ込んでみる

以下のサンプルを実行してみた。

package org.mydomain.defaultinterface;

// インターフェースには暗黙的に final, public, static が付与されるが、
// 今回は明示的に上記の予約語を付与
public interface HelloService {

	// 普通はこんな定義はしないで、あくまで実験用としていただけると
	public static final MyDto dto = new MyDto();

	public String sayGoodBye(String name);

	default public String sayHello(String name) {
		dto.setName(name);
		System.out.println("ここは侵されていないデフォルトメソッド(*´Д`)ハァハァ");
		return name + "さん、卑猥ですね!";
	}
}

class MyDto {
	String name;

	public MyDto() {
		name = "何とも卑猥";
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
package org.mydomain.defaultinterface;

public class Main {
	public static void main(String[] args) {
		HelloService service = new HelloService() {
			@Override
			public String sayGoodBye(String name) {
				System.out.println("実装してみた系");
				return name + "さん、さようなら(/ω\)イヤン";
			}
		};
		System.out.println("名前 = " + HelloService.dto.getName());
		System.out.println(service.sayHello("卑猥"));
		System.out.println(service.sayGoodBye("淫猥"));
		System.out.println("名前 = " + HelloService.dto.getName());
		System.out.println();

		// もちろんメソッドの上書きも可能
		HelloService service2 = new HelloService() {
			@Override
			public String sayHello(String name) {
				return "もう何もない";
			}

			@Override
			public String sayGoodBye(String name) {
				return null;
			}
		};
		System.out.println(service2.sayHello("aaa"));
	}
}
  • 実行結果
名前 = 何とも卑猥
ここは侵されていないデフォルトメソッド(*´Д`)ハァハァ
卑猥さん、卑猥ですね!
実装してみた系
淫猥さん、さようなら(/ω\)イヤン
名前 = 卑猥

もう何もない

ご覧のとおり、デフォルト実装を定義した場合でも実装クラスにおける処理の上書きは可能であり、インターフェースで状態は持てないことがわかる(final static public なインスタンスは状態が持てるが、これは Java 7 以前からと同様)。

じゃあインターフェースと抽象クラスの区別は?

インターフェースと抽象クラスとの区分けが気になるところだが、以下の言質をいただいておる。

今更 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 に種類が収束しそうな気配だ。