normalian blog

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

Web サイトのバックアップ/復元 機能を利用してみる

Azure: ExpressRoute Dedicated Networking, Web Site Backup Restore, Mobile Services .NET support, Hadoop 2.2, and more にて発表された Webサイトのバックアップ/復元機能が便利そうだったのでちょっと触ってた。

まず簡単なWEBアプリを作る

Node.js で簡単にテーブル操作する処理を以下の様に実装した。今回は Node.js アプリ& git デプロイした場合のフォルダ構成となる点だけ認識頂ければ幸いだ。

  • server.js
var http = require('http')
var port = process.env.PORT || 1337;

var azure = require('azure');
var ServiceClient = azure.ServiceClient;
var TableQuery = azure.TableQuery;
var tableClient = azure.createTableService("<ストレージアカウント名>",
 "<ストレージアカウントキー>",
 "http://<ストレージアカウント名>.table.core.windows.net/");

var tableName = 'posts';
var partition = 'part1';
var rowkey = 'bbbbbb';

http.createServer(function(req, res) {
  tableClient.createTableIfNotExists(tableName, function (err, created) {
      var now = new Date().toGMTString();
      var entity = {
          PartitionKey: partition,
          RowKey: rowkey + now,
          title: 'Post one',
          body: 'Body one at WebSite',
          created_at: now };
      tableClient.insertEntity(tableName, entity, null, function(err){
         console.log(err);
      });
  });

  res.writeHead(200, { 'Content-Type': 'text/html; charset=UTF-8' });
  res.end('無事にテーブルストレージにインサートした模様\n');
}).listen(port);

以下のコマンドでモジュールをインストール後、git push してアプリケーションを動作させる。

npm install azure 
git add .
git commit -m "commit"
git push <hogehoge>

バックアップを試す

Webサイトの管理画面から「バックアップ プレビュー」のタブがあるので選択する。「標準モード」でしか利用できないので、注意が必要だ。
f:id:waritohutsu:20140304011118j:plain

自動化されたバックアップを頻度を含め設定できるほか、何処のストレージサービスに格納するかを設定できる。
f:id:waritohutsu:20140304011144j:plain

「今すぐバックアップ」を選択すると選択したストレージサービスにデータが格納される。ストレージサービスに「websitebackups」フォルダが作成され、「_<日付>」フォーマットの xml ファイルと zip ファイルが格納されている。
f:id:waritohutsu:20140304011207j:plain

  • normalianbkup_201403031549.xml
<?xml version="1.0" encoding="utf-8"?>
<BackupDescription xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure">
  <Version>0</Version>
  <Name><Webサイト名>_<日付></Name>
  <HostNames xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d2p1:string><Webサイト名>.azurewebsites.net</d2p1:string>
  </HostNames>
  <Databases />
</BackupDescription>
  • normalianbkup_201403031549.zip ファイルの中身
normalianbkup_201403031549.zip
└─fscontent
   ├─LogFiles
   │  └─Git
   │      └─trace
   └─site
       ├─deployments
       │  ├─9e3f836b7daa0648bb95a9371f3fd748d
       │  └─tools
       ├─repository
       │  └─.git
       └─wwwroot
           └─node_modules

zip ファイル側は site 以下のファイルがまるっと zip に固められていることがわかる。

復元を試す

「今すぐ復元」を選ぶとどのバックアップファイルからWebサイトを復元するか選択可能だ。
f:id:waritohutsu:20140304011637j:plain

また、復元先はもともとのWebサイトはもちろん、新たなWebサイトを作成してバックアップすることも可能だ。
f:id:waritohutsu:20140304011648j:plain

Open JDK ベースの JVM Zulu が Web Platform Installer でインストール可能に!

ご存じでない方もいると思うが、OpenJDK ベースである Azul Zulu が Web Platform でインストール可能になった。Zulu は Windows Azure Plugin for Eclipse with Java にも統合されており、Windows Azure 上で Java を動作させる際に重要な役割を果たしている。

Web Platform Installer を利用してインストール

Web Platform Installer を起動し、検索までから Java と検索することで以下の画面が表示される。
f:id:waritohutsu:20140209143119p:plain

そのまま[追加]ボタンを押下すると以下の様にインストールされる。

  • C:\Azul\zulu1.7.0_45-7.2.0.3-win64 以下に JDK がインストールされる(※注 バージョンによって適宜読み替えること
  • 環境変数 JAVA_HOME が以下に設定される(※注 既存の JDK をインストールしている場合は注意)
C:\Users\normalian>echo %JAVA_HOME%
C:\Azul\zulu1.7.0_45-7.2.0.3-win64

Web Platform Installer を利用することで、常に最新のバージョンの Zulu を利用することが可能だ。

余談

Windows Azure Plugin for Eclipse with Java をインストールしている場合、生成される Windows Azure 向けのプロジェクトテンプレートに含まれる package.xml にも zulu を取得する URL が含まれている。以下に抜粋したものを記載する。

  • package.xml の抜粋
        <!-- Create a sample role -->
        <workerrole approotdir="${basedir}\WorkerRole1\approot" name="WorkerRole1">
          <startupenv cloudvalue="%DEPLOYROOT%\zulu1.7.0_40-7.1.0.0-win64" name="JAVA_HOME" type="jdk.home" value="%DEPLOYROOT%\zulu1.7.0_40-7.1.0.0-win64"/>
          <startupenv name="PATH" type="jdk.path" value="%JAVA_HOME%\bin;%PATH%"/>
          <startupenv name="CATALINA_HOME" type="server.home" value="%DEPLOYROOT%\apache-tomcat-7.0.47"/>
          <startupenv name="SERVER_APPS_LOCATION" type="server.app.loc" value="%CATALINA_HOME%\webapps"/>
          <component cloudaltsrc="http://azure.azulsystems.com/zulu/zulu1.7.0_40-7.1.0.0-win64.zip" cloudmethod="unzip" cloudsrc="auto" cloudupload="AUTO" deploydir="%DEPLOYROOT%" deploymethod="copy" importmethod="copy" importsrc="C:\Program Files\Java\jdk1.7.0_40" type="jdk.deploy"/>
          <component cloudmethod="unzip" cloudsrc="auto" cloudupload="AUTO" deploydir="%DEPLOYROOT%" deploymethod="copy" importmethod="copy" importsrc="C:\opt\tomcat\apache-tomcat-7.0.47" type="server.deploy"/>

Windows 向けの Open JDK で公式ビルドは存在しないが、Zulu という選択肢も取れることがわかる。

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 以前からと同様)。

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

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