normalian blog

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

Windows Azure Media Services .NET SDK 2.3.0.0 の変更 - 複数のストレージアカウントをアタッチ編

2013/7/1 に NuGet にて Windows Azure Media Service 2.3.0.0 がリリースされた。今回のリリースにおける主要な更新は以下となる。

こちらの メディア with Microsoft - Windows Azure Media Services .NET SDK 2.3.0.0 の更新 記事にも SDK 2.3.0.0 の更新内容がまとまっているので、別途参照して頂きたい。

複数のストレージアカウントをアタッチする方法

サーバ側には複数のストレージアカウントをアタッチする機能が追加されたが、現時点(2013/7/15)では管理ポータルにも.NET SDK にも本機能を利用するインターフェースは用意されておらず、REST API を利用することによってのみ本機能が利用可能だ。
そのため、今回はHow to: Use Media Services Management REST API に記載されたクラスを参考にしてREST API を操作するコードを以下のように作成する。

ManagementRESTAPIHelper helper =
new ManagementRESTAPIHelper("https://management.core.windows.net",
    "<証明書のThumbprintを記載。管理ポータルを参照のこと>",
    "<上記の証明書に紐づいたサブスクリプションID。同じく管理ポータルを参照のこと>");

// Initialize the AccountInfo class.
MediaServicesAccountInfo accountInfo = new MediaServicesAccountInfo();
accountInfo.MediaServicesAccountName = "<メディアサービスのアカウント名>";
accountInfo.Region = "<メディアサービスを作成したリージョン例:East Asia>";
accountInfo.StorageAccountName = "<ストレージサービスにアタッチしたストレージアカウント名>";
accountInfo.StorageAccountKey = "<ストレージサービスにアタッチしたストレージアカウントのキー>";
accountInfo.BlobStorageEndpoint = "<BLOBストレージのエンドポイント 例:http://xxxxxxxxxxxxxxx.blob.core.windows.net/>";

var storageAccount = new AttachStorageAccountRequest()
{
StorageAccountName = "<アタッチしたいストレージアカウント名>",
StorageAccountKey = "<アタッチしたいストレージアカウントのキー>",
BlobStorageEndpointUri = "<アタッチしたいBLOBストレージのエンドポイント>"
};

Call one of the public methods and attach storage account to media service 
helper.AttachStorageAccountToMediaServiceAccount(accountInfo, storageAccount);

内部では直接REST API を叩いているため、特に Media Service SDK を取得しなくても操作は可能だ。上記のパラメータのうち、証明書に関する情報を取得する方法を以下に図でまとめたので参考にしてほしい。

次に、上記のコードをコンソールアプリケーション等で実行する。処理が正常に実行された場合は以下のメッセージが表示される(別に parimary key が再生成された様子はないがなんでだろう?)。

The primary key was regenerated.

また、すでに自身のメディアサービスにアタッチ済みのストレージアカウントを再アタッチしようとした場合は System.Net.WebException が発生し、以下のメッセージが返される。

{"リモート サーバーがエラーを返しました: (409) 競合"}

System.Net.WebException.Response.StatusCode は System.Net.HttpStatusCode.Conflict で返ってくるので、必要ならば参考にしてほしい。

REST API の処理が完了した後、数十秒後(直後では反映されていなかった)に以下のコードを実行するとメディアサービスに複数のストレージサービスがアタッチされていることが確認できる。また以下のコードは Media Service .NET SDK を利用している。

CloudMediaContext context = new CloudMediaContext("<メディアサービスのアカウント名>", "<メディアサービスのキー名>");

foreach (var account in context.StorageAccounts)
{
Console.WriteLine("Name={0}, IsDefault={1}", account.Name, account.IsDefault);
}

実行結果は以下となる(二つのストレージアカウントを紐づけた後に上記のコードを実行した)。最初にメディアサービスに紐づけたストレージアカウントがデフォルトとして扱われる点に注意してほしい。

Name=xxxxxxmediaservicestorage1, IsDefault=False
Name=xxxxxxmediaservicestorage2, IsDefault=True
Name=xxxxxxmediaservicestorage3, IsDefault=False

参考

Windows版のFirefoxは ver.21 からデフォルトで H.264形式動画が再生可能に

今回は Windows Azure Media Service をいじっていて得た Windows版 Firefox に関する情報を備忘録として記載する。
実は Windows 版に限るが、FirefoxH.264 動画再生については以下の環境になっている様だ

さっそく Firefox ver.22 × Windows 8 で動作検証

以下のコードを利用し、Firefox ver.22 で H.264 動画を再生した。

<!DOCTYPE HTML>
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h2>動画</h2>
<video style="width:400pt;" src="mymovie.mp4" controls></video>
</body>
</html>

配置したのは H.264 の動画のみでありOGGやWebM系の動画は配置していないが、以下の様に動画再生が可能だった。

HTML5 な動画再生については…

まだ色々ありますな(汗

MultiTouch Behavior を利用して Silverlight でゼスチャー操作をしてみる

今回は SilverlightWPF に対して ゼスチャー操作を利用可能にする MultiTouch Behavior について紹介する。

MultiTouch Behavior のインストール

MultiTouch Behavior パッケージは NuGet で利用可能であり、パッケージマネージャコンソールで以下のコマンドを入力することで利用可能になる。

PM> Install-Package MultiTouchBehaviors 

といいつつ、まだまだバージョンは 0.6.9 な辺りでどうなんだろうかと思ったり(汗。インストールして確認したところ、どうやら Silverlight 4 向けにビルドされたらしく Silverlight 5 では利用できなかった。。。。
仕方がないので CodePlex - Windows Phone, Silverlight and WPF Multi-Touch Manipulations から最新版のソースコードを取得し、Silverlight 5 向けにビルドしなおすことで MultiTouch Behavior を Silverlight 5 でも利用可能となった。

MultiTouch Behavior を利用したアプリケーソン

特にこだわりがなければ、まずは一旦 Silverlight 4 アプリケーションを新規に作成し、NuGet を利用して MultiTouch Behavior をインストールする。更に、 XAML を以下のように書き換えて Silverlight アプリケーションを起動する。

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:behaviors="clr-namespace:MultiTouch.Behaviors.Silverlight4;assembly=MultiTouch.Behaviors.Silverlight4"
    xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="800">

    <Grid x:Name="LayoutRoot" Background="White">
        <Canvas>
            <Image Source="Images/P1000555.JPG" Width="675" Canvas.Left="61" Canvas.Top="43" Height="506">
                <interactivity:Interaction.Behaviors>
                    <behaviors:MultiTouchBehavior IsRotateEnabled="True" IsScaleEnabled="True" 
                                              MinimumScale="100" MaximumScale="500"  />
                </interactivity:Interaction.Behaviors>
            </Image>
        </Canvas>
    </Grid>
</UserControl>

アプリケーション後、回転・拡大・移動のゼスチャ操作を行うことが可能となる。

また、ソースコード内を確認したところ、Touch系のイベントが存在しない PC向け Silverlight は、マウス系のイベントを美味く利用して対応しているようだ。興味のある方はソースコードを参照しても良いだろう。

参考サイト

利用可能な操作等々は以下の動画を参考にして頂ければよいだろう。

JSR 303 Bean Validator を使ってみる

JavaEE6 から? JSR の標準に取り込まれた JSR 303 Bean Validatorを使ってみた。JavaEE5 時代は Hibernate Validator としてOSSとして実装されていたものが JavaEE6 では Bean Validator として標準仕様として取り込まれている(中身の実装はやはり Hibernate Validator だが)。今回は使えるまでの手順ベースで手短にささっと書いてしまうが、参考になれば幸いだ*1

依存ライブラリのインストール

まずは Bean Validator に依存しているライブラリを解決する。本体である validation-api.jar(インターフェース部分) と hibernator-validator.jar(実装部分)はもちろん必要だが、単体環境でBean Validator を利用する場合は el-api.jar や el-impl.jar も必要な点に注意して頂きたい。TomcatGlassFish、JBoss AS 等の AP サーバは独自に el-api.jar, el-impl.jar を持っているが、単体環境で実行する場合はこれらの依存関係も解決してやる必要がある。

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mydomain.sample.study</groupId>
	<artifactId>beanvalidator-sample</artifactId>
	<packaging>jar</packaging>
	<version>0.9.0-SNAPSHOT</version>
	<name>Bean Validator Sample</name>
	<dependencies>
		<!-- http://mvnrepository.com/artifact/javax.validation/validation-api/1.1.0.Final -->
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/org.hibernate/hibernate-validator/5.0.1.Final -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.0.1.Final</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/javax.el/el-api/2.2 -->
		<dependency>
			<groupId>javax.el</groupId>
			<artifactId>el-api</artifactId>
			<version>2.2</version>
		</dependency>
		<!-- http://mvnrepository.com/artifact/org.glassfish.web/el-impl/2.2 -->
		<dependency>
			<groupId>org.glassfish.web</groupId>
			<artifactId>el-impl</artifactId>
			<version>2.2</version>
		</dependency>
	</dependencies>
</project>

サンプルコード

Javaソースコードを記載する。DTOクラスのプロパティに対し、@Size や @Min のアノテーションを付与してバリデーション属性を指定する。以下の例では @Size で3文字以上かつ20文字以下の文字列を許可文字列とし、エラーメッセージも別途指定してる。また、@Min で0未満の値は入力できないようにしている。

package com.mydomain.sample.study.dto;

import javax.validation.constraints.Min;
import javax.validation.constraints.Size;

public class PersonDto {
	@Size(min = 3, max = 20)
	private String name;

	@Min(value = 0, message = "流石に  0 歳以下は無いと思いますので、{value} は嘘じゃないかと…")
	private int age;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

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

以下は PersonDto に対して不正な値を入力したうえでバリデーションを実行するソースコードになる。

package com.mydomain.sample.study;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import com.mydomain.sample.study.dto.PersonDto;

public class Main {
	public static void main(String[] args) {
		PersonDto dto = new PersonDto();
		dto.setAge(-10);
		dto.setName("ああ");

		ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
		Validator v = vf.getValidator();
		Set<ConstraintViolation<PersonDto>> results = v.validate(dto);
		for (ConstraintViolation<PersonDto> result : results) {
			System.out.println(result.getMessage());
		}
	}
}

サンプルの実行

サンプルの実行結果は以下になる。バリデーションが実行されているのが確認できると思う。

6 08, 2013 11:08:19 午前 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 5.0.1.Final
size must be between 3 and 20
流石に  0 歳以下は無いと思いますので、0 は嘘じゃないかと…

*1:自分がちょこちょこ確認するるので備忘録気味

RazorEngine の使い方 〜それなりな応用編 その2〜

前回紹介した RazorEngine のアセンブリ参照方法 id:waritohutsu:20130514:1368525761 について http://jsakamoto.tumblr.com/post/50628186640/razorengine-wa-na のコメントを頂いたので、実際に試してみた結果を共有する。

コンソールアプリの場合

コンソールアプリの場合、前回の投稿で記載した通り、Assembly.LoadFrom()等で明示的にサードパーティ製のアセンブリを読み込むことで対応が可能な様だ。以下のコードを利用することで、RazorEngine内でMarkdownSharpのアセンブリを利用している。また、出力時のHTMLエスケープ処理を抑止するために @Raw() メソッドを利用している点に留意して欲しい。

namespace RazorTemplateConsoleApp
{
   public class Person
    {
        public string Name { get; set; }
        public uint Age { get; set; }
        public IList<string> FavoriteTechs { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Assembly.LoadFrom(Path.Combine(Directory.GetCurrentDirectory(), "MarkdownSharp.dll"));

                var viewBag = new DynamicViewBag();
                viewBag.AddValue("Comment", @"
# 見出し h1
## 見出し h2
### 見出し h3");
                Console.WriteLine(RazorEngine.Razor.Parse(@"@{
var str = new MarkdownSharp.Markdown().Transform(ViewBag.Comment);
}
@Raw(str)", STR_LIST, viewBag, "mycache")); //キャッシュ名がnullだと落ちる
            }
            catch (Exception e)
            {
                Console.WriteLine("ここは通らないと思われる:" + e.Message);
            }
        }
    }
}

アプリケーションの出力結果は以下となる。

<h1>見出し h1</h1>

<h2>見出し h2</h2>

<h3>見出し h3</h3>

ASP.NET MVC の場合

ASP.NET の場合は BuildManager が動的にアセンブリを読み込んでいるため、サードパーティ製のアセンブリを利用する場合でも明示的なアセンブリの読み込み処理は不要だ。以下のコードでも問題なく動作するので、是非確認して欲しい。また、こちらでも出力時のHTMLエスケープ処理を抑止するために @Raw() メソッドを利用している。

  • HomeController.cs の抜粋
    public class HomeController : Controller
    {
        private readonly string STR_LIST = "";
        public ActionResult Index()
        {
            var viewBag = new DynamicViewBag();
            viewBag.AddValue("Comment", @"
# 見出し h1
## 見出し h2
### 見出し h3");
            ViewBag.Message = RazorEngine.Razor.Parse(@"@{
var str = new MarkdownSharp.Markdown().Transform(ViewBag.Comment);
}
@Raw(str)", STR_LIST, viewBag, "mycache"); //キャッシュ名がnullだと落ちる

            return View();
        }
  • Index.cshtml の抜粋
@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>

出力結果は以下となる。

BuildManager の参考

ASP.NET MVC の内部に踏み込んでいるわけではないが、BuildManager の機能の参考にはなるだろう

RazorEngine の使い方 〜それなりな応用編〜

.NET 系でテンプレートエンジンが必要になった際、twitter で「Java だと Velocity とかの古参&王道ライブラリがあるけど、.NETだとなんじゃらほい?」と呟いたところ、「ASP.NET MVC 系なら RazorEngine では?」との言葉を頂いた。確かに ASP.NET MVC3 から HTML のテンプレートエンジンは Razor を利用しており、安定度はかなり高そうだと思いためしてみた。

まずはの参考情報

まずは公式+αである以下の情報を眺めた。

[ソリューション エクスプローラー]上のプロジェクト項目を右クリックし、[[NuGet パッケージ管理]..]を実行する。これにより選択し、以下の画面の[NuGet パッケージの管理]ダイアログがウィザードを起動するので、右上の検索欄項目にから「Razor」を入力して、「RazorEngine」のライブラリを検索してRazorEngineライブラリをインストールする。
また、別途 NuGet パッケージ管理のコンソールから以下を入力することでもインストール可能だ。

Install-Package RazorEngine

RazorEngine の TIPS 系 〜ViewBag を利用する場合〜

ASP.NET MVC でも利用可能な ViewBag を利用することができる。以下の様にインスタンスを作成し、Razor.Parse() メソッドの引数として渡すことで利用できる。

//出力:ビューバグ用
var viewBag = new RazorEngine.Templating.DynamicViewBag();
viewBag.AddValue("Comment", "ビューバグ用");
Console.WriteLine(RazorEngine.Razor.Parse(@"
@ViewBag.Comment", new List<string>() {
     "ASP.NET MVC",
     "ASP.NET Web API",
     "SignalR"
 }, viewBag, null));

RazorEngine の TIPS 系 〜LINQ を利用する場合〜

LINQを利用する場合、ASP.NET MVC 側でも行っているように、一行目で「@model」を利用して型付を行う必要がある。RazorEngine 内部で dynamic として受けてしまうため、拡張メソッドである LINQ が上手く動作しないために @model の記載が必要となる。

//出力:Model の方を宣言すればそのままでもLINQはOK
Console.WriteLine(RazorEngine.Razor.Parse(@"@model IEnumerable<string>
@if(Model.Any()){
@: Model の方を宣言すればそのままでもLINQはOK
}else{
@: 存在してない
}", STR_LIST));

当然 @model を記載しなくとも、拡張メソッドとしてでなければLINQのメソッドは利用可能だ。

//出力:Enumerable.Any() は OK
Console.WriteLine(RazorEngine.Razor.Parse(@"@if(Enumerable.Any(Model)){
@: Enumerable.Any() は OK
}else{
@: 存在してない
}", STR_LIST));

また、キャストを行ってもLINQの利用は可能だ。

Console.WriteLine(RazorEngine.Razor.Parse(@"@{
var list = (IEnumerable<string>)Model;
}
@if(list.Any()){
@: (IEnumerable<string>) すればOK
}else{
@: 存在してない
}", STR_LIST));

RazorEngine の TIPS 系 〜名前空間の追加〜

名前空間を追加する場合、以下の様に RazorEngine.Templating.TemplateService インスタンスを生成し、AddNamespace() メソッドを利用して名前空間を追加する。

namespace ConsoleApp1{
  public class Person
  {
  	public string Name { get; set; }
  	public uint Age { get; set; }
  	public IList<string> FavoriteTechs { get; set; }
  }
  (中略)

static void RazorEngineShow()
{
        using (var templateService = new RazorEngine.Templating.TemplateService())
        {
            // 自分のアセンブリの名前空間を追加
            templateService.AddNamespace(@"ConsoleApp1");
            // 自分のアセンブリじゃない名前空間をそのまま追加しようとすると落ちる
            //templateService.AddNamespace(@"Newtonsoft.Json");

            var viewBag = new RazorEngine.Templating.DynamicViewBag();
            viewBag.AddValue("Str", "ビューバグ用");

            Console.WriteLine(templateService.Parse<IEnumerable<string>>(@"@{
var person = new Person(){ Age=22 };
}
@person.Age
@ViewBag.Str", STR_LIST, viewBag, "mycache")); //キャッシュ名がnullだと落ちる
        }
}

ただし、サードパーティ製のDLLを利用している場合等は、これだけでは不十分だ。こちらについては次節を参照して欲しい。

RazorEngine の TIPS 系 〜アセンブリ名前空間の追加〜

アセンブリを追加する場合、以下の様に RazorEngine.Compilation.DirectCompilerServiceBase を継承したクラスと RazorEngine.Compilation.ICompilerServiceFactory を継承したクラスを作成し、RazorEngine.Configuration.TemplateServiceConfiguration のインスタンスを作成して拡張ポイントに値を設定する必要がある。
以下は Json.NET の DLL を読み込むサンプルとなる。

namespace ConsoleApp1{
    //アセンブリ追加用クラス①
    public class JsonNetCSharpDirectCompilerService : RazorEngine.Compilation.DirectCompilerServiceBase
    {
        public JsonNetCSharpDirectCompilerService(bool strictMode = true, Func<ParserBase> markupParserFactory = null)
            : base(
                new CSharpRazorCodeLanguage(strictMode),
                new CSharpCodeProvider(),
                markupParserFactory) { }
        public override IEnumerable<string> IncludeAssemblies()
        {
            // Json.NET のアセンブリの場所を追加
            return new[] { typeof(Binder).Assembly.Location, typeof(Newtonsoft.Json.JsonConverter).Assembly.Location };
        }
    }
    //アセンブリ追加用クラス②
    public class CustomCompilerServiceFactory : RazorEngine.Compilation.ICompilerServiceFactory
    {
        public ICompilerService CreateCompilerService(Language language)
        {
            switch (language)
            {
                case Language.CSharp:
                    return new JsonNetCSharpDirectCompilerService();

                case Language.VisualBasic:
                    return new VBDirectCompilerService();

                default:
                    throw new ArgumentException("Unsupported language: " + language);
            }
        }
    }

  public class Person
  {
  	public string Name { get; set; }
  	public uint Age { get; set; }
  	public IList<string> FavoriteTechs { get; set; }
  }
}
  (中略)

static void RazorEngineShow()
{
        var config = new RazorEngine.Configuration.TemplateServiceConfiguration();
        config.CompilerServiceFactory = new CustomCompilerServiceFactory();
        using (var templateService = new RazorEngine.Templating.TemplateService(config))
        {
            templateService.AddNamespace(@"ConsoleApp1");
            templateService.AddNamespace(@"Newtonsoft.Json");

            //設定した TemplateService を Razor に設定
            RazorEngine.Razor.SetTemplateService(templateService);
            Console.WriteLine(RazorEngine.Razor.Parse<IEnumerable<string>>(@"@{
var str = JsonConvert.SerializeObject(new Person()
    {
        Age = 22
    });
}
@str", STR_LIST, new DynamicViewBag(), "mycache")); //キャッシュ名がnullだと落ちる
        }
}

独自アセンブリを RazorEngine のテンプレート内部で利用するのはちょっと骨が折れるようだ…。

ARR(Application Request Routing)を利用して IIS と Tomcat を連携する

皆様、Windows 上で Webサーバ と Tomcat を連携する場合には何を利用しているだろうか? Tomcat と連携するからには、Apache+mod_jk を利用している場合も多いと思う。だが、Windows Vista 以降からは原則的に IIS がクライアントOSにもインストールされており、Web Platform Installer を利用することで容易にApache+mod_jk相当の環境が構築可能だ。
更に、Application Request Routing はリクエストに対するルーティング機能のほか、負荷分散、キャッシュ機能等も利用できる。やや語弊もあるが、ざっくりとは以下の対応だ。

今回は、ARR を利用し、Windows 上で IISTomcat を容易に連携する方法について紹介する。

環境構築周り

Application Request Routing の概要や環境構築については、以下の記事を参考にして欲しい。CodeZine 側の記事を参考に Web Platform でインストールすると環境構築が容易だ。

IIS+Tomcat の環境を構築

IIS, ARR, Tomcat を利用して、以下の環境を構築する。

まず、[ファイル名を指定して実行]から inetmgr を入力し、[インターネット インフォーメーション サービス(IIS) マネージャ]を起動する。まず、プロキシ機能の有効化を行う。まず、Application Request Routing のアイコンを押下する。

次に、Server Proxy Setting を押下する。

Enable proxy にチェックボックスにチェックをし、プロキシ機能を有効化する。

次にURL書き換えの設定を行う。まず、IIS マネージャの画面から URL 書き換えを選択し、[規則の追加]リンクを押下する。

[空の規則]の追加を押下する。

規則の設定画面にて、[パターン(T)]に「(HelloWorld.*)」を入力し、[URL書き換え(L)]に「http://localhost:8080/{R:0}」と入力して設定を完了する。

war ファイルの作成と Tomcat の実行

IIS 側の環境構築が完了したので、まずはデプロイ用の war ファイルを作成する。以下の index.jsp ファイルを含む HelloWorld.war を作成して欲しい。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Tomcat on IIS8</title>
</head>
<body>
	<h2>Tomcat 7.X on IIS 8</h2>
	<img src="/welcome.png" />
</body>
</html>

次に、作成した HelloWorld.war を C:\opt\apache-tomcat-7.0.34\webapps 以下に配置する(ファイルパスは適宜読みかえること)。war ファイルの配置後は、C:\opt\apache-tomcat-7.0.34\bin\startup.bat を実行し、ブラウザを利用して http://localhost/HelloWorld にアクセスし、以下の画面が表示されれば設定は無事完了だ。