2015年8月11日火曜日

Redis Cluster を構築してみた

概要

Redis Cluster を Mac 上に構築してみました
とりあえずシングルノードで構築しています

環境

  • Mac OS X 10.10.4
  • Redis 3.0.2
  • Ruby 2.2.0p0
  • Gem 2.4.5
  • redis-rb 3.2.1

インストール方法

  • Redis
brew install redis
  • Ruby
brew install ruby
gem install redis

Cluster 設定

mkdir -p work/redis-cluster
cd work/redis-cluster
mkdir 7000 7001 7002
cp /usr/local/etc/redis.conf.default 7000/redis.conf
cp /usr/local/etc/redis.conf.default 7001/redis.conf
cp /usr/local/etc/redis.conf.default 7002/redis.conf
vim 7000/redis.conf
vim 7001/redis.conf
vim 7002/redis.conf

以下の部分を書き換えてください

  • port 7000
  • appendonly yes
  • cluster-enabled yes
  • cluster-config-file nodes-7000.conf
  • cluster-node-timeout 5000

portcluster-config-fileの数字の部分は各設定ファイルごとに書き換えてください
設定ファイルの書き換えが完了したら一旦各 redis ノードを起動しましょう

redis-server work/redis-cluster/7000/redis.conf
redis-server work/redis-cluster/7001/redis.conf
redis-server work/redis-cluster/7002/redis.conf

Cluster 構築

公式で公開している Ruby スクリプトを使って Cluster は構築します

cd work/redis-cluster
wget http://download.redis.io/releases/redis-3.0.3.tar.gz
tar zvxf redis-3.0.3.tar.gz
./redis-3.0.3/src/redis-trib.rb create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002

Can I set the above configuration?
と聞かれるので yes を入力します

[OK] All nodes agree about slots configuration.
となれば Cluster の構築は完了です

動作確認

アクセスはお馴染みのredis-cliを使います
Cluster 環境にアクセスする場合は以下のように-c-pオプションを利用します

redis-cli -c -p 7000

Cluster の状態の確認は以下でできます

> cluster info

実際に値を投入してみましょう

127.0.0.1:7000> set hoge fuga
OK
127.0.0.1:7000> set hoge1 fuga
-> Redirected to slot [6811] located at 127.0.0.1:7001
OK
127.0.0.1:7001> set hoge2 fuga
-> Redirected to slot [11000] located at 127.0.0.1:7002
OK

こんな感じで key ごとに値を持つホストが変わってきます
Redis Cluster は SLOTS という概念を持っています
簡単に言うと SET したい key ごとに SLOTS 番号が計算されて、その番号に応じた適切なノードに値が保存されるという仕組みです

Cluster の SLOTS の設定状況の確認は以下でできます

> cluster slots

例えば hoge の SLOTS 番号は 1525 番で 1525 番はどのノードに割り当てられるかと言うとポート 7000 番のノードに割り振られます

127.0.0.1:7000> cluster keyslot hoge
(integer) 1525
127.0.0.1:7000> cluster slots
1) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 7001
2) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 7002
3) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 7000

上記の SLOTS の設定はデフォルトの状態です
もちろん好きな SLOTS 番号を好きなノードに割り当てることもできます
そのあたりの設定は別の記事で紹介できればと思います

最後に

Redis Cluster はバージョン 3.0 以上の Redis でサポートされています
Cluster を構築すること自体は非常に簡単にできます
また、Master - Slave 構成もサポートしているので 6 台のノードがあれば、 Master - Slave 構成も組めます

実運用でどこまでスケールできるのかとか、運用上発生する具体的なトラブルシューティングの検証とかが結構必要かなと思いました
まだ、Web 上にもその辺の運用話は少ないのかなという印象を受けました
その辺のノウハウが溜まってくるのはもう少し先なのかな

参考サイト

2015年8月6日木曜日

Production 用の証明書を使って NCMB で APNs プッシュ送信の実機テストをする方法

概要

過去に紹介した記事で iOS のプッシュ方法を紹介しました
このときに使用していたプッシュ送信用の証明書は Development 用の証明書を使っており、この証明書は普通アプリ公開時には使用しません
別途 Production (Distribution) 用の証明書を作成してこれを使うのですが、公開する前にテストしたい場合も多いと思います
結構ハマったので自分が実施した手順を紹介します

環境

  • Mac OS X 10.10.4
  • Xcode 6.4
  • iPhone6 (iOS 8.4)
  • Developer Member Center 20150806 時点

手順

基本的な手順はこれと同じです
上記手順の中で注意することを紹介していきます

作成済みのAppIDのプッシュ通知の Distribution 側を有効にする

AppIDがない場合は適当に作成してください

すでに作成したAppIDがある場合は「Edit」からプッシュ通知を有効にすることができます
Edit したらプッシュを有効にする項目があるのでそれのチェックボックスをONにします

ONにしたら続けて証明書を作成しましょう
すぐ下に「Create Certificate」というボタンが出ると思うので Production SSL Certificate の方のボタンをクリックして証明書を作成します
証明書の作成は過去に紹介した方法と同じになります
事前に作成していると思われる CSR を指定して証明書を作成しましょう
Production 用のプッシュ送信用の証明書を作成すると証明書の情報に「APNs Production iOS」と表示されます
production_cert.png

以下は Production 用の証明書まで作成が完了してプッシュ通知が有効になっている状態のAppIDになります
この状態になっていればOKです

enable_push.png

証明書から .p12 ファイルを作成する

先ほど作成した Production 用の証明書ダウンロードします
ダウンロードした証明書をダブルクリックしてキーチェーンアクセスで開きましょう
あとは過去の記事を参考に .p12 ファイルを作成すればOKです

一応、NCMB を想定しているので作成した .p12 ファイルはアップロードしておくといいと思います

Adhoc 用のプロビジョニングプロファイルを作成する

これが一番重要かと思います
Developer Member Center で Adhoc 用のプロビジョニングプロファイルを作成しましょう

作成を開始するとプロビジョニングプロファイルファイルの種類を選択する画面になると思います
そこで Distribution 側の「Ad hoc」を選択しましょう

choice_adhoc.png

次は AppID を選択する画面なのでプッシュ通知を有効にした AppID を選択しましょう
証明書の選択画面ではプッシュ通知用の証明書を選択しません(というかできません)配布用の証明書を素直に選択しましょう
その次で Ad hoc で配布する端末を選択することになると思います
基本は全部チェックでいいと思います、適宜変更してください
そして、最後にプロビジョニングプロファイルの名前を入力します

これで Adhoc 用のプロビジョニングプロファイルの作成完了です
作成したプロビジョニングプロファイルはあとで Xcode と同期して Xcode 上のプロジェクトに指定します

サンプル用のプロジェクトを作成

特に気にすることはないと思います
普通に作成しましょう
過去の記事を参考にデバイストークンを登録する処理までコーディングしてしまってください

プロジェクトをAdhocビルドできるようにする

ここもポイントです

まず

Xcode -> Preferences -> Accounts -> View Details (右下) -> 更新ボタン (左下)

を実行し Developer Menter Center 上のプロビジョニングプロファイルの情報を Xcode に同期しましょう

同期が完了したらプロジェクトのビルドの設定をします

プロジェクトを選択 -> Build Settings -> Provisioning Profile

で先ほど作成した Adhoc 用のプロビジョニングプロファイルを指定しましょう
Provisioning Profile の欄がない場合は実機を接続して一回実機でビルドしようとするとエラーになり、その欄が登場すると思います

Provisioning Profile の欄を設定したら、その上の「Release」の欄を Distribution 用の証明書を指定しましょう
最終的に以下のようになっていればOKです

set_adhoc_build.png

ビルドして .ipa ファイルを作成する

実機を接続 -> Product -> Archive

でビルドします
すると Archives というビルド結果の一覧のダイアログが表示されるので、右側にある「Export」を実行します

まずエクスポート方法を選択します
真ん中の「Save for Ad hoc Development」をクリックします

choice_export_mode.png

次にユーザを指定して、アーカイブが完了したら「Export」ボタンをクリックして .ipa ファイルをエクスポートします
保存先を選択しましょう

作成できた .ipa ファイルを実機に転送する

これは調べるといろいろと方法があるようです (Webサーバを使ったり、TestFlightを使ったり)
今回は iTunes を使った方法を紹介します

実機をつないだ状態で iTunes を立ち上げましょう
立ち上げたら iPhone を選択して App でアプリ一覧を表示しましょう

select_app.png

そしたら

ファイル -> ライブラリに追加

で先ほど保存した .ipa ファイルを指定します
すると iTunes 上に先ほど作成したアプリが登録されます

登録できたらインストールをクリックし右したので「転送」をクリックしましょう
これで Adhoc ビルドしたアプリを実機に転送することができます

動作確認してみる

ここまで来たら後はアプリを開いて動作確認してみましょう
今回は前回同様で NCMB の利用を想定しているためアプリを開いたときに NCMB 側にデバイストークンの情報が登録されればOKです
もちろん、サンプルプロジェクトを作成した段階で必要なコーディングは済ませておいてください

デバーストークンが登録できれば、あとは NCMB に .p12 ファイルをアップロードして、プッシュ通知を作成すればOKです

最後に

噂によると Xcodeだけでも .ipa ファイルの転送ができるので、それができる方はわざわざ iTunes とか起動しなくてもいいと思います
ハマったのはやはり Adhoc ビルドするところでしょうか
ずっと Development 用のプロビジョニングプロファイル指定して来たので Adhoc ビルドの存在自体を知りませんでした

Adhoc ビルドしていないアプリでもデバイストークンの登録はできるのですが、.p12 ファイルが Production 用だと証明書と一致せずうまく送信できません

とりあえず APNs は複雑すぎると思います

2015年8月5日水曜日

Google の 2 段階認証を有効にしてみた

概要

2段階認証とはログイン時にID/PWだけでなく、登録した携帯電話に送られてくる確認用のコードを入力して初めてログインしたことにできる機能です
Googleの2段階認証を有効にしたところいろいろとややこしい自体になったので状況をメモしておきます
2段階認証を有効にする方法はここでは紹介しません

環境

  • Google 2段階認証 2015/08/05 時点での機能
  • 有効にしたGoogleアカウントは1つ
  • Google アカウントを利用しているデバイスは全部で7台 (Windows x 2, Mac x 2, iPhone, Android x 2)

遭遇したケース

とりあえず全てがログアウト状態になる

Gmail, GoogleDrive, その他Google認証を使っているアプリ等すべてのアプリがログアウト状態になりました
かつアプリをインストールしている全マシンでログアウト状態になります
かつ各マシンでアプリにログインするたびに携帯に確認コードが飛んできます

SMTP 認証が使えなくなった

Jenkinsのビルド失敗時のメールサーバをGoogleのSMTPサーバを使っていたのですがこれも2段階認証の対象になるようです
2段階認証を有効にすると、そんなケースのために「アプリ用のパスワード」というものを発行できるようになります
Googleアカウントの管理画面からアプリ用のパスワードは発行できます
ここを参考にするといいと思います
アプリ用のパスワードを発行したら、そのパスワードを認証用のパスワードとして使うことで2段階認証が発生せずJenkinsから認証できるようになります
また、アプリ用のパスワードは基本使い捨てなので忘れたら再度発行して入力しましょう
アプリ用パスワードの使い回しはやめたほうがいいです

iPhoneの連絡先の連携ができなくなった

これは上記の理由と同じで「アプリ用のパスワード」を発行して、そのパスワードでiPhoneから認証すればOKです

iPhone上のGoogle系のアプリについて

iPhone上でログアウトになっていたアプリは先ほどの連絡先連携の他に

  • Gmail
  • Google Chrome
  • Google Drive
  • Youtube

でした、これらの認証ですが、Google Chrome だけ 2 段階認証を一回実施したら他のアプリはすでにログイン状態になっていました
理由はよくわからないですが、iPhone上で一度 2 段階認証を実施すると他のアプリの認証もOKになったりするんでしょうか
自分は iPhone 上にインストールしている Google系のアプリはそれだけだったのですが、他のアプリもインストールしている人はもしかしたら別途 2 段階認証 or アプリ用パスワードの発行が必要になるかもしれません

Android上のGoogle系のアプリについて

基本的にはiPhoneと同じです
アプリそれぞれで 2 段階認証してください
Androidの場合は、設定に「アカウントと同期」できる端末もありここで Google アカウントを認証すれば全アプリに認証が適用されるので楽です

Tips

  • 携帯(メールアドレス)が変わる場合は 2 段階認証をOFFにしてから携帯を変更したほうがよさそう
    そうしないと 2 段階認証用のコードを受け取れなくなってしまいログインできなくなるためです
    そんなときを想定しているのか、バックアップコードというものが発行できるようです
    バックアップコードには 10 個の確認コードが含まれており、それぞれ一回しか使えないですが、携帯に送られてくる確認コードと同様に使用することができます
    バックアップコードは印刷して保存しておくことを薦めていました

最後に

とにかく面倒くさいです
それだけセキュアになったと思えば良いといえば良いかなと
まぁ 2 段階認証を設定していても結局 PC とか盗まれて PC にパスワードを設定していなかったらほぼ終わりなんですけどね
それでも Google アカウントを Webのアイデンティティのメインにしているのであれば絶対設定したほうがいいと思います

2015年8月3日月曜日

現在サービスが実行されていないため、Windows Update で更新プログラムを確認できません

ネットでいろいろ調べてドライバが変だったり変なアプリが入っているから
みたいな記事をみたけど全部ハズレ
自分は以下で解決しました

WindodwsUpdateの自動更新をOFFにして手動で更新を行う

ポイントは自動更新をOFFにすること
理由は全然わからんないけど、これで無事WindowsUpdateできました
1年ぶりくらいに起動したWindowsだったので、長期間起動していないケースの方はお試しあれ

ちなみに 2014/06/30 から 2015/08/03 までの1年ちょっとの更新で

更新プログラム数は約 170
更新 -> 再起動の作業を 7 回
更新完了までの時間は約 4:00 ほど

かかりました
面倒くさすぎる

2015年7月29日水曜日

Java の Mockito を使ってみた

概要

mockito はJUnit等のユニットテストで使用する Mock オブジェクトを作成するためのライブラリです
例えば外部サービスと接続するアプリでローカルでテストする場合は接続しないでテストしたかったりするケースなんかに使えます

環境

  • Mac OX X 10.10.4
  • Eclipse Luna 4.4
  • Java 1.8.0_31
  • Maven 3.2.1
  • Mockito 1.10.19

使ってみる

サンプルプロジェクトの作成

EclipseでMavenプロジェクトを作成します
maven-archetype-quickstartを ArtifactId に選択してプロジェクトを作成しました
本記事中で扱うパッケージは「com.sample.test」としています

Mockito を使ったテストを書いてみる

maven-archetype-quickstartでプロジェクトを作成すれば「src/main/java/com.sample.test.AppTest.java」があると思うのでそれを使います

pom.xml へライブラリ追加

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>1.10.19</version>
</dependency>

上記でOKです
使えるようになったら早速試してみます

verify

verifyは作成した Mock オブジェクトが正確に何回動作したかをチェックするための機能です
とりあえず以下がverifyを使ったサンプルコードです

public void testVerify() {
  List mockedList = mock(List.class);
  mockedList.add("one");
  mockedList.clear();
  verify(mockedList).add("one");
  verify(mockedList).clear();
}

まずはじめに冒頭でimport static org.mockito.Mockito.*;で書いておくといいと思います
Mockitoクラス配下の static なメソッドを使う機会が
多いためです

mockメソッドの引数に Mock オブジェクトとして作成したクラスのオブジェクトを渡します
サンプルのList.classjava.util.Listの List クラスです
作成した Mock オブジェクトは普通にそのクラスに存在しているメソッドをコールすることができます
作成した Mock オブジェクトに対してadd, clearを1回ずつコールします

そしてその後でverifyを使って Mock オブジェクトが正しく動作したかを評価します
verifyの引数に評価したい Mock オブジェクトを指定して、続けて評価したいメソッドをコールします

verify(mockedList).add("one");

こうすることで何が起きているかいうと

mockedList の add メソッドが引数 “one” で1回ちゃんとコールされているか

を評価することができます
なので、例えばverifyする前のadd

mockedList.add("two");

こんな感じでコールし直すように変更するとverifyが失敗することが確認できると思います

verifyは引数をもう一つ持つことができて評価する回数を指定することができます

verify(mockedList, times(2)).add("one");

Mock クラスのtimesというメソッドを使って評価された回数を指定できます

when

whenはある Mock オブジェクトのメソッドがコールされたときにこういう値を返しますという振る舞いを設定することができる機能です
とりあえず以下がwhenを使ったサンプルコードです

public void testWhen() {
  App app = mock(App.class);
  when(app.getAppname()).thenReturn("myapp");
  System.out.println(app.getAppname());
}

Mock オブジェクトを作成してwhenメソッドを呼び出します
引数に作成した Mock オブジェクトのあるメソッドを指定します
指定するメソッドは必ず返り値を保つ必要があるためvoidのメソッドでwhenを利用することはできません
whenで返り値を設定した場合、Setter などで値を上書きしたと思ってもwhenで指定した値が取得されます

when(app.getAppname()).thenReturn("myapp");
app.setAppname("hoge");
System.out.println(app.getAppname());

-> myapp が出力される

要するにあるメソッドの戻り値を意図的に設定することができます
他にも Exception を投げたり引数を元に複雑な返り値を設定できるthenAnswerがあります
※参考 : http://docs.mockito.googlecode.com/hg/org/mockito/stubbing/OngoingStubbing.html

最後に

Java の Mock 系のライブラリは他にもいろいろあります (JMock, EasyMock etc…)
他のライブラリも使って書き方や機能を比較してみるとおもしろいかも
今回

2015年7月28日火曜日

ニフティクラウド RDB で utf8_mb4 が使えるか試してみた

概要

ここの記事を参考にニフティクラウドRDBでも utf8_mb4 が使えるか試してみました
ちなみに utf8_mb4 は 4 バイト長の utf8 文字をサポートする文字コードで emoticon (絵文字) や 第4水準漢字が使えるようになるみたいです

環境

  • Mac OS X 10.10.4
  • MySQL Client 14.14 Distrib 5.6.25
  • MySQL Server 5.6.22 (ニフティクラウド RDB)

RDBの作成

コントロールパネルから簡単に行えるので詳細は割愛します
流れだけ紹介します

  1. コントロールパネルにログインしDBサーバの一覧から「DBサーバー新規作成」
  2. 入力する内容はとりあえず適当でOK (後で utf8_mb4 を有効にした DB パラメータグループに変更します)

今回はテストなのでスペックは mini として冗長化やディスク拡張はしない最小限のスペックで作成しました
また、DB名やユーザ名も適当に指定しました

30分ほどで作成できるので待ちます

utf8_mb4 用の DB パラメータグループの作成

DBサーバーが作成できたら DB パラメータグループを作成します
今回は utf8_mb4 を設定した専用のパラメータグループを作成します
DBパラメータの一覧から「DBパラメーターグループの新規作成」として作成します
名前は「utf8mb4」とでもしておきましょう
作成できたらパラメータを変更していきます

作成した DB パラメータグループをチェックしてプルダウンから「DB パラメータグループの編集」を選択します
ここで以下4パターンのパラメータを変更します

character 系パラメータの変更

検索ボックスに「character」と入力してください
ここで表示された以下5つの項目を utf8mb4 に変更します

  • character_set_client
  • character_set_connection
  • character_set_database
  • character_set_results
  • character_set_server

変更はプルダウンの一覧から選択してください

set_character.png

collation 系パラメータの変更

次に検索ボックスに「collation」と入力してください
ここで表示された以下2つの項目を utf8mb_general_ci に変更します

  • collation_connection
  • collation_server

変更はプルダウンの一覧から選択してください

set_collation.png

skip-character-set-client-handshake パラメータの変更

3つ目は検索ボックスに「skip-character-set-handshake」と入力してください
このパラメータの値を 1 に変更します

set_handshake.png

init_connect パラメータの変更

最後に検索ボックスに「init_connect」と入力してください
このパラメータは文字列を直接入力する必要があるので「SET NAMES utf8mb4;」と入力してください

set_init_connect.png

上記の作業は一度に変更してもOKですし、1つずつ保存してから実施してもOKです
全項目が設定できたら DB パラメータグループの設定は終わりです

DB サーバの DB パラメータグループを変更する

DB サーバーの一覧に移動します
作成待ちになった DB サーバーができていれば DB パラメーターグループを変更します
DB サーバーをチェックして「設定変更」をクリックします

DB パラメーターグループを変更できる箇所があるのでプルダウンから作成した DB パラメーターグループを選択して変更を適用します

これで DB サーバーに適用されている DB パラメータグループの変更は完了しました
ただ、まだ MySQL 自体にそのパラメータが適用されていないので一度 DB サーバー自体を再起動します
これも DB サーバーをチェックして「DBサーバー再起動」を選択すればOKです
再起動は数分で完了します

MySQL クライアントで接続して動作確認してみる

今回は Mac で動作確認しています
mysqlコマンドがインストールされていない場合はbrew isntall mysqlでインストールしましょう

では接続してみましょう
接続方法は普通の MySQL と同じです

mysql -u usename -p db_name -h grobal_ip_address

username, db_name, grobal_ip_address は作成された DB サーバーの情報を入力してください
パスワードを入力して接続できればOKです

文字コードの設定を確認してみる

statusコマンドで確認してみましょう

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.6.25, for osx10.10 (x86_64) using  EditLine wrapper

Connection id:          41
Current database:       utf8mb4_test
Current user:           username@xxx.xxx.xxx.xxx
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.6.22 MySQL Community Server (GPL)
Protocol version:       10
Connection:             111.171.222.67 via TCP/IP
Server characterset:    utf8mb4
Db     characterset:    utf8
Client characterset:    utf8mb4
Conn.  characterset:    utf8mb4
TCP port:               3306
Uptime:                 1 hour 31 min 41 sec

Threads: 1  Questions: 135  Slow queries: 0  Opens: 73  Flush tables: 1  Open tables: 65  Queries per second avg: 0.024
--------------

utf8mb4 になっていることが確認できます
では実際に絵文字が登録できるか試してみます

絵文字を登録してみる

まず適当にテーブルを作成しましょう

CREATE TABLE test_utf8mb4 (str VARCHAR(50)) CHARACTER SET utf8mb4;

このテーブルにデータを入れてみます
コマンドは以下の通りです
※絵文字を使用しているため画像になります

insert_emoticon.png

完了したらSELECTしてみましょう

select_emoticon.png

絵文字が登録されていることを確認できました
ちなみに utf8mb4 になっていない MySQL に対してもテストしてみたのですが、以下のように絵文字は登録されないことが確認できました

select_empty_character.png

最後に

ニフティクラウドRDBでも utf8mb4 が動くことを確認できました
サーバ側の設定はコンパネおよびターミナルでできるのですが、動作確認時の絵文字の入力が Mac じゃないとちょっと大変かもしれません
Mac の場合Shift+Cmd+Spaceで絵文字一覧がでるのでそこから選択するだけ絵文字入力できるので楽です

2015年7月27日月曜日

Spring でファイルアップロードを PUT メソッドで処理する方法

概要

Spring で multipart/form-data + MultipartFile を扱う場合は、通常は POST メソッドでなければなりません
どうやら MultipartFile は POST メソッドでないと操作できないようです
実装上そもそも不要なケースが多いですが、PUT メソッドで multipart/form-data を受け取る方法を紹介します

環境

  • Mac OS X 10.10.4
  • Eclipse Luna 4.4.1
  • Spring Framework 4.1.6
  • Spring Tool Suite 3.6.4
  • Java 1.8.0_31
  • Maven 3.2.1

サンプルプロジェクト作成

過去の記事を参考に作成してください
Spring Framework のバージョンが最新でない場合は pom.xml 「org.springframework-version」のバージョン記載部分を最新のバージョンに変更してください

コーディング

@RequestMapping に PUT で multipart/form-data を受け取る定義を記載する

@RequestMapping(value = "/**", method = {RequestMethod.PUT}, headers = "content-type=multipart/form-data")
@ResponseBody
public void receivePutMulti(@RequestParam(value = "file", required = true) MultipartFile file) {
  ...
}

受け取りたいURLマッピングに上記のようにメソッド名とヘッダーのタイプを指定します
今回はファイルのアップロードを想定しているため MultipartFile を受け取るように指定します
返り値は jsp を返却したい場合は String 型を返り値に指定し@ResponseBodyを削除してください

ちなみにこの状態で以下のように PUT メソッドでファイルのアップロードを実行しようとすると500エラーになります

curl -v -X PUT -H "Content-Type: multipart/form-data" http://localhost:8080/test/put/ -F "file=@pom.xml"
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?

このエラーがでないように設定していきます

servlet-context.xml に独自 MultipartResolver を指定する

<beans:bean id="multipartResolver" class="com.sample.test.ExtendedMultipartResolver">
</beans:bean>

Multipart なデータは MultipartResolver というクラスでどう受け取るかのルール付けがされており、このクラスを拡張することで PUT メソッドでも MultipartFile が受信できるようにします

次に指定した独自の MultipartResolver クラスを作成していきます

独自 MultipartResolver クラスの作成

pom.xml に定義を追加する

<!-- FileUpload -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

独自の MultipartResolver を作成するのに commons-fileupload が必要になるので pom.xml に追記します
既存の<dependencies>タグ内に追記してください

ExtendedMultipartResolver クラスの作成

今回はcom.sampe.test.ExtendedMultipartResolverとして作成します
とりあえず以下がサンプルになります

package com.sample.test;

import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

public class ExtendedMultipartResolver extends CommonsMultipartResolver {

    @Override
    public boolean isMultipart(HttpServletRequest request) {
        if (request != null) {
            String httpMethod = request.getMethod().toLowerCase();
            // test for allowed methods here...
            String contentType = request.getContentType();
            return (contentType != null && contentType.toLowerCase().startsWith("multipart"));
        } else {
            return false;
        }
    }
}

ポイントは@OverrideしているisMultipartメソッドでこの中でHttpServletRequestからアクセスしているメソッド情報を取得して PUT メソッドでもtrueを返却するように実装し直します
サンプルは全メソッドで multipart/form-data を許可する設定になっており特にメソッド名に応じてfalseを返却するような if 分岐は入れていません

動作確認

ここまで実装できたらアプリを再起動してコードを反映しましょう
先ほどの PUT + multipart/form-data の curl のサンプルリクエストを投げてみましょう

すると先ほどのエラーにはならず定義したメソッド内の処理が実行されていることが確認できると思います

最後に

ちょっと調べてみたんですが Spring がデフォルトで実装していないのは RFC 的にそうなっているため(?) みたいな記事があったような気がします
詳細には調べていないので、わかりませんが PUT で multipart/form-data は基本は受け取らないんですかね
そもそもそれは実装が悪いってことになるのか

参考サイト