<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4586301750364911142</id><updated>2024-10-25T20:47:52.631+09:00</updated><category term="コンテナ管理トランザクションであそぶ"/><category term="DocBook"/><category term="JBossのテンプレートでDocBook"/><category term="Samba4"/><category term="RabbitMQ"/><category term="Spring"/><category term="わたしもやってみた"/><category term="AspectJ"/><category term="Google App Engine"/><category term="Wicket"/><category term="air"/><category term="flex"/><category term="maven"/><category term="ごあいさつ"/><title type='text'>諸行無常 - As Time Goes By -</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fummy-net.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>37</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-3223755634666377906</id><published>2016-05-16T00:08:00.000+09:00</published><updated>2016-05-16T00:08:12.183+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="RabbitMQ"/><title type='text'>RabbitMQ の 冗長化を検証する / Consumer 編</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
前回、『&lt;a href=&quot;/2016/05/rabbitmq_1.html&quot;&gt;RabbitMQ の 冗長化を考える&lt;/a&gt;』でRabbitMQ の冗長化の設定を行いました。
今回は、その冗長化した RabbitMQ サーバーを実際、Failover させて、その様子を観察したいと思います。
&lt;/pre&gt;
&lt;pre&gt;
冗長化を考える理由に
何かしらの障害が発生したとしても Publisher が送信したメッセージを
Consumer によって確実に処理させたいということが挙げられると思います。
&lt;/pre&gt;
&lt;pre&gt;
全体を通してでは、
Publisher が送信したメッセージは、Consumer で確実に処理されるということになると思います。
&lt;/pre&gt;
&lt;pre&gt;
これを Publisher と Consumer の間に Queue を置いて考えると
Publisher は、何かしらの障害が発生したとしてもメッセージを確実に Queue に届けるところまでが
責任範囲で、その後、Queue のメッセージが Consumer でどう処理されるかは、責任の範囲外になると思います。
一方、Consumer は、Queue にメッセージがどのような方法で届けられたかはということは、関係なく
Queue にあるメッセージを確実に処理することが責任範囲になると思います。
&lt;/pre&gt;
&lt;pre&gt;
このように Publisher と Consumer の責任範囲は、それぞれ被らないものだと思います。
そう考えると Failover をそれぞれ別々の視点で観察できるとのではないかと思いました。
(RPC 方式のメッセージ処理では、また違ってくると思うのですが今回は、考えません。
そんな訳で今回は、Consumer側 の Failover の様子を観察してみたと思います。
&lt;/pre&gt;

&lt;pre&gt;
JavaClient では、Cluster の Node を複数、ConnectionFactory に設定することができ、
ConnectionFactory は、その中から接続可能な Node を選択して Connection を生成しているようです。
このことが接続している Node が落ちてしまった場合に ConnectionFactory に設定されている
別の接続可能な Node で自動的に接続が復帰することを可能にしているようです。
Node に仮想IP などを設定する必要がないようです。
厳密に負荷分散を考える場合は、仮想IP などが必要かもしれません。
(接続可能な IP の選択方法が Collections.shuffle() になってそうなので...
&lt;/pre&gt; 

&lt;pre&gt;
検証には、２種類の Consumer を考えています。
１つは、適当にスリープさせながらメッセージ処理をする HappyPath での Consumer と
もう１つは、スリープさせずに連続的にメッセージ処理をする UnhappyPath での Consumer になります。
&lt;/pre&gt; 
&lt;pre&gt;
以下、３点を Failover 時に検証します。

  &lt;span&gt;1. &lt;/span&gt;接続可能な Node に切り替わること。
  &lt;span&gt;2. &lt;/span&gt;メッセージが消えたしまわないこと。
  &lt;span&gt;3. &lt;/span&gt;メッセージが２重に処理されないこと。
&lt;/pre&gt;

&lt;pre&gt;
始める前に...
RabbitMQ がインストール済であること
RabbitMQ サーバーにログイン可能でリソースにアクセスできるユーザが作成済であること
(ユーザとパスワードを &quot;user01&quot; と &quot;passwd&quot; としています。
ManagementPlugin が有効になっていて CommandLineTool が使えること
ホスト名が設定されていること
以上が前提になります。
このあたりはわたしも『&lt;a href=&quot;/2016/04/yum-local-repository-rabbitmq.html&quot;&gt;yum の Local Repository に RabbitMQ をインストール&lt;/a&gt;』と
『&lt;a href=&quot;/2016/05/rabbitmq_1.html&quot;&gt;RabbitMQ の 冗長化を考える&lt;/a&gt;』にまとめていますので
よろしければ、こちらも合わせて、ご参考に。
&lt;/pre&gt;
&lt;pre&gt;
前置きが長くなってしまいましたが
ではでは！Getting Started!!
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;環境&lt;/h2&gt;
&lt;b&gt;検証機&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;開発言語&lt;/th&gt;
    &lt;td&gt;Java 1.8.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;rabbitmq-java-client 3.6.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Gradle 2.12&lt;br /&gt;※gradle コマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows 8.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre&gt;
前回と同様に以下、２台のサーバー(vm-1st と vm-2nd)で冗長化します。
&lt;/pre&gt;
&lt;b&gt;RabbitMQ Server (vm-1st)&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;ホスト名&lt;/th&gt;
    &lt;td&gt;vm-1st&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;RabbitMQ Server (vm-2nd)&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;ホスト名&lt;/th&gt;
    &lt;td&gt;vm-2nd&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.120&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;pre&gt;
C ドライブ直下に「studying-rabbitmq-failover」フォルダを作成して各ファイルを以下のように配置します。
ファイルは、UTF-8 で保存します。
&lt;/pre&gt;
&lt;div id=&quot;studying-rabbitmq-failover&quot; class=&quot;fummy_tree&quot;&gt;
  &lt;ul&gt;
    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-rabbitmq-failover&lt;/span&gt;
      &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
          &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
              &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
                  &lt;ul&gt;
                    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
                      &lt;ul&gt;
                        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyAbstractConsumer.java&lt;/span&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyCancelConsumer.java&lt;/span&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyHappyConsumer.java&lt;/span&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyUnhappyConsumer.java&lt;/span&gt;&lt;/li&gt;
                      &lt;/ul&gt;
                    &lt;/li&gt;
                  &lt;/ul&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;publisher.gradle&lt;/span&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  tree1 = new YAHOO.widget.TreeView(&quot;studying-rabbitmq-failover&quot;);
  tree1.render();
  tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;準備&lt;/h2&gt;
&lt;pre&gt;
前回のおさらいも兼ねて Clustering の設定から始めたいと思います。
vm-2nd を vm-1st の Cluster に参加させます。
&lt;/pre&gt;
&lt;pre&gt;
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@vm-1st
rabbitmqctl start_app
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Cluster の設定を確認します。
vm-1st と vm-2nd の両方のターミナルで以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

Cluster status of node &#39;rabbit@vm-1st&#39; ...
[{nodes,[{disc,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-2nd&#39;,&#39;rabbit@vm-1st&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-1st&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

[{nodes,[{disc,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-1st&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のどちらも &quot;rabbit@vm-1st&quot; という Cluster に参加していて起動中である。
問題ないようですね。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次に Queue と directタイプの Exchange と宣言します。
Queue名と Exchange名は、それぞれ &quot;myQueue&quot; と &quot;myExchange&quot; にします。
RoutingKey を &quot;myKey&quot; にして &quot;myQueue&quot; と &quot;myExchange&quot; を Bind します。
vm-1st が HomeNode になるようにします。
&lt;/pre&gt;
&lt;pre&gt;
また、今回使用する Queue名と Exchange名で Queue と Exchange がすでに存在していると
ややこしくなるので念のため削除してから作成します。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin delete exchange name=myExchange
rabbitmqadmin delete queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin declare queue name=myQueue
rabbitmqadmin declare exchange name=myExchange type=direct
rabbitmqadmin declare binding source=myExchange destination=myQueue routing_key=myKey
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
宣言した Queue と Exchange そして、Bind を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list exchanges name type durable

+--------------------+---------+---------+
|        name        |  type   | durable |
+--------------------+---------+---------+
|                    | direct  | True    |
...
| myExchange         | direct  | True    |
+--------------------+---------+---------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list bindings

+------------+-------------+-------------+
|   source   | destination | routing_key |
+------------+-------------+-------------+
|            | myQueue     | myQueue     |
| myExchange | myQueue     | myKey       |
+------------+-------------+-------------+
&lt;/pre&gt;
&lt;pre&gt;
問題なく宣言されているのが確認できると思います。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
最後に QueueMirroring の設定をします。
先ほど宣言した Queue を Cluster 内のすべての Node で Mirroring するようにします。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_policy myPolicy &quot;^myQueue$&quot; &#39;{&quot;ha-mode&quot;:&quot;all&quot;, &quot;ha-sync-mode&quot;:&quot;automatic&quot;}&#39;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
追加でもう１つ
検証機が Windows なので Windows の hosts ファイルにも vm-1st と vm-2nd を追加しておきます。
&lt;/pre&gt;
&lt;u&gt;C:\Windows\System32\drivers\etc\hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
192.168.10.110 vm-1st
192.168.10.120 vm-2nd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
準備は、これで終わりです。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;ビルドスクリプトの作成&lt;/h2&gt;
&lt;pre&gt;
以下のような内容でビルドスクリプトを作成します。
&lt;/pre&gt;

&lt;u&gt;build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
apply from: &#39;publisher.gradle&#39;

apply plugin: &#39;java&#39;
apply plugin: &#39;eclipse&#39;
apply plugin: &#39;application&#39;

sourceCompatibility = &#39;1.8&#39;
targetCompatibility = &#39;1.8&#39;
[compileJava, compileTestJava]*.options*.encoding = &#39;UTF-8&#39;

mainClassName = (project.hasProperty(&#39;mainClass&#39;)) ? &quot;$mainClass&quot; : &#39;&#39;

repositories {
  jcenter()
}

dependencies {
  compile &#39;com.rabbitmq:amqp-client:3.6.1&#39;
}
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer側の冗長化の検証に RabbitMQ サーバーにメッセージを送信するスクリプトがあった方が
やりやすいと思ったので Java でメッセージ送信する Publisher側のプロジェクトも考えたのですが
今回は、以下のような gradle の task で済ませようと思います。
&lt;/pre&gt;
&lt;u&gt;publisher.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

def messageCount = project.hasProperty(&#39;messageCount&#39;) ? &quot;$messageCount&quot; : 10000

// (1)
task happyPublisher &amp;lt;&amp;lt; {
  println &#39;start&#39;
  Connection connection = newConnection();
  Channel channel = connection.createChannel();

  def tasks = [
    &#39;Maurice&#39;, &#39;Verdine&#39;, &#39;Philip&#39;, &#39;Jessica&#39;, &#39;Larry&#39;, 
    &#39;Johnny&#39;, &#39;Ralph&#39;, &#39;Al&#39;, &#39;Andrew&#39;, &#39;XXX&#39;
  ]

  tasks.each {
    byte[] body = it.getBytes();
    channel.basicPublish(&quot;myExchange&quot;, &quot;myKey&quot;, MessageProperties.PERSISTENT_TEXT_PLAIN, body);
  }

  connection.close();
  println &#39;done&#39;
}

// (2)
task unhappyPublisher &amp;lt;&amp;lt; {
  println &#39;start&#39;
  
  Connection connection = newConnection();
  Channel channel = connection.createChannel();
      
  for (int ct = 0; ct &amp;lt; messageCount; ct++) {
    byte[] body = String.format(&quot;test-%d&quot;, ct + 1).getBytes();
    channel.basicPublish(&quot;myExchange&quot;, &quot;myKey&quot;, MessageProperties.PERSISTENT_TEXT_PLAIN, body);
  }

  connection.close();
  println &#39;done&#39;
}

// RabbitMQ Server に接続して connection を生成する。
Connection newConnection() {  
  ConnectionFactory factory = new ConnectionFactory();
  factory.setHost(&quot;vm-1st&quot;);
  factory.setUsername(&quot;user01&quot;);
  factory.setPassword(&quot;passwd&quot;);
  factory.newConnection();
}

buildscript {
  repositories { jcenter() }
  dependencies { classpath &#39;com.rabbitmq:amqp-client:3.6.1&#39; }
}
&lt;/pre&gt;
&lt;pre&gt;
スクリプトの内容は、以下のようになります。
&lt;/pre&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;(1) happyPublisher タスク&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
&lt;pre&gt;
HappyPath の検証ケースで使用するメッセージを RabbitMQサーバーに送信します。
メッセージを10通送信します。
&lt;/pre&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;(2) unhappyPublisher タスク&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
&lt;pre&gt;
UnhappyPath の検証ケースで使用するメッセージを RabbitMQサーバーに送信します。
メッセージを10,000通送信します。
&lt;/pre&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;RabbitMQ サーバー接続クラスを作成する&lt;/h2&gt;
&lt;pre&gt;
RabbitMQ サーバーに接続する処理などを共通化した以下のようなクラスを作成します。
検証で作成する Consumerクラスはこのクラスを継承する形にします。
&lt;/pre&gt;
&lt;u&gt;src/main/java/studying/rabbitmq/MyAbstractConsumer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.rabbitmq;

import java.io.IOException;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Recoverable;
import com.rabbitmq.client.impl.recovery.AutorecoveringChannel;
import com.rabbitmq.client.impl.recovery.AutorecoveringConnection;

public abstract class MyAbstractConsumer {
  
  public void doAction() throws Exception {
 
    // RabbitMQ Server 接続情報
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUsername(&quot;user01&quot;);
    factory.setPassword(&quot;passwd&quot;);


    // (1) 冗長化の設定をして RabbitMQ Server に接続する。
    Address[] addrs = new Address[]{new Address(&quot;vm-1st&quot;), new Address(&quot;vm-2nd&quot;)};
    factory.setAutomaticRecoveryEnabled(true);
    AutorecoveringConnection connection = (AutorecoveringConnection)factory.newConnection(addrs);

    System.out.printf(&quot;listening... %s %n&quot;, connection.getAddress().getHostName());
    Thread.sleep(10 * 1000);

    
    // (2) Queue と Exchange の宣言と Bind をする。
    boolean durable = true;
    String queueName    = &quot;myQueue&quot;;
    String exchangeType = &quot;direct&quot;;
    String exchangeName = &quot;myExchange&quot;;
    String routingKey   = &quot;myKey&quot;;

    AutorecoveringChannel channel = (AutorecoveringChannel)connection.createChannel();
    channel.exchangeDeclare(exchangeName, exchangeType, durable);
    channel.queueDeclare(queueName, durable, false, false, null);
    channel.queueBind(queueName, exchangeName, routingKey);
    channel.basicQos(1);

    
    // (3) Connection と Channel に ShutdownListener を設定する。
    connection.addShutdownListener(cause -&gt; System.out.printf(&quot;Connection Shutdown... %n&quot;));
    channel.addShutdownListener(cause -&gt; System.out.printf(&quot;Channel    Shutdown... %n&quot;));


    // (4) Connection と Channel に RecoveryListener を設定する。
    connection.addRecoveryListener((Recoverable recoverable) -&gt; {
      AutorecoveringConnection conn = (AutorecoveringConnection)recoverable;
      String hostName = conn.getAddress().getHostName();
      System.out.printf(&quot;Connection Recovered !!! %s %n&quot;, hostName);
    });
    channel.addRecoveryListener((Recoverable recoverable) -&gt; {
      AutorecoveringChannel chan = (AutorecoveringChannel)recoverable;
      String hostName = chan.getConnection().getAddress().getHostName();
      System.out.printf(&quot;Channel    Recovered !!! %s %n&quot;, hostName);
    });

 
    // Consumer を設定する。
    this.assignConsumerFor(channel, queueName);
  }

  // (5)
  protected abstract void assignConsumerFor(Channel channel, String queue) throws IOException;
}
&lt;/pre&gt;
&lt;pre&gt;
処理内容は、以下のようになります。
&lt;/pre&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;(1) 冗長化の設定をして RabbitMQ Server に接続する。&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;
ClusterNode のホスト名(または、IP)を複数指定して Connection を作成します。
AutomaticRecovery も忘れずに有効化しておきます。
&lt;/pre&gt;&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;(2) Queue と Exchange の宣言と Bind をする。&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;
準備の時に行っているので不要だと思いますが
ここでも Queue と Exchange の宣言とその Bind をしています。
念のため basicQos でメッセージの取り出しを１回につき１つにしています。
&lt;/pre&gt;&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;(3) Connection と Channel に ShutdownListener を設定する。&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;
Connection と Channel の Shutdown時、コンソールにログを出力します。
&lt;/pre&gt;&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;(4) Connection と Channel に RecoveryListener を設定する。&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;
Connection と Channel の Recovery時、コンソールにログを出力します。
&lt;/pre&gt;&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;(5) assignConsumerFor() メソッド&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;
サブクラスはこのメソッドを実装して Consumer を Channel に割り当てます。
&lt;/pre&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Happy Path を検証する&lt;/h2&gt;
&lt;pre&gt;
初めは、おおよそ想定通りにいってくれるであろうパターンから検証してみたいと思います。
適当にスリープさせながら10通のメッセージを処理します。
以下のような内容で Consumerクラスを作成します。
&lt;/pre&gt;

&lt;u&gt;src/main/java/studying/rabbitmq/MyHappyConsumer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.rabbitmq;

import java.io.IOException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class MyHappyConsumer extends MyAbstractConsumer {

  @Override
  protected void assignConsumerFor(Channel channel, String queue) throws IOException {

    Consumer consumer = new DefaultConsumer(channel) {

      protected int ack;
      
      @Override
      public void handleDelivery(String consumerTag, Envelope envelope, 
          AMQP.BasicProperties properties, byte[] body) throws IOException {
        
        String message = new String(body, &quot;UTF-8&quot;);
        String hostName = channel.getConnection().getAddress().getHostName();
        
        System.out.printf(&quot; Received &#39;%-7s&#39;&quot;, message);
        System.out.printf(&quot; - %s&quot;, hostName);
        System.out.printf(&quot; deliveryTag:%s&quot;,  envelope.getDeliveryTag());
        System.out.printf(&quot; redeliver:%s %n&quot;, envelope.isRedeliver());

        try {
          // 3秒 sleep する。
          Thread.sleep(3 * 1000);
          
          // ack を返す。
          channel.basicAck(envelope.getDeliveryTag(), false);
          this.ack++;
          System.out.printf(&quot;  Done &#39;%s&#39; ack:%d %n&quot;, message, this.ack);

        } catch (InterruptedException e) {
          System.out.printf(&quot;  Interrupted &#39;%s&#39; %n&quot;, message);
          e.printStackTrace();
        } catch (Exception e) {
          System.out.printf(&quot;  Error &#39;%s&#39; %n&quot;, message);
          throw e;
        }
      }
    };

    boolean autoAck = false;
    channel.basicConsume(queue, autoAck, consumer);
  }

  
  public static void main(String[] args) {
    try {
      new MyHappyConsumer().doAction();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
DefaultConsumerクラスの handleDelivery() メソッドをオーバーライドしてメッセージ処理をしています。
&lt;/pre&gt;
&lt;pre&gt;
処理内容としては、

  &lt;span&gt;1. &lt;/span&gt;受信したメッセージをコンソールに表示する。
  &lt;span&gt;2. &lt;/span&gt;３秒スリープさせてタイミングを整える。
  &lt;span&gt;3. &lt;/span&gt;RabbitMQ サーバーに ack を返す。
  &lt;span&gt;   &lt;/span&gt;RabbitMQ サーバーに返した ack をカウントする。
  &lt;span&gt;   &lt;/span&gt;ただし Exception が発生した場合は、ack を返さないようにします。
  &lt;span&gt;4. &lt;/span&gt;最後にもう一度メッセージをコンソールに表示する。

のようになります。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
想定されるケースとしては、
HomeNode に接続している状態で Failover が発生する場合と
HomeNode 以外に接続している状態で Failover が発生する場合があると思います。
とりあえず、HomeNode に接続しているケースから見ていきたいと思います。
&lt;/pre&gt;

&lt;pre&gt;
検証の前に Queue のメッセージをクリアしてから
HomeNode が vm-1st になっているのと QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.3.297.0&gt;]       [&amp;lt;rabbit@vm-2nd.3.297.0&gt;]
&lt;/pre&gt;
&lt;pre&gt;
問題がなければ、検証を始めます。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer を HomeNode の vm-1st に接続させてメッセージを送信します。
適当なところで vm-1st の RabbitMQ サーバーを停止させます。
その時、どうように Failover するか観察したいと思います。
&lt;/pre&gt;
&lt;pre&gt;
コマンドプロンプトを開いて gradle から Consumer を起動します。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle clean run -PmainClass=studying.rabbitmq.MyHappyConsumer -q

listening... vm-1st
&lt;/pre&gt;
&lt;pre&gt;
と表示され vm-1st に接続できたらメッセージを送信します。
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;vm-2nd に接続されたら&lt;/span&gt;
&lt;pre&gt;
どの ClusterNode に接続されるかは、ランダムのようです。
(よく分かりませんが...
今回の場合、vm-1st と vm-2nd の２台なので
確率論的にみて３回くらいやり直せば希望する方に接続される筈なんですが...
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
新たにコマンドプロンプトを開いて gradle から 以下のようにメッセージを送信します。
&lt;/pre&gt;
&lt;b&gt;Publisher&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle happyPublisher -q

start
done
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
しばらくすると Consumer のコマンドプロンプトからメッセージが処理されていく様子が確認できると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-1st
 Received &#39;Maurice&#39; - vm-1st deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-1st deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-1st deliveryTag:3 redeliver:false
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ここで vm-1st の RabbitMQ サーバーを停止させます。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
そうすると Consumer のコマンドプロンプトから Failover する様子が確認できると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-1st
 Received &#39;Maurice&#39; - vm-1st deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-1st deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-1st deliveryTag:3 redeliver:false
  Done &#39;Philip&#39; ack:3
 Received &#39;Jessica&#39; - vm-1st deliveryTag:4 redeliver:false
Channel    Shutdown...
  Error &#39;Jessica&#39;
com.rabbitmq.client.impl.DefaultExceptionHandler: ...
com.rabbitmq.client.AlreadyClosedException: connection is already closed due to connection error; ...
        at com.rabbitmq.client.impl.AMQChannel.ensureIsOpen(AMQChannel.java:195)
        at com.rabbitmq.client.impl.AMQChannel.transmit(AMQChan ...
        ...
Channel    Recovered !!! vm-2nd
Connection Recovered !!! vm-2nd
Connection Shutdown...
 Received &#39;Jessica&#39; - vm-2nd deliveryTag:5 redeliver:true
  Done &#39;Jessica&#39; ack:4
 Received &#39;Larry  &#39; - vm-2nd deliveryTag:6 redeliver:true
  Done &#39;Larry&#39; ack:5
 Received &#39;Johnny &#39; - vm-2nd deliveryTag:7 redeliver:true
  Done &#39;Johnny&#39; ack:6
 Received &#39;Ralph  &#39; - vm-2nd deliveryTag:8 redeliver:true
  Done &#39;Ralph&#39; ack:7
 Received &#39;Al     &#39; - vm-2nd deliveryTag:9 redeliver:true
  Done &#39;Al&#39; ack:8
 Received &#39;Andrew &#39; - vm-2nd deliveryTag:10 redeliver:true
  Done &#39;Andrew&#39; ack:9
 Received &#39;XXX    &#39; - vm-2nd deliveryTag:11 redeliver:true
  Done &#39;XXX&#39; ack:10
&lt;/pre&gt;
&lt;pre&gt;
検証してみると

  &lt;span&gt;1. &lt;/span&gt;&quot;Jessica&quot; のメッセージ処理をしている時に vm-1st の停止が発生していて
  &lt;span&gt;   &lt;/span&gt;HomeNode を vm-2nd に Failover できているようです。
  &lt;span&gt;2. &lt;/span&gt;最後のメッセージ &quot;XXX&quot; まで処理できているようです。
  &lt;span&gt;3. &lt;/span&gt;Queue にあったメッセージの数と ack の数が一致しているので
  &lt;span&gt;   &lt;/span&gt;２重にメッセージが処理されていることもないようです。

概ね想定通りうまくいっているのではないかと思います。
&lt;/pre&gt;
&lt;pre&gt;
Failover のタイミングを拾えているのでここに Rollback 処理などを挟み込めると思います。
また、Failover 時に Queue に残っていたメッセージは、redeliver フラグが &quot;true&quot; になっているので
Re-Queue 扱いになっているのも分かると思います。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;pre&gt;
次に HomeNode でない Node に接続しているケースを見てみたいと思います。
恐らく今、HomeNode は、vm-2nd になっていると思います。
vm-1st の RabbitMQ サーバーを再起動させて確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode を vm-1st に戻したいので vm-2nd の RabbitMQ サーバーを再起動させます。
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
先ほどと同様に Queue のメッセージをクリアしてから
HomeNode が vm-1st に戻ってきているのと QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.3.295.0&gt;]       [&amp;lt;rabbit@vm-2nd.3.295.0&gt;]
&lt;/pre&gt;
&lt;pre&gt;
問題がなければ、検証を始めます。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer を vm-2nd に接続させてメッセージを送信します。
適当なところで vm-1st の RabbitMQ サーバーを停止させて Failover する様子を観察したいと思います。
&lt;/pre&gt;
&lt;pre&gt;
コマンドプロンプトを開いて gradle から Consumer を起動します。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle clean run -PmainClass=studying.rabbitmq.MyHappyConsumer -q

listening... vm-2nd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-2nd に接続されたらメッセージを送信します。
新たにコマンドプロンプトを開いて gradle から以下のようにメッセージを送信します。
&lt;/pre&gt;
&lt;b&gt;Publisher&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle happyPublisher -q

start
done
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer のコマンドプロンプトからメッセージ処理が始まるのが確認できたら
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-2nd の RabbitMQ サーバーを停止して Failover させます。
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;


&lt;pre&gt;
Consumer のコマンドプロンプトを見ると Failover する様子が確認できると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
  Done &#39;Philip&#39; ack:3
 Received &#39;Jessica&#39; - vm-2nd deliveryTag:4 redeliver:false
Channel    Shutdown...
  Error &#39;Jessica&#39;
com.rabbitmq.client.impl.DefaultExceptionHandler: ...
com.rabbitmq.client.AlreadyClosedException: connection is already closed due to connection error; ...
        at com.rabbitmq.client.impl.AMQChannel.ensureIsOpen(AMQChannel.java:195)
        at com.rabbitmq.client.impl.AMQChannel.transmit(AMQChan ...
        ...
Channel    Recovered !!! vm-1st
Connection Recovered !!! vm-1st
Connection Shutdown...
 Received &#39;Jessica&#39; - vm-1st deliveryTag:5 redeliver:true
  Done &#39;Jessica&#39; ack:4
 Received &#39;Larry  &#39; - vm-1st deliveryTag:6 redeliver:false
  Done &#39;Larry&#39; ack:5
 Received &#39;Johnny &#39; - vm-1st deliveryTag:7 redeliver:false
  Done &#39;Johnny&#39; ack:6
 Received &#39;Ralph  &#39; - vm-1st deliveryTag:8 redeliver:false
  Done &#39;Ralph&#39; ack:7
 Received &#39;Al     &#39; - vm-1st deliveryTag:9 redeliver:false
  Done &#39;Al&#39; ack:8
 Received &#39;Andrew &#39; - vm-1st deliveryTag:10 redeliver:false
  Done &#39;Andrew&#39; ack:9
 Received &#39;XXX    &#39; - vm-1st deliveryTag:11 redeliver:false
  Done &#39;XXX&#39; ack:10
&lt;/pre&gt;
&lt;pre&gt;
検証してみると

  &lt;span&gt;1. &lt;/span&gt;&quot;Jessica&quot; のメッセージ処理をしている時に vm-1st の停止が発生していて
  &lt;span&gt;   &lt;/span&gt;HomeNode を vm-2nd に Failover できているようです。
  &lt;span&gt;2. &lt;/span&gt;最後のメッセージ &quot;XXX&quot; まで処理できているようです。
  &lt;span&gt;3. &lt;/span&gt;Queue にあったメッセージの数と ack の数が一致しているので
  &lt;span&gt;   &lt;/span&gt;２重にメッセージが処理されていることもないようです。

こちらもうまくいっていると思います。
&lt;/pre&gt;
&lt;pre&gt;
HomeNode に接続しているケースとの違いとして
Failover 時に処理していたメッセージは、redeliver フラグが &quot;true&quot; なので
Re-Queue 扱いになっているようですが、
それ以外の Queue に残っていたメッセージの redeliver フラグは &quot;false&quot; のままのようです。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Unhappy Path を検証する&lt;/h2&gt;
&lt;pre&gt;
不幸なタイミングで処理される可能性が高いパターンを検証してみたいと思います。
内容的には、先ほどの HappyPath とほぼ同じなのですが
今度は、スリープさせずに連続してメッセージ処理をするとどうなるか試してみたいと思います。
メッセージは、10,000通処理します。
以下のような内容で Consumerクラスを作成します。
&lt;/pre&gt;

&lt;u&gt;src/main/java/studying/rabbitmq/MyUnhappyConsumer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.rabbitmq;

import java.io.IOException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class MyUnhappyConsumer extends MyAbstractConsumer {

  public final static int MESSAGE_COUNT = 10000;
  
  @Override
  protected void assignConsumerFor(Channel channel, String queue) throws IOException {
    
    int count = MyUnhappyConsumer.MESSAGE_COUNT;

    Consumer consumer = new DefaultConsumer(channel) {
      
      protected int ack;
      
      @Override
      public void handleDelivery(String consumerTag, Envelope envelope, 
          AMQP.BasicProperties properties, byte[] body) throws IOException {
        
        String message = new String(body, &quot;UTF-8&quot;);
        long deliveryTag = envelope.getDeliveryTag();

        try {
          // ack を返す。
          boolean multiple = false;
          channel.basicAck(deliveryTag, multiple);
          this.ack++;
          
          if ((deliveryTag == 1) || (deliveryTag &gt; (count - 2)) || 
              ((deliveryTag % (count / 5)) == 0)) {

            System.out.printf(&quot; Done &#39;%s&#39;&quot;, message);
            System.out.printf(&quot; - %s&quot;, channel.getConnection().getAddress().getHostName());
            System.out.printf(&quot; deliveryTag:%s&quot;, deliveryTag);
            System.out.printf(&quot; ack:%d %n&quot;, this.ack);
          }
        } catch (Exception e) {
          System.out.printf(&quot; Error &#39;%s&#39; - deliveryTag:%s %n&quot;, message, deliveryTag);
          throw e;
        }
      }
    };

    boolean autoAck = false;
    channel.basicConsume(queue, autoAck, consumer);
  }

  
  public static void main(String[] args) {
    try {      
      new MyUnhappyConsumer().doAction();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
DefaultConsumerクラスの handleDelivery() メソッドをオーバーライドしてメッセージ処理をしています。
&lt;/pre&gt;
&lt;pre&gt;
処理内容としては、

  &lt;span&gt;1. &lt;/span&gt;受信したメッセージをコンソールに表示する。
  &lt;span&gt;   &lt;/span&gt;今回は、メッセージを10,000通処理するので最初の１通目と最後の３通、そして途中５回だけにします。
  &lt;span&gt;2. &lt;/span&gt;RabbitMQ サーバーに ack を返す。
  &lt;span&gt;   &lt;/span&gt;RabbitMQ サーバーに返した ack をカウントする。
  &lt;span&gt;   &lt;/span&gt;ただし Exception が発生した場合は、ack を返さないようにします。

のようになります。
&lt;/pre&gt;
&lt;pre&gt;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
こちらも HomeNode に接続している状態で Failover が発生する場合と
HomeNode 以外に接続している状態で Failover が発生する場合が考えられるのですが
HomeNode に接続している状態で Failover が発生する場合で検証したいと思います。
&lt;/pre&gt;

&lt;pre&gt;
検証の前に Queue のメッセージをクリアしてから
HomeNode が vm-1st になっているのと QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.1.295.0&gt;]       [&amp;lt;rabbit@vm-2nd.1.295.0&gt;]
&lt;/pre&gt;
&lt;pre&gt;
問題がなければ、検証を始めます。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
今回は、先にメッセージを送信してしまいます。
コマンドプロンプトを開いて gradle から 以下のようにメッセージを送信します。
&lt;/pre&gt;
&lt;b&gt;Publisher&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle unhappyPublisher -q

start
done
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
送信したメッセージが Queue に届いているか確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 10000    |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Queue にメッセージが届いているのが確認できたら
コマンドプロンプトを開いて gradle から以下のように Consumer を vm-1st に接続させて起動します。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle clean run -PmainClass=studying.rabbitmq.MyUnhappyConsumer -q

listening... vm-1st
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;vm-2nd に接続されたら&lt;/span&gt;
&lt;pre&gt;
メッセージが処理されるまで10秒空けていますので
その間に「CTRL」+「C」キーで処理を止めてもう一度、Consumer を起動し直します。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
しばらくして Consumer のコマンドプロンプトからメッセージ処理が始まるのが確認できたら
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-1st
 Done &#39;test-1&#39; - vm-1st deliveryTag:1 ack:1
 Done &#39;test-2000&#39; - vm-1st deliveryTag:2000 ack:2000
 Done &#39;test-4000&#39; - vm-1st deliveryTag:4000 ack:4000
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-1st の RabbitMQ サーバーを停止して Failover させます。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer のコマンドプロンプトを見ると
以下のように Failover する様子が確認できると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-1st
 Done &#39;test-1&#39; - vm-1st deliveryTag:1 ack:1
 Done &#39;test-2000&#39; - vm-1st deliveryTag:2000 ack:2000
 Done &#39;test-4000&#39; - vm-1st deliveryTag:4000 ack:4000
Channel    Shutdown...
Channel    Recovered !!! vm-2nd
Connection Recovered !!! vm-2nd
Connection Shutdown...
 Done &#39;test-5999&#39; - vm-2nd deliveryTag:6000 ack:6000
 Done &#39;test-7999&#39; - vm-2nd deliveryTag:8000 ack:8000
 Done &#39;test-9998&#39; - vm-2nd deliveryTag:9999 ack:9999
 Done &#39;test-9999&#39; - vm-2nd deliveryTag:10000 ack:10000
 Done &#39;test-10000&#39; - vm-2nd deliveryTag:10001 ack:10001
&lt;/pre&gt;
&lt;pre&gt;
検証してみると

  &lt;span&gt;1. &lt;/span&gt;4,000通目から6,000通目のメッセージ処理をしている時に vm-1st の停止が発生していて
  &lt;span&gt;   &lt;/span&gt;HomeNode を vm-2nd に Failover できているようです。
  &lt;span&gt;2. &lt;/span&gt;最後のメッセージ &quot;test-10000&quot; まで処理できているようです。
  &lt;span&gt;3. &lt;/span&gt;Queue にあったメッセージの数より ack の数が１つ多くなっています。
  &lt;span&gt;   &lt;/span&gt;２重にメッセージが処理されてしまっているようです。
&lt;/pre&gt;

&lt;pre&gt;
メッセージの２重処理が発生しているのに加えて Failover のタイミングも拾えていないようです。
Exception が catch できていないということは、channel.basicAck() メソッドで
ack を返すところまでは成功しているけれども ack が 届く前に RabbitMQ サーバーが
停止したのだろうと考えられます。
&lt;/pre&gt;


&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;HomeNode 以外に接続している場合に Failover したら&lt;/span&gt;
&lt;pre&gt;
こちらの場合も同様にメッセージの２重処理が発生するようです。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
listening... vm-2nd
 Done &#39;test-1&#39; - vm-2nd deliveryTag:1 ack:1
 Done &#39;test-2000&#39; - vm-2nd deliveryTag:2000 ack:2000
 Done &#39;test-4000&#39; - vm-2nd deliveryTag:4000 ack:4000
Channel    Shutdown...
Channel    Recovered !!! vm-1st
Connection Recovered !!! vm-1st
Connection Shutdown...
 Done &#39;test-5999&#39; - vm-1st deliveryTag:6000 ack:6000
 Done &#39;test-7999&#39; - vm-1st deliveryTag:8000 ack:8000
 Done &#39;test-9998&#39; - vm-1st deliveryTag:9999 ack:9999
 Done &#39;test-9999&#39; - vm-1st deliveryTag:10000 ack:10000
 Done &#39;test-10000&#39; - vm-1st deliveryTag:10001 ack:10001
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;Consumer Cancel Notification&lt;/h2&gt;
&lt;pre&gt;
Consumer に割り当てられている Queue が削除されたり、その他の理由で Queue にアクセスできなくなった時に
RabbitMQ サーバー から Consumer Cancel Notification というものが送られる仕組みがあるようです。
このキャンセル通知を Consumer が受け取ると以降のメッセージ処理を中止するようです。
Failover 時も、このキャンセル通知が送信されるようなので、ちょっと試してみようと思います。
&lt;/pre&gt;
&lt;pre&gt;
わたしが確認できたところでは、
Failover 時、以下の条件をすべて満たす場合に、このキャンセル通知を受け取れるようです。

  &lt;span&gt;・&lt;/span&gt; QueueMirroring している。
  &lt;span&gt;・&lt;/span&gt; HomeNode 以外に接続している。
  &lt;span&gt;・&lt;/span&gt; HomeNode が原因で Failover が発生した。
  &lt;span&gt;・&lt;/span&gt; Consumer 引数の x-cancel-on-ha-failover が &quot;true&quot; になっている。
&lt;/pre&gt;
&lt;pre&gt;
上記のケースで x-cancel-on-ha-failover を設定していないとどうなるか
先ほどの HappyPath の検証を使って見てみます。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
検証の前に Queue のメッセージをクリアして
HomeNode が vm-1st になっているのと QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.3.297.0&gt;]       [&amp;lt;rabbit@vm-2nd.3.297.0&gt;]
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer を HomeNode でない vm-2nd に接続します。
コマンドプロンプトを開いて gradle から Consumer を起動します。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle clean run -PmainClass=studying.rabbitmq.MyHappyConsumer -q

listening... vm-2nd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-2nd に接続できたのを確認したらメッセージを送信します。
新たにコマンドプロンプトを開いて gradle から以下のようにメッセージを送信します。
&lt;/pre&gt;
&lt;b&gt;Publisher&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle happyPublisher -q

start
done
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer のコマンドプロンプトからメッセージ処理が始まるのが確認できたら
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode の vm-1st の RabbitMQ サーバーを停止して Failover させます。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer のコマンドプロンプトでは以下のようにメッセージ処理が続いていると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
  Done &#39;Philip&#39; ack:3
 Received &#39;Jessica&#39; - vm-2nd deliveryTag:4 redeliver:false
  Done &#39;Jessica&#39; ack:4
 Received &#39;Jessica&#39; - vm-2nd deliveryTag:5 redeliver:true
  Done &#39;Jessica&#39; ack:5
 Received &#39;Larry  &#39; - vm-2nd deliveryTag:6 redeliver:true
  Done &#39;Larry&#39; ack:6
 Received &#39;Johnny &#39; - vm-2nd deliveryTag:7 redeliver:true
  Done &#39;Johnny&#39; ack:7
 Received &#39;Ralph  &#39; - vm-2nd deliveryTag:8 redeliver:true
  Done &#39;Ralph&#39; ack:8
 Received &#39;Al     &#39; - vm-2nd deliveryTag:9 redeliver:true
  Done &#39;Al&#39; ack:9
 Received &#39;Andrew &#39; - vm-2nd deliveryTag:10 redeliver:true
  Done &#39;Andrew&#39; ack:10
 Received &#39;XXX    &#39; - vm-2nd deliveryTag:11 redeliver:true
  Done &#39;XXX&#39; ack:11
&lt;/pre&gt;
&lt;pre&gt;
見てみると Queue のメッセージの数より ack の数が１つ多くなっています。
メッセージの２重処理が発生してしまっているようです。
&lt;/pre&gt;
&lt;pre&gt;
もう少し見てみると &quot;Jessica&quot; のメッセージ処理が２回実行されているようです。
１回目の redeliver は、&quot;false&quot; ですが２回目の &quot;true&quot; になっています。
&quot;Jessica&quot; の２回目のメッセージ処理以降、メッセージの redeliver が &quot;true&quot; になっています。
恐らく vm-2nd で &quot;Jessica&quot; のメッセージを処理している間に vm-1st が Failover したのだと思います。
vm-1st は、&quot;Jessica&quot; のメッセージ処理の ack を受け取っていないので Re-Queue したのだと思います。
これがメッセージの２重処理の原因ではないでしょうか？
HappyPath と思っていましたが、実は、うまくいかないパターンもあるようです。
&lt;/pre&gt;
&lt;pre&gt;
このような場合に Consumer Cancel Notification を利用して Consumer のメッセージ処理を止めることが
できるようです。
先ほどの HappyPath の検証で使用した MyHappyConsumerクラスを変更して
以下のような MyCancelConsumerクラスを作成します。
&lt;/pre&gt;
&lt;u&gt;src/main/java/studying/rabbitmq/MyCancelConsumer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.rabbitmq;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class MyCancelConsumer extends MyAbstractConsumer {
  
  @Override
  protected void assignConsumerFor(Channel channel, String queue) throws IOException {

    Consumer consumer = new DefaultConsumer(channel) {
 
      protected int ack;
       
      @Override
      public void handleDelivery(String consumerTag, Envelope envelope, 
          AMQP.BasicProperties properties, byte[] body) throws IOException {
         
        String message = new String(body, &quot;UTF-8&quot;);
        String hostName = channel.getConnection().getAddress().getHostName();
         
        System.out.printf(&quot; Received &#39;%-7s&#39;&quot;, message);
        System.out.printf(&quot; - %s&quot;, hostName);
        System.out.printf(&quot; deliveryTag:%s&quot;,  envelope.getDeliveryTag());
        System.out.printf(&quot; redeliver:%s %n&quot;, envelope.isRedeliver());
 
        try {
          // 3秒 sleep する。
          Thread.sleep(3 * 1000);
           
          // ack を返す。
          channel.basicAck(envelope.getDeliveryTag(), false);
          this.ack++;
          System.out.printf(&quot;  Done &#39;%s&#39; ack:%d %n&quot;, message, this.ack);
 
        } catch (InterruptedException e) {
          System.out.printf(&quot;  Interrupted &#39;%s&#39; %n&quot;, message);
          e.printStackTrace();
        } catch (Exception e) {
          System.out.printf(&quot;  Error &#39;%s&#39; %n&quot;, message);
          throw e;
        }
      }
        
      @Override
      public void handleCancel(String consumerTag) throws IOException {
        System.out.printf(&quot; Canceled !!!!! %n&quot;);
      }
    };
 
    // x-cancel-on-ha-failover を true にして Consumer を Channel に割り当てる。
    Map&amp;lt;String, Object&gt; args = new HashMap&amp;lt;String, Object&gt;();
    args.put(&quot;x-cancel-on-ha-failover&quot;, true);
    boolean autoAck = false;
    channel.basicConsume(queue, autoAck, args, consumer);
  }


  public static void main(String[] args) {
    try {      
      new MyCancelConsumer().doAction();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
MyHappyConsumerクラスからの変更点としては、

  &lt;span&gt;1. &lt;/span&gt;DefaultConsumerクラスの handleDelivery() メソッドをオーバーライドしてメッセージを処理しています。
  &lt;span&gt;2. &lt;/span&gt;Channel に Consumer を割り当てる時に x-cancel-on-ha-failover引数を &quot;true&quot; にしています。

になります。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
再度、検証したいと思います。
検証の前に Queue のメッセージをクリアして
HomeNode が vm-1st になっているのと QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.1.297.0&gt;]       [&amp;lt;rabbit@vm-2nd.1.297.0&gt;]
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer を HomeNode でない vm-2nd に接続します。
コマンドプロンプトを開いて gradle から Consumer を起動します。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle clean run -PmainClass=studying.rabbitmq.MyCancelConsumer -q

listening... vm-2nd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-2nd に接続できたのを確認したらメッセージを送信します。
新たにコマンドプロンプトを開いて gradle から以下のようにメッセージを送信します。
&lt;/pre&gt;
&lt;b&gt;Publisher&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-failover
gradle happyPublisher -q

start
done
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Consumer のコマンドプロンプトからメッセージ処理が始まるのが確認できたら
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-1st の RabbitMQ サーバーを停止させて Consumer Cancel Notification が受け取れるか確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
以下のように Consumer のコマンドプロンプトから Consumer Cancel Notification を受け取って
Consumer が停止しているのが確認できると思います。
&lt;/pre&gt;
&lt;b&gt;Consumer&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening... vm-2nd
 Received &#39;Maurice&#39; - vm-2nd deliveryTag:1 redeliver:false
  Done &#39;Maurice&#39; ack:1
 Received &#39;Verdine&#39; - vm-2nd deliveryTag:2 redeliver:false
  Done &#39;Verdine&#39; ack:2
 Received &#39;Philip &#39; - vm-2nd deliveryTag:3 redeliver:false
  Done &#39;Philip&#39; ack:3
 Received &#39;Jessica&#39; - vm-2nd deliveryTag:4 redeliver:false
  Done &#39;Jessica&#39; ack:4
 Canceled !!!!!
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-1st の RabbitMQ サーバーを再起動させて Queue のメッセージを見ています。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 7        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
見てみると Consumer は、ack を ４つカウントしているのですが
Queue のメッセージは、７通あります。
恐らく Failover が発生したため Consumer が最後に処理していたメッセージの ack が受け取れずに
Re-Queue されたのだと思います。
Consumer が止まらずにメッセージ処理を続けてたらメッセージの２重処理が発生していると思います。
それをどう回避するか、どう対応するかは、アプリの実装に委ねられるのだと思います。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
Failover は、ライブラリーやミドルウェアの設定でなんとかなりそうですが、
メッセージの２重処理については、アプリ側での対応になるようです。
このあたりは、アプリの要件によって変わってくるので仕方ないと思います。
(２重にメッセージが処理されてもサービス的に影響がない場合もあると思います。
いづれにせよ Consumer は、メッセージが２重に処理されることも
考慮した作りにしておく必要がありそうです。
また、２重処理されたメッセージを捕捉できるようなログも出力しておきたいところです。
&lt;/pre&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/clustering.html&quot;&gt;
    http://www.rabbitmq.com/clustering.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/ha.html&quot;&gt;
    http://www.rabbitmq.com/ha.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/consumer-cancel.html&quot;&gt;
    http://www.rabbitmq.com/consumer-cancel.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/api-guide.html&quot;&gt;
    http://www.rabbitmq.com/api-guide.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/3223755634666377906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/3223755634666377906'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2016/05/rabbitmq-consumer.html' title='RabbitMQ の 冗長化を検証する / Consumer 編'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-956217869715874889</id><published>2016-05-12T00:23:00.000+09:00</published><updated>2016-05-16T00:20:26.944+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="RabbitMQ"/><title type='text'>RabbitMQ の 冗長化を考える</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
RabbitMQ の冗長化の機能として
Clustering(クラスタリング)と QueueMirroring(Queueミラーリング)が提供されているようです。
今回は、これらを使って冗長化を考えてみたいと思います。
&lt;/pre&gt;
&lt;pre&gt;
Cluster の Node では、Queue、Exchange、Binding、User などが共有できるようです。
しかし Queue だけは、他と扱いが異なるということです。
Queue は、Cluster 内のどこか１つの Node が HomeNode となり保持される仕組みになっているようです。
HomeNode 以外の Node は、Queue を参照する形になるようです。
そのため HomeNode が落ちてしまうと他の Node は、Queue の参照できるけれども
Queue にあるメッセージが参照できなくなるという事態に陥ってしまうようです。
さらにメッセージが Persistence でなければメッセージ自体もなくなってしまう。
これをカバーするものが QueueMirroring となるようです。
HomeNode のメッセージを他の Node でも共有しようという仕組みのようです。
&lt;/pre&gt; 

&lt;pre&gt;
始める前に...
RabbitMQ がインストール済であること
RabbitMQ サーバーにログイン可能でリソースにアクセスできるユーザが作成済であること
(ユーザとパスワードを &quot;user01&quot; と &quot;passwd&quot; としています。
ManagementPlugin が有効になっていて CommandLineTool がインストールていること
以上が前提になります。
このあたりはわたしも『&lt;a href=&quot;/2016/04/yum-local-repository-rabbitmq.html&quot;&gt;yum の Local Repository に RabbitMQ をインストール&lt;/a&gt;』にまとめていますので
よろしければ、こちらも合わせて、ご参考に。
&lt;/pre&gt;
&lt;pre&gt;
ではでは！Getting Started!
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;環境&lt;/h2&gt;
&lt;pre&gt;
今回は、以下の２台のサーバー(vm-1st と vm-2nd)で冗長化構成を考えたいと思います。
&lt;/pre&gt;
&lt;b&gt;RabbitMQ Server (vm-1st)&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;ホスト名&lt;/th&gt;
    &lt;td&gt;vm-1st&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;RabbitMQ Server (vm-2nd)&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;ホスト名&lt;/th&gt;
    &lt;td&gt;vm-2nd&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.120&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;準備&lt;/h2&gt;
&lt;pre&gt;
Clustering と QueueMirroring の設定をするにあたりホスト名とその名前解決が必要になるようです。
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;( ! ) 注意  RabbitMQ のリソース&lt;/span&gt;
&lt;pre&gt;
ホスト名を変更すると User や Queue などが消えてしまう？
&lt;/pre&gt;
&lt;pre&gt;
Queue、Exchange、Binding、User などは、ホスト名に関連付けされて
/var/lib/rabbitmq/mnesia ディレクトリ以下に保存されるようです。
(Mnesia という分散データベースが使われているようです。
実際、消えてはないのですが、変更前のホスト名で保存されているので
アクセスできなくなってしまうようです。
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
それぞれ以下のように設定します。
ホスト名を設定した後は、一度、再起動しておきます。
&lt;/pre&gt;

&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;u&gt;/etc/hostname&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
vm-1st
&lt;/pre&gt;

&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.10.110 vm-1st
192.168.10.120 vm-2nd
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;u&gt;/etc/hostname&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
vm-2nd
&lt;/pre&gt;

&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.10.110 vm-1st
192.168.10.120 vm-2nd
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ホスト名を変更した場合&lt;/span&gt;
&lt;pre&gt;
guest 以外のユーザが存在するか確認します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list users name tags

+--------+---------------+
|  name  |     tags      |
+--------+---------------+
| guest  | administrator |
+--------+---------------+
&lt;/pre&gt;
&lt;pre&gt;
guest しか存在しない場合、恐らく User が消えてしまっていると思います。
ユーザとパスワードを &quot;user01&quot; と &quot;passwd&quot; にして再作成します。
リソースへのアクセス権限と Web の管理コンソールを使用するための Role も設定します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl add_user user01 passwd
rabbitmqctl set_permissions -p / user01 &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
rabbitmqctl set_user_tags user01 administrator
&lt;/pre&gt;
&lt;pre&gt;
もう一度ユーザを確認します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list users name tags

+--------+---------------+
|  name  |     tags      |
+--------+---------------+
| guest  | administrator |
| user01 | administrator |
+--------+---------------+
&lt;/pre&gt;
&lt;pre&gt;
user01 が追加されていれば OK です。
&lt;/pre&gt;
&lt;pre&gt;
ユーザ作成は、Main の ClusterNode だけで OK です。
Sub の ClusterNode は、Main の ClusterNode の情報がレプリケートされます。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;


&lt;pre&gt;
Clustering の設定方法には、いくつかあるのですが、
今回は、ManagementPlugin の CommandLineTool を使おうと思います。
そのためには、Cluster に参加する Node は、同じ ErlangCookie を持っていないといけないようです。
(コマンド実行時の認証に必要なようです。
ErlangCookie に特別な制限は、なくアルファベットの文字列であればなんでもよいようです。
&lt;/pre&gt;

&lt;pre&gt;
とりあえず、vm-1st と vm-2nd に適当な同じ ErlangCookie 文字列を設定します。
手順としては、

  1. RabbitMQ サーバーを停止してから ErlangCookie を変更する。
  2. ErlangCookie を変更した後、RabbitMQ サーバーを再起動する。

とします。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のターミナルからそれぞれ以下のコマンドを実行します。
&lt;/pre&gt;

&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
echo MYERLANGCOOKIESTR &gt; /var/lib/rabbitmq/.erlang.cookie
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
echo MYERLANGCOOKIESTR &gt; /var/lib/rabbitmq/.erlang.cookie
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ErlangCookie&lt;/span&gt;
&lt;pre&gt;
vm-1st の ErlangCookie を vm-2nd にコピーするのもよいと思います。
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /var/lib/rabbitmq/.erlang.cookie /var/lib/rabbitmq/.erlang.cookie_bk
scp root@vm-1st:/var/lib/rabbitmq/.erlang.cookie /var/lib/rabbitmq/.erlang.cookie
chown rabbitmq /var/lib/rabbitmq/.erlang.cookie
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
準備は、これで終わりです。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;Clustering の設定&lt;/h2&gt;
&lt;pre&gt;
今、vm-1st と vm-2nd の Clustering がどうなっているか確認してみましょう。
&lt;/pre&gt;
&lt;pre&gt;
まず、vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;

&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

Cluster status of node &#39;rabbit@vm-1st&#39; ...
[{nodes,[{disc,[&#39;rabbit@vm-1st&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-1st&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-1st&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;pre&gt;
見てみると &quot;rabbit@vm-1st&quot; という Cluster があり
このサーバー(vm-1st) だけが参加していて起動している。
こんな感じでしょうか？
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次に、vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;

&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

Cluster status of node &#39;rabbit@vm-2nd&#39; ...
[{nodes,[{disc,[&#39;rabbit@vm-2nd&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-2nd&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-2nd&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;pre&gt;
こちらも &quot;rabbit@vm-2nd&quot; という Cluster があり
このサーバー(vm-2nd) だけが参加していて起動している。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
整理してみると
vm-1st と vm-2nd のどちらも
それぞれが Cluster を持っていて自サーバーのみ参加していて起動している。
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Disk Node と RAM Node&lt;/span&gt;
&lt;pre&gt;
Node の保存先に Disk と RAM があるようだ。
保存先に RAM を選ぶとパフォーマンスを稼げるということらしいが
一般的には、Disk を選択するのが無難なようだ。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
ではでは、vm-2nd を vm-1st の Cluster に参加させてみましょう。
手順としては、

  1. vm-2nd を offline にする。
  2. vm-2nd を vm-1st の Cluster に参加させる。
  3. vm-2nd を online にする。

のようになります。
&lt;/pre&gt;
&lt;pre&gt;
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@vm-1st
rabbitmqctl start_app
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ちゃんと Cluster に参加できたか確認します。
vm-1st と vm-2nd の両方のターミナルで以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

Cluster status of node &#39;rabbit@vm-1st&#39; ...
[{nodes,[{disc,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-2nd&#39;,&#39;rabbit@vm-1st&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-1st&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl cluster_status

[{nodes,[{disc,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]}]},
 {running_nodes,[&#39;rabbit@vm-1st&#39;,&#39;rabbit@vm-2nd&#39;]},
 {cluster_name,&amp;lt;&amp;lt;&quot;rabbit@vm-1st&quot;&gt;&gt;},
...
&lt;/pre&gt;
&lt;pre&gt;
どうなったでしょう？
ちょっと見てみると
vm-1st と vm-2nd のどちらにも
&quot;rabbit@vm-1st&quot; という Cluster があり
そこにどちらも参加していてどちらも起動している。
いけてそうな感じですね！
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Clustering の解除&lt;/span&gt;
&lt;pre&gt;
vm-2nd を vm-1st の Cluster から解除する手順としては、

  1. vm-2nd を offline にする。
  2. vm-2nd を vm-1st の Cluster から解除する。
  3. vm-2nd を online にする。

のようになります。
&lt;/pre&gt;
&lt;pre&gt;
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;RabbitMQ と Erlang の version&lt;/span&gt;
&lt;pre&gt;
RabbitMQ の version が違う Node は、Cluster に参加できないようです。
同様に Erlang の version が違う Node も Cluster に参加できないようです。
Clustering された RabbitMQ の version up は、なかなか大変？！
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;Queue を宣言する&lt;/h2&gt;
&lt;pre&gt;
とりあえず、Queue がないと始まらないので宣言をします。
Queue名を &quot;myQueue&quot; とします。
vm-1st と vm-2nd のどちらでも構わないのですが
ここでは、vm-2nd を HomeNode にして Queue を宣言したいと思います。
&lt;/pre&gt;
&lt;pre&gt;
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;

&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin declare queue name=myQueue
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
宣言できたか確認します。
デフォルト(無名)の Exchange に Bind されているかも確認します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
Queue名が &quot;myQueue&quot;、durable が &quot;True&quot;、HomeNode が &quot;vm-2nd&quot; で宣言されているようです。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list bindings

+------------+-------------+-------------+
|   source   | destination | routing_key |
+------------+-------------+-------------+
|            | myQueue     | myQueue     |
+------------+-------------+-------------+
&lt;/pre&gt;
&lt;pre&gt;
Queue名を RoutingKey にしてデフォルトの Exchange に Bind されているようです。
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Queue の HomeNode&lt;/span&gt;
&lt;pre&gt;
デフォルトでは、Queue を宣言した Node が HomeNode になるようです。
&lt;/pre&gt;
&lt;pre&gt;
以下で HomeNode の決定条件を変更できるようです。
  &lt;span&gt;・&lt;/span&gt;Queue 作成時の x-queue-master-locator 引数
  &lt;span&gt;・&lt;/span&gt;queue-master-locator policy
  &lt;span&gt;・&lt;/span&gt;configuration ファイル
&lt;/pre&gt;
&lt;pre&gt;
HomeNode の決定条件として以下の値が設定可能なようです。
  &lt;span&gt;・&lt;/span&gt;min-masters
  &lt;span&gt;・&lt;/span&gt;client-local (デフォルト)
  &lt;span&gt;・&lt;/span&gt;random
&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Queue の削除&lt;/span&gt;
&lt;pre&gt;
Queue を指定して以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin delete queue name=myQueue
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode でない vm-1st からはどう見えているか確認したいと思います。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
vm-2nd と同じように見えているのが分かると思います。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode でない vm-1st からメッセージを送信するとどうなるか試してみたいと思います。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin publish routing_key=myQueue payload=&quot;Hello?&quot;
&lt;/pre&gt;
&lt;pre&gt;
送信されたメッセージが Queue に届いたか確認します。
vm-1st と vm-2nd の両方のターミナルで以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
ちゃんと届いているようです。
&lt;/pre&gt;
&lt;pre&gt;
Queue には、HomeNode があり Master / Slave のような構成になっているが
どこの Node からメッセージを送信しても Cluster 内のすべての Queue にレプリケートされるようです。
ある意味、MultiMaster的な動きになっていると思います。
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;メッセージの取り出しと削除&lt;/span&gt;
&lt;pre&gt;
メッセージを取り出す場合は、Queue を指定して以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin get queue=myQueue requeue=false
&lt;/pre&gt;
&lt;pre&gt;
requeue を省略、または &quot;true&quot; にすると取り出したメッセージがもう一度、Queue に戻されます。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
メッセージを削除する場合は、Queue を指定して以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin purge queue name=myQueue
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
QueueMirroring していない状態で HomeNode になっている vm-2nd が落ちるとどうなるか試してみます。
vm-2nd の RabbitMQ サーバーを停止させた後、vm-1st から Queue がどう見えているか確認します。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のターミナルでそれぞれ以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd |          |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
Queue は見えているもののメッセージが見えなくなりました。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
vm-2nd が復帰した時、メッセージがどうなっているか確認したいと思います。
vm-2nd の RabbitMQ サーバーを起動させた後、vm-1st と vm-2nd から Queue が
どう見えているか確認します。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のターミナルでそれぞれ以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
メッセージが消えてしまっているのが分かると思います。
&lt;/pre&gt;
&lt;pre&gt;
HomeNode が落ちてもメッセージが消えないように QueueMirroring の設定をすることにします。
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;DeliveryMode&lt;/span&gt;
&lt;pre&gt;
メッセージ送信時に DeliveryMode を persistent にすると
送信したメッセージがディスク領域に保存されるようになるため
HomeNode が落ちてもメッセージが消えてしまうことはないようです。
ただし HomeNode が落ちている間、メッセージが見えなくなるので取り出しはできないようです。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
DeliveryMode を persistent にしてメッセージを送信する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin publish routing_key=myQueue payload=&quot;Hello?&quot; properties=&#39;{&quot;delivery_mode&quot;:2}&#39;
&lt;/pre&gt;
&lt;pre&gt;
HomeNode を停止する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;pre&gt;
メッセージを取り出してみる。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin get queue=myQueue requeue=false

*** Not found: /api/queues/%2F/myQueue/get
&lt;/pre&gt;
&lt;pre&gt;
Not found になりメッセージを取り出せない。
&lt;/pre&gt;
&lt;pre&gt;
HomeNode を起動させて Queue を確認する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
メッセージは、残っている。
メッセージを取り出してみる。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin get queue=myQueue requeue=false

+-------------+----------+---------------+---------+---------------+------------------+-------------+
| routing_key | exchange | message_count | payload | payload_bytes | payload_encoding | redelivered |
+-------------+----------+---------------+---------+---------------+------------------+-------------+
| myQueue     |          | 0             | Hello?  | 6             | string           | False       |
+-------------+----------+---------------+---------+---------------+------------------+-------------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-2nd | 0        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
メッセージが取り出せた。
&lt;/pre&gt;

&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;QueueMirroring の設定&lt;/h2&gt;
&lt;pre&gt;
Clustering の設定と Queue の宣言に続いて QueueMirroring の設定に入りたいと思います。
policy を使って設定することになるようです。
設定する policy は、ha-mode とその値の ha-params になるようです。
&lt;/pre&gt;
&lt;pre&gt;
先ほど宣言した Queue を Cluster 内のすべての Node で Mirroring するようにします。
policy名を &quot;myPolicy&quot; とします。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のどちらでも構わないのですが
とりあえず、vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;

&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_policy myPolicy &quot;^myQueue$&quot; &#39;{&quot;ha-mode&quot;:&quot;all&quot;}&#39;
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;rabbitmqctl set_policy    コマンドの書式&lt;/span&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_policy    policy名    パターン    policy設定
&lt;/pre&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;
&lt;li&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;policy名&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;policy名を指定する。&lt;/p&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;パターン&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;policy を適用する Queue を正規表現を使って指定する。&lt;/p&gt;    
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;policy設定&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;設定する policy をJSON 形式で指定する。 &lt;/p&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ha-mode と ha-params&lt;/span&gt;
&lt;pre&gt;
ha-mode は、３つあり ha-params と合わせて以下のように設定できるようです。
&lt;/pre&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;
&lt;li&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;all&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;すべての Node で mirroring する。(ha-params はない)&lt;/p&gt;
    &lt;p&gt;例)&amp;nbsp;&amp;nbsp;&#39;{&quot;ha-mode&quot;:&quot;all&quot;}&#39;&lt;/p&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;exactly&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;指定した数の Node で mirroring する。&lt;/p&gt;
    &lt;p&gt;ha-params には、Node数を指定する。&lt;/p&gt;
    &lt;p&gt;例)&amp;nbsp;&amp;nbsp;&#39;{&quot;ha-mode&quot;:&quot;exactly&quot;,&quot;ha-params&quot;:2}&#39;&lt;/p&gt;
    
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;nodes&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;指定した Node で mirroring する。&lt;/p&gt;
    &lt;p&gt;ha-params には、Node名を指定する。&lt;/p&gt;
    &lt;p&gt;例)&amp;nbsp;&amp;nbsp;&#39;{&quot;ha-mode&quot;:&quot;nodes&quot;,&quot;ha-params&quot;:[&quot;rabbit@vm-1st&quot;, &quot;rabbit@vm-2nd&quot;]}&#39;&lt;/p&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
policy が作成できたか確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_policies

Listing policies ...
/       myPolicy        all     ^myQueue$       {&quot;ha-mode&quot;:&quot;all&quot;}       0
&lt;/pre&gt;
&lt;pre&gt;
作成できているようです。
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;policy の削除&lt;/span&gt;
&lt;pre&gt;
policy を指定して以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl clear_policy myPolicy
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
Cluster 内の Node で QueueMirroring が同期しているか確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-1st.3.24159.1&gt;]     [&amp;lt;rabbit@vm-1st.3.24159.1&gt;]
&lt;/pre&gt;
&lt;pre&gt;
&quot;myQueue&quot; の slave に vm-1st があり、同期している slave にも vm-1st があるようなので
問題ないと思います。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
では、メッセージを送信してみて HomeNode が落ちてもメッセージが消えてしまわないか確認したいと思います。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st からメッセージを送信します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin publish routing_key=myQueue payload=&quot;Hello?&quot;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode になっている vm-2nd の RabbitMQ サーバーを停止します。
vm-2nd のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
この状態で HomeNode でない vm-1st で Queue がどういう状態になっているか確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
メッセージが消えずに残っているのが確認できると思います。
もう一点、HomeNode が vm-2nd から vm-1st に変わっているのも確認できると思います。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ここで vm-2nd の RabbitMQ サーバーが起動すると
vm-1st と vm-2nd から Queue がどう見えているか確認します。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st と vm-2nd のターミナルでそれぞれ以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-2nd&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl start rabbitmq-server.service
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages 

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin list queues name durable node messages

+---------+---------+---------------+----------+
|  name   | durable |     node      | messages |
+---------+---------+---------------+----------+
| myQueue | True    | rabbit@vm-1st | 1        |
+---------+---------+---------------+----------+
&lt;/pre&gt;
&lt;pre&gt;
HomeNode が vm-1st に変更されたままになっているのが確認できると思います。
HomeNode が落ちたタイミングで別の Node が HomeNode になり
メッセージを引き継ぐ形になっているようです。
そのため HomeNode が落ちてもメッセージが消えることは、ないようです。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
HomeNode が変更されたので QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.3.293.0&gt;]       []
&lt;/pre&gt;
&lt;pre&gt;
slave として、vm-2nd が追加されているのは、確認できますが同期がうまくいっていないようです。
&lt;/pre&gt;
&lt;pre&gt;
vm-1st のターミナルから以下のコマンドを実行して同期の設定をします。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl sync_queue myQueue
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
もう一度、QueueMirroring の同期を確認します。
vm-1st のターミナルから以下のコマンドを実行します。
&lt;/pre&gt;
&lt;b&gt;vm-1st&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues name slave_pids synchronised_slave_pids

Listing queues ...
myQueue [&amp;lt;rabbit@vm-2nd.3.293.0&gt;]       [&amp;lt;rabbit@vm-2nd.3.293.0&gt;]
&lt;/pre&gt;
&lt;pre&gt;
今度は、問題なく同期できているのが確認できると思います。
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;QueueMirroring の同期&lt;/span&gt;
&lt;pre&gt;
デフォルトの QueueMirroring の同期設定は、手動になっているようです。
policy 作成時に ha-sync-mode を &quot;automatic&quot; にすると自動同期にすることができるようです。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_policy myPolicy &quot;^myQueue$&quot; &#39;{&quot;ha-mode&quot;:&quot;all&quot;, &quot;ha-sync-mode&quot;:&quot;automatic&quot;}&#39;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
なかなかいい感じに MultiMaster のような形で冗長化できたのではと思います。
後は、Cluster 内の Node に仮想の IP を設定して、アプリからアクセスする感じでしょうか？
&lt;/pre&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/clustering.html&quot;&gt;
    http://www.rabbitmq.com/clustering.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/ha.html&quot;&gt;
    http://www.rabbitmq.com/ha.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/956217869715874889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/956217869715874889'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2016/05/rabbitmq_1.html' title='RabbitMQ の 冗長化を考える'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-5656696457355183477</id><published>2016-05-04T21:05:00.000+09:00</published><updated>2016-05-16T00:34:45.274+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="RabbitMQ"/><title type='text'>RabbitMQ で JSON RPC</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
前回、『&lt;a href=&quot;/2016/05/rabbitmq.html&quot;&gt;RabbitMQ で同時処理実行数を制限する&lt;/a&gt;』で送信処理と受信処理を見てきましたが
今回は、それに加えてレスポンスを取得できるようにしたいと思います。
やり方として RabbitMQ Tutorials の『&lt;a href=&quot;http://www.rabbitmq.com/tutorials/tutorial-six-java.html&quot;&gt;6 Remote procedure call (RPC)&lt;/a&gt;』が
参考になるのではないかと思います。
&lt;/pre&gt;
&lt;pre&gt;
tutorial では、送信用の Queue とは別に 返信用の Queue を用意して
レスポンスを処理する方法になっていたかと思います。
非同期的にレスポンス処理するのであれば、このやり方になるのだろうと思います。
でも、なにか、こう、もうちょっと、そこまででなくていいのでお手軽にできる方法はないかと
JavaDoc を眺めていると JsonRpcServer と JsonRpcClient なるものを見つけました！
名前的にみてメッセージを JSON で RPC風に送受信処理と返信処理ができそうな雰囲気です！
これは！と思い試してみることにしました。
&lt;/pre&gt;
&lt;pre&gt;
始める前に...
RabbitMQ がインストール済であること
RabbitMQ サーバーにログイン可能でリソースにアクセスできるユーザが作成済であること
(ユーザとパスワードを &quot;user01&quot; と &quot;passwd&quot; としています。
以上が前提になります。
このあたりはわたしも『&lt;a href=&quot;/2016/04/yum-local-repository-rabbitmq.html&quot;&gt;yum の Local Repository に RabbitMQ をインストール&lt;/a&gt;』にまとめていますので
よろしければ、こちらも合わせて、ご参考に。
&lt;/pre&gt;
&lt;pre&gt;
ではでは！Getting Started!
&lt;/pre&gt;
&lt;br /&gt;



&lt;h2&gt;環境&lt;/h2&gt;
&lt;b&gt;開発機&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;開発言語&lt;/th&gt;
    &lt;td&gt;Java 1.8.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;rabbitmq-java-client 3.6.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Gradle 2.12&lt;br /&gt;※gradle コマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows 8.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;RabbitMQ Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;pre&gt;
今回は、RPC ということでプロジェクトを Client と Service そして Gateway の３つに分けました。
というのも Client、Service、Gateway の３つそれぞれ別の jar ファイルになっていた方が
RPC では、何が何に依存していて、どこで何が必要なのかなど
その雰囲気がより伝わるだろうと思ったからです。
もちろん、１つのプロジェクトにするこもできます。
&lt;/pre&gt;
&lt;pre&gt;
Client と Service のインターフェイス部分と両者で使用するデータクラスを Gateway に切り出しました。
Service は、Gateway のインターフェイスを実装してサービスとして公開します。
Client は、Gateway のインターフェイスからスタブを作成してサービスにアクセスします。
Service と Client は、Gateway のデータクラスを使って通信します。
おおよそ、こんな感じになると思います。
&lt;/pre&gt;
&lt;pre&gt;
C ドライブ直下に「studying-rabbitmq-rpc」フォルダを作成して各ファイルを以下のように配置します。
ファイルは、UTF-8 で保存します。
&lt;/pre&gt;
&lt;div id=&quot;studying-rabbitmq-rpc&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-rabbitmq-rpc&lt;/span&gt;
   &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;client&lt;/span&gt;
          &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
               &lt;ul&gt;
                            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;client&lt;/span&gt;
                              &lt;ul&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyRpcClient.java&lt;/span&gt;&lt;/li&gt;
                              &lt;/ul&gt;
                            &lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;gateway&lt;/span&gt;
          &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
              &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
                  &lt;ul&gt;
                    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
                      &lt;ul&gt;
                        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
                          &lt;ul&gt;
                            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;gateway&lt;/span&gt;
                              &lt;ul&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyRequest.java&lt;/span&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyResponse.java&lt;/span&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyService.java&lt;/span&gt;&lt;/li&gt;
                              &lt;/ul&gt;
                            &lt;/li&gt;
                          &lt;/ul&gt;
                        &lt;/li&gt;
                      &lt;/ul&gt;
                    &lt;/li&gt;
                  &lt;/ul&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;service&lt;/span&gt;
          &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
              &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
                  &lt;ul&gt;
                    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
                      &lt;ul&gt;
                        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
                          &lt;ul&gt;
                            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;service&lt;/span&gt;
                              &lt;ul&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyRpcServer.java&lt;/span&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyServiceImpl.java&lt;/span&gt;&lt;/li&gt;
                              &lt;/ul&gt;
                            &lt;/li&gt;
                          &lt;/ul&gt;
                        &lt;/li&gt;
                      &lt;/ul&gt;
                    &lt;/li&gt;
                  &lt;/ul&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;settings.gradle&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-rabbitmq-rpc&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;準備&lt;/h2&gt;
&lt;pre&gt;
ビルドスクリプトを作成します。
プロジェクトは、Client、Service、Gateway と３つあるのですがビルドスクリプトは１つにしています。
&lt;/pre&gt;

&lt;u&gt;build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
subprojects {
  apply plugin: &#39;java&#39;
  apply plugin: &#39;eclipse&#39;
  apply plugin: &#39;application&#39;

  sourceCompatibility = &#39;1.8&#39;
  targetCompatibility = &#39;1.8&#39;
  [compileJava, compileTestJava]*.options*.encoding = &#39;UTF-8&#39;
  
  mainClassName = (project.hasProperty(&#39;mainClass&#39;)) ? &quot;$mainClass&quot; : &#39;&#39;

  repositories {
    jcenter()
  }

  dependencies {
    compile &#39;com.rabbitmq:amqp-client:3.6.1&#39;
    compile &#39;org.slf4j:slf4j-api:1.7.18&#39;
    testCompile &#39;junit:junit:4.12&#39;
  }
}

project(&#39;:client&#39;) {
  dependencies {
    compile project(&#39;:gateway&#39;)
  }
}

project(&#39;:service&#39;) {
  dependencies {
    compile project(&#39;:gateway&#39;)
  }
}
&lt;/pre&gt;
&lt;pre&gt;
client と service の compile 時の依存関係に gateway の project/jar ファイルを追加しています。
&lt;/pre&gt;

&lt;u&gt;settings.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
rootProject.name = &#39;studying-rabbitmq-rpc&#39;
include &#39;client&#39;, &#39;gateway&#39;, &#39;service&#39;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
RabbitMQ のユーザが未作成の場合は、以下のようにして作成します。
作成したユーザがリソースにアクセスできるように権限も設定します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl add_user user01 passwd
rabbitmqctl set_permissions -p / user01 &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
&lt;/pre&gt;
&lt;pre&gt;
ユーザとパスワードは、&quot;user01&quot; と &quot;passwd&quot; にしています。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;Gateway の作成&lt;/h2&gt;
&lt;pre&gt;
まず、Client側と Service側の両方で使う Gateway から作成します。
&lt;/pre&gt;
&lt;pre&gt;
送信と返信に使うメッセージデータクラスを作成します。
&lt;/pre&gt;
&lt;u&gt;gateway/src/main/java/studying/gateway/MyRequest.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.gateway;

public class MyRequest {
  private String message;

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}
&lt;/pre&gt;

&lt;u&gt;gateway/src/main/java/studying/gateway/MyResponse.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.gateway;

public class MyResponse {
  private String result;

  public String getResult() {
    return result;
  }

  public void setResult(String result) {
    this.result = result;
  }
}
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次にインターフェイスを作成します。
メソッドは２つ定義します。
&lt;/pre&gt;
&lt;u&gt;gateway/src/main/java/studying/gateway/MyService.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.gateway;

public interface MyService {
  
  // (1)
  public String echo(String message);

  // (2)
  public Object print(Object param);
}
&lt;/pre&gt;
&lt;pre&gt;
メソッドは、以下のようになります。
&lt;/pre&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;(1) echo()メソッド&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;文字列を引数に受けて、また文字列を返します。&lt;/p&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;(2) print()メソッド&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
&lt;pre&gt;
Object型の引数で Object型の戻り値になっていますが、
実装的には、MyRequestクラスを Object型の引数で受けて、
MyResponseクラスを Object型で返すようになります。
(MyRequestクラスと MyResponseクラスを直接、引数と戻り値にできない事情が...
&lt;/pre&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;Service の作成&lt;/h2&gt;
&lt;pre&gt;
Gateway のインターフェイスの実装クラスを作成します。
&lt;/pre&gt;
&lt;u&gt;service/src/main/java/studying/service/MyServiceImpl.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.service;

import java.util.Map;
import com.rabbitmq.tools.json.JSONUtil;
import studying.gateway.MyRequest;
import studying.gateway.MyResponse;
import studying.gateway.MyService;

public class MyServiceImpl implements MyService {

  // (1)
  @Override
  public String echo(String message) {
    System.out.printf(&quot;Received &#39;%s&#39; %n&quot;, message);
    
    return &quot;Hello? &quot; + message;
  }

  // (2)
  @SuppressWarnings(&quot;unchecked&quot;)
  @Override
  public Object print(Object param) {
    System.out.printf(&quot;Received &#39;%s&#39; %n&quot;, param);

    MyRequest request = new MyRequest();
    JSONUtil.tryFill(request, (Map&amp;lt;String, Object&gt;) param);
    
    MyResponse response = new MyResponse();
    response.setResult(&quot;Hello! &quot; + request.getMessage());
    
    return response;
  }
}
&lt;/pre&gt;
&lt;pre&gt;
実装内容は、以下のようになります。
&lt;/pre&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;(1) echo()メソッド&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;受信したメッセージをコンソールに出力します。&lt;br /&gt;メッセージに文字列 &quot;Hello?&quot; を付けて返します。&lt;/p&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;(2) print()メソッド&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
&lt;pre&gt;
受信したメッセージをコンソールに出力します。
メッセージは、一旦、Object型で受けて JSONUtil を使って MyRequestクラスに出力します。
MyRequestクラスからメッセージを取り出して文字列 &quot;Hello!&quot; を付けて返信データにします。
返信データを MyResponseクラスに設定して返します。
    &lt;/pre&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;引数と戻り値&lt;/span&gt;
&lt;pre&gt;
JsonRpcServer/JsonRpcClient というだけあってメッセージデータは、JSON になっているようです。
String型や int型など primitive型は、直接 JSON に変換できるようなので
そのまま引数や戻り値にしても問題ないようです。
シンプルな JavaBean の形式をとっているクラスであえば、JSON に変換可能なようで
同じように引数や戻り値に使えるみたいです。
ただし、この場合、注意が必要で Proxy 越しに呼び出されるのが原因だと思うのですが
一旦 Object型にしないとうまく動かないようです。
&lt;/pre&gt;
&lt;pre&gt;
RPC といっても今回は、Java クラスをシリアライズしたメッセージデータを使っている訳ではないので
Service側と Client側で同じ Javaクラスを使っていないと通信できないことはないと思います。
飽くまでも今回のメッセージデータは、JSON なので Service側と Client側がメッセージデータに
それぞれの Javaクラスを使っていたとしても JSON をそれぞれのクラスに変換(出力)できれば問題ないと思います。
今回は、Gateway という形でメッセージデータに同じクラスを使っていますが
Service側と Client側がそれぞれでメッセージデータ用の Javaクラスを用意してもよいと思います。
(おおよそ、フィールド名を合わせておけばクラス名が違っていてもいけそうです。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;

&lt;pre&gt;
次にサービスを公開してメッセージの受付を開始するクラスを作成します。
&lt;/pre&gt;
&lt;u&gt;service/src/main/java/studying/service/MyRpcServer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.service;

import java.io.IOException;
import java.util.Map;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.tools.jsonrpc.JsonRpcServer;
import studying.gateway.MyService;

public class MyRpcServer {

  public static void main(String[] args) {
    System.out.printf(&quot;listening... %n&quot;);
    
    // RabbitMQ Server 接続情報
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost(&quot;192.168.10.110&quot;);
    factory.setUsername(&quot;user01&quot;);
    factory.setPassword(&quot;passwd&quot;);
    Connection connection = null;
    
    try {
      // RabbitMQ Server に接続して Channel を生成する。
      connection = factory.newConnection();
      Channel channel = connection.createChannel();
      
      
      // Queue と Exchange を宣言して Bind をする。
      String queueName    = &quot;myRpcQueue&quot;;
      String exchangeType = &quot;direct&quot;;
      String exchangeName = &quot;myRpcExchange&quot;;
      String routingKey   = &quot;myRpcKey&quot;;
   
      boolean durable = true;
      boolean exclusive = false;
      boolean autoDelete = false;
      Map&amp;lt;String, Object&gt; arguments = null;
      
      channel.exchangeDeclare(exchangeName, exchangeType, durable);
      channel.queueDeclare(queueName, durable, exclusive, autoDelete, arguments);
      channel.queueBind(queueName, exchangeName, routingKey);
      
      
      // メッセージの受付を開始する。
      JsonRpcServer server = new JsonRpcServer(channel, queueName, MyService.class, new MyServiceImpl());
      server.mainloop();
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (connection != null &amp;amp;&amp;amp; connection.isOpen()) {
        try {
          connection.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
処理内容としては、
送受信処理に使う Queue と Exchange (direct タイプ) を宣言しています。
Queue名と Exchange名は、&quot;myRpcQueue&quot; と &quot;myRpcExchange&quot; になります。
RoutingKey を &quot;myRpcKey&quot; にして &quot;myRpcQueue&quot; と &quot;myRpcExchange&quot; を Bind しています。
MyServiceインターフェイスとその実装クラス MyServiceImpl を使って
メッセージの受信処理を開始しています。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Client の作成&lt;/h2&gt;
&lt;pre&gt;
サービスの呼び出しクラスを作成します。
echo()メソッドと print()メソッドを呼び出して結果をコンソールに出力します。
&lt;/pre&gt;
&lt;pre&gt;
以下の内容で作成します。
&lt;/pre&gt;
&lt;u&gt;client/src/main/java/studying/client/MyRpcClient.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.client;

import java.io.IOException;
import java.util.Map;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.tools.json.JSONUtil;
import com.rabbitmq.tools.jsonrpc.JsonRpcClient;
import studying.gateway.MyRequest;
import studying.gateway.MyResponse;
import studying.gateway.MyService;

public class MyRpcClient {
  
  @SuppressWarnings(&quot;unchecked&quot;)
  public void doAction() {

    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost(&quot;192.168.10.110&quot;);
    factory.setUsername(&quot;user01&quot;);
    factory.setPassword(&quot;passwd&quot;);
    Connection connection = null;
    
    try {
      connection = factory.newConnection();
      Channel channel = connection.createChannel();

      String exchangeName = &quot;myRpcExchange&quot;;
      String routingKey   = &quot;myRpcKey&quot;;
      int timeout = 1000 * 5;

      // Stub を作成する。
      JsonRpcClient client = new JsonRpcClient(channel, exchangeName, routingKey, timeout);
      MyService serivce = (MyService)client.createProxy(MyService.class);

      
      // (1) echo()メソッド呼び出し
      String message = serivce.echo(&quot;dolphin&quot;);
      System.out.printf(&quot;echo: %s %n&quot;, message);
      
      
      // (2) print()メソッド呼び出し
      MyRequest request = new MyRequest();
      request.setMessage(&quot;revolution&quot;);
      Object source = serivce.print(request);
      
      MyResponse response = new MyResponse();
      JSONUtil.fill(response, (Map&amp;lt;String, Object&gt;)source);
      System.out.printf(&quot;print: %s %n&quot;, response.getResult());
      
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (connection != null &amp;amp;&amp;amp; connection.isOpen()) {
        try {
          connection.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) {
    new MyRpcClient().doAction();
  }
}
&lt;/pre&gt;
&lt;pre&gt;
Exchange と Queue の宣言と Bind は、Service側で行うので
Client側では、送信時に Exchange と RoutingKey を指定するだけにします。
送信する Exchange と RoutingKey は、&quot;myRpcExchange&quot; と &quot;myRpcKey&quot; にしています。
送信するまでに最低限 Exchange が存在していないとうまくいかないので
先に Service側を起動することになります。
&lt;/pre&gt;
&lt;pre&gt;
処理内容は、以下のようになります。
&lt;/pre&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;(1) echo()メソッド呼び出し&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;p&gt;引数と戻り値とも文字 型なのでそのまま送信して、その結果をコンソールに出力します。&lt;/p&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;(2) print()メソッド呼び出し&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
&lt;pre&gt;
メッセージを MyRequest クラスで送信します。
(インターフェイス的には、Object 型ですが実装は、MyRequest クラスになっています。
結果メッセージを一旦、Object 型で受けて JSONUtil を使って MyResponse クラスに出力します。
MyResponse クラスから結果メッセージを取り出してコンソールに出力します。
&lt;/pre&gt;
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;返信用の Queue&lt;/span&gt;
&lt;pre&gt;
RabbitMQ の &lt;a href=&quot;http://www.rabbitmq.com/tutorials/tutorial-six-java.html&quot;&gt;Tutorial&lt;/a&gt; 的には、返信メッセージの受付に Queue が宣言されているのですが
JsonRpcServer/JsonRpcClient では、Direct reply-to (amq.rabbitmq.reply-to.XXX) という
疑似的な Queue が暗黙的に返信用の Queue の代わりに使われるため
明示的に返信用の Queue を宣言する必要はないようです。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;



&lt;h2&gt;実行する&lt;/h2&gt;
&lt;pre&gt;
Queue と Exchange の設定をしている Service側を先に起動します。
コマンドプロンプトを開いて、gradle から Service側を起動します。
&lt;/pre&gt;
&lt;u&gt;Service&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-rpc
gradle :service:clean :service:run -PmainClass=studying.service.MyRpcServer -q

listening...
&lt;/pre&gt;
&lt;pre&gt;
Service側が起動したのが確認できたら Client側を実行します。
Service側のコマンドプロンプトはそのままにしておいて
新たにコマンドプロンプトを開いて以下のコマンドを実行します。
&lt;/pre&gt;
&lt;pre&gt;
以下のように表示されると思います。
&lt;/pre&gt;
&lt;u&gt;Client&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-rpc
gradle :client:clean :client:run -PmainClass=studying.client.MyRpcClient -q

echo: Hello? dolphin
print: Hello! revolution
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Service側のコマンドプロンプトには、以下のように表示されると思います。
&lt;/pre&gt;
&lt;u&gt;Service&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
...
listening...

Received &#39;dolphin&#39;
Received &#39;{message=revolution}&#39;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
また、以下のように jar ファイルを作って実行してみるのもいいと思います。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq-rpc
gradle :service:clean :service:distZip -PmainClass=studying.service.MyRpcServer -q
gradle :client:clean :client:distZip -PmainClass=studying.client.MyRpcClient -q
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
同期処理ではありますがレスポンスを取得できるようになったと思います。
(レスポンスを非同期処理で取得できるようにするのは、なかなか手間だと思います...
&lt;/pre&gt;
&lt;pre&gt;
今回は、お手軽に、というのも裏テーマにあったので JsonRpcServer と JsonRpcClient を使いました。
使ってみたところ、Client側の実装が Java に限定されてしまいそうな懸念を持ちました。
しかも JsonRpcClientクラスを使わないとうまく通信できないかもしれません。
(RPC は、こんな感じ？
JsonRpcServerクラスの doCall() メソッド、JsonRpcClientクラスの call()メソッドあたりを
読み解いてみると Java 以外の Client を使って今回のサービスにアクセスできるかもしれません。
それもどうかな？
Service側と Client側がどちらも Java なら JMS でいいような気がします。
(RabbitMQ の JMS は有償だったような気が...
&lt;/pre&gt;
&lt;pre&gt;
わたしは、Service側の実装が Java に依存するのには、なんら抵抗ないのですが
Client側が特定のプログラム言語(Java であっても)に依存してしまうのはイマイチだったりします。
ここは、なんとか考えないと。
&lt;/pre&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/getstarted.html&quot;&gt;
    http://www.rabbitmq.com/getstarted.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/api-guide.html&quot;&gt;
    http://www.rabbitmq.com/api-guide.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5656696457355183477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5656696457355183477'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2016/05/rabbitmq-json-rpc.html' title='RabbitMQ で JSON RPC'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-2517561852757664141</id><published>2016-05-01T23:21:00.001+09:00</published><updated>2016-05-16T00:33:47.385+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="RabbitMQ"/><title type='text'>RabbitMQ で同時処理実行数を制限する</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
長い時間かかる処理を実行している間、ユーザー(またはクライアント)の対応をどう考えるか？
処理が終わるまで待たせるか？
待たせないようにするために処理を切り出して非同期的に実行できるようにするか？
どっちにしても長い時間かかる処理だけあってサーバー負荷は高い。
サーバー負荷の高い処理が連打されたりすると大変なことになってしまう。
処理が終わるまで待たせないようにする場合はもちろん、待たせる場合であっても
複数ユーザが同時に実行することを考えると何かしらの対策は必要になる。
理想としては、同時に実行できる処理数を制限して超えた分は Queue のようなものを使って処理待ちさせる。
処理に空きができたところで処理待ちしている処理を順に処理していく。
&lt;/pre&gt;
&lt;pre&gt;
なにか簡単にできる方法はないかと考えていたところ MessageQueue に至った訳です。
MessageQueue サーバーとして RabbitMQ を使って検証してみようと思います。
&lt;/pre&gt;
&lt;pre&gt;
始める前に...
RabbitMQ がインストール済であること
RabbitMQ サーバーにログイン可能でリソースにアクセスできるユーザが作成済であること
(ユーザとパスワードを &quot;user01&quot; と &quot;passwd&quot; としています。
以上が前提になります。
このあたりはわたしも『&lt;a href=&quot;/2016/04/yum-local-repository-rabbitmq.html&quot;&gt;yum の Local Repository に RabbitMQ をインストール &lt;/a&gt;』にまとめていますので
よろしければ、こちらも合わせて、ご参考に。
&lt;/pre&gt;
&lt;pre&gt;
ではでは！Getting Started!!
&lt;/pre&gt;
&lt;br /&gt;



&lt;h2&gt;ちょっと整理&lt;/h2&gt;
&lt;pre&gt;
主な Class と interface を整理したいと思います。
(com.rabbitmq.client パッケージにあるものです。
&lt;/pre&gt;

&lt;b&gt;ConnectionFactory&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
hostName、userName、password などを元に RabbitMQサーバーと接続する。
接続情報を Connectionオブジェクトにして返す。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;b&gt;Connection&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
接続情報を保持する。
接続の Abort、Close を行う。
接続中に発生するイベントのハンドリングを行う。
Channelオブジェクトを生成する。
など。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;b&gt;Channel&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
Exchange と Queue の宣言を行う。
宣言した Exchange と Queue を Bind する。
メッセージの送信処理と受信処理を行う。
メッセージの受信処理に使う Consumer の割り当てを行う。
その他 AMQP の各種操作メソッドを提供する。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;b&gt;Exchange&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
メッセージの送信先になる。
メッセージ送信者(sender)はメッセージを直接 Queue に送信することはできない。
送られてきたメッセージを RoutingKey を元に格納する Queue を決定する。
&lt;/pre&gt;
&lt;pre&gt;
以下４つのタイプがある。
&lt;/pre&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;fanout&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;無条件に Queue へメッセージを格納する。&lt;br /&gt;※ ルーティングなし。(RoutingKey は無効)&lt;/dd&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;direct&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;RoutingKey にマッチする Queue へ格納する。&lt;/dd&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;topic&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;
RoutingKey にパターンマッチする Queue へ格納する。&lt;br /&gt;
RoutingKey は「.」ドット区切りの単語で指定する。&lt;br /&gt;
&lt;span class=&quot;fummy_quote&quot;&gt;&amp;nbsp;&amp;nbsp;例) &quot;kern.critical&quot;&amp;nbsp;&amp;nbsp;&quot;auth.info&quot;&amp;nbsp;&amp;nbsp;&quot;www.google.com&quot;&lt;/span&gt;&lt;br /&gt;
ワイルドカード(&quot;*&quot;&amp;nbsp;&quot;#&quot;)を含めたマッチング条件を指定できる。&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&quot;*&quot; は、１の単語にマッチする。&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&quot;#&quot; は、複数の単語にマッチする。&lt;br /&gt;
&lt;span class=&quot;fummy_quote&quot;&gt;&amp;nbsp;&amp;nbsp;例) &quot;kern.*&quot;&amp;nbsp;&amp;nbsp;&quot;*.info&quot;&amp;nbsp;&amp;nbsp;&quot;#.com&quot;&lt;/span&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;headers&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;
メッセージ Header にマッチする Queue へ格納する。
&lt;/dd&gt;
&lt;/dl&gt;
&lt;pre&gt;
direct タイプで無名の Exchange がデフォルトの Exchange として必ず提供されている。
Queue は、宣言と同時に暗黙的にこのデフォルトの Exchange と Queue名を RoutingKey にして
Bind される。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;b&gt;Queue&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
メッセージを格納する。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;

&lt;b&gt;Consumer&lt;/b&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;pre&gt;
割り当てられた Queue からメッセージを取り出して処理する。
&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;br /&gt;


&lt;h2&gt;環境&lt;/h2&gt;
&lt;b&gt;開発機&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;開発言語&lt;/th&gt;
    &lt;td&gt;Java 1.8.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;rabbitmq-java-client 3.6.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Gradle 2.12&lt;br /&gt;※gradle コマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows 8.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;RabbitMQ Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;1&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;pre&gt;
作成するファイルは少ないのですがプロジェクトをApp と Worker に分けました。
というのも別の jar ファイルにしてそれぞれ実行した方が
プロセス間でメッセージのやりとりをしている様子をより実感できるかなと思ったからです。
&lt;/pre&gt;
&lt;pre&gt;
C ドライブ直下に「studying-rabbitmq」フォルダを作成して各ファイルを以下のように配置します。
ファイルは、UTF-8 で保存します。
&lt;/pre&gt;
&lt;div id=&quot;studying-rabbitmq&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-rabbitmq&lt;/span&gt;
   &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;app&lt;/span&gt;
          &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
               &lt;ul&gt;
                            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;sender&lt;/span&gt;
                              &lt;ul&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;App.java&lt;/span&gt;&lt;/li&gt;
                              &lt;/ul&gt;
                            &lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;worker&lt;/span&gt;
          &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
              &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
                  &lt;ul&gt;
                    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
                      &lt;ul&gt;
                        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying&lt;/span&gt;
                          &lt;ul&gt;
                            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;receiver&lt;/span&gt;
                              &lt;ul&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyConsumer.java&lt;/span&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;Worker.java&lt;/span&gt;&lt;/li&gt;
                              &lt;/ul&gt;
                            &lt;/li&gt;
                          &lt;/ul&gt;
                        &lt;/li&gt;
                      &lt;/ul&gt;
                    &lt;/li&gt;
                  &lt;/ul&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;settings.gradle&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-rabbitmq&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;準備&lt;/h2&gt;
&lt;pre&gt;
ビルドスクリプトを作成します。
プロジェクトは、App と Worker と２つあるのですがビルドスクリプトは１つにしています。
&lt;/pre&gt;

&lt;u&gt;build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
subprojects {
  apply plugin: &#39;java&#39;
  apply plugin: &#39;eclipse&#39;
  apply plugin: &#39;application&#39;

  sourceCompatibility = &#39;1.8&#39;
  targetCompatibility = &#39;1.8&#39;
  [compileJava, compileTestJava]*.options*.encoding = &#39;UTF-8&#39;
  
  mainClassName = (project.hasProperty(&#39;mainClass&#39;)) ? &quot;$mainClass&quot; : &#39;&#39;

  repositories {
    jcenter()
  }

  dependencies {
    compile &#39;com.rabbitmq:amqp-client:3.6.1&#39;
    compile &#39;org.slf4j:slf4j-api:1.7.18&#39;
    testCompile &#39;junit:junit:4.12&#39;
  }
}
&lt;/pre&gt;

&lt;u&gt;settings.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
rootProject.name = &#39;studying-rabbitmq&#39;
include &#39;app&#39;, &#39;worker&#39;
&lt;/pre&gt;


&lt;pre&gt;
RabbitMQ のユーザが未作成の場合は、以下のようにして作成します。
作成したユーザがリソースにアクセスできるように権限も設定します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl add_user user01 passwd
rabbitmqctl set_permissions -p / user01 &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
&lt;/pre&gt;
&lt;pre&gt;
ユーザとパスワードは user01 と passwd にしています。
&lt;/pre&gt;
&lt;br /&gt;

&lt;h2&gt;Worker の作成&lt;/h2&gt;
&lt;pre&gt;
Worker側から作成していきます。
全体的な内容としては Queue に届いたメッセージを取り出してコンソールに出力するとった感じになります。
メッセージを出力した後は、３秒間スリープします。
(これを長い時間かかる処理と想定しています。
Connection が使用できるスレッドを３つに制限します。
Channel を３つ作成してそれぞれに Consumer を１つ割り当てます。
３つまで同時にメッセージを処理してそれを超えた分は Queue で待たされるといった感じになると思います。
&lt;/pre&gt;
&lt;pre&gt;
まず、Consumer クラスを以下の内容で作成します。
&lt;/pre&gt;
&lt;u&gt;worker/src/main/java/studying/receiver/MyConsumer.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.receiver;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class MyConsumer extends DefaultConsumer {
  
  private final String name;

  public MyConsumer(Channel channel, String name) {
    super(channel);
    
    this.name = name;
  }
  
  @Override
  public void handleDelivery(String consumerTag, Envelope envelope, 
      AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    String message = new String(body, &quot;UTF-8&quot;);
    long threadId = Thread.currentThread().getId();
    System.out.printf(&quot; [thread-id:%02d] - %s Received &#39;%s&#39; %n&quot;, threadId, name, message);
    
    try {
      Thread.sleep(3 * 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      System.out.printf(&quot;  [thread-id:%02d] - %s Done &#39;%s&#39; %n&quot;, threadId, name, message);
      this.getChannel().basicAck(envelope.getDeliveryTag(), false);
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
処理内容としては、
受信したメッセージと共にスレッドID、Consumer名をコンソールに出力します。
３秒後(長い時間かかる処理を想定)にもう一度スレッドID、Consumer名、メッセージをコンソールに出力します。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次にメッセージの受付を開始するクラスを作成します。
&lt;/pre&gt;
&lt;u&gt;worker/src/main/java/studying/receiver/Worker.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.receiver;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Worker {

  public static void main(String[] args) {
    try {
      new Worker().start();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  public void start() throws Exception {
    
    final long threadId = Thread.currentThread().getId();
    System.out.printf(&quot;[thread-id:%02d] start %n&quot;, threadId);
    
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost(&quot;192.168.10.110&quot;);
    factory.setUsername(&quot;user01&quot;);
    factory.setPassword(&quot;passwd&quot;);

    // Worker の数を３つにする。
    ExecutorService es = Executors.newFixedThreadPool(3);
    Connection connection = factory.newConnection(es);

    boolean durable = true;
    boolean autoAck = false;
    String queueName    = &quot;myQueue&quot;;
    String exchangeType = &quot;direct&quot;;
    String exchangeName = &quot;myExchange&quot;;
    String routingKey   = &quot;myKey&quot;;


    // Worker A
    Channel channelA = connection.createChannel();
    channelA.exchangeDeclare(exchangeName, exchangeType, durable);
    channelA.queueDeclare(queueName, durable, false, false, null);
    channelA.queueBind(queueName, exchangeName, routingKey);
    
    MyConsumer myConsumerA = new MyConsumer(channelA, &quot;ConsumerA&quot;);
    channelA.basicConsume(queueName, autoAck, myConsumerA);
    

    // Worker B
    Channel channelB = connection.createChannel();
    channelB.exchangeDeclare(exchangeName, exchangeType, durable);
    channelB.queueDeclare(queueName, durable, false, false, null);
    channelB.queueBind(queueName, exchangeName, routingKey);
    
    MyConsumer myConsumerB = new MyConsumer(channelB, &quot;ConsumerB&quot;);
    channelB.basicConsume(queueName, autoAck, myConsumerB);
    

    // Worker C
    Channel channelC = connection.createChannel();
    channelC.exchangeDeclare(exchangeName, exchangeType, durable);
    channelC.queueDeclare(queueName, durable, false, false, null);
    channelC.queueBind(queueName, exchangeName, routingKey);

    MyConsumer myConsumerC = new MyConsumer(channelC, &quot;ConsumerC&quot;);
    channelC.basicConsume(queueName, autoAck, myConsumerC);
  }  
}
&lt;/pre&gt;
&lt;pre&gt;
処理内容としては、
送受信処理に使う Queue と Exchange (direct タイプ) を宣言しています。
Queue名と Exchange名は、&quot;myQueue&quot; と &quot;myExchange&quot; になります。
RoutingKey を &quot;myKey&quot; にして &quot;myQueue&quot; と &quot;myExchange&quot; を Bind しています。
１つの Queue に対して３つの Consumer を割り当ててメッセージの受信処理を開始しています。
&lt;/pre&gt;
&lt;br /&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Connection が使用できるスレッドの数&lt;/span&gt;
&lt;pre&gt;
Workerクラスの以下の部分でスレッドの数を設定していますが
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
// Worker の数を３つにする。
ExecutorService es = Executors.newFixedThreadPool(3);
Connection connection = factory.newConnection(es);
...
&lt;/pre&gt;
&lt;pre&gt;
以下のように設定しなかった場合、どうなるか？
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
Connection connection = factory.newConnection();
...
&lt;/pre&gt;
&lt;pre&gt;
JavaVM が使用できるプロセッサの数 × 2 になるようです。
(com.rabbitmq.client.impl.ConsumerWorkService 参照)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Consumer の実装&lt;/span&gt;
&lt;pre&gt;
Consumerインターフェイスのメソッドの実装コードを１つ１つ書いていくのは、なかなかなので
今回は DefaultConsumerクラスを継承して必要なメソッドをオーバーライドする方法をとりました。
別のやり方として以下のように QueueingConsumerクラスを使ってメッセージの処理をする方法もあるようです。
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, false, consumer);

while (true) {
  QueueingConsumer.Delivery delivery = consumer.nextDelivery();
  String message = new String(delivery.getBody());
  
  channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Worker側は、だいたいこんな感じです。
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;App の作成&lt;/h2&gt;
&lt;pre&gt;
メッセージを送信するクラスをを作成します。
&lt;/pre&gt;
&lt;pre&gt;
以下の内容で作成します。
&lt;/pre&gt;
&lt;u&gt;app/src/main/java/studying/sender/App.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package studying.sender;

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

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

public class App {

  public static void main(String[] args) {    
    try {
      List&amp;lt;String&gt; tasks = new ArrayList&amp;lt;String&gt;();
      tasks.add(&quot;Maurice&quot;);
      tasks.add(&quot;Verdine&quot;);
      tasks.add(&quot;Philip&quot;);
      
      tasks.add(&quot;Jessica&quot;);
      tasks.add(&quot;Larry&quot;);
      tasks.add(&quot;Johnny&quot;);
      
      tasks.add(&quot;Ralph&quot;);
      tasks.add(&quot;Al&quot;);
      tasks.add(&quot;Andrew&quot;);

      tasks.add(&quot;XXX&quot;);
      
      ConnectionFactory factory = new ConnectionFactory();
      factory.setHost(&quot;192.168.10.110&quot;);
      factory.setUsername(&quot;user01&quot;);
      factory.setPassword(&quot;passwd&quot;);
      Connection connection = factory.newConnection();

      System.out.printf(&quot;send start %n&quot;);
      System.setProperty(&quot;java.util.concurrent.ForkJoinPool.common.parallelism&quot;, &quot;10&quot;);
      tasks.parallelStream().forEach((String message) -&gt; {        
        try {
          String exchangeName = &quot;myExchange&quot;;
          String routingKey = &quot;myKey&quot;;
          
          Channel channel = connection.createChannel();
          AMQP.BasicProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
          channel.basicPublish(exchangeName, routingKey, props, message.getBytes());

          System.out.printf(&quot;[thread-id:%02d] &#39;%s&#39; %n&quot;, Thread.currentThread().getId(), message);
        } catch (Exception e) {
          e.printStackTrace();
        }
      });
      System.out.printf(&quot;send end %n&quot;);

      connection.close();
      
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
&lt;/pre&gt;
&lt;pre&gt;
Exchange と Queue の宣言と Bind は、Worker側で行うので
App側では、送信時に Exchange と RoutingKey を指定するだけにします。
送信する Exchange と RoutingKey は、&quot;myRpcExchange&quot; と &quot;myRpcKey&quot; にしています。
送信するまでに最低限 Exchange が存在していないとうまくいかないので
先に Worker側を起動することになります。
&lt;/pre&gt;
&lt;br /&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;デフォルトの Exchange&lt;/span&gt;
&lt;pre&gt;
今回、Exchange を宣言しましたが direct タイプの Exchange で問題なければ
デフォルトの Exchange を使ってもいいと思います。
Queue を宣言すると自動的にデフォルトの Exchange に Bind されるようです。
以下のようなコードが暗黙的に実行されているようなイメージだと思います。
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
String queueName    = &quot;myQueue&quot;;
String exchangeName = &quot;&quot;;
String routingKey   = queueName;

...
// 以下の Bind が暗黙的に行われていると思います。
channel.queueBind(queueName, exchangeName, routingKey);
...
&lt;/pre&gt;
&lt;pre&gt;
rabbitmqctl list_bindings コマンドを使って Bind を確認できると思います。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_bindings -p / source_name source_kind destination_name destination_kind routing_key
&lt;/pre&gt;
&lt;pre&gt;
送信処理にデフォルトの Exchange を使うと以下のようになると思います。
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
String queueName    = &quot;myQueue&quot;;
String exchangeName = &quot;&quot;;
String routingKey   = queueName;
 
Channel channel = connection.createChannel();
AMQP.BasicProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
channel.basicPublish(exchangeName, routingKey, props, message.getBytes());
...
&lt;/pre&gt;
&lt;pre&gt;
受信処理にデフォルトの Exchange を使うと以下のようになると思います。
&lt;/pre&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
boolean durable = true;
boolean autoAck = false;
String queueName    = &quot;myQueue&quot;;

Channel channel = connection.createChannel();
channel.queueDeclare(queueName, durable, false, false, null);
 
MyConsumer myConsumer = new MyConsumer(channel, &quot;Consumer&quot;);
channel.basicConsume(queueName, autoAck, myConsumer);
...
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;


&lt;h2&gt;実行する&lt;/h2&gt;
&lt;pre&gt;
本来、以下のようにして jar ファイルを作って実行したいところなのですが
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq
gradle :worker:clean :worker:distZip -PmainClass=studying.receiver.Worker -q
gradle :app:clean :app:distZip -PmainClass=studying.sender.App -q
&lt;/pre&gt;
&lt;pre&gt;
gradle から実行してしまいます。
コマンドプロンプトを開いて、まず Worker側から起動します。
&lt;/pre&gt;
&lt;u&gt;Worker&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq
gradle :worker:clean :worker:run -PmainClass=studying.receiver.Worker -q

[thread-id:01] start
&lt;/pre&gt;
&lt;pre&gt;
Worker側が起動したのが確認できたら App側を実行します。
Worker側のコマンドプロンプトはそのままにしておいて
新たにコマンドプロンプトを開いて以下のコマンドで実行します。
&lt;/pre&gt;
&lt;u&gt;App&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\studying-rabbitmq
gradle :app:clean :app:run -PmainClass=studying.sender.App -q

send start
[thread-id:11] &#39;XXX&#39;
...
&lt;/pre&gt;
&lt;pre&gt;
しばらくすると Worker側のコマンドプロンプトに３秒間隔で以下のように表示されると思います。
&lt;/pre&gt;

&lt;u&gt;Worker&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;Larry&#39;
 [thread-id:13] - ConsumerC Received &#39;Jessica&#39;
&lt;/pre&gt;
&lt;pre&gt;
３秒間後...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;Larry&#39;
 [thread-id:13] - ConsumerC Received &#39;Jessica&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Maurice&#39;
  [thread-id:12] - ConsumerB Done &#39;Larry&#39;
 [thread-id:12] - ConsumerB Received &#39;Verdine&#39;
  [thread-id:13] - ConsumerC Done &#39;Jessica&#39;
 [thread-id:13] - ConsumerC Received &#39;Andrew&#39;
&lt;/pre&gt;
&lt;pre&gt;
３秒間後...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;Larry&#39;
 [thread-id:13] - ConsumerC Received &#39;Jessica&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Maurice&#39;
  [thread-id:12] - ConsumerB Done &#39;Larry&#39;
 [thread-id:12] - ConsumerB Received &#39;Verdine&#39;
  [thread-id:13] - ConsumerC Done &#39;Jessica&#39;
 [thread-id:13] - ConsumerC Received &#39;Andrew&#39;
  [thread-id:11] - ConsumerA Done &#39;Maurice&#39;
 [thread-id:11] - ConsumerA Received &#39;Johnny&#39;
  [thread-id:12] - ConsumerB Done &#39;Verdine&#39;
 [thread-id:12] - ConsumerB Received &#39;Ralph&#39;
  [thread-id:13] - ConsumerC Done &#39;Andrew&#39;
 [thread-id:13] - ConsumerC Received &#39;Al&#39;
&lt;/pre&gt;
&lt;pre&gt;
３秒間後...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;Larry&#39;
 [thread-id:13] - ConsumerC Received &#39;Jessica&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Maurice&#39;
  [thread-id:12] - ConsumerB Done &#39;Larry&#39;
 [thread-id:12] - ConsumerB Received &#39;Verdine&#39;
  [thread-id:13] - ConsumerC Done &#39;Jessica&#39;
 [thread-id:13] - ConsumerC Received &#39;Andrew&#39;
  [thread-id:11] - ConsumerA Done &#39;Maurice&#39;
 [thread-id:11] - ConsumerA Received &#39;Johnny&#39;
  [thread-id:12] - ConsumerB Done &#39;Verdine&#39;
 [thread-id:12] - ConsumerB Received &#39;Ralph&#39;
  [thread-id:13] - ConsumerC Done &#39;Andrew&#39;
 [thread-id:13] - ConsumerC Received &#39;Al&#39;
  [thread-id:11] - ConsumerA Done &#39;Johnny&#39;
 [thread-id:11] - ConsumerA Received &#39;Philip&#39;
  [thread-id:12] - ConsumerB Done &#39;Ralph&#39;
  [thread-id:13] - ConsumerC Done &#39;Al&#39;
&lt;/pre&gt;
&lt;pre&gt;
３秒間後...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;Larry&#39;
 [thread-id:13] - ConsumerC Received &#39;Jessica&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Maurice&#39;
  [thread-id:12] - ConsumerB Done &#39;Larry&#39;
 [thread-id:12] - ConsumerB Received &#39;Verdine&#39;
  [thread-id:13] - ConsumerC Done &#39;Jessica&#39;
 [thread-id:13] - ConsumerC Received &#39;Andrew&#39;
  [thread-id:11] - ConsumerA Done &#39;Maurice&#39;
 [thread-id:11] - ConsumerA Received &#39;Johnny&#39;
  [thread-id:12] - ConsumerB Done &#39;Verdine&#39;
 [thread-id:12] - ConsumerB Received &#39;Ralph&#39;
  [thread-id:13] - ConsumerC Done &#39;Andrew&#39;
 [thread-id:13] - ConsumerC Received &#39;Al&#39;
  [thread-id:11] - ConsumerA Done &#39;Johnny&#39;
 [thread-id:11] - ConsumerA Received &#39;Philip&#39;
  [thread-id:12] - ConsumerB Done &#39;Ralph&#39;
  [thread-id:13] - ConsumerC Done &#39;Al&#39;
  [thread-id:11] - ConsumerA Done &#39;Philip&#39;
&lt;/pre&gt;
&lt;pre&gt;
同時に３つずつメッセージが処理されているのが分かると思います。
また３つのスレッドが使い回されているのも確認できると思います。
さらに Consumer の処理が終わるまでスレッドが独占されている様子も分かると思います。 
この意味でスレッドセーフであるといえると思います。
&lt;/pre&gt;
&lt;br /&gt;


&lt;h2&gt;おまけ&lt;/h2&gt;
&lt;pre&gt;
Connection が使用できるスレッドを１つに制限したらどうなるか？
ちょっと試してみます。
&lt;/pre&gt;
&lt;pre&gt;
Worker側の処理で Connection を作成しているところを以下のように変更します。
&lt;/pre&gt;
&lt;u&gt;worker/src/main/java/studying/receiver/Worker.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
// Worker の数を３つにする。
// ExecutorService es = Executors.newFixedThreadPool(3);
// Connection connection = factory.newConnection(es);

ExecutorService es = Executors.newFixedThreadPool(1);
Connection connection = factory.newConnection(es);
...
&lt;/pre&gt;
&lt;pre&gt;
Worker側を再起動してから App側を実行すると
Worker側のコマンドプロンプトに３秒間隔で１つずつメッセージが以下のように表示されると思います。
&lt;/pre&gt;
&lt;u&gt;Worker&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;Andrew&#39;
  [thread-id:11] - ConsumerA Done &#39;Andrew&#39;
 [thread-id:11] - ConsumerB Received &#39;Maurice&#39;
  [thread-id:11] - ConsumerB Done &#39;Maurice&#39;
 [thread-id:11] - ConsumerB Received &#39;Ralph&#39;
  [thread-id:11] - ConsumerB Done &#39;Ralph&#39;
 [thread-id:11] - ConsumerB Received &#39;Larry&#39;
  [thread-id:11] - ConsumerB Done &#39;Larry&#39;
 [thread-id:11] - ConsumerC Received &#39;Al&#39;
  [thread-id:11] - ConsumerC Done &#39;Al&#39;
 [thread-id:11] - ConsumerC Received &#39;Verdine&#39;
  [thread-id:11] - ConsumerC Done &#39;Verdine&#39;
 [thread-id:11] - ConsumerC Received &#39;Johnny&#39;
  [thread-id:11] - ConsumerC Done &#39;Johnny&#39;
 [thread-id:11] - ConsumerA Received &#39;Jessica&#39;
  [thread-id:11] - ConsumerA Done &#39;Jessica&#39;
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Philip&#39;
  [thread-id:11] - ConsumerA Done &#39;Philip&#39;
&lt;/pre&gt;
&lt;pre&gt;
３の Consumer が１つのスレッドを使い回して順番に処理してく様子が分かると思います。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
今回のテーマから逸れるのですが
１つのメッセージを３つある Consumer のうちのどれか１つで処理するのでなく
１つのメッセージを３つある Consumer ３つで処理する方法を考えてみたいと思います。
&quot;Publish/Subscribe&quot; 方式でのメッセージ処理になると思います。
&lt;/pre&gt;
&lt;pre&gt;
１つの Queue に３つの Consumer を割り当てていたのでラウンドロビンする形で処理されていました。
今度は、Consumer ごとにそれぞれ Queue を割り当てるようにします。
&lt;/pre&gt;
&lt;pre&gt;
Worker側の処理で Queue の宣言しているところと
Consumer に割り当てているところを以下のように変更します。
また Connection が使用できるスレッド数を３つに戻しています。
&lt;/pre&gt;
&lt;u&gt;worker/src/main/java/studying/receiver/Worker.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
// Worker の数を３つにする。
ExecutorService es = Executors.newFixedThreadPool(3);
Connection connection = factory.newConnection(es);
...

// Worker A
Channel channelA = connection.createChannel();
channelA.exchangeDeclare(exchangeName, exchangeType, durable);
channelA.queueDeclare(queueName + &quot;A&quot;, durable, false, false, null);
channelA.queueBind(queueName + &quot;A&quot;, exchangeName, routingKey);

MyConsumer myConsumerA = new MyConsumer(channelA, &quot;ConsumerA&quot;);
channelA.basicConsume(queueName + &quot;A&quot;, autoAck, myConsumerA);


// Worker B
Channel channelB = connection.createChannel();
channelB.exchangeDeclare(exchangeName, exchangeType, durable);
channelB.queueDeclare(queueName + &quot;B&quot;, durable, false, false, null);
channelB.queueBind(queueName + &quot;B&quot;, exchangeName, routingKey);

MyConsumer myConsumerB = new MyConsumer(channelB, &quot;ConsumerB&quot;);
channelB.basicConsume(queueName + &quot;B&quot;, autoAck, myConsumerB);


// Worker C
Channel channelC = connection.createChannel();
channelC.exchangeDeclare(exchangeName, exchangeType, durable);
channelC.queueDeclare(queueName + &quot;C&quot;, durable, false, false, null);
channelC.queueBind(queueName + &quot;C&quot;, exchangeName, routingKey);

MyConsumer myConsumerC = new MyConsumer(channelC, &quot;ConsumerC&quot;);
channelC.basicConsume(queueName + &quot;C&quot;, autoAck, myConsumerC);
...
&lt;/pre&gt;
&lt;pre&gt;
Worker側を再起動してから App側を実行すると
Worker側のコマンドプロンプトに３秒間隔でメッセージが以下のように表示されると思います。
&lt;/pre&gt;
&lt;u&gt;Worker&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
[thread-id:01] start
 [thread-id:11] - ConsumerA Received &#39;XXX&#39;
 [thread-id:13] - ConsumerC Received &#39;XXX&#39;
 [thread-id:12] - ConsumerB Received &#39;XXX&#39;
  [thread-id:11] - ConsumerA Done &#39;XXX&#39;
 [thread-id:11] - ConsumerA Received &#39;Al&#39;
  [thread-id:13] - ConsumerC Done &#39;XXX&#39;
  [thread-id:12] - ConsumerB Done &#39;XXX&#39;
 [thread-id:13] - ConsumerC Received &#39;Al&#39;
 [thread-id:12] - ConsumerB Received &#39;Al&#39;
  [thread-id:11] - ConsumerA Done &#39;Al&#39;
 [thread-id:11] - ConsumerA Received &#39;Johnny&#39;
  [thread-id:13] - ConsumerC Done &#39;Al&#39;
 [thread-id:13] - ConsumerC Received &#39;Johnny&#39;
  [thread-id:12] - ConsumerB Done &#39;Al&#39;
 [thread-id:12] - ConsumerB Received &#39;Johnny&#39;
 ...
&lt;/pre&gt;
&lt;pre&gt;
今度は３つの Consumer が送信されたメッセージをそれぞれ処理するのでログが３倍出力されると思います。
&lt;/pre&gt;
&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
MessageQueue サーバーを使って同時実行数を制御して処理できることが一応確認できたと思います。
しかし実際これでシステム開発しようとした場合、
冗長化であったり、トランザクションであったり、認証であったりと
考慮しないといけないことがまだまだあります。
今回は、３つの Worker を１つのプロセスで動かしていたので
プロセスが落ちると全部の Worker が止まっていまします。
簡単な冗長化であれば１プロセス１ Worker にして３つプロセスを起動すれば
同じように Worker ３つで処理できます。
この場合、プロセスが１つ落ちても Worker 全体が止まってしまうことはありません。
しかし Worker は動いていても MessageQueueサーバー自体が止まってしまうと
システム全体に影響が出ます。
ミドルウェアを導入することで開発は軽減されるものの管理が増える。
このあたりがミドルウェア導入の考えどころか？
&lt;/pre&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/getstarted.html&quot;&gt;
    http://www.rabbitmq.com/getstarted.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/api-guide.html&quot;&gt;
    http://www.rabbitmq.com/api-guide.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2517561852757664141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2517561852757664141'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2016/05/rabbitmq.html' title='RabbitMQ で同時処理実行数を制限する'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-1335484459302330730</id><published>2016-04-28T21:27:00.000+09:00</published><updated>2016-06-11T13:41:34.800+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="RabbitMQ"/><title type='text'>yum の Local Repository に RabbitMQ をインストール</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
みなさんミドルウェアのインストールってどうしてます？
わたしはディストリビューションのパッケージで用意されているのもをそのままインストールしています。
わたしの場合、CentOS なので yum でインストールすることになるのですが
yum でインストールするとだいたい最新のものでなかったりします。
わたしはどちらかというと開発者寄りなので、とりあえず開発ができれば少々古くても気になりません。
ところが yum で提供されていなかったりすると非常に困ってしまいます。
また、どうしても最新のものが使いた場合も同様です。
さぁどう考えるか？
&lt;/pre&gt;
&lt;pre&gt;
まず他にだれか Repository を公開してくれていないか探します。
PostgreSQL は本家のサイトで最新の Repository を公開してくれているので助かります。
Samba4 は以前 &lt;a href=&quot;http://www.enterprisesamba.com/samba/&quot;&gt;SerNet&lt;/a&gt; というところのが使えたのですが 4.3 系から有償になったので使えません。
&lt;/pre&gt;
&lt;pre&gt;
yum の Repository が見つからなかった場合、rpm を探します
無ければ泣く泣くソースからインストールになります。
手順どうりやればソースからインストールしても全然問題ないのですが
わたしの場合、Makefile の管理が悪いのでバーションアップに困って OS からやり直す場合が多いです。
(最近は VM を使っていたりするので OS からやり直すことは少なくなりました。
まぁ開発機なのでこれもOKかなと...
&lt;/pre&gt;
&lt;pre&gt;
RabbitMQ は、EPEL で提供されているようですが
今回は、RabbitMQ の本家のサイトにある最新の rpm を利用しようと思います。
rpm はビルドこそしなくていいものの、その依存関係まではみてくれません。
RabbitMQ の場合、Erlang が依存関係にあるので
先にインストールしておくか、一緒にインストールしないといけません。
実際、この依存関係を調べるのもなかなかだったりします。
なにか、こう、一撃でインストールできる方法はないかと考えていたところ
yum の LocalRepository に至った訳です。
&lt;/pre&gt;
&lt;pre&gt;
今回は、LocalRepository を使った RabbitMQ のインストールと
ユーザ設定などを中心に進めていきたいと思います。
ではでは！Getting Started!
&lt;/pre&gt;
&lt;br /&gt;

&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.10.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;2&quot;&gt;ミドルウェア&lt;/th&gt;
    &lt;td&gt;
      &lt;span&gt;RabbitMQ server 3.6.1&lt;/span&gt;
      &lt;br /&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;ユーザ&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;user01&lt;/span&gt;
      &lt;span&gt;&amp;nbsp;&amp;nbsp;パスワード&lt;/span&gt;&amp;nbsp;&lt;span&gt;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;passwd&lt;/span&gt;
      &lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;span&gt;Apache 2.4.6&lt;/span&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;CentOS-7-x86_64-Minimal-1511.iso&lt;br /&gt;VirtualBox 5.0.16&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;LocalRepository の作成&lt;/h2&gt;
&lt;pre&gt;
repository 作成のために creatrepo コマンドと LocalRepository を内部ネットワーク向けに
PrivateRepository として公開したいので Apache もインストールします。

&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install createrepo httpd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
repository用のディレクトリを作成します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mkdir -p /var/www/repo/centos/7/x86_64/rpms
&lt;/pre&gt;
&lt;pre&gt;
場所はどこでもいいのですが、今回は、Apache のディレクトリの中に作成しました。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
repository を作成します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
createrepo /var/www/repo/centos/7/x86_64
&lt;/pre&gt;
&lt;pre&gt;
LocalRepository の作成としてはこんな感じです。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
LocalRepository を yum の repository に追加します。
&lt;/pre&gt;
&lt;pre&gt;
local.repo ファイルを以下のような内容で作成します。
&lt;/pre&gt;
&lt;u&gt;/etc/yum.repos.d/local.repo&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[local]
name=CentOS-$releasever - local
baseurl=file:///var/www/repo/centos/$releasever/$basearch
enabled=1
gpgcheck=0
protect=1
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;repo ファイルで使える変数&lt;/span&gt;
&lt;pre&gt;
以下の変数が使えるようです。
&lt;/pre&gt;
&lt;ul style=&quot;list-style:none;&quot;&gt;&lt;li&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;$releasever&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;Red Hat Linux のリリースバージョン&lt;/dd&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;$basearch&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;システムのベースアーキテクチャー&lt;br /&gt;i686 と i586 は i386&lt;br /&gt;AMD64 と Intel64 は x86_64 など&lt;/dd&gt;
&lt;dt&gt;&lt;u&gt;&lt;b&gt;$arch&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
&lt;dd&gt;CPU アーキテクチャー&lt;br /&gt;i586、i686、x86_64 など&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;

&lt;pre&gt;
以下のコマンドでリポジトリ一覧に LocalRepository が追加されていれば OK です。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
LANG=C yum repolist

repo id             repo name           status
...
local/7/x86_64      CentOS-7 - local    0
...
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;PrivateRepository の作成&lt;/h2&gt;
&lt;pre&gt;
今度は、内部ネットワーク向けの PrivateRepository を作成したいと思います。
Repository のディレクトリは、先ほど作成した LocalRepository を流用します。
Repository のディレクトリを公開ディレクトリにする訳ですが
Apache の公開ディレクトリの設定方法にはいろいろあると思います。
今回は、手っ取り早く Apache の DocumentRoot の下に
LocalRepository のディレクトリのシンボリックリンクを張ります。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ln -s /var/www/repo/centos /var/www/html/centos 
&lt;/pre&gt;
&lt;pre&gt;
念のため Apache を再起動します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl enable httpd
systemctl restart httpd
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
PrivateRepository の private.repo ファイルを以下のような内容で作成します。
&lt;/pre&gt;
&lt;u&gt;/etc/yum.repos.d/private.repo&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[private]
name=CentOS-$releasever - private
baseurl=http://192.168.10.110/centos/$releasever/$basearch
enabled=1
gpgcheck=0
protect=1
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
リポジトリ一覧に追加されているか確認します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
LANG=C yum repolist

repo id             repo name           status
...
private/7/x86_64      CentOS-7 - private  0
...
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;RabbitMQ のインストール &lt;/h2&gt;
&lt;pre&gt;
Repository が完成したところで RabbitMQ をインストールしたいと思います。 
LocalRepository と PrivateRepository のどちらを使ってもいいと思います。
&lt;/pre&gt;
&lt;pre&gt;
Erlang が依存関係にあるのですが、これが EPEL での提供になっているようなので
先に EPEL の Repository を追加します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install epel-release.noarch
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
RabbitMQ の rpm をダウンロードしてきて Local/PrivateRepository のディレクトリに置きます。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /var/www/repo/centos/7/x86_64/rpms
curl -o rabbitmq-server-3.6.1-1.noarch.rpm \
  http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/rabbitmq-server-3.6.1-1.noarch.rpm
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Erlang のインストール&lt;/span&gt;
&lt;pre&gt;
わたしの場合、Erlang は最新でなくても気にならないので EPEL のものにしました。
気になる場合はLocal/PrivateRepository に最新のものを持ってきて
一緒にインストールするのもよいと思います。
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
createrepo コマンドで Repository を再設定します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
createrepo /var/www/repo/centos/7/x86_64
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
yum のキャッシュをクリアします。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum clean all
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
後は yum で RabbitMQ をインストール します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install rabbitmq-server
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
サービスの設定をして終わりです。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl enable rabbitmq-server
systemctl start rabbitmq-server
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;rabbitmqctl コマンドで RabbitMQ の設定&lt;/h2&gt;
&lt;pre&gt;
ユーザを作成します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl add_user user01 passwd
&lt;/pre&gt;
&lt;pre&gt;
user01 ユーザを作成してパスワードを passwd にしています。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
作成したユーザがリソースにアクセスできるように権限を付与します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_permissions -p / user01 &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
&lt;/pre&gt;
&lt;pre&gt;
user01 がすべてのリソースに対して write と read の権限を付与しています。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ユーザ一覧、パスワード変更、削除など。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_users
rabbitmqctl change_password user01 newpasswd
rabbitmqctl delete_user user01
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ユーザタグの設定と削除。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_user_tags user01 administrator
rabbitmqctl set_user_tags user01 administrator test
rabbitmqctl set_user_tags user01
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
権限一覧、ユーザ権限一覧、権限取り消しなど。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_permissions -p /
rabbitmqctl list_user_permissions user01
rabbitmqctl clear_permissions -p / user01
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Queue、Exchange、Binding の一覧。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl list_queues -p / name durable messages
rabbitmqctl list_exchanges -p / name durable type
rabbitmqctl list_bindings -p / source_name source_kind destination_name destination_kind routing_key
&lt;/pre&gt;
&lt;pre&gt;
詳しいコマンドのパタメータについては man 参照。
&lt;/pre&gt;
&lt;br /&gt;

&lt;h2&gt;ManagementPlugin を有効にする&lt;/h2&gt;
&lt;p&gt;
Queue や Exchange などを Web 画面から管理できる ManagementPlugin を有効にします。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmq-plugins enable rabbitmq_management
&lt;/pre&gt;
&lt;pre&gt;
Web の管理コンソール を利用できるように tag を使ってユーザに Role を設定します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqctl set_user_tags user01 administrator
&lt;/pre&gt;

&lt;pre&gt;
Web の管理コンソールを開く前に一旦、RabbitMQ サーバーを再起動させます。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
systemctl stop rabbitmq-server
systemctl start rabbitmq-server
&lt;/pre&gt;
&lt;pre&gt;
ブラウザから「http://192.168.10.110:15672/」にアクセスします。
先ほどの Role を設定したユーザでログインできれば OK です。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
CommandLineTool も合わせてインストールしておきます。
インストール方法としては、Web の管理コンソールのページから rabbitmqadmin のスクリプトを
ダウンロードしてきて /usr/local/bin に置くといった流れになります。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /usr/local/bin
curl -o rabbitmqadmin http://localhost:15672/cli/rabbitmqadmin
chmod 755 rabbitmqadmin
&lt;/pre&gt;
&lt;pre&gt;
各コマンドのヘルプが表示されればインストールは、OK です。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin --help
rabbitmqadmin help subcommands
rabbitmqadmin help config
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
さらに CommandLineTool の bash 補完を有効にしておきます。
bash-completion がインストールされていない場合は、yum でインストールした後、
CommandLineTool の bash 補完を有効にします。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install bash-completion
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin --bash-completion &gt; /etc/bash_completion.d/rabbitmqadmin
&lt;/pre&gt;
&lt;pre&gt;
再ログイン後に有効になるようです。
&lt;/pre&gt;
&lt;pre&gt;
以下のように入力して
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin he
&lt;/pre&gt;
&lt;pre&gt;
「TAB」キーを押すと
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rabbitmqadmin help
&lt;/pre&gt;
&lt;pre&gt;
のように補完されれば OK です。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
とりあえず、わたしの Makefile 紛失問題は解決しそうです。
rpm の管理もいい感じに yum で一元化できるかなぁと思いました。
&lt;/pre&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.centos.org/HowTos/CreateLocalRepos&quot;&gt;
    https://wiki.centos.org/HowTos/CreateLocalRepos
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.serverlab.ca/tutorials/linux/network-services/creating-a-yum-repository-server-for-red-hat-and-centos/&quot;&gt;
    http://www.serverlab.ca/tutorials/linux/network-services/creating-a-yum-repository-server-for-red-hat-and-centos/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/sec-Using_Yum_Variables.html&quot;&gt;
    https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/sec-Using_Yum_Variables.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com&quot;&gt;
    http://www.rabbitmq.com
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.rabbitmq.com/man/rabbitmqctl.1.man.html&quot;&gt;
    http://www.rabbitmq.com/man/rabbitmqctl.1.man.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;

</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1335484459302330730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1335484459302330730'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2016/04/yum-local-repository-rabbitmq.html' title='yum の Local Repository に RabbitMQ をインストール'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-8645661501730037474</id><published>2014-05-17T02:04:00.001+09:00</published><updated>2014-05-17T02:10:24.550+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><title type='text'>Samba4 で ActiveDirectory / Apache で Windows 統合認証</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
ユーザアカウントをそれぞれのシステムで別々に管理するのでなく
ActiveDirectory で一元的に管理したいというのはある。
例えば HTTP 認証で保護されたページにアクセスする時の ユーザID / パスワードなど。
もうちょっと欲張るとドメインにログインしている状態であれば
その ユーザID / パスワード でもって自動的に認証処理がパスできればなお素晴らしい！
まぁそういう訳で Windows 統合認証 / Windows Desktop SSO 認証になります。
Apache であればモジュールを追加すると試せそうだ。
ActiveDirectory については Samba4 を使います。
ではでは Getting Started!!
&lt;/pre&gt;
&lt;br /&gt;


&lt;u&gt;&lt;b&gt;環境&lt;/b&gt;&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Primary Domain Controller&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.5(CentOS-6.5-x86_64-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.1.7&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;127.0.0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Web Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.5(CentOS-6.5-x86_64-minimal.iso)&lt;br /&gt;VMware Player 6.0.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Apache&lt;/th&gt;
    &lt;td&gt;2.2.15&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;web01&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.110&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;FQDN&lt;/th&gt;
    &lt;td&gt;web01.mydomain.local&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Windows クライアント&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows 8.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;win01&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.150&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Primary Domain Controller の構築&lt;/h2&gt;
&lt;pre&gt;
わたしも blog にまとめているのでそちらを参考に構築してみてください。
&lt;a href=&quot;http://fummy-net.blogspot.jp/2014/05/samba4-active-directory-sernet-repo.html&quot;&gt;『Samba4 で Active Directory / SerNet Repo でセットアップ』&lt;/a&gt;
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory.html&quot;&gt;『Samba4 で ActiveDirectory / インストール編 - わたしもやってみた - 』&lt;/a&gt;
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory-pdc.html&quot;&gt;『Samba4 で ActiveDirectory / PDC編 - わたしもやってみた - 』&lt;/a&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Web Server の構築&lt;/h2&gt;
&lt;pre&gt;
とりあえず SELinux と iptables は止めておこう。
&lt;/pre&gt;
&lt;u&gt;/etc/selinux/config&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
SELINUX=disabled
...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service iptables stop
service ip6tables stop
chkconfig iptables off
chkconfig ip6tables off
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;pre&gt;
ネットワークの設定は次のような感じ。
&lt;/pre&gt;
&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.110
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;
&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=web01
&lt;/pre&gt;

&lt;pre&gt;
DNS は PDC のものを利用する。
&lt;/pre&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 192.168.0.10
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
必要なモジュールをインストールしていく。
Apache は当然として Samba AD とアクセスするために Kerberos 関連のものと
テストページ用に php もインストールする。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum -y install httpd krb5-workstation mod_auth_kerb php
&lt;/pre&gt;
&lt;pre&gt;サービスの起動設定をしておく。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig httpd on
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ここで再起動します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
PDC にログインして Web Server の 正引き DNS の設定をします。
一応確認もしておく。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh root@pdc
samba-tool dns add pdc mydomain.local web01 A 192.168.0.110
dig web01.mydomain.local
exit
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Windows クライアントの設定&lt;/h2&gt;
&lt;pre&gt;
とりあえず MYDOMAIN.LOCAL ドメインへの参加くらいでしょうか？
ドメイン参加しているのであれば DNS は PDC に設定されていると思うので DNS の設定は 大丈夫だと思う。
もし hosts ファイルに Web Server の IP を追加するなら以下のような感じ。
&lt;/pre&gt;
&lt;u&gt;C:\Windows\System32\drivers\etc\hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
192.168.0.110  web01.mydomain.local web01
&lt;/pre&gt;
&lt;br /&gt;
&lt;pre&gt;
ここまでできたら Web ブラウザで「http://web01.mydomain.local」を開いて Apache が動いているか確認する。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;Samba AD のユーザで Basic 認証&lt;/h2&gt;
&lt;pre&gt;
手始めに Apache から Samba AD にアクセスできているかの確認も兼ねて
Basic 認証の設定をしてみようと思います。
雰囲気としては、.htpasswd ファイルの代わりに Samba AD のユーザデータを使う感じだと思います。
では Web Server の設定をしていきます。
httpd.conf に追加してもよいのだけれども mod_auth_kerb をインストールすると
auth_kerb.conf が生成されていると思うのでこちらを編集します。
&lt;/pre&gt;
&lt;u&gt;/etc/httpd/conf.d/auth_kerb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
&amp;lt;location &quot;/&quot;&gt;
  AuthName &quot;AD authentication&quot;
  AuthBasicProvider ldap
  AuthType Basic
  AuthLDAPGroupAttribute member
  AuthLDAPGroupAttributeIsDN On
  AuthLDAPURL ldap://pdc:389/cn=Users,dc=mydomain,dc=local?sAMAccountName?sub?(objectClass=*)
  AuthLDAPBindDN cn=administrator,cn=Users,dc=mydomain,dc=local
  AuthLDAPBindPassword passwd-123
  require valid-user 
&amp;lt/Location&gt;
&lt;/pre&gt;
&lt;pre&gt;
Apache から Samba AD にアクセスするユーザを administrator にしています。
パスワードは passwd-123 にしています。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
確認用のテストページを以下の内容で作成します。
&lt;/pre&gt;
&lt;u&gt;/var/www/html/index.php&lt;/u&gt;
&lt;pre class=&quot;brush: php&quot;&gt;
&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;title&gt;Samba AD User&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;h2&gt;Samba AD User&amp;lt;/h2&gt;
    &amp;lt;pre&gt;
      USER : &amp;lt;?php echo $_SERVER[&#39;PHP_AUTH_USER&#39;]; ?&gt;
      PASS : &amp;lt;?php echo $_SERVER[&#39;PHP_AUTH_PW&#39;]; ?&gt;
    &amp;lt;/pre&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ここで再起動します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
再起動が終わったら Windows で Web ブラウザから「http://web01.mydomain.local」を開いてみます。
Basic 認証 のダイアログが表示されると思います。
適当な Samba AD ユーザを入力して「OK」ボタンを押します。
すると画面に入力したユーザIDとパスワードが表示されていると思います。
パスワードまでが表示されてしまっているのは Basic 認証 なんで仕方ないのでしょう。
&lt;/pre&gt;

&lt;pre&gt;
今は Basic 認証 の確認なので Windows はドメインログインでなくローカルログインでも
検証できると思います。
また、Web Server もドメイン参加していなくても大丈夫そうです。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Windows Desktop SSO 認証の設定&lt;/h2&gt;
&lt;pre&gt;
さて、いよいよ本題の Windows 統合認証の設定に入りたいと思います。
まず、PDC 側での作業から始めます。
Web Server の DNS の正引きの設定に加えて逆引きの設定も必要だとのことなので
PDC の DNS に逆引きゾーンを追加して Web Server の 逆引き設定をする。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh root@pdc
samba-tool dns zonecreate pdc 0.168.192.in-addr.arpa
samba-tool dns add pdc 0.168.192.in-addr.arpa 110 PTR web01.mydomain.local
&lt;/pre&gt;
&lt;pre&gt;
わたしのPDCの場合 administrator@MYDOMAIN.LOCAL のパスワードは passwd-123 になります。
&lt;/pre&gt;
&lt;pre&gt;
DNS の正引き、逆引き設定ができているか確認します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
dig web01.mydomain.local 
dig -x 192.168.0.110
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次に Web Server のための Service Principal を追加します。
どのユーザでも構わないと思うのですが
Service Principal 追加時に関連付けするユーザが必要なので
ここでは sso ユーザを作成しています。
そして作成した Service Principal を keytab ファイルに出力します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /root
samba-tool user create --random-password sso
samba-tool user setexpiry --noexpiry sso
samba-tool spn add HTTP/web01.mydomain.local sso
samba-tool domain exportkeytab httpd.keytab --principal=HTTP/web01.mydomain.local  
&lt;/pre&gt;

&lt;pre&gt;
出力した keytab ファイルを Web Server に転送して
Web Server に戻ります。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
scp httpd.keytab root@192.168.0.110:/etc/httpd/conf/httpd.keytab
rm httpd.keytab
exit
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Web Server に戻ってきたら 転送した keytab ファイルの権限を変更します。
一応、keytab ファイルの中身も確認しておきます。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chown root:apache /etc/httpd/conf/httpd.keytab
chmod 640 /etc/httpd/conf/httpd.keytab
klist -ke /etc/httpd/conf/httpd.keytab
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次は Kerberos の設定をします。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf.original
&lt;/pre&gt;

&lt;pre&gt;
以下のような内容で krb5.conf を作成します。
&lt;/pre&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[logging]
  default = FILE:/var/log/krb5libs.log
  kdc = FILE:/var/log/krb5kdc.log
  admin_server = FILE:/var/log/kadmind.log

[libdefaults]
  default_realm = MYDOMAIN.LOCAL
  dns_lookup_realm = false
  dns_lookup_kdc = false
  ticket_lifetime = 24h
  renew_lifetime = 7d
  forwardable = true

[realms]
  MYDOMAIN.LOCAL = {
    kdc = pdc.mydomain.local
    admin_server = pdc.mydomain.local
  }

[domain_realm]
  .mydomain.local = MYDOMAIN.LOCAL
  mydomain.local = MYDOMAIN.LOCAL
&lt;/pre&gt;

&lt;pre&gt;
Kerberos チケットのクリアと初期化、そして確認をします。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kdestroy
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
その次に Apache の設定をします。
auth_kerb.conf を以下のような内容に編集します。
&lt;/pre&gt;
&lt;u&gt;/etc/httpd/conf.d/auth_kerb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
&amp;lt;location &quot;/&quot;&gt;
  AuthType Kerberos
  AuthName &quot;Network Login&quot;
  KrbMethodNegotiate On
  KrbMethodK5Passwd On
  KrbAuthRealms MYDOMAIN.LOCAL
  require valid-user
  Krb5KeyTab /etc/httpd/conf/httpd.keytab
  KrbLocalUserMapping On 
&amp;lt/Location&gt;
&lt;/pre&gt;

&lt;pre&gt;
Apache を再起動します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service httpd restart
&lt;/pre&gt;

&lt;pre&gt;
確認用のテストページは、先ほどの Basic 認証で作成したものをそのまま使います。
これで Web Server の設定は終わりです。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Windows にドメインログインします。
確認用のテストページを開く前に Windows のブラウザの設定をします。
&lt;/pre&gt;
&lt;pre&gt;
IE の場合
  「ツール」/「インターネット オプション」を開く。
  「セキュリティ」タブを選択する。
      「ローカル イントラネット」を選択する。
        「サイト」ボタンを押す。
          「ローカル イントラネット」ダイアログの「詳細設定」ボタンを押す。
            「http://web01.mydomain.local」を追加する。
        「レベルのカスタマイズ」ボタンを押す。
            「イントラネット ゾーンでのみ自動的にログインする」にチェックを入れる。
  「詳細設定」タブを選択する。
      「統合 Windows 認証を使用する」にチェックを入れる。
&lt;/pre&gt;
&lt;pre&gt;
Firefox の場合
  アドレスバーに「about:config」と入力して以下のプロパティを設定する。
      network.negotiate-auth.delegation-uris    web01.mydomain.local
      network.negotiate-auth.trusted-uris       web01.mydomain.local
&lt;/pre&gt;

&lt;pre&gt;
Web ブラウザから「http://web01.mydomain.local」を開いてみます。
HTTP 認証 のダイアログなしに画面にドメインログインしているユーザIDが
表示されていれば OK です。
Basic 認証の時と違ってパスワードは表示されていないと思います。
&lt;/pre&gt;

&lt;pre&gt;
Windows はドメインログインしていないとうまくいかないと思うけど
Web Server はドメイン参加していなくても問題なさそうです。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
内々で使うちょっとしたサイトでユーザ認証がほしいという時には使えそうだ。
ユーザが所属するドメインのグループを条件にアクセス制限もかけられそうだ。
ガッツリ作りこむような Web アプリには厳しそうだ。
HTTP 認証のダイアログでなく独自のログイン画面がほしいだろうし
ユーザID 以外の ActiveDirectory に登録された情報もほしいところだ。
&lt;/pre&gt; 

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/beyond&quot;&gt;
   https://wiki.samba.org/index.php/Samba4/beyond
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://community.zarafa.com/pg/blog/read/18332/zarafa-outlook-amp-webaccess-sso-with-samba4&quot;&gt;
   https://community.zarafa.com/pg/blog/read/18332/zarafa-outlook-amp-webaccess-sso-with-samba4
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://int128.hatenablog.com/entry/20130829/1377768762&quot;&gt;
   http://int128.hatenablog.com/entry/20130829/1377768762
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://passing.breeze.cc/mt/archives/2008/12/active-directory-apache-window.html&quot;&gt;
   http://passing.breeze.cc/mt/archives/2008/12/active-directory-apache-window.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/8645661501730037474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/8645661501730037474'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2014/05/samba4-activedirectory-apache-windows.html' title='Samba4 で ActiveDirectory / Apache で Windows 統合認証'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-6393544854844253738</id><published>2014-05-14T20:13:00.000+09:00</published><updated>2016-04-28T21:39:05.118+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><title type='text'>Samba4 で Active Directory / SerNet Repo でセットアップ</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;pre&gt;
Samba4.1 がリリースされていた。
またセットアップしてみようかなと &lt;a href=&quot;https://wiki.samba.org/index.php/Main_Page&quot;&gt;Samba Wiki&lt;/a&gt; をぶらぶらしていると
Samba4 の yum リポジトリを公開しているサイトを見つけた。
おぉ！これは助かる！！
という訳で今回は、yum で Samba4 をしてみようと思います。
ではでは Getting Started!
&lt;/pre&gt;
&lt;br /&gt;


&lt;h2&gt;yum リポジトリの設定&lt;/h2&gt;
&lt;pre&gt;
これがないと始まらない。
&lt;a href=&quot;http://www.enterprisesamba.com/&quot;&gt;EnterpriseSamba.com&lt;/a&gt; というところが Samba4 yum リポジトリを公開しているようだ。
利用するためにはユーザ登録が必要になる。
このサイトの &lt;a href=&quot;http://www.enterprisesamba.com/samba/&quot;&gt;Samba&lt;/a&gt; のページに移動して &lt;a href=&quot;https://portal.enterprisesamba.com/&quot;&gt;register&lt;/a&gt; のリンクから  &lt;a href=&quot;https://portal.enterprisesamba.com/users/sign_up&quot;&gt;Sign up&lt;/a&gt; を行う。
メールアドレスの確認やなんやかんやあって一通りユーザ登録が終わったら &lt;a href=&quot;https://portal.enterprisesamba.com/users/sign_in&quot;&gt;Login&lt;/a&gt; してみる。
ログインしたページをよ～く見ると username と accesskey が表示されているので
これをしっかりメモっておく。
&lt;/pre&gt;

&lt;pre&gt;
さて準備が整ったところで yum リポジトリの設定をします。
まず yum リポジトリを取得します。
以下のコマンドの &lt;b&gt;USERNAME:ACCESSKEY&lt;/b&gt; の部分を
先ほどメモった username と accesskey に置き換えて実行します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /etc/yum.repos.d
wget https://&lt;b&gt;USERNAME:ACCESSKEY&lt;/b&gt;@download.sernet.de/packages/samba/4.1/centos/6/sernet-samba-4.1.repo
&lt;/pre&gt;

&lt;pre&gt;
今度は、取ってきた yum リポジトリをエディタで開いて baseurl と gpgkey の
&lt;b&gt;USERNAME:ACCESSKEY&lt;/b&gt; の部分を先ほどメモった username と accesskey に置き換えて保存します。
&lt;/pre&gt;
&lt;u&gt;/etc/yum.repos.d/sernet-samba-4.1.repo&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[sernet-samba-4.1]
name=SerNet Samba 4.1 Packages (centos-6)
type=rpm-md
baseurl=https://USERNAME:ACCESSKEY@download.sernet.de/packages/samba/4.1/centos/6/
gpgcheck=1
gpgkey=https://USERNAME:ACCESSKEY@download.sernet.de/packages/samba/4.1/centos/6/repodata/repomd.xml.key
enabled=1
&lt;/pre&gt;

&lt;pre&gt;
最後に gpg build key の設定をして終わりです。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /root
wget http://ftp.sernet.de/pub/sernet-build-key-1.1-4.noarch.rpm
rpm -i sernet-build-key-1.1-4.noarch.rpm
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;インストール&lt;/h2&gt;
&lt;pre&gt;
といっても yum を実行するだけです。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install -y sernet-samba sernet-samba-ad  sernet-samba-client
&lt;/pre&gt;
&lt;pre&gt;
/var/lib/samba ディレクトリにデータファイルがインストールされていると思います。
smb.conf は /etc/samba ディレクトリに出力されるようです。
ただ、サービス名が sernet-samba-ad、sernet-samba-smbd、sernet-samba-nmbd、sernet-samba-winbindd
となりソースからインストールした時と異なるのがちょっと微妙かなぁ。
&lt;/pre&gt;
&lt;br /&gt;

&lt;h2&gt;PDC の構築&lt;/h2&gt;
&lt;pre&gt;
せっかくなので Primary Domain Controller も構築してみたいと思います。
&lt;/pre&gt;

&lt;u&gt;&lt;b&gt;環境&lt;/b&gt;&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.5(CentOS-6.5-x86_64-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.1.7&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;127.0.0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;

&lt;pre&gt;
Samba 以外の設定でハマりたくないので SELinux と iptables は止めておく。
&lt;/pre&gt;
&lt;u&gt;/etc/selinux/config&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
SELINUX=disabled
...
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service iptables stop
service ip6tables stop
chkconfig iptables off
chkconfig ip6tables off
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ネットワークの設定は次のような感じ。
&lt;/pre&gt;
&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.10
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;
&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=pdc
&lt;/pre&gt;
&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.10 pdc.mydomain.local pdc
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Samba のサービスのなかで PDC で使わないものを止めておきます。
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig sernet-samba-ad on
chkconfig sernet-samba-smbd off
chkconfig sernet-samba-nmbd off
chkconfig sernet-samba-winbindd off
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
このあたりで再起動してみる。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
セットアップに必要なライブラリは一通り yum でインストールされているのだけど
Bind と NTP がインストールされていないので追加します。
Kerbors のコマンド類もインストールされていないのでそれも追加します。
NTP も signed ntp が サポートされたものが yum で提供されているようなので
合わせてインストールします。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install -y bind ntp krb5-workstation
&lt;/pre&gt;
&lt;pre&gt;サービスの起動設定をしておく。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig named on
chkconfig ntpd on
&lt;/pre&gt;

&lt;br /&gt;

&lt;pre&gt;
では、ドメインコントローラーの設定します。
まず、Samba が Active Directory モードで起動するように sernet-samba ファイルを編集します。
&lt;/pre&gt;
&lt;u&gt;/etc/default/sernet-samba&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
SAMBA_START_MODE=&quot;ad&quot;
...
&lt;/pre&gt;

&lt;pre&gt;
smb.conf などドメインコントローラーに必要なファイル一式を出力します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba-tool domain provision \
  --use-rfc2307 \
  --function-level=2008_R2 \
  --use-ntvfs \
  --realm=MYDOMAIN.LOCAL \
  --domain=MYDOMAIN \
  --server-role=dc \
  --dns-backend=BIND9_DLZ \
  --adminpass &#39;passwd-123&#39;
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;--use-ntvfs&lt;/span&gt;
&lt;pre&gt;
--function-level=2008_R2 にした場合 --use-ntvfs オプションも必要になるみたいです。
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;出力された smb.conf をチェックする。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
testparm
&lt;/pre&gt;

&lt;pre&gt;smb.conf は以下のような感じ。&lt;/pre&gt;
&lt;u&gt;/etc/samba/smb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[global]
 workgroup = MYDOMAIN
 realm = MYDOMAIN.LOCAL
 netbios name = PDC
 server role = active directory domain controller
 server services = rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbind, ntp_signd, kcc, dnsupdate, smb
 dcerpc endpoint servers = epmapper, wkssvc, rpcecho, samr, netlogon, lsarpc, spoolss, drsuapi, dssetup, unixinfo, browser, eventlog6, backupkey, dnsserver, winreg, srvsvc
 idmap_ldb:use rfc2307 = yes
 
 winbind use default domain = yes
 template shell = /bin/bash

[netlogon]
 path = /var/lib/samba/sysvol/fummy.local/scripts
 read only = No

[sysvol]
 path = /var/lib/samba/sysvol
 read only = No
&lt;/pre&gt;
&lt;br /&gt;

&lt;p&gt;Kerberos の設定ファイルが出力されているので差し替える。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf.original
cp /var/lib/samba/private/krb5.conf /etc/krb5.conf
&lt;/pre&gt;

&lt;p&gt;krb5.conf は以下のような感じ。&lt;/p&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[libdefaults]
  default_realm = MYDOMAIN.LOCAL
  dns_lookup_realm = false
  dns_lookup_kdc = true
&lt;/pre&gt;
&lt;br /&gt;


&lt;pre&gt;
Bind の設定は次のような感じ。
ソースからインストールした時と変わらないのだけど
ファイル Path が違っているのでそのあたりを確認する。
&lt;/pre&gt;
&lt;u&gt;/etc/named.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
options {
  ...
  listen-on port 53 { any; };
  ...
  allow-query     { any; };
  ...
  tkey-gssapi-keytab &quot;/var/lib/samba/private/dns.keytab&quot;;
};
...
include &quot;/var/lib/samba/private/named.conf&quot;;
&lt;/pre&gt;

&lt;pre&gt;dns.keytab の権限の設定をする。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chgrp named /var/lib/samba/private/
chgrp named /var/lib/samba/private/named.conf
chmod g+r /var/lib/samba/private/dns.keytab
chown -R named:named /var/lib/samba/private/dns
&lt;/pre&gt;

&lt;pre&gt;/etc/rndc.key を生成する。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rndc-confgen -a -r /dev/urandom
&lt;/pre&gt;

&lt;pre&gt;resolv.conf の設定をする。&lt;/pre&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 127.0.0.1
&lt;/pre&gt;

&lt;p&gt;Samba4、Bindを再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service sernet-samba-ad start
service named restart
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ここで NTP の設定もしておこう。
ここもソースからインストールした時と変わらないのだけど
ntp_signd ディレクトリの Path が違っているのでそのあたりを確認する。
&lt;/pre&gt;
&lt;u&gt;/etc/ntp.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
server 127.127.1.0
fudge 127.127.1.0 stratum 10
server 0.pool.ntp.org iburst prefer
server 1.pool.ntp.org iburst prefer
driftfile /var/lib/ntp/ntp.drift
logfile /var/log/ntp
ntpsigndsocket /var/lib/samba/ntp_signd
restrict default kod nomodify notrap nopeer mssntp
restrict 127.0.0.1
restrict 0.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
restrict 1.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
&lt;/pre&gt;

&lt;pre&gt;
NTP との連動ために ntp_signd ディレクトリの権限を変更する。
時刻合せした後、NTP を起動する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chmod 750 /var/lib/samba/ntp_signd
ntpdate ntp.nict.jp
service ntpd start
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
DNS を更新する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba_dnsupdate --verbose --all-names
&lt;/pre&gt;

&lt;pre&gt;
更新した DNS の設定を確認する。
not found: 3(NXDOMAIN) とかが返ってこないことを確認する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
host -t SRV _ldap._tcp.mydomain.local.
host -t SRV _kerberos._udp.mydomain.local.
host -t A pdc.mydomain.local.
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
Kerberos のチケットを一旦クリアした後、初期化と確認をします。
administrator@MYDOMAIN.LOCAL のパスワードは今回の場合、passwd-123 になります。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kdestroy
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
とりあえず以下のコマンドで ログインできれば設定は OK だ。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
smbclient //localhost/netlogon -U administrator
&lt;/pre&gt;

&lt;pre&gt;
以下のようにして administrator のパスワードの期限を無効にしておく方が検証には便利かも？
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba-tool user setexpiry --noexpiry administrator
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ドメインユーザを追加してログインしてみる。
Windows からだとこの状態でもログインできるのだけれども 
CentOS からだと、もうすこし設定が必要だ。
Winbind を使ってドメインユーザがログインできるように PAM の設定する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
LANG=en_US.UTF-8 authconfig --enablewinbindauth --update
LANG=en_US.UTF-8 authconfig --enablemkhomedir --update
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;authconfig について&lt;/span&gt;
&lt;pre&gt;
authconfig コマンドで Winbind の PAM 設定をすると
「サービス winbind に関する情報の読み込み中にエラーが発生しました: 
そのようなファイルやディレクトリはありません
winbind: 認識されていないサービスです。
error reading information on service winbind: No such file or directory
winbind: unrecognized service」
というようなエラーメッセージが表示されると思います。
原因はきっと winbind のサービス名が sernet-samba-winbindd になっているからだと思います。
なので sernet-samba-winbindd のサービス名を winbind に変更して authconfig を実行してみると
今度は「sernet-samba ファイルの SAMBA_START_MODE の設定を ad から classic に変更してほしい」的な
警告が出ます。
エラーメッセージや警告は出るものの PAM 設定自体はできている。
また、Samba AD にした場合、Winbind そのものを使う訳でもない。
この辺りは PAM 設定でなく yum のパッケージング(winbind の起動スクリプト？)の問題だと思う。
エラーメッセージや警告が気になるからといって PAM を手動で設定するのはちょっと…
またいつの日にかこの問題が解消されることを期待しましょう。
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
nsswitch.conf を以下のように編集して
&lt;/pre&gt;
&lt;u&gt;/etc/nsswitch.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
... 
passwd:     compat winbind
shadow:     files
group:      compat winbind
...
&lt;/pre&gt;

&lt;pre&gt;
ドメインユーザを追加したら
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba-tool user add user01
&lt;/pre&gt;

&lt;pre&gt;
ドメインログイン!!!
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh user01@pdc
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;administrator でドメインログイン&lt;/span&gt;
&lt;pre&gt;
やっぱり administrator は ssh で 自身の PDC に CentOS からドメインログインできなかった。
普通に追加したドメインユーザであればログインはできるのに。
まぁこれはこんなもんなんだろう。
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;


&lt;h2&gt;Member Server の構築&lt;/h2&gt;
&lt;pre&gt;
Member Server も構築してみたいと思います。
先ほど構築した PDC にドメイン参加する形になります。
&lt;/pre&gt;

&lt;b&gt;環境&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.5(CentOS-6.5-x86_64-minimal.iso)&lt;br /&gt;VMware Player 6.0.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.1.7&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;mbr01&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.100&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;pre&gt;
PDC の時と同様に SELinux と iptables は一旦止めておいた方がハマらないと思う…
ネットワークの設定は次のような感じ。
&lt;/pre&gt;

&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.100
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;

&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=mbr01
&lt;/pre&gt;

&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.100 mbr01.mydomain.local mbr01
&lt;/pre&gt;

&lt;pre&gt;
DNS は PDC のものを利用しようと思うので DNS を PDC に設定する。
&lt;/pre&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 192.168.0.10
&lt;/pre&gt;

&lt;pre&gt;
ipv6 を無効化しておきます。
以下のような内容で disable-ipv6.conf を作成します。
&lt;/pre&gt;
&lt;u&gt;/etc/modprobe.d/disable-ipv6.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
options ipv6 disable=1
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ipv6 の無効化&lt;/span&gt;
&lt;pre&gt;
ドメイン参加時に
「print_kdc_line: can&#39;t resolve name for kdc with non-default port [::]. Error 
名前またはサービスが不明です」的なエラーメッセージは出るものの 
ipv6 を無効にしなくてもドメイン参加はできているようです。
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
Samba のサービスのなかで Member Server で使わないものを止めておきます。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig sernet-samba-ad off
chkconfig sernet-samba-smbd off
chkconfig sernet-samba-nmbd off
chkconfig sernet-samba-winbindd on
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;ここで一度、再起動する。&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
追加で必要なライブラリをインストールする。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install -y ntp krb5-workstation
&lt;/pre&gt;

&lt;pre&gt;
サービスの起動設定をしておく。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig ntpd on
&lt;/pre&gt;
&lt;pre&gt;
今回の Member Server では Bind を使わないのでインストールしている場合は OFF っておいてください。
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
まずは、Kerberos の設定から行います。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf.original
&lt;/pre&gt;

&lt;pre&gt;
以下のような内容で krb5.conf を作成します。
&lt;/pre&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[logging]
  default = FILE:/var/log/krb5libs.log
  kdc = FILE:/var/log/krb5kdc.log
  admin_server = FILE:/var/log/kadmind.log

[libdefaults]
  default_realm = MYDOMAIN.LOCAL
  dns_lookup_realm = true
  dns_lookup_kdc = true
  ticket_lifetime = 24h
  forwardable = yes

[appdefaults]
  pam = {
    debug = false
    ticket_lifetime = 36000
    renew_lifetime = 36000
    forwardable = true
    krb4_convert = false
  }
&lt;/pre&gt;

&lt;pre&gt;
Kerberos のチケットをクリアしてから初期化と確認をします。
administrator@MYDOMAIN.LOCAL のパスワードは今回の場合、passwd-123 になります。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kdestroy
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
では Member Server の設定をします。
きっと Winbind を使うことになりそうなので
Samba が Classic モードで起動するように sernet-samba ファイルを編集します。
&lt;/pre&gt;
&lt;u&gt;/etc/default/sernet-samba&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
SAMBA_START_MODE=&quot;classic&quot;
...
&lt;/pre&gt;

&lt;pre&gt;
smb.conf は以下のような感じ。
&lt;/pre&gt;
&lt;u&gt;/etc/samba/smb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[global]
  netbios name = MBR01
  workgroup = MYDOMAIN
  security = ads
  realm = MYDOMAIN.LOCAL
  encrypt passwords = yes

  idmap config *:backend = tdb
  idmap config *:range = 70001-80000
  idmap config MYDOMAIN:default = yes
  idmap config MYDOMAIN:backend = rid
  idmap config MYDOMAIN:range = 10000 - 19999

  winbind nss info = rfc2307
  winbind trusted domains only = no
  winbind use default domain = yes
  winbind enum users  = yes
  winbind enum groups = yes

  template shell = /bin/bash
&lt;/pre&gt;

&lt;pre&gt;
ドメインに参加します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
net ads join -U administrator
&lt;/pre&gt;
&lt;pre&gt;
DNS を確認しておきます。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
dig mbr01.mydomain.local
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;samba-tool について&lt;/span&gt;
&lt;pre&gt;
&lt;a href=&quot;https://wiki.samba.org/index.php/Setup_a_Samba_AD_Member_Server&quot;&gt;Setup_a_Samba_AD_Member_Server&lt;/a&gt; によると 
samba-tool コマンドを使った Member Server のドメイン参加は、まだ未対応のようで
現段階では、traditional な方法でのセットアップになるみたいだ。
&lt;/pre&gt;
&lt;pre&gt;
以下のように smb.conf など Member Server に必要なファイル一式を出力するとか
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba-tool domain provision \
  --realm=MYDOMAIN.LOCAL \
  --domain=MYDOMAIN \
  --server-role=member \
  --dns-backend=NONE \
  --adminpass &#39;passwd-123&#39;
&lt;/pre&gt;
&lt;pre&gt;
以下のようにしてドメイン参加もできないみたいだ。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
samba-tool domain join mydomain.local MEMBER \
  -U administrator \
  --password=passwd-123 \
  --realm=mydomain.local
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre&gt;
NTP の設定もしておこう。
&lt;/pre&gt;
&lt;u&gt;/etc/ntp.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
server 127.127.1.0
fudge  127.127.1.0 stratum 10

server pdc.mydomain.local iburst prefer

driftfile /var/lib/ntp/ntp.drift
logfile   /var/log/ntp

restrict default ignore
restrict 127.0.0.1
restrict pdc.mydomain.local mask 255.255.255.255 nomodify notrap nopeer noquery
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
設定が終わったら時刻合せをして Winbind、NTP を起動する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ntpdate ntp.nict.jp
service ntpd start
service sernet-samba-winbindd start
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
ドメインユーザを追加してログインできるか確かめてみる。
Winbind の PAM を設定することになるのだけど
この状態で authconfig を使うとサービス名が違うたら無いたらふぅたらいわれる。
別に設定はできるので問題ないと思うのだが
うるさいと感じる場合はサービス名を変更してしまう。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service sernet-samba-winbindd stop
cd /etc/rc.d/init.d
cp sernet-samba-winbindd winbind
chkconfig --del sernet-samba-winbindd
chkconfig --add winbind
chkconfig winbind on
chkconfig --list
service winbind start
&lt;/pre&gt;

&lt;pre&gt;
静になったところで Winbind の PAM の設定する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
LANG=en_US.UTF-8 authconfig --enablewinbindauth --update
LANG=en_US.UTF-8 authconfig --enablemkhomedir --update
&lt;/pre&gt;

&lt;pre&gt;
nsswitch.conf を以下のように編集したら
&lt;/pre&gt;
&lt;u&gt;/etc/nsswitch.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
... 
passwd:     compat winbind
shadow:     files
group:      compat winbind
...
&lt;/pre&gt;

&lt;pre&gt;
あとは PDC 側でドメインユーザを追加したら
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh root@pdc
samba-tool user add user02
&lt;/pre&gt;

&lt;pre&gt;
ログインして Member Server に戻ってこれればとりあえず OK だ。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh user02@mbr01
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
微妙なところ(サービス名など)もあったが yum を使うと格段にセットアップが楽になった。
検証用には十分楽しめると思う。
しかし実運用となると MS AD を使いたいというのが本音か？！
&lt;/pre&gt; 

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://folgaizer.wordpress.com/2013/12/12/samba4-on-centos-6-4/&quot;&gt;
   http://folgaizer.wordpress.com/2013/12/12/samba4-on-centos-6-4/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.reddrop.net/howto_samba4_ActiveDirectory_Bind_DLZ&quot;&gt;
   http://www.reddrop.net/howto_samba4_ActiveDirectory_Bind_DLZ
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Setup_a_Samba_AD_Member_Server&quot;&gt;
   https://wiki.samba.org/index.php/Setup_a_Samba_AD_Member_Server
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://web.chaperone.jp/w/index.php?samba4%2Fuser&quot;&gt;
   http://web.chaperone.jp/w/index.php?samba4%2Fuser
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6393544854844253738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6393544854844253738'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2014/05/samba4-active-directory-sernet-repo.html' title='Samba4 で Active Directory / SerNet Repo でセットアップ'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-5037363647902536823</id><published>2014-04-23T23:30:00.000+09:00</published><updated>2016-05-16T00:31:58.847+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Spring"/><title type='text'>Spring Security で OAuth 2.0 Provider する</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;

&lt;pre&gt;
ついに Windows 8 にした。
XP のサポートが終了したというのもあったのだけれども
Java 8 がインストールできなかったのが致命的だった。
.Net Framework 4.5 や PHP 5.5 このあたりが使えなくなったのは耐えていたのだけれど
この春、Java に見捨てられてもう我慢できなくなった。
Java 8 が使えたらきっと今も XP 使ってると思う。

で、OAuth
わたしにとって OAuth といえば Facebook なのですが
以前、Facebook のアプリを作る機会があり OAuth の認証処理に結構悩まされた。
リクエストが行ったり来たり、どのタイミングでが何が送られて来るのか？また何を送るのか？
と同時に向こう側の処理はどうなっているんだ！？
相手を知ればこちら側の処理がもっと明確に見えてくるのではと思ったりした。
そんな訳で今回は、Spring Security OAuth Provider を試してみようと思います。
&lt;/pre&gt;
&lt;pre&gt;
現段階では、あまり詳しいドキュメントがない。
とりあえずサンプルがあるのでそれを参考にしつつソースを読むことにした。
なのでは、きっと何々だろう的なわたしの想像を元に進めていくことになる。
方針としては、
　・アリものは最大限利用させて頂く
　・データのやり取りを観察したいので InMemory のものでなくデータベースを使う
　・リクエストのやり取りの中でデータが変わりそうにないものは InMemory でもよしとしよう
&lt;/pre&gt;
&lt;pre&gt;
サンプルで作るアプリをしては２つ
１つは、OAuth Provider として my-sns
想定としては、Facebook とか Twitter になる。
まぁいえばあちら側のサイトになる。
そしてもう１つは、OAuth Client として my-app
これは、自前のサイト、こちら側になる。

Windows 8 にして心機一転、ビルドツールも Maven から Gradle に乗り換えました。
ではでは！Getting Started!! 
&lt;/pre&gt;

&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows 8.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;データベース&lt;/th&gt;
    &lt;td&gt;MySQL 5.6.16&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;アプリケーションサーバー&lt;/th&gt;
    &lt;td&gt;Tomcat 7.0.53&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Java&lt;/th&gt;
    &lt;td&gt;JDK 1.8&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;3&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;Spring Framework 4.0.3.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Spring Security 3.2.3.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Spring Security OAuth 1.0.5.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Gradle 1.11&lt;br /&gt;※gradle コマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;p&gt;
Cドライブ直下に「work」フォルダを作成し各ファイルを以下のように配置します。&lt;br /&gt;
ファイルは、すべて UTF-8 で保存します。
&lt;/p&gt;
&lt;div id=&quot;studying-spring-security-oauth&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
   &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\work&lt;/span&gt;
     &lt;ul&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;studying-spring-security-oauth&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;my-app&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;webapp&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;WEB-INF&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;web.xml&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;index.jsp&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
       &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;my-sns&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
               &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
                 &lt;ul&gt;
                  &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;ApiController.java&lt;/span&gt;&lt;/li&gt;
                 &lt;/ul&gt;
                &lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;META-INF&lt;/span&gt;
               &lt;ul&gt;
                &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
                 &lt;ul&gt;
                  &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-oauth.xml&lt;/span&gt;&lt;/li&gt;
                 &lt;/ul&gt;
                &lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;log4j.properties&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;webapp&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;WEB-INF&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;studying-oauth-servlet.xml&lt;/span&gt;&lt;/li&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;web.xml&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
       &lt;/ul&gt;
        &lt;/li&gt;
     &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;build.gradle&lt;/span&gt;&lt;/li&gt;
      &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;settings.gradle&lt;/span&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-spring-security-oauth&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;データベースとテーブルの作成&lt;/h2&gt;
&lt;p&gt;
データベースユーザとして、パスワードなしのrootユーザが作成済みという前提で&lt;br /&gt;
何はともあれデータベースから
&lt;/p&gt;


&lt;pre class=&quot;fummy_screen&quot;&gt;
DROP DATABASE IF EXISTS studying_oauth;
CREATE DATABASE IF NOT EXISTS studying_oauth DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE studying_oauth;
&lt;/pre&gt;

&lt;p&gt;
では、メインの Access Token を保存するテーブルから作成します。&lt;br /&gt;
Schema に関しては、JdbcTokenStore(org.springframework.security.oauth2.provider.token パッケージ)を参考に逆算してみた。&lt;br /&gt;
恐らく token_id が PK になるだろう。&lt;br /&gt;
varchar は、とりあえず255文字あればいいでしょう。&lt;br /&gt;
user_name と refresh_token は使わない場合もあるので NULL OK にしてあとは強気に NOT NULL
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
  `token_id`          varchar(255) NOT NULL,
  `token`             blob         NOT NULL,
  `authentication_id` varchar(255) NOT NULL,
  `user_name`         varchar(255) NULL,
  `client_id`         varchar(255) NOT NULL,
  `authentication`    blob         NOT NULL,
  `refresh_token`     varchar(255) NULL,
  PRIMARY KEY (`token_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
&lt;/pre&gt;

&lt;p&gt;
次に Refresh Token を保存するテーブルを作成します。&lt;br /&gt;
同様に JdbcTokenStore から Schema を逆算してみた。&lt;br /&gt;
ここは token_id が PK で問題ないでしょう。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
  `token_id`       varchar(255) NOT NULL,
  `token`          blob         NOT NULL,
  `authentication` blob         NOT NULL,
  PRIMARY KEY (`token_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
&lt;/pre&gt;

&lt;p&gt;
最後に Authorization Code Grant（認可コードグラント）で使用する Request Token を保存するテーブルを作成します。&lt;br /&gt;
Schema は、JdbcAuthorizationCodeServices(org.springframework.security.oauth2.provider.code パッケージ)から逆算した。&lt;br /&gt;
code が PK で問題ないでしょう。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
  `code`           varchar(255) NOT NULL,
  `authentication` blob         NOT NULL,
  PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ServletとLoggerの設定&lt;/h2&gt;
&lt;pre&gt;
OAuth Provider あちら側 my-sns の設定から始めます。
&lt;/pre&gt;
&lt;u&gt;my-sns/src/main/webapp/WEB-INF/web.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;web-app id=&quot;WebApp_ID&quot; version=&quot;3.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
  xmlns:web=&quot;http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;&gt;
  
  &amp;lt;display-name&gt;studying-oauth&amp;lt;/display-name&gt;
  &amp;lt;context-param&gt;
    &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
    &amp;lt;param-value&gt;classpath*:META-INF/spring/applicationContext*.xml&amp;lt;/param-value&gt;
  &amp;lt;/context-param&gt;
    
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;

  &amp;lt;!-- Filter 設定 --&gt;
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;CorsFilter&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.apache.catalina.filters.CorsFilter&amp;lt;/filter-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;cors.allowed.origins&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;*&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;CorsFilter&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;
  
  &amp;lt;servlet&gt;
    &amp;lt;servlet-name&gt;studying-oauth&amp;lt;/servlet-name&gt;
    &amp;lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&gt;
    &amp;lt;load-on-startup&gt;1&amp;lt;/load-on-startup&gt;
  &amp;lt;/servlet&gt;
  &amp;lt;servlet-mapping&gt;
    &amp;lt;servlet-name&gt;studying-oauth&amp;lt;/servlet-name&gt;
    &amp;lt;url-pattern&gt;/&amp;lt;/url-pattern&gt;
  &amp;lt;/servlet-mapping&gt;

  &amp;lt;listener&gt;
    &amp;lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&gt;
  &amp;lt;/listener&gt;
  
  &amp;lt;session-config&gt;
    &amp;lt;cookie-config&gt;
      &amp;lt;http-only&gt;true&amp;lt;/http-only&gt;
    &amp;lt;/cookie-config&gt;
    &amp;lt;tracking-mode&gt;COOKIE&amp;lt;/tracking-mode&gt;
  &amp;lt;/session-config&gt;
&amp;lt;/web-app&gt;
&lt;/pre&gt;


&lt;ul style=&quot;list-style:none;&quot;&gt;
&lt;li&gt;
&lt;dl&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;Filter 設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   クロスドメインで javascript から json データのやりとりをできるようにするための Filter を追加しています。&lt;br /&gt;
   Response ヘッダーに 「Access-Control-Allow-Origin: *」を出力しています。 &lt;br /&gt;
   今回、Tomcat を my-sns と my-app で port を変えて２つ起動します。&lt;br /&gt;
   ドメインが同じでも Port が違うとクロスドメインと扱われるようです。&lt;br /&gt;
   Tomcat で用意されてるアリものの Filter を使わせてもらっています。
   &lt;/p&gt;
 &lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
どの処理でどのクラスが呼び出されるか知りたいので
とりあえず springframework.security.oauth パッケージ以下にあるクラスの
デバッグログを出力するようにしています。
&lt;/pre&gt;
&lt;u&gt;my-sns/src/main/resources/log4j.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
log4j.rootLogger=error, stdout
 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 
# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
 
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=application.log
 
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
 
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

log4j.logger.org.springframework.security.oauth2=debug, stdout
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring Security の設定&lt;/h2&gt;
&lt;pre&gt;
あちら側 my-sns の OAuth Provider としてのメインの設定をします。
この設定が今回のキモになります。
&lt;/pre&gt;

&lt;u&gt;my-sns/src/main/resources/META-INF/spring/applicationContext-oauth.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;beans:beans
  xmlns=&quot;http://www.springframework.org/schema/security&quot; 
  xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot; 
  xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot; 
  xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
  xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd
    http://www.springframework.org/schema/security/oauth2
    http://www.springframework.org/schema/security/spring-security-oauth2.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd&quot;&gt;

  &amp;lt;!-- URL 設定① --&gt;
  &amp;lt;http pattern=&quot;/members/**&quot; create-session=&quot;stateless&quot;
    entry-point-ref=&quot;myOauthAuthenticationEntryPoint&quot;
    authentication-manager-ref=&quot;myOAuth2AuthenticationManager&quot;
    access-decision-manager-ref=&quot;accessDecisionManager&quot;&gt;
    &amp;lt;anonymous enabled=&quot;false&quot; /&gt;
    &amp;lt;intercept-url pattern=&quot;/members/**&quot; access=&quot;SCOPE_TRUST&quot; /&gt;
    &amp;lt;custom-filter ref=&quot;myOauth2ProviderFilter&quot; before=&quot;PRE_AUTH_FILTER&quot; /&gt;
  &amp;lt;/http&gt;
  
  &amp;lt;!-- URL 設定② --&gt;
  &amp;lt;http pattern=&quot;/oauth/token*&quot; create-session=&quot;stateless&quot; 
    entry-point-ref=&quot;myOauthAuthenticationEntryPoint&quot;
    authentication-manager-ref=&quot;myClientAuthenticationManager&quot;&gt;
    &amp;lt;custom-filter ref=&quot;myClientCredentialsTokenEndpointFilter&quot; position=&quot;BASIC_AUTH_FILTER&quot;/&gt;
  &amp;lt;/http&gt;

  &amp;lt;!-- URL 設定③ --&gt;
  &amp;lt;http pattern=&quot;/**&quot;
    authentication-manager-ref=&quot;resourceOwner&quot; 
    disable-url-rewriting=&quot;true&quot;&gt;
    &amp;lt;intercept-url pattern=&quot;/oauth/authorize*&quot; access=&quot;ROLE_USER&quot; /&gt;
    &amp;lt;form-login /&gt;
  &amp;lt;/http&gt;


  &amp;lt;beans:bean
    id=&quot;myOauthAuthenticationEntryPoint&quot;
    class=&quot;org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint&quot; /&gt;

  &amp;lt;beans:bean
    id=&quot;myOAuth2AuthenticationManager&quot;
    class=&quot;org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager&quot;&gt;
    &amp;lt;beans:property name=&quot;tokenServices&quot; ref=&quot;myTokenServices&quot;/&gt;
  &amp;lt;/beans:bean&gt;
  
  &amp;lt;beans:bean
    id=&quot;myTokenServices&quot;
    class=&quot;org.springframework.security.oauth2.provider.token.DefaultTokenServices&quot;&gt;
    &amp;lt;beans:property name=&quot;tokenStore&quot; ref=&quot;myTokenStore&quot; /&gt;
    &amp;lt;beans:property name=&quot;clientDetailsService&quot; ref=&quot;myClientDetailsService&quot; /&gt;
    &amp;lt;beans:property name=&quot;supportRefreshToken&quot; value=&quot;true&quot;/&gt;
  &amp;lt;/beans:bean&gt;
  
  &amp;lt;!-- Access Token 設定 --&gt;
  &amp;lt;beans:bean
    id=&quot;myTokenStore&quot;
    class=&quot;org.springframework.security.oauth2.provider.token.JdbcTokenStore&quot; &gt;
    &amp;lt;beans:constructor-arg name=&quot;dataSource&quot; ref=&quot;myDataSource&quot;/&gt;
  &amp;lt;/beans:bean&gt;
  
  &amp;lt;!-- 連携アプリ設定 --&gt;
  &amp;lt;oauth:client-details-service
    id=&quot;myClientDetailsService&quot; &gt;
    &amp;lt;oauth:client
      client-id=&quot;my-auth&quot;
      secret=&quot;pass123&quot;
      scope=&quot;trust&quot; 
      authorized-grant-types=&quot;authorization_code,implicit,password,client_credentials,refresh_token&quot;
      authorities=&quot;ROLE_CLIENT&quot;
      resource-ids=&quot;myResource&quot; /&gt;
  &amp;lt;/oauth:client-details-service&gt;

  &amp;lt;!-- 権限設定 --&gt;
  &amp;lt;beans:bean
    id=&quot;accessDecisionManager&quot;
    class=&quot;org.springframework.security.access.vote.UnanimousBased&quot;&gt;
    &amp;lt;beans:constructor-arg&gt;
      &amp;lt;beans:list&gt;
        &amp;lt;beans:bean class=&quot;org.springframework.security.oauth2.provider.vote.ScopeVoter&quot; /&gt;
        &amp;lt;beans:bean class=&quot;org.springframework.security.access.vote.RoleVoter&quot; /&gt;
        &amp;lt;beans:bean class=&quot;org.springframework.security.access.vote.AuthenticatedVoter&quot; /&gt;
      &amp;lt;/beans:list&gt;
    &amp;lt;/beans:constructor-arg&gt;
  &amp;lt;/beans:bean&gt;

  &amp;lt;oauth:resource-server
    id=&quot;myOauth2ProviderFilter&quot; resource-id=&quot;myResource&quot; token-services-ref=&quot;myTokenServices&quot; /&gt;


  &amp;lt;authentication-manager id=&quot;myClientAuthenticationManager&quot;&gt;
    &amp;lt;authentication-provider user-service-ref=&quot;myClientDetailsUserService&quot; /&gt;
  &amp;lt;/authentication-manager&gt;

  &amp;lt;beans:bean
    id=&quot;myClientDetailsUserService&quot;
    class=&quot;org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService&quot;&gt;
    &amp;lt;beans:constructor-arg ref=&quot;myClientDetailsService&quot; /&gt;
  &amp;lt;/beans:bean&gt;
  
  &amp;lt;beans:bean
    id=&quot;myClientCredentialsTokenEndpointFilter&quot;
    class=&quot;org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter&quot;&gt;
    &amp;lt;beans:property name=&quot;authenticationManager&quot; ref=&quot;myClientAuthenticationManager&quot; /&gt;
    &amp;lt;beans:property name=&quot;authenticationEntryPoint&quot; ref=&quot;myOauthAuthenticationEntryPoint&quot; /&gt;
  &amp;lt;/beans:bean&gt;


  &amp;lt;!-- ユーザアカウント 設定 --&gt;
  &amp;lt;authentication-manager id=&quot;resourceOwner&quot;&gt;
    &amp;lt;authentication-provider&gt;
      &amp;lt;user-service id=&quot;userDetailsService&quot;&gt;
        &amp;lt;user name=&quot;user01&quot; password=&quot;pass01&quot; authorities=&quot;ROLE_USER&quot; /&gt;
        &amp;lt;user name=&quot;user02&quot; password=&quot;pass02&quot; authorities=&quot;ROLE_USER&quot; /&gt;
      &amp;lt;/user-service&gt;
    &amp;lt;/authentication-provider&gt;
  &amp;lt;/authentication-manager&gt;


  &amp;lt;!-- OAuth Provider 全体設定 --&gt;
  &amp;lt;oauth:authorization-server
    client-details-service-ref=&quot;myClientDetailsService&quot;
    token-services-ref=&quot;myTokenServices&quot;
    user-approval-handler-ref=&quot;myUserApprovalHandler&quot;&gt;
    &amp;lt;oauth:authorization-code authorization-code-services-ref=&quot;myAuthorizationCodeServices&quot;/&gt;
    &amp;lt;oauth:implicit /&gt;
    &amp;lt;oauth:refresh-token /&gt;
    &amp;lt;oauth:client-credentials /&gt;
    &amp;lt;oauth:password authentication-manager-ref=&quot;resourceOwner&quot;/&gt;
  &amp;lt;/oauth:authorization-server&gt;
  
  &amp;lt;beans:bean
    id=&quot;myUserApprovalHandler&quot;
    class=&quot;org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler&quot;&gt;
    &amp;lt;beans:property name=&quot;tokenServices&quot; ref=&quot;myTokenServices&quot;/&gt;
  &amp;lt;/beans:bean&gt;
   
  &amp;lt;!-- Request Token 設定 --&gt;
  &amp;lt;beans:bean
    id=&quot;myAuthorizationCodeServices&quot;
    class=&quot;org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices&quot; &gt;
    &amp;lt;beans:constructor-arg name=&quot;dataSource&quot; ref=&quot;myDataSource&quot;/&gt;
  &amp;lt;/beans:bean&gt;


  &amp;lt;beans:bean
    id=&quot;oauthAccessDeniedHandler&quot;
    class=&quot;org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler&quot; /&gt;

  &amp;lt;beans:bean
    id=&quot;myDataSource&quot;
    class=&quot;org.apache.commons.dbcp.BasicDataSource&quot; destroy-method=&quot;close&quot;&gt;
    &amp;lt;beans:property name=&quot;driverClassName&quot; value=&quot;com.mysql.jdbc.Driver&quot;/&gt;
    &amp;lt;beans:property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/studying_oauth&quot;/&gt;
    &amp;lt;beans:property name=&quot;username&quot; value=&quot;root&quot;/&gt;
    &amp;lt;beans:property name=&quot;password&quot; value=&quot;&quot;/&gt;
  &amp;lt;/beans:bean&gt;

  &amp;lt;context:component-scan base-package=&quot;com.mydomain&quot; /&gt;
  &amp;lt;mvc:annotation-driven /&gt;

&amp;lt;/beans:beans&gt;
&lt;/pre&gt;

&lt;ul style=&quot;list-style:none;&quot;&gt;
&lt;li&gt;
&lt;dl&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;URL 設定①&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   Access Token が必要な URL&lt;br /&gt;
   ここはセッション管理しないので 強気に stateless とした。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;URL 設定②&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   Access Token を発行する URL&lt;br /&gt;
   この段階でどのユーザアカウントが Access Token を求めてきているか&lt;br /&gt;
   なにかしらの方法で分かるはずなのでここもセッションは stateless とした。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;URL 設定③&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   あちら側 my-sns のログイン画面、アプリ承認画面を表示するURL&lt;br /&gt;
   Authorization Code Grant であれば Request Token を&lt;br /&gt;
   Implicit Grant であれば Access Token を発行する URL になる。&lt;br /&gt;
   ここはログイン画面などを使ってどのユーザアカウントであるか&lt;br /&gt;
   判別する必要があるだろうからセッション管理は通常どうりとした。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;Access Token 設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;ここのデータのやりとりを観察したいのでデータベース用のもを使った。&lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;連携アプリ設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   こちら側 my-app があちら側 my-sns の認証が必要な URL に&lt;br /&gt;
   アクセスするためのサイトのアカウント登録になる。&lt;br /&gt;
   Facebook、Twitter でいうアプリの登録になると思う。&lt;br /&gt;
   client-id、secret が アカウントIDとパスワードになる。&lt;br /&gt;
   ここは InMemory のものを使った。&lt;br /&gt;
   データベース用のものも用意されているので&lt;br /&gt;
   JdbcClientDetailsService(org.springframework.security.oauth2.provider パッケージ) &lt;br /&gt;
   あたりを解析すれば テーブル Schema が割り出せると思う。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;権限設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   ScopeVoter によって Access Token の scope が Spring Security の Role に変換される。&lt;br /&gt;
   Access Token 取得時に Request パラメータで scope=trust というように指定された場合&lt;br /&gt;
   prefix 「SCOPE_」が付けられ 「SCOPE_TRUST」というような Role になる。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;ユーザアカウント設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;
   あちら側 my-sns のユーザアカウントになる。&lt;br /&gt;
   ユーザアカウントは２つ、IDとパスワードはそれぞれ「user01/pass01」「user02/pass02」を登録した。&lt;br /&gt;
   ここも InMemory のものを使った。&lt;br /&gt;
   ここは Spring Security 本体でデータベース用のもが用意されている。
   &lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;OAuth Provider 全体設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;OAuth の４つの Grant と Refresh Token が試せるように設定した。&lt;/p&gt;
 &lt;/dd&gt;
 &lt;dt&gt;&lt;u&gt;&lt;b&gt;Request Token 設定&lt;/b&gt;&lt;/u&gt;&lt;/dt&gt;
 &lt;dd&gt;
   &lt;p&gt;このあたりもデータのやりとりを観察したいのでデータベース用のもを使った。&lt;/p&gt;
 &lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
内容は空なのですが「アプリケーションコンテキスト名-servlet.xml」ファイルがないと
うまく動かないようなので一応作成しておきます。
&lt;/pre&gt;
&lt;u&gt;my-sns/src/main/webapp/WEB-INF/studying-oauth-servlet.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;beans:beans
  xmlns=&quot;http://www.springframework.org/schema/security&quot; 
  xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot; 
  xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot; 
  xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd
    http://www.springframework.org/schema/security/oauth2
    http://www.springframework.org/schema/security/spring-security-oauth2.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd&quot;&gt;
&amp;lt;/beans:beans&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;Controller クラスの作成&lt;/h2&gt;
&lt;pre&gt;
「/my-sns/members」でログインユーザ情報を json 形式で返すようにします。
&lt;/pre&gt;
&lt;u&gt;my-sns/src/main/java/com/mydomain/ApiController.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain.web;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class ApiController {

  @RequestMapping(&quot;/members&quot;)
  @ResponseBody
  public UserDetails members() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return (UserDetails)auth.getPrincipal();
  }
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;my-app の設定&lt;/h2&gt;
&lt;pre&gt;
こちら側 my-app の設定をします。
以下のような内容でOAuthのテストページを作成します。
&lt;/pre&gt;
&lt;u&gt;my-app/src/main/webapp/index.jspx&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; %&gt;
&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;title&gt;OAuthのテスト&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;h2&gt;OAuthのテスト&amp;lt;/h2&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
いらないかもしれないけど以下のような内容で web.xml も作成しておきます。
&lt;/pre&gt;
&lt;u&gt;my-app/src/main/webapp/WEB-INF/web.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;web-app id=&quot;WebApp_ID&quot; version=&quot;3.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
  xmlns:web=&quot;http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;&gt;

  &amp;lt;session-config&gt;
    &amp;lt;cookie-config&gt;
      &amp;lt;http-only&gt;true&amp;lt;/http-only&gt;
    &amp;lt;/cookie-config&gt;
    &amp;lt;tracking-mode&gt;COOKIE&amp;lt;/tracking-mode&gt;
  &amp;lt;/session-config&gt;
&amp;lt;/web-app&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドスクリプトの作成&lt;/h2&gt;
&lt;pre&gt;
まずは Top プロジェクトのビルドスクリプトを以下の内容で作成します。
&lt;/pre&gt;

&lt;u&gt;settings.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
include &#39;my-app&#39;, &#39;my-sns&#39;
&lt;/pre&gt;

&lt;u&gt;build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
ext { 
  springVersion = &#39;4.0.3.RELEASE&#39;
  springSecurityVersion = &#39;3.2.3.RELEASE&#39;
  springSecurityOAuth2Version = &#39;1.0.5.RELEASE&#39;
  tomcatVersion = &#39;7.0.53&#39;
}

allprojects {
  repositories {
    mavenCentral()
  }
}

subprojects {
  apply plugin: &#39;war&#39;
  apply plugin: &#39;tomcat&#39;
  
  dependencies {
    compile &quot;org.springframework:spring-context:${springVersion}&quot;
    compile &quot;org.springframework:spring-webmvc:${springVersion}&quot;
    compile &quot;org.springframework:spring-jdbc:${springVersion}&quot;    
    
    compile &quot;org.springframework.security:spring-security-web:${springSecurityVersion}&quot;
    compile &quot;org.springframework.security:spring-security-config:${springSecurityVersion}&quot;
    compile &quot;org.springframework.security:spring-security-taglibs:${springSecurityVersion}&quot;   
    
    compile &quot;org.springframework.security.oauth:spring-security-oauth2:${springSecurityOAuth2Version}&quot;
    
    compile &#39;commons-dbcp:commons-dbcp:1.4&#39;
    compile &#39;mysql:mysql-connector-java:5.1.30&#39;
    compile &#39;log4j:log4j:1.2.17&#39;
    
    tomcat &quot;org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}&quot;,
           &quot;org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}&quot;
    tomcat(&quot;org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}&quot;) {
           exclude group: &#39;org.eclipse.jdt.core.compiler&#39;, module: &#39;ecj&#39;
    }
  }
  
  sourceCompatibility = &#39;1.8&#39;
  targetCompatibility = &#39;1.8&#39;
  [compileJava, compileTestJava]*.options*.encoding = &#39;UTF-8&#39;
}

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath &#39;org.gradle.api.plugins:gradle-tomcat-plugin:1.2.3&#39;
  }
}
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次は my-sns プロジェクトのビルドスクリプトを以下の内容で作成します。
&lt;/pre&gt;
&lt;u&gt;my-sns/build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
war {
  baseName = &#39;my-sns&#39;
}
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
最後は my-app プロジェクトのビルドスクリプトを以下の内容で作成します。
Tomcat を２つ起動することになるので my-sns とは違う port を指定しています。
&lt;/pre&gt;
&lt;u&gt;my-app/build.gradle&lt;/u&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
war {
  baseName = &#39;my-app&#39;
}

tomcat {
  httpPort  = 15080
  httpsPort = 15443
  ajpPort   = 15009
  stopPort  = 15081
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;OAuth 認証を試す準備&lt;/h2&gt;
&lt;pre&gt;
とりあえずデータベースのデータを削除します。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
DELETE FROM oauth_access_token;
DELETE FROM oauth_code;
DELETE FROM oauth_refresh_token;
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
そしてそれぞれの Tomcat を起動します。
まずは my-sns から
コマンドプロンプトを起動して以下を入力する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\work\studying-spring-security-oauth
gradle :my-sns:clean :my-sns:tomcatRun
&lt;/pre&gt;
&lt;br /&gt;

&lt;pre&gt;
次は my-app 
もう一枚コマンドプロンプトを起動して以下を入力する。
&lt;/pre&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /d C:\work\studying-spring-security-oauth
gradle :my-app:clean :my-app:tomcatRun
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;Authorization Code Grant を試す&lt;/h2&gt;
&lt;pre&gt;
手始めに一番一般的な方法、認可コードグラントで Access Token 取得をしてみます。

OAuthのテストに用意した my-app のページ「http://localhost:15080/my-app/」を開きます。
認可コードグラントの場合、Access Token の取得に Request Token が必要になってくるので
Request Token から取得してみます。
以下のURLをアドレスバーに入力して my-sns にアクセスします。
&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/authorize?client_id=my-auth&amp;scope=trust&amp;response_type=code&amp;redirect_uri=http://localhost:15080/my-app/
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;Request パラメータ にはアプリのID(client_id)は必要なのですがこの段階ではまだアプリのパスワード(client_secret)は不要です。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
上記のURLにアクセスするとログイン画面が表示されると思います。
my-sns のユーザアカウント「user01/pass01」または「user02/pass02」でログインします。

ログインするとアプリの承認画面が表示されると思います。
「Authorize」ボタンを押してアプリを承認します。

今度は my-sns から my-app にリダイレクトされます。
この時 URL に ?code=&lt;b&gt;XXXXX&lt;/b&gt; のような形で Request Token が送られてきているかと思います。
ここで oauth_code テーブルを見てみると送られてきた Request Token が登録されていると思います。

次に取得した Request Token から Access Token 取得をしてみます。
以下のURLをアドレスバーに入力して my-sns にアクセスします。
&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/token?client_id=my-auth&amp;client_secret=pass123&amp;grant_type=authorization_code&amp;redirect_uri=http://localhost:15080/my-app/&amp;code=&lt;b&gt;XXXXX&lt;/b&gt;
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;この段階で Request パラメータ にアプリのID(client_id)と共にアプリのパスワード(client_secret)が必要になります。&lt;/li&gt;
  &lt;li&gt;最後の部分 code=&lt;b&gt;XXXXX&lt;/b&gt; で取得した Request Token を指定します。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
レスポンスデータとして Access Token が json 形式で画面に表示されると思います。
表示された access_token と refresh_token をメモしておきます。
ここで oauth_access_token テーブルと oauth_refresh_token テーブルを見てみると
Access Token と Refresh Token が登録されていると思います。
また一回使った Request Token でもう一度 Access Token 取得しようとすると
&quot;error&quot;:&quot;invalid_grant&quot; とかになり Access Token が取得できないようになっていると思います。
また oauth_code テーブルを見てみると先ほどの Request Token 削除されていると思います。

これで Access Token を使って my-sns のユーザ認証が必要な情報にアクセスができるように
なっていると思います。
以下のURLをアドレスバーに入力して my-sns にアクセスしてログインユーザ情報が取得してみます。
&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/members/?access_token=&lt;b&gt;YYYYY&lt;/b&gt;
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;?access_token=&lt;b&gt;YYYYY&lt;/b&gt; で取得した Request Token を指定します。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
レスポンスデータとして ログインユーザ情報 が json 形式で画面に表示されると思います。
また、Request Token なしでアクセスしてみると 
An Authentication object was not found in the SecurityContext 的なデータが表示されると思います。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Refresh Token を試す&lt;/h2&gt;
&lt;pre&gt;
リフレッシュトークンも試してみたいと思います。
先ほどと同様に認可コードグラントを使います。

Access Token の有効期間は初期値で12時間になっている。
ここではそんなに待てないので1分にしてみる。
「applicationContext-oauth.xml」の myTokenServices に以下を追加する。
&lt;/pre&gt;

&lt;u&gt;my-sns/src/main/resources/META-INF/spring/applicationContext-oauth.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
  ...
  &amp;lt;beans:bean
    id=&quot;myTokenServices&quot;
    class=&quot;org.springframework.security.oauth2.provider.token.DefaultTokenServices&quot;&gt;
    ...
    &amp;lt;beans:property name=&quot;accessTokenValiditySeconds&quot; value=&quot;60&quot;/&gt;
  &amp;lt;/beans:bean&gt;
  ...
&lt;/pre&gt;
&lt;pre&gt;
OAuth 認証を試す準備で行ったデータベースの削除と my-sns の Tomcat を再起動してから
認可コードグラントの手順で Access Token と Refresh Token を取得します。
一応取得した Access Token で my-sns からログインユーザ情報が取得してみます。
それで待つこと1分以下のURLでログインユーザ情報が取得してみます。
&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/members/?access_token=&lt;b&gt;YYYYY&lt;/b&gt;
&lt;/pre&gt;

&lt;pre&gt;
Access token expired: 的なデータが表示されると思います。
以下のURLをアドレスバーに入力して my-sns から Access Token を再取得してみます。
&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/token?client_id=my-auth&amp;client_secret=pass123&amp;scope=trust&amp;grant_type=refresh_token&amp;refresh_token=&lt;b&gt;ZZZZZ&lt;/b&gt;
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;Request パラメータ にアプリのID(client_id)とアプリのパスワード(client_secret)が必要になります。&lt;/li&gt;
  &lt;li&gt;?refresh_token=&lt;b&gt;ZZZZZ&lt;/b&gt; で取得した Refresh Token を指定します。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
レスポンスデータとして Access Token が json 形式で画面に表示されると思います。
ここは手際よく再取得した Access Token で my-sns からログインユーザ情報を取得してみてください。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Implicit Grant を試す&lt;/h2&gt;
&lt;pre&gt;
インプリシットグラントで Access Token 取得をしてみます。
テストデータをリセットしたいのでOAuth 認証を試す準備で行った
データベースの削除と my-sns の Tomcat を再起動を行います。

OAuthのテストに用意した my-app のページ「http://localhost:15080/my-app/」を開きます。
以下のURLをアドレスバーに入力して my-sns にアクセスします。&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/authorize?client_id=my-auth&amp;scope=trust&amp;response_type=token&amp;redirect_uri=http://localhost:15080/my-app/
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;Request パラメータ にアプリのID(client_id)は必要なのですがアプリのパスワード(client_secret)は不要なようです。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
認可コードグラントの時と同様に my-sns のユーザアカウントのログイン、アプリの承認と進むのですが
インプリシットグラントの場合、Access Token の取得に Request Token がいらないので
直接 Access Token が指定した call back に指定した URL に送られてきます。

ここで注意が必要なのですが、&lt;b&gt;?&lt;/b&gt;access_token=YYYYY の形ではなく &lt;b&gt;#&lt;/b&gt;access_token=YYYYY で送られてくるので
通常の Request パラメータの取り出し方法では Access Token が取り出せない。
URL のハッシュ「#」以降を自力で解析して Access Token を取り出さないといけないようです。

また Access Token の取得にアプリのパスワードが不要なところも微妙だと思う。
さらに認可コードグラントのように Access Token をレスポンスデータでなく
call back URL の Request パラメータ(厳密には Request パラメータではない？)から
取得するところもどうかと思う。
call back URL といえど他の URL と変わらないので
悪意？のあるサイトが #access_token=&lt;b&gt;YYYYY&lt;/b&gt; 的な Request パラメータで
関係ない(なりすまし用？) Access Token 直接送り付けてくることも考えられる。
とりあえず、こちら側のサイト my-app 側も送られてくる Request パラメータを無条件に信用するのでなく
何かしらのチェックは必要になるでしょう。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Resource Owner Password Credentials Grant を試す&lt;/h2&gt;
&lt;pre&gt;
リソースオーナーパスワードクレデンシャルグラントで Access Token 取得をしてみます。
まず試す前にテストデータのリセットを行います。
OAuth 認証を試す準備で行ったデータベースの削除と my-sns の Tomcat を再起動をします。

OAuthのテストに用意した my-app のページ「http://localhost:15080/my-app/」から
以下のURLをアドレスバーに入力して my-sns にアクセスします。&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/token?client_id=my-auth&amp;client_secret=pass123&amp;scope=trust&amp;grant_type=password&amp;username=user01&amp;password=pass01
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;Request パラメータ username、password で my-sns のユーザアカウントのログインID、パスワードを指定します。&lt;/li&gt;
  &lt;li&gt;またアプリのIDとパスワードも必要になるようです。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
ユーザアカウントのログイン画面が無ければアプリ承認画面もなく
レスポンスデータとして Access Token が json 形式で画面に表示されると思います。
まぁ username、password を Request パラメータで送っていくるのだから
あちら側 my-sns としてはログイン画面は必要ないのでしょう。

ここで微妙な仕様に気づくと思います。
あちら側 my-sns のユーザアカウントのログインID、パスワードを 
こちら側 my-app でどうやって知りえるのか？
たぶん、こちら側 my-app でログイン画面を用意して
あちら側 my-sns の ログインID、パスワード入力してもらい
こちら側 my-app で送られてきたログインID、パスワードを Request パラメータに付けて 
Access Token 取得 URL を呼び出すことになるのだろう。
本来 OAuth こちら側 my-app があちら側 my-sns のユーザアカウントのログインID、パスワードを知ることなく
あちら側 my-sns のユーザ認証の必要なデータにアクセスするということだと思う。
微妙な仕様だ。
アプリ承認画面に関してもきっとこちら側 my-app で用意する仕様なのだろう。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Client Credentials Grant を試す&lt;/h2&gt;
&lt;pre&gt;
クライアントクレデンシャルグラントで Access Token 取得をしてみます。
例によってテストデータのリセットのためにデータベースの削除と my-sns の Tomcat を再起動を行います。

OAuthのテストに用意した my-app のページ「http://localhost:15080/my-app/」から
以下のURLをアドレスバーに入力して my-sns にアクセスします。&lt;/pre&gt;
&lt;pre class=&quot;brush: shell gutter: false;&quot;&gt;
http://localhost:8080/my-sns/oauth/token?client_id=my-auth&amp;client_secret=pass123&amp;scope=trust&amp;grant_type=client_credentials
&lt;/pre&gt;
&lt;ul&gt;
  &lt;li&gt;Request パラメータ には、アプリのIDとパスワードが必要になるようです。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;

&lt;pre&gt;
先ほどのリソースオーナーパスワードクレデンシャルグラントと同様に
ユーザアカウントのログイン画面が無ければアプリ承認画面もなく
レスポンスデータとして Access Token が json 形式で画面に表示されると思います。

クライアントクレデンシャルグラントの場合、特定のユーザアカウントとという訳でなく
アプリのIDとパスワードに対して Access Token 発行されるような感じになるようです。
なのでこの場合 Access Token でログインユーザ情報が取得できないようです。
まぁこれも微妙といえば微妙な仕様だなぁ。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;pre&gt;
触ってみて、なるほどと思うところとやっぱりそうなっていたのかと思うところがあった。
あたり前だけど発行した Token をユーザと紐づけてデータベースで管理する。
OAuth Provider の実装は、他にもいろいろある(java 以外を含めて)とあると思うけど
このあたりはどれも変わらないのだろう。

今回は、Access Token を Request パラメータで渡していたけど
Restful な API を目指すなら Basic認証、Digest認証と同じように
Request ヘッダーでやりとりするのが正しいようだ。
この辺は、Client 側の対応になりそうだ。
それは、また次回に。
次の Version の開発も着々と進んでそうなのでその時にでも。
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.atmarkit.co.jp/ait/articles/1209/10/news105.html&quot;&gt;
    http://www.atmarkit.co.jp/ait/articles/1209/10/news105.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://penq.co.jp/blog/engineer/amazon-web-service/3113&quot;&gt;
    http://penq.co.jp/blog/engineer/amazon-web-service/3113
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5037363647902536823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5037363647902536823'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2014/04/spring-security-oauth20-provider.html' title='Spring Security で OAuth 2.0 Provider する'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-6453970106363085836</id><published>2013-05-28T22:00:00.000+09:00</published><updated>2016-05-16T00:29:39.668+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AspectJ"/><title type='text'>AspectJ の Pointcut - this と target の違いって？</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;

&lt;p&gt;
this と target についてそれぞれ thisは、実行中のオブジェクト、target は、対象オブジェクトであれば&lt;br /&gt;
A というクラスが B というクラスを呼出している場合、this は A クラス、target は B クラスになってほしいのですが&lt;br /&gt;
Spring の AspectJ では this と target が同じオブジェクトになってしまう。&lt;br /&gt;
調べてみると call では、上記のような動きになり、execution では同じオブジェクトになるようだ。&lt;br /&gt;
Spring の AspectJ は、execution のみで call に対応していないらしい。&lt;br /&gt;
そんな訳で本家の AspectJ を使って call と execution での this と target の違いを調べてみたいと思います。
&lt;/p&gt;
&lt;p&gt;
今回は、簡単な実験クラスをいくつか作成して検証してみたいと思います。&lt;br /&gt;
ではでは！Getting Started!!
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;環境&lt;/h2&gt;

&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;Java&lt;/th&gt;
    &lt;td&gt;JDK 1.7.0_21&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;aspectjrt 1.7.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Maven 3.0.5&lt;br /&gt;※mvnコマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows XP&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;p&gt;
Cドライブ直下に「studying-aspectj-pointcut」フォルダを作成し各ファイルを以下のように配置します。&lt;br /&gt;
ファイルは、すべて UTF-8 で保存します。
&lt;/p&gt;
&lt;div id=&quot;studying-aspectj-pointcut&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-aspectj-pointcut&lt;/span&gt;
   &lt;ul&gt;
    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
     &lt;ul&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;App.java&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;Casino.java&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;Gambler.java&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;Money.java&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyAspect.java&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
       &lt;/ul&gt;
      &lt;/li&gt;
     &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;pom.xml&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-aspectj-pointcut&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;POM ファイルの作成&lt;/h2&gt;
&lt;p&gt;
AspectJ のコンパイルに aspectj-maven-plugin を使用しています。&lt;br /&gt;
また jar の生成には、maven-shade-plugin を使用しています。&lt;br /&gt;
このプラグインは、依存する jar を一旦バラして 今回作成したクラスと一緒に１つの jar ファイルにまとめてくれます。&lt;br /&gt;
POM ファイルを以下の内容で作成します。
&lt;/p&gt;

&lt;u&gt;pom.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;project
  xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
    
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;

  &amp;lt;groupId&gt;com.mydomain&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;studying-aspectj&amp;lt;/artifactId&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;packaging&gt;jar&amp;lt;/packaging&gt;

  &amp;lt;name&gt;studying-aspectj&amp;lt;/name&gt;

  &amp;lt;properties&gt;
    &amp;lt;java.version&gt;1.7&amp;lt;/java.version&gt;
    &amp;lt;project.build.sourceEncoding&gt;UTF-8&amp;lt;/project.build.sourceEncoding&gt;
  &amp;lt;/properties&gt;

  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.aspectj&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;aspectjrt&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.7.2&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;

  &amp;lt;build&gt;
    &amp;lt;finalName&gt;studying-aspectj&amp;lt;/finalName&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;3.1&amp;lt;/version&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;${java.version}&amp;lt;/source&gt;
          &amp;lt;target&gt;${java.version}&amp;lt;/target&gt;
          &amp;lt;encoding&gt;${project.build.sourceEncoding}&amp;lt;/encoding&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.codehaus.mojo&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;aspectj-maven-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;1.4&amp;lt;/version&gt;
        &amp;lt;executions&gt;
          &amp;lt;execution&gt;
            &amp;lt;goals&gt;
              &amp;lt;goal&gt;compile&amp;lt;/goal&gt;
              &amp;lt;goal&gt;test-compile&amp;lt;/goal&gt;
            &amp;lt;/goals&gt;
            &amp;lt;configuration&gt;
              &amp;lt;complianceLevel&gt;1.6&amp;lt;/complianceLevel&gt;
            &amp;lt;/configuration&gt;
          &amp;lt;/execution&gt;
        &amp;lt;/executions&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-shade-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.0&amp;lt;/version&gt;
        &amp;lt;executions&gt;
          &amp;lt;execution&gt;
            &amp;lt;phase&gt;package&amp;lt;/phase&gt;
            &amp;lt;goals&gt;
              &amp;lt;goal&gt;shade&amp;lt;/goal&gt;
            &amp;lt;/goals&gt;
            &amp;lt;configuration&gt;
              &amp;lt;transformers&gt;
                &amp;lt;transformer implementation=&quot;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer&quot;&gt;
                  &amp;lt;mainClass&gt;com.mydomain.App&amp;lt;/mainClass&gt;
                &amp;lt;/transformer&gt;
              &amp;lt;/transformers&gt;
            &amp;lt;/configuration&gt;
          &amp;lt;/execution&gt;
        &amp;lt;/executions&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
&amp;lt;/project&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Aspect 実験クラス作成&lt;/h2&gt;
&lt;p&gt;
概要としては、次のような感じです。&lt;br /&gt;
App クラスのメインメソッドで Money クラスを作成して Gambler クラスの paySalary() メソッドに渡します。&lt;br /&gt;
Gambler クラスは、Casino クラスの gambling() メソッドに 受け取った Money クラスを渡します。&lt;br /&gt;
そして、持ち金の 20% が無くなります。&lt;br /&gt;
簡単に言うと貰った給料で即パチンコ打ってまぁまぁ負けるというような感じです。(笑
&lt;/p&gt;

&lt;u&gt;App.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

public class App {

  public static void main(String[] args) {

    Money money = new Money();
    money.setValue(100);

    Gambler gambler = new Gambler();
    gambler.paySalary(money);
  }
}
&lt;/pre&gt;

&lt;u&gt;Money.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

public class Money {

  private int value;

  public int getValue() {
    return value;
  }

  public void setValue(int value) {
    this.value = value;
  }
}
&lt;/pre&gt;

&lt;u&gt;Gambler.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

public class Gambler {

  public void paySalary(Money money) {

    System.out.printf(&quot;Gambler paySalary %s %n&quot;, money.getValue());

      Casino casino = new Casino();
      casino.gambling(money);
  }
}
&lt;/pre&gt;

&lt;u&gt;Casino.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

public class Casino {

  public void gambling(Money money) {

    money.setValue((int)(money.getValue() * 0.8));
    System.out.printf(&quot;Casino gambling %s %n&quot;, money.getValue());
  }
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Aspect クラスを作成して Pointcut で遊ぶ&lt;/h2&gt;
&lt;p&gt;
Casino クラスの gambling() メソッドに着目して this と target を調べてみたいと思います。&lt;br /&gt;
this と target が call と execution ではどう違うかを Around アドバイスを使って見てみます。&lt;br /&gt;
MyAspect クラスを以下の内容で作成します。
&lt;/p&gt;


&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspect {

  @Around(&quot;call(void com.mydomain.Casino.gambling(Money))&quot;)
  public Object aroundCall(ProceedingJoinPoint pjp) throws Throwable {
    
    System.out.printf(&quot;AroundCall [this:%s] [target:%s] %n&quot;, pjp.getThis(), pjp.getTarget());
    
    Object retVal = pjp.proceed();
    return retVal;
  }

  @Around(&quot;execution(void com.mydomain.Casino.gambling(Money))&quot;)
  public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
    
    System.out.printf(&quot;AroundExec [this:%s] [target:%s] %n&quot;, pjp.getThis(), pjp.getTarget());
    
    Object retVal = pjp.proceed();
    return retVal;
  }
}
&lt;/pre&gt;

&lt;p&gt;
コマンドプロンプトを開きCドライブ直下の「studying-aspectj-pointcut」フォルダに移動後、 mvn コマンドでビルドします。&lt;br /&gt;
そして生成された jar ファイルを実行します。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd c:\studying-aspectj-pointcut
mvn clean package
java -jar C:\studying-aspectj-pointcut\target\studying-aspectj.jar
&lt;/pre&gt;

&lt;p&gt;
実行結果は以下のようになると思います。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
Gambler paySalary 100
AroundCall [this:com.mydomain.Gambler@1c7e2da] [target:com.mydomain.Casino@1fe571f]
AroundExec [this:com.mydomain.Casino@1fe571f] [target:com.mydomain.Casino@1fe571f]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
なるほど call では、this が呼出元クラス、target は、呼出先クラスになり execution では、this、target 両方対象クラスになる。&lt;br /&gt;
納得だ！&lt;br /&gt;
今度は、Before アドバイスでも試してみる。&lt;br /&gt;
MyAspect クラスのメソッドを以下の内容に差し替えます。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Before(&quot;call(void com.mydomain.Casino.gambling(Money)) &amp;&amp; this(Gambler) &amp;&amp; target(Casino)&quot;)
  public void beforeCall(){ 
    System.out.printf(&quot;Before Call %n&quot;);
  }

  @Before(&quot;execution(void com.mydomain.Casino.gambling(Money)) &amp;&amp; this(Casino) &amp;&amp; target(Casino)&quot;)
  public void beforeExecution(){ 
    System.out.printf(&quot;Before Execution %n&quot;);
  }
...
&lt;/pre&gt;

&lt;p&gt;
再ビルド後、実行すると以下のようになると思います。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
Gambler paySalary 100
Before Call
Before Execution
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
Aspect は呼出されているようだけど、なんだかよく分からないので this と target をキャプチャして print してみる。&lt;br /&gt;
MyAspect クラスのメソッドを以下の内容に差し替えて再ビルド後、実行する。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Before(&quot;call(void com.mydomain.Casino.gambling(Money)) &amp;&amp; this(obj01) &amp;&amp; target(obj02)&quot;)
  public void beforeCall(Gambler obj01, Casino obj02){ 
    System.out.printf(&quot;Before Call [this:%s] [target:%s] %n&quot;, obj01, obj02);
  }

  @Before(&quot;execution(void com.mydomain.Casino.gambling(Money)) &amp;&amp; this(obj01) &amp;&amp; target(obj02)&quot;)
  public void beforeExecution(Casino obj01, Casino obj02){ 
    System.out.printf(&quot;Before Execution [this:%s] [target:%s] %n&quot;, obj01, obj02);
  }
...
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
Gambler paySalary 100
Before Call [this:com.mydomain.Gambler@7eb366] [target:com.mydomain.Casino@33f0de]
Before Execution [this:com.mydomain.Casino@33f0de] [target:com.mydomain.Casino@33f0de]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
ふむふむ、Before アドバイスでも call と execution で this と target が異なることが分かる。&lt;br /&gt;
試しに execution の this を Gambler クラス(call での呼出元クラス)変更してみる。&lt;br /&gt;
MyAspect クラスの beforeExecution() メソッドを以下の内容に差し替えて再ビルド後、実行する。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Before(&quot;execution(void com.mydomain.Casino.gambling(Money)) &amp;&amp; this(obj01) &amp;&amp; target(obj02)&quot;)
  public void beforeExecution(Gambler obj01, Casino obj02){ 
    System.out.printf(&quot;Before Execution [this:%s] [target:%s] %n&quot;, obj01, obj02);
  }
...
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
Gambler paySalary 100
Before Call [this:com.mydomain.Gambler@d1c778] [target:com.mydomain.Casino@7eb366]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
おぉ！execution の アドバイスが呼出されなくなった。&lt;br /&gt;
当たり前か？(笑&lt;br /&gt;
ノッてきたぞ！&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;
もう少し遊んでみる。&lt;br /&gt;
今度は、args を使ってメソッドの引数で使われている Money クラスを Pointcut 条件にしてみる。&lt;br /&gt;
args も this と target と同じようにクラス・インターフェイスの型で Pointcut 条件を指定する。&lt;br /&gt;
キャプチャもできるようだ。&lt;br /&gt;
within は、this と target と同じようにクラス・インターフェイスの型で Pointcut 条件を指定するのだけどキャプチャはできない。&lt;br /&gt;
また実行オブジェクト、対象オブジェクトというような区別もなく指定した型が Pointcut 条件 になるようだ。&lt;br /&gt;
!within(MyAspect) で MyAspect クラスの before() メソッドを Aspect の対象外にしています。&lt;br /&gt;
それというのも before() メソッドも引数で Money クラスを受けるの Pointcut 条件 にマッチして&lt;br /&gt;
結果再帰呼出になってしまうからです。&lt;br /&gt;
MyAspect クラスのメソッドを以下の内容に差し替えて再ビルド後、実行する。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Before(&quot;args(obj01) &amp;&amp; !within(MyAspect)&quot;)
  public void before(Money obj01){ 
    System.out.printf(&quot;Before [args:%s] %n&quot;, obj01.getValue());
  }
...
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
Before [args:100]
Before [args:100]
Gambler paySalary 100
Before [args:100]
Before [args:100]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
むむむ… Aspect は呼出されているようだけど、なんだかよく分からない。&lt;br /&gt;
Around アドバイスを使ってthis、target、メソッドを print してみる。&lt;br /&gt;
MyAspect クラスのメソッドを以下の内容に差し替えて再ビルド後、実行する。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Around(&quot;args(Money) &amp;&amp; !within(MyAspect)&quot;)
  public Object around(ProceedingJoinPoint pjp) throws Throwable {
 
    System.out.printf(
      &quot;Around [this:%s] [target:%s] [signature:%s] %n&quot;,
      pjp.getThis(), pjp.getTarget(), pjp.getSignature());

    Object retVal = pjp.proceed();
    return retVal;
  }
...
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
Around [this:null] [target:com.mydomain.Gambler@83b1b] [signature:void com.mydomain.Gambler.paySalary(Money)]
Around [this:com.mydomain.Gambler@83b1b] [target:com.mydomain.Gambler@83b1b] [signature:void com.mydomain.Gambler.paySalary(Money)]
Gambler paySalary 100
Around [this:com.mydomain.Gambler@83b1b] [target:com.mydomain.Casino@b32ed4] [signature:void com.mydomain.Casino.gambling(Money)]
Around [this:com.mydomain.Casino@b32ed4] [target:com.mydomain.Casino@b32ed4] [signature:void com.mydomain.Casino.gambling(Money)]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
なるほど、call と execution の両方に作用しているようだ。&lt;br /&gt;
this が null になっているところは、App クラスの main() メソッドが static だからだろう。 &lt;br /&gt;
また、main() メソッド中 の Gambler クラスの paySalary() メソッドの呼出には、call が作用しているのだろう。&lt;br /&gt;
もう少し Pointcut 条件を絞ってみる。&lt;br /&gt;
Gamblerクラス が Casino クラスの gambling() メソッドを呼出しているところあたりに作用するようにしてみる。&lt;br /&gt;
MyAspect クラスのメソッドを以下の内容に差し替えて再ビルド後、実行する。
&lt;/p&gt;

&lt;u&gt;MyAspect.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
...
  @Around(&quot;this(Gambler) &amp;&amp; target(Casino) &amp;&amp; args(Money)&quot;)
  public Object around(ProceedingJoinPoint pjp) throws Throwable {

    System.out.printf(
      &quot;Around [this:%s] [target:%s] [signature:%s] %n&quot;,
   pjp.getThis(), pjp.getTarget(), pjp.getSignature());

    Object retVal = pjp.proceed();
    return retVal;
  }
...
&lt;/pre&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
Gambler paySalary 100
Around [this:com.mydomain.Gambler@1d0d45b] [target:com.mydomain.Casino@125d06e] [signature:void com.mydomain.Casino.gambling(Money)]
Casino gambling 80
&lt;/pre&gt;

&lt;p&gt;
あれ？これは、「@Around(&quot;call(void com.mydomain.Casino.gambling(Money))&quot;)」と同じでは？&lt;br /&gt;
最初に戻ってしまった…&lt;br /&gt;
なんとなくオチがついたようなので今回はこの辺で。&lt;br /&gt;
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
this と target について、とりあえずは整理できたと思う。&lt;br /&gt;
本家の AspectJ には、まだまだ、ディープな Pointcut があるのだけれども&lt;br /&gt;
Spring が対応している AspectJ の主な  Pointcut は、今回、使った思う。&lt;br /&gt;
残りは、アノテーションを Pointcut 条件にするものがある。&lt;br /&gt;
また、Spring 独自の bean なんてなのもあるようだ。&lt;br /&gt;
これらは、またいつかの機会に。
&lt;/p&gt;



&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.eclipse.org/aspectj/doc/next/progguide/index.html&quot;&gt;
   http://www.eclipse.org/aspectj/doc/next/progguide/index.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-pointcuts-designators&quot;&gt;
    http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://netail.net/aosdwiki/index.php?AspectJ%2F%B4%CA%B0%D7%A5%EA%A5%D5%A5%A1%A5%EC%A5%F3%A5%B9&quot;&gt;
    http://netail.net/aosdwiki/index.php?AspectJ%2F%B4%CA%B0%D7%A5%EA%A5%D5%A5%A1%A5%EC%A5%F3%A5%B9
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://maven.apache.org/plugins/maven-shade-plugin/&quot;&gt;
    http://maven.apache.org/plugins/maven-shade-plugin/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://mojo.codehaus.org/aspectj-maven-plugin/&quot;&gt;
 http://mojo.codehaus.org/aspectj-maven-plugin/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6453970106363085836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6453970106363085836'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/05/aspectj-pointcut-this-target.html' title='AspectJ の Pointcut - this と target の違いって？'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-849004644544254416</id><published>2013-05-22T22:00:00.000+09:00</published><updated>2016-05-16T00:27:33.350+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Spring"/><title type='text'>Spring Security で Active Directory にログインする</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;

&lt;p&gt;
Active Directory のユーザで Spring Security の From 認証ができるか試してみたいと思います。&lt;br /&gt;
前提としては、Active Directory を構築済みということになります。&lt;br /&gt;
Active Directoryは、無料で楽しめる Samba 4 AD を使用しました。&lt;br /&gt;
Samba 4 の PDC の構築手順は、わたしも をまとめてみたりしています。&lt;br /&gt;
(&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory.html&quot;&gt;
 『Samba4 で Active Directory / インストール編』
&lt;/a&gt;
&amp;nbsp;
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory-pdc.html&quot;&gt;
 『Samba4 で Active Directory / PDC編』
&lt;/a&gt;）
&lt;br /&gt;

それから、もう一点、Spring MVC Framework など特定の MVC Framework、ライブラリーを使用しなくても&lt;br /&gt;
単独で Spring Security が動くことも確認したいと思います。
&lt;/p&gt;
&lt;p&gt;
今回は、簡単な ログインアプリを作成して動作を検証してみたいと思います。&lt;br /&gt;
ではでは！Getting Started!!
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;環境&lt;/h2&gt;
&lt;u&gt;プリケーションサーバー&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;アプリケーションサーバー&lt;/th&gt;
    &lt;td&gt;Tomcat 7.0.37&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Java&lt;/th&gt;
    &lt;td&gt;JDK 1.7.0_21&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;2&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;Spring Framework 3.2.3.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Spring Security 3.1.4.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Maven 3.0.5&lt;br /&gt;※mvnコマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows XP&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;

&lt;u&gt;Active Directory&lt;/u&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;Active Directory&lt;/th&gt;
    &lt;td&gt;Samba 4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;p&gt;
Cドライブ直下に「studying-spring-security-ad」フォルダを作成し各ファイルを以下のように配置します。&lt;br /&gt;
ファイルは、すべて UTF-8 で保存します。
&lt;/p&gt;
&lt;div id=&quot;studying-spring-security-ad&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-spring-security-ad&lt;/span&gt;
   &lt;ul&gt;
    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
     &lt;ul&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyGrantedAuthoritiesMapper.java&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;META-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-locale.xml&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-security.xml&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;log4j.properties&lt;/span&gt;&lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;webapp&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
           &lt;ul&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;style.css&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;WEB-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;i18n&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;messages_ja.properties&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;web.xml&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;admin.jsp&lt;/span&gt;&lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;denied.jsp&lt;/span&gt;&lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;index.jsp&lt;/span&gt;&lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;login.jsp&lt;/span&gt;&lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
       &lt;/ul&gt;
      &lt;/li&gt;
     &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;pom.xml&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-spring-security-ad&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ServletとLoggerの設定&lt;/h2&gt;
web.xml ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;web.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;web-app version=&quot;3.0&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
  xmlns:xml=&quot;http://www.w3.org/XML/1998/namespace&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;&gt;

  &amp;lt;display-name&gt;studying-spring-security-ad&amp;lt;/display-name&gt;
  
  &amp;lt;context-param&gt;
    &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
    &amp;lt;param-value&gt;classpath*:META-INF/spring/applicationContext*.xml&amp;lt;/param-value&gt;
  &amp;lt;/context-param&gt;
  &amp;lt;listener&gt;
    &amp;lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&gt;
  &amp;lt;/listener&gt;

  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&amp;lt;/filter-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;encoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;UTF-8&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;forceEncoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;true&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;

  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;

  &amp;lt;session-config&gt;
    &amp;lt;session-timeout&gt;10&amp;lt;/session-timeout&gt;
    &amp;lt;tracking-mode&gt;COOKIE&amp;lt;/tracking-mode&gt;
  &amp;lt;/session-config&gt;
&amp;lt;/web-app&gt;
&lt;/pre&gt;
&lt;br /&gt;

ログ設定ファイルを以下の内容で作成する。&lt;br /&gt;
Active Directory 認証処理の Debug ログがコンソールに出力されるようにしました。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;log4j.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
log4j.rootLogger=error, stdout

log4j.logger.org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider=debug, stdout
log4j.logger.org.springframework.security.ldap.authentication=debug, stdout
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;エラーメッセージの日本語化設定&lt;/h2&gt;
Spring 設定 ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;applicationContext-locale.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;beans
  xmlns=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd&quot;&gt;

  &amp;lt;bean id=&quot;messageSource&quot;
    class=&quot;org.springframework.context.support.ReloadableResourceBundleMessageSource&quot;
    p:basenames=&quot;WEB-INF/i18n/messages&quot;
    p:fallbackToSystemLocale=&quot;false&quot;
    p:fileEncodings=&quot;UTF-8&quot;
    p:defaultEncoding=&quot;UTF-8&quot; /&gt;
&amp;lt;/beans&gt;
&lt;/pre&gt;
&lt;br /&gt;

エラーメッセージファイルを以下の内容で作成する。&lt;br /&gt;
日本語化については雰囲気です。（笑
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;messages_ja.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
LdapAuthenticationProvider.badCredentials=ログインユーザ、パスワードが違います。
LdapAuthenticationProvider.credentialsExpired=パスワードをもう一度入力してください。
LdapAuthenticationProvider.disabled=無効なユーザです。
LdapAuthenticationProvider.expired=期限切れユーザです。
LdapAuthenticationProvider.locked=ロックされています。
LdapAuthenticationProvider.emptyUsername=ログインユーザを入力してください。
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;プロパティファイルのキーについて&lt;/span&gt;
&lt;p&gt;
AbstractUserDetailsAuthenticationProvider.XXX で始まるものを使用していたため&lt;br /&gt;
エラーメッセージをなかなか日本語表示できず結構ハマっていました。&lt;br /&gt;
Active Directory (というか Ldap ？)の場合、LdapAuthenticationProvider.XXX のものを使用するようです。&lt;br /&gt;
元々のメッセージプロパティファイルは、spring-security-core のバイナリの方の jar ファイルに含まれています。&lt;br /&gt;
/org/springframework/security フォルダあたりにあると思います。&lt;br /&gt;
割と他の言語のものは、揃っているのに日本語はありませんでした。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;権限 Mapping クラスの作成&lt;/h2&gt;
&lt;p&gt;
Active Directory の Group と Spring Security の Role の紐付けを行います。&lt;br /&gt;
Active Directory の memberOf 属性を元に紐付けを行うのですが&lt;br /&gt;
わたしの環境では、なぜか Primary Group に設定された Group が memberOf 属性に追加されないみたいでした。&lt;br /&gt;
となると Domain Users Group にしか所属しない AD ユーザの場合、memberOf 属性がなしになり Role を割り当てられません。&lt;br /&gt;
本来なら Domain Users あたりに所属する AD ユーザに ROLE_USER 権限を付与したいところなのですが&lt;br /&gt;
とりあえず、認証OK の場合は、無条件に ROLE_USER 権限を付与することにしました。&lt;br /&gt;
あとは、memberOf 属性から Domain Admins または、Administrators に所属する AD ユーザに ROLE_ADMIN 権限を付与しています。
&lt;/p&gt;

&lt;u&gt;MyGrantedAuthoritiesMapper.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;

public class MyGrantedAuthoritiesMapper implements
    GrantedAuthoritiesMapper {

  public Collection&amp;lt;? extends GrantedAuthority&gt; mapAuthorities(
      Collection&amp;lt;? extends GrantedAuthority&gt; authorities) {

    Set&amp;lt;SimpleGrantedAuthority&gt; roles = new HashSet&amp;lt;SimpleGrantedAuthority&gt;();
    roles.add(new SimpleGrantedAuthority(&quot;ROLE_USER&quot;));

    for (GrantedAuthority authority : authorities) {
      switch (authority.toString()) {
        case &quot;Domain Admins&quot; :
        case &quot;Administrators&quot; :
          roles.add(new SimpleGrantedAuthority(&quot;ROLE_ADMIN&quot;));
          break;          
      }
    }

    return roles;
  }
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring Securityの設定&lt;/h2&gt;
&lt;p&gt;
「/resources」以下は、認証を設定せず、それ以外の URL に認証を設定しています。&lt;br /&gt;
「/admin.jsp」へのアクセスは、管理者権限を持つユーザに制限しています。&lt;br /&gt;
ログイン後にログインページが表示されるのが微妙なので&lt;br /&gt;
「/login.jsp」へのアクセスは、ログインしていないユーザに制限しています。&lt;br /&gt;
権限エラーページを使い回ししていますが、別に作った方がいいかも？&lt;br /&gt;
ログイン Top ページにリダイレクトするのもありかも？？
&lt;/p&gt;
&lt;p&gt;
ActiveDirectoryLdapAuthenticationProvider を定義して&lt;br /&gt;
AuthoritiesMapper に先ほど作成した MyGrantedAuthoritiesMapper クラスを設定します。
&lt;/p&gt;

&lt;u&gt;applicationContext-security.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;beans:beans
  xmlns=&quot;http://www.springframework.org/schema/security&quot;
  xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:c=&quot;http://www.springframework.org/schema/c&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd&quot;&gt;

  &amp;lt;http pattern=&quot;/resources/**&quot; security=&quot;none&quot;/&gt;
  
  &amp;lt;http pattern=&quot;/login.jsp*&quot; auto-config=&#39;true&#39; access-denied-page=&quot;/denied.jsp&quot;&gt;
    &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;ROLE_ANONYMOUS&quot; /&gt;
  &amp;lt;/http&gt;
  
  &amp;lt;http pattern=&quot;/**&quot; auto-config=&#39;true&#39; access-denied-page=&quot;/denied.jsp&quot; use-expressions=&quot;true&quot;&gt;
    &amp;lt;intercept-url pattern=&quot;/admin.jsp&quot; access=&quot;hasRole(&#39;ROLE_ADMIN&#39;)&quot; /&gt;
    &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;isFullyAuthenticated()&quot; /&gt;
    &amp;lt;form-login
      login-page=&quot;/login.jsp&quot;
      authentication-failure-url=&quot;/login.jsp?login_error=t&quot; /&gt;
  &amp;lt;/http&gt;

  &amp;lt;authentication-manager&gt;
    &amp;lt;authentication-provider ref=&quot;ldapActiveDirectoryAuthProvider&quot; /&gt;
  &amp;lt;/authentication-manager&gt;

  &amp;lt;beans:bean id=&quot;ldapActiveDirectoryAuthProvider&quot;
    class=&quot;org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider&quot;
    c:domain=&quot;mydomain.local&quot;
    c:url=&quot;ldap://pdc:389/&quot;
    p:authoritiesMapper-ref=&quot;myAuthoritiesMapper&quot;
    p:useAuthenticationRequestCredentials=&quot;true&quot;
    p:convertSubErrorCodesToExceptions=&quot;true&quot; /&gt;
  
  &amp;lt;beans:bean id=&quot;myAuthoritiesMapper&quot; class=&quot;com.mydomain.MyGrantedAuthoritiesMapper&quot;/&gt;
&amp;lt;/beans:beans&gt;
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;&amp;lt;http&gt; タグの use-expressions 属性について&lt;/span&gt;
&lt;p&gt;
true にすると &amp;lt;intercept-url&gt; タグの access 属性は、Spring EL の Security Expressions での設定になるようです。&lt;br /&gt;
access=&quot;ROLE_ADMIN&quot;、access=&quot;IS_AUTHENTICATED_FULLY&quot; は&lt;br /&gt;
access=&quot;hasRole(&#39;ROLE_ADMIN&#39;)&quot;、access=&quot;isFullyAuthenticated()&quot; となるようです。&lt;br /&gt;
結果が ture / false になるような EL 式 をaccess 属性値に設定する感じになるみたいです。&lt;br /&gt;
また、View ファイル中で Spring Security の Taglib の属性値に EL 式を使用する場合も&lt;br /&gt;
use-expressions=&quot;true&quot; としておく必要があるようです。
&lt;/p&gt;
&lt;/div&gt;

&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;View ファイルの作成&lt;/h2&gt;
ログインページ、Top ページ、管理者ページ、権限エラーページの４つを作成します。&lt;br /&gt;
なんとなく、ヘッダーとフッターをテンプレート化できそうですが&lt;br /&gt;
今回は、他のライブラリーなしでも Spring Security が動くこと確認したかったので使用しませんでした。&lt;br /&gt;
ページ遷移についても MVC フレームワークなどを使用していないので直リンクになっています。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;login.jsp&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/functions&quot; prefix=&quot;fn&quot; %&gt;
&amp;lt;%@ taglib uri=&quot;http://www.springframework.org/tags&quot; prefix=&quot;spring&quot; %&gt;
&amp;lt;%@ taglib uri=&quot;http://www.springframework.org/tags/form&quot; prefix=&quot;form&quot; %&gt;

&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot;/&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;ログイン&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;h3&gt;ログイン&amp;lt;/h3&gt;
      &amp;lt;c:if test=&quot;${not empty param.login_error}&quot;&gt;
      &amp;lt;div class=&quot;errors&quot;&gt;
        &amp;lt;p&gt;&amp;lt;c:out value=&quot;${SPRING_SECURITY_LAST_EXCEPTION.message}&quot; /&gt;&amp;lt;/p&gt;
      &amp;lt;/div&gt;
      &amp;lt;/c:if&gt;
      &amp;lt;spring:url value=&quot;/j_spring_security_check&quot; var=&quot;form_url&quot; /&gt;
      &amp;lt;form name=&quot;f&quot; action=&quot;${fn:escapeXml(form_url)}&quot; method=&quot;POST&quot;&gt;
        &amp;lt;table&gt;
          &amp;lt;tbody&gt;
            &amp;lt;tr&gt;
              &amp;lt;th&gt;&amp;lt;label for=&quot;j_username&quot;&gt;ログインユーザ&amp;lt;/label&gt;&amp;lt;/th&gt;
              &amp;lt;td&gt;&amp;lt;input id=&quot;j_username&quot; type=&quot;text&quot; name=&quot;j_username&quot;/&gt;&amp;lt;/td&gt;
            &amp;lt;/tr&gt;
            &amp;lt;tr&gt;
              &amp;lt;th&gt;&amp;lt;label for=&quot;j_password&quot;&gt;パスワード&amp;lt;/label&gt;&amp;lt;/th&gt;
              &amp;lt;td&gt;
                &amp;lt;input id=&quot;j_password&quot; type=&quot;password&quot; name=&quot;j_password&quot; /&gt;
                &amp;lt;input id=&quot;proceed&quot; type=&quot;submit&quot; value=&quot;ログイン&quot; /&gt;
              &amp;lt;/td&gt;
            &amp;lt;/tr&gt;
          &amp;lt;/tbody&gt;
        &amp;lt;/table&gt;
      &amp;lt;/form&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;u&gt;index.jsp&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot;%&gt;
&amp;lt;%@ taglib uri=&quot;http://www.springframework.org/security/tags&quot; prefix=&quot;sec&quot; %&gt;

&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot; /&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;Top ページ&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;h3&gt;Top ページ&amp;lt;/h3&gt;

      ようこそ&amp;lt;sec:authentication property=&quot;principal.username&quot; /&gt;さん&amp;lt;br /&gt;
      &amp;lt;sec:authentication property=&quot;principal.dn&quot; /&gt;&amp;lt;br /&gt;
      &amp;lt;br /&gt;
      以下の権限が設定されています。
      &amp;lt;sec:authentication property=&quot;authorities&quot; var=&quot;roles&quot; scope=&quot;page&quot; /&gt;
      &amp;lt;ul&gt;
        &amp;lt;c:forEach var=&quot;role&quot; items=&quot;${roles}&quot;&gt;
          &amp;lt;li&gt;${role}&amp;lt;/li&gt;
        &amp;lt;/c:forEach&gt;
      &amp;lt;/ul&gt;

      &amp;lt;div class=&quot;footer&quot;&gt;
        &amp;lt;a href=&quot;./admin.jsp&quot;&gt;Admin ページへ&amp;lt;/a&gt;
        &amp;lt;sec:authorize access=&quot;hasRole(&#39;ROLE_ADMIN&#39;) == false&quot;&gt;
        ※管理者権限は設定されていません。
        &amp;lt;/sec:authorize&gt;
        &amp;lt;br /&gt;
        &amp;lt;a href=&quot;./j_spring_security_logout&quot;&gt;ログアウト&amp;lt;/a&gt;
      &amp;lt;/div&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;u&gt;admin.jsp&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; %&gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot;%&gt;

&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot; /&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;管理者ページ&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;h3&gt;管理者ページ&amp;lt;/h3&gt;
      &amp;lt;div class=&quot;footer&quot;&gt;
        &amp;lt;a href=&quot;./&quot;&gt;Top ページへ&amp;lt;/a&gt;&amp;lt;br /&gt;
        &amp;lt;a href=&quot;./j_spring_security_logout&quot;&gt;ログアウト&amp;lt;/a&gt;
      &amp;lt;/div&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;u&gt;denied.jsp&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
&amp;lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot;%&gt;

&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot; /&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;権限エラー&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;div class=&quot;errors&quot;&gt;
        &amp;lt;h3&gt;権限エラー&amp;lt;/h3&gt;
        &amp;lt;p&gt;このページにアクセスする権限がありません。&amp;lt;/p&gt;
      &amp;lt;/div&gt;
      &amp;lt;div class=&quot;footer&quot;&gt;
        &amp;lt;a href=&quot;./&quot;&gt;Top ページへ&amp;lt;/a&gt;&amp;lt;br /&gt;
        &amp;lt;a href=&quot;./j_spring_security_logout&quot;&gt;ログアウト&amp;lt;/a&gt;
      &amp;lt;/div&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;u&gt;style.css&lt;/u&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
body {
  margin: 0px;
  padding: 0px;
  font: 13px Arial, Helvetica, sans-serif;
  color: #212121;
}
 
h1 {
  margin-top: 0px;
  font-size: 2.4em;
}
 
p {
  margin-bottom: 1.8em;
  line-height: 160%;
}
 
table {
  margin: 0px auto;
}
 
th, td {
  text-align: left;
}
 
div.errors {
  background-color: #FFEBE8;
  border: solid 1px #DD3C10;
  width: 300px;
  margin: 3px auto;
  padding: 3px;
}

div.footer {
  border-top: solid 1px #777;
  margin-top: 7px;
  padding-top: 3px;
  text-align: left;
}
 
#page {
  width: 320px;
  margin: 0px auto;
  padding: 30px 0px;
  text-align: center;
}

#j_username {
  width: 98%;
}

#j_password {
  width: 100px;
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;POM ファイルの作成&lt;/h2&gt;
POM ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;pom.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;project
  xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;

  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;studying-spring-security-ad&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;war&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;name&gt;studying-spring-security-ad&amp;lt;/name&gt;

  &amp;lt;properties&gt;
    &amp;lt;java.version&gt;1.7&amp;lt;/java.version&gt;
    &amp;lt;project.build.sourceEncoding&gt;UTF-8&amp;lt;/project.build.sourceEncoding&gt;
    &amp;lt;spring.version&gt;3.2.3.RELEASE&amp;lt;/spring.version&gt;
    &amp;lt;spring-security.version&gt;3.1.4.RELEASE&amp;lt;/spring-security.version&gt;
  &amp;lt;/properties&gt;

  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;javaee-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;6.0&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax.servlet&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;jstl&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.2&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-context&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-webmvc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-jdbc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-core&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-config&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-web&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-ldap&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-taglibs&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;junit&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;junit&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;4.11&amp;lt;/version&gt;
      &amp;lt;scope&gt;test&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;log4j&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;log4j&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.2.17&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;

  &amp;lt;build&gt;
    &amp;lt;finalName&gt;studying-spring-security-ad&amp;lt;/finalName&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;3.1&amp;lt;/version&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;${java.version}&amp;lt;/source&gt;
          &amp;lt;target&gt;${java.version}&amp;lt;/target&gt;
          &amp;lt;encoding&gt;${project.build.sourceEncoding}&amp;lt;/encoding&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.tomcat.maven&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;tomcat7-maven-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.1&amp;lt;/version&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
&amp;lt;/project&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドと実行&lt;/h2&gt;
&lt;p&gt;
コマンドプロンプトを開きCドライブ直下の「studying-spring-security-ad」フォルダに移動後、以下の mvn コマンドを実行します。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd c:\studying-spring-security-ad
mvn clean package
mvn tomcat7:run
&lt;/pre&gt;
&lt;p&gt;
ブラウザから「http://localhost:8080/studying-spring-security-ad/」を開いて AD ユーザでログインできるか試してみてください。&lt;br /&gt;
また、管理者ページを表示できるユーザとできないユーザも確認できると思います。
&lt;/p&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Administrator について&lt;/span&gt;
&lt;p&gt;
なぜかわたしの環境では最初、Administrator でログインできませんでした。&lt;br /&gt;
ログイン処理の際に Active Directory の userPrincipalName 属性を元にユーザーが検索されるようなのですが&lt;br /&gt;
これの登録がありませんでした。&lt;br /&gt;
Samba 4 で生成された Administrator は、自動的に userPrincipalName 属性が登録されないのでしょか？&lt;br /&gt;
Windows の Active Directory 管理から Administrator の「プロパティ」を開いて&lt;br /&gt;
「アカウント」タブにある「ユーザログオン名」を入力することでログインできるようになりました。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
割と簡単に AD ユーザでの Form認証ができたのでは思う。（Primary Group など微妙なところがあったにせよ…&lt;br /&gt;
この感じだとアプリ側をあまり変更せず、DB を使う認証プロバイダーにも変更できそうだ。&lt;br /&gt;
よくできているなぁと思った。&lt;br /&gt;
とはいっても、実際の業務アプリなどでお客さんに提案するのには勇気がいる。&lt;br /&gt;
Spring Security のものをベースにしつつも自分で MyActiveDirectoryLdapAuthenticationProvider 的なものを&lt;br /&gt;
作る覚悟は必要になるだろう。&lt;br /&gt;
Spring MVC Framework を使わなくても認証処理が動くことも確認できたと思う。&lt;br /&gt;
認証処理が Servlet Filter として実装されているからなのだろうけど。&lt;br /&gt;
Spring MVC Framework 以外の MVC Framework を選択したとしても&lt;br /&gt;
認証処理には、Spring Security を使うということも可能だと思う。



&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.1.x/reference/ldap.html#ldap-active-directory&quot;&gt;
    http://static.springsource.org/spring-security/site/docs/3.1.x/reference/ldap.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://comdynamics.net/blog/544/spring-security-3-integration-with-active-directory-ldap/&quot;&gt;
    http://comdynamics.net/blog/544/spring-security-3-integration-with-active-directory-ldap/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://raymondhlee.wordpress.com/2012/03/17/active-directory-authentication-with-spring-security-3-1/&quot;&gt;
    http://raymondhlee.wordpress.com/2012/03/17/active-directory-authentication-with-spring-security-3-1/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.1.x/reference/taglibs.html&quot;&gt;
    http://static.springsource.org/spring-security/site/docs/3.1.x/reference/taglibs.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://doanduyhai.wordpress.com/2012/02/26/spring-security-part-v-security-tags/&quot;&gt;
 http://doanduyhai.wordpress.com/2012/02/26/spring-security-part-v-security-tags/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://support.microsoft.com/kb/275523/ja&quot;&gt;
    http://support.microsoft.com/kb/275523/ja
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/849004644544254416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/849004644544254416'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/05/spring-security-active-directory.html' title='Spring Security で Active Directory にログインする'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-1427863495308918016</id><published>2013-05-20T22:00:00.000+09:00</published><updated>2016-05-16T00:25:50.450+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Spring"/><title type='text'>Spring Security で Digest 認証をテストする</title><content type='html'>
&lt;h2&gt;はじめに&lt;/h2&gt;

&lt;p&gt;
なぜに Digest 認証なのか？&lt;br /&gt;
RESTful な Web アプリでユーザ認証を考えた場合&lt;br /&gt;
普通に Form 認証的な感じで Cookie に Session Id を保存するパターンでもいいのでは？&lt;br /&gt;
と思ったのですが RESTful の要件に Stateless というのもがあり&lt;br /&gt;
アクセスは 1回ごとに完結しないといけないとのことです。&lt;br /&gt;
クライアントのセッション状態をサーバーで管理するのはよくないようです。&lt;br /&gt;
このあたりは、理念というか、こだわりというか、なんとなくどちらでもいいような…&lt;br /&gt;
まぁ、とりあえず、Cookie / Session はダメということで&lt;br /&gt;
Http 認証ということになる訳ですが&lt;br /&gt;
Basic 認証では、ちょっと…なので Digest 認証になりました。&lt;br /&gt;
どの場合であっても SSL にはしておきたいところです。
&lt;/p&gt;
&lt;p&gt;
今回は、簡単な Web アプリとテストを作成して動作を検証してみたいと思います。&lt;br /&gt;
また、Spring Framework 3.2 から Spring MVC Test Framework が利用できるので、これも試してみます。&lt;br /&gt;
ではでは！Getting Started!!
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows XP&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;アプリケーションサーバー&lt;/th&gt;
    &lt;td&gt;Tomcat 7.0.37&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Java&lt;/th&gt;
    &lt;td&gt;JDK 1.7.0_21&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;2&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;Spring Framework 3.2.2.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Spring Security 3.1.4.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;4&quot;&gt;ライブラリー&lt;/th&gt;
    &lt;td&gt;HttpClient 4.2.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;JsonPath 0.8.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Jackson 2.2.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;JXPath Component 1.3&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Maven 3.0.5&lt;br /&gt;※mvnコマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;p&gt;
Cドライブ直下に「studying-spring-security-digest」フォルダを作成し各ファイルを以下のように配置します。&lt;br /&gt;
ファイルは、すべて UTF-8 で保存します。
&lt;/p&gt;
&lt;div id=&quot;studying-spring-security-digest&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-spring-security-digest&lt;/span&gt;
   &lt;ul&gt;
    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
     &lt;ul&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;web&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;ApiController.java&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;META-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-security.xml&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;log4j.properties&lt;/span&gt;&lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;webapp&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;WEB-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;webmvc-config.xml&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;web.xml&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
       &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;test&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;web&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;ApiControllerDigestAuthTest.java&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
       &lt;/ul&gt;
      &lt;/li&gt;
     &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;pom.xml&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-spring-security-digest&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ServletとLoggerの設定&lt;/h2&gt;
web.xml ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;web.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;web-app version=&quot;3.0&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
  xmlns:xml=&quot;http://www.w3.org/XML/1998/namespace&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;&gt;
    
  &amp;lt;display-name&gt;studying-spring-security-digest&amp;lt;/display-name&gt;

  &amp;lt;context-param&gt;
    &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
    &amp;lt;param-value&gt;classpath*:META-INF/spring/applicationContext*.xml&amp;lt;/param-value&gt;
  &amp;lt;/context-param&gt;
  &amp;lt;listener&gt;
    &amp;lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&gt;
  &amp;lt;/listener&gt;
  
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&amp;lt;/filter-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;encoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;UTF-8&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;forceEncoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;true&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;
  
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;
  
  &amp;lt;servlet&gt;
    &amp;lt;servlet-name&gt;springMVCServlet&amp;lt;/servlet-name&gt;
    &amp;lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;WEB-INF/spring/webmvc-config.xml&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
    &amp;lt;load-on-startup&gt;1&amp;lt;/load-on-startup&gt;
  &amp;lt;/servlet&gt;
  &amp;lt;servlet-mapping&gt;
    &amp;lt;servlet-name&gt;springMVCServlet&amp;lt;/servlet-name&gt;
    &amp;lt;url-pattern&gt;/&amp;lt;/url-pattern&gt;
  &amp;lt;/servlet-mapping&gt;
&amp;lt;/web-app&gt;
&lt;/pre&gt;
&lt;br /&gt;

ログ設定ファイルを以下の内容で作成する。&lt;br /&gt;
Digest 認証の Debug ログがコンソールに出力されるようにしています。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;log4j.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
log4j.rootLogger=error, stdout

log4j.logger.org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint=debug, stdout
log4j.logger.org.springframework.security.web.authentication.www.DigestAuthenticationFilter=debug, stdout
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring MVCの設定&lt;/h2&gt;
Spring MVCの設定 ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;webmvc-config.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
  xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd&quot;&gt;

  &amp;lt;context:component-scan base-package=&quot;com.mydomain&quot; /&gt;
  &amp;lt;mvc:annotation-driven /&gt;
&amp;lt;/beans&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring Securityの設定&lt;/h2&gt;
&lt;p&gt;
「/」は、認証を設定せず、それ以外の URL に認証を設定しています。&lt;br /&gt;
「/Admin」へのアクセスは、管理者権限を持つユーザに制限しています。&lt;br /&gt;
セッションは使用しないので stateless としています。&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;
Spring Security で Digest 認証を行うためには、いろいろ決め事があるみたいで&lt;br /&gt;
とりあえず、ユーザーのパスワードは平文で保存しておかないといけないようです。&lt;br /&gt;
ハッシュ化して保存したい場合は、 DigestAuthenticationFilter あたりを改造することになるのでしょうか？&lt;br /&gt;
Digest 認証の Filter も明示的に定義する必要があるようです。&lt;br /&gt;
今回、Basic 認証を使わないので、その位置に挟んでいます。&lt;br /&gt;
また、Digest 認証の Filter には、AuthenticationManager でなく&lt;br /&gt;
直接 UserDetailsService を設定するようです。&lt;br /&gt;
nonce の有効期限は 3秒に設定しています。
&lt;/p&gt;
&lt;p&gt;
ログインユーザー情報は、InMemoryDaoImpl から取得するようにしました。&lt;br /&gt;
ユーザーアカウントは一般ユーザと管理者ユーザを登録しています。
&lt;/p&gt;

&lt;u&gt;applicationContext-security.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;beans:beans
  xmlns=&quot;http://www.springframework.org/schema/security&quot;
  xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd&quot;&gt;

  &amp;lt;http pattern=&quot;/&quot; security=&quot;none&quot; create-session=&quot;stateless&quot;/&gt;
  
  &amp;lt;http entry-point-ref=&quot;digestEntryPoint&quot; create-session=&quot;stateless&quot;&gt;
    &amp;lt;intercept-url pattern=&quot;/Admin&quot; access=&quot;ROLE_ADMIN&quot; /&gt;
    &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;ROLE_USER&quot; /&gt;
    &amp;lt;custom-filter position=&quot;BASIC_AUTH_FILTER&quot; ref=&quot;digestFilter&quot;/&gt;
  &amp;lt;/http&gt;

  &amp;lt;beans:bean id=&quot;digestFilter&quot;
    class=&quot;org.springframework.security.web.authentication.www.DigestAuthenticationFilter&quot;&gt;
    &amp;lt;beans:property name=&quot;userDetailsService&quot; ref=&quot;inMemoryDaoImpl&quot; /&gt;
    &amp;lt;beans:property name=&quot;authenticationEntryPoint&quot; ref=&quot;digestEntryPoint&quot; /&gt;
  &amp;lt;/beans:bean&gt;

  &amp;lt;beans:bean id=&quot;digestEntryPoint&quot;
    class=&quot;org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint&quot;&gt;
    &amp;lt;beans:property name=&quot;realmName&quot; value=&quot;My Realm&quot; /&gt;
    &amp;lt;beans:property name=&quot;key&quot; value=&quot;mykey&quot; /&gt;
    &amp;lt;beans:property name=&quot;nonceValiditySeconds&quot; value=&quot;3&quot; /&gt;
  &amp;lt;/beans:bean&gt;
  
  &amp;lt;authentication-manager&gt;
    &amp;lt;authentication-provider&gt;
      &amp;lt;user-service id=&quot;inMemoryDaoImpl&quot;&gt;
        &amp;lt;user name=&quot;admin&quot; password=&quot;admin-pass&quot; authorities=&quot;ROLE_USER, ROLE_ADMIN&quot; /&gt;
        &amp;lt;user name=&quot;user&quot; password=&quot;user-pass&quot; authorities=&quot;ROLE_USER&quot; /&gt;
      &amp;lt;/user-service&gt;
    &amp;lt;/authentication-provider&gt;
  &amp;lt;/authentication-manager&gt;
&amp;lt;/beans:beans&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Controller クラスの作成&lt;/h2&gt;
ApiController クススを以下の内容で作成する。&lt;br /&gt;
呼出可能な URL は、「/」「/Home」「/Admin」「UserDetails」４つです。&lt;br /&gt;
「/」「/Home」「/Admin」は、文字列を返します。&lt;br /&gt;
「UserDetails」は、ログインユーザ情報を Json 形式で返します。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;ApiController.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain.web;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class ApiController {

  @RequestMapping(&quot;/&quot;)
  @ResponseBody
  public String index() { return &quot;Index&quot;; }
  
  @RequestMapping(&quot;/Home&quot;)
  @ResponseBody
  public String home() { return &quot;Home&quot;; }

  @RequestMapping(&quot;/Admin&quot;)
  @ResponseBody
  public String admin() { return &quot;Admin&quot;; }

  @RequestMapping(&quot;/UserDetails&quot;)
  @ResponseBody
  public UserDetails userDetails() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return (UserDetails)auth.getPrincipal();
  }
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Controller クラスのテスト作成&lt;/h2&gt;
Spring Security が起動するように setup() メソッドの中で FilterChainProxy を&lt;br /&gt;
MockMvc の Filter に追加しています。&lt;br /&gt;
MockHttpServletRequestBuilder から Digest 認証の Authorization リクエストヘッダー を&lt;br /&gt;
追加できそうになかったので Apache HttpComponents を使用することにしました。&lt;br /&gt;
このあたりを自作してしまうと今度はそこのテストが必要になってきそうなので…&lt;br /&gt;
Json データの検証は、JsonPath を使う方法と Jackson と JXPath を使う方法を試してみました。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;ApiControllerDigestAuthTest.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain;

import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.Map;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.BasicHttpContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;

import com.fasterxml.jackson.databind.ObjectMapper;


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration()
@ContextConfiguration({
  &quot;file:src/main/webapp/WEB-INF/spring/webmvc-config.xml&quot;,
  &quot;file:src/main/resources/META-INF/spring/applicationContext-security.xml&quot;})
public class ApiControllerDigestAuthTest {

  @Autowired
  private WebApplicationContext wac;
  
  @Autowired
  private FilterChainProxy springSecurityFilterChain;

  private MockMvc mockMvc;

    
  @Before
  public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
      .addFilters(this.springSecurityFilterChain).build();
  }
    
  @SuppressWarnings(&quot;unchecked&quot;)
  @Test
  public void RoleUserTest() throws Exception {

    // ===== RoleUser Test Data ===== //
    String userName = &quot;user&quot;;
    String password = &quot;user-pass&quot;;
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userName, password);
    BasicHttpRequest request = new BasicHttpRequest(&quot;GET&quot;, &quot;/&quot;);
    BasicHttpContext context = new BasicHttpContext();
    BasicHeader header = null;
    DigestScheme digestScheme = null;
    Header reqHeader = null;
    String resHeader = &quot;&quot;;
    MvcResult mvcResult = null;
  

    // ===== Test 1 ===== //
    this.mockMvc
      .perform(
        get(&quot;/&quot;)
        .accept(MediaType.TEXT_PLAIN))
      .andExpect(status().isOk())
      .andExpect(content().string(&quot;Index&quot;));


    // ===== Test 2 ===== //
    mvcResult = this.mockMvc
      .perform(
        get(&quot;/Home&quot;)
        .accept(MediaType.TEXT_PLAIN))
      .andExpect(status().isUnauthorized())
      .andReturn();
  

    // ===== Test 3 ===== //
    // Response Header から Authorization Digest Request Header を作成する。
    resHeader = mvcResult.getResponse().getHeaderValue(&quot;WWW-Authenticate&quot;).toString();
    resHeader = resHeader.substring(7); // 先頭文字列 Digest を削除
    header = new BasicHeader(&quot;WWW-Authenticate&quot;, resHeader);
  
    digestScheme = new DigestScheme();
    for (HeaderElement elm : header.getElements()) {      
      switch (elm.getName()) {
        case &quot;realm&quot; :
        case &quot;nonce&quot; :
          digestScheme.overrideParamter(elm.getName(), elm.getValue());
          break;
      }
    }
    digestScheme.overrideParamter(&quot;qop&quot;, &quot;auth&quot;);
    reqHeader = digestScheme.authenticate(credentials, request, context);
  
    // URL 呼出し
    this.mockMvc
      .perform(
        get(&quot;/Home&quot;)
        .accept(MediaType.TEXT_PLAIN)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isOk())
      .andExpect(content().string(&quot;Home&quot;));


    // ===== Test 4 ===== //
    reqHeader = digestScheme.authenticate(credentials, request, context);
    this.mockMvc
      .perform(
        get(&quot;/Admin&quot;)
        .accept(MediaType.TEXT_PLAIN)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isForbidden());
  

    // ===== Test 5 ===== //
  
    // 3秒スリープして nonce を期限切れにする。
    Thread.sleep(3000);
    reqHeader = digestScheme.authenticate(credentials, request, context);
    mvcResult = this.mockMvc
      .perform(
        get(&quot;/UserDetails&quot;)
        .accept(MediaType.TEXT_PLAIN)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isUnauthorized())
      .andReturn();
  

    // ===== Test 6 ===== //
    // Response Header から Authorization Digest Request Header を作成する。
    resHeader = mvcResult.getResponse().getHeaderValue(&quot;WWW-Authenticate&quot;).toString();
    resHeader = resHeader.substring(7);
    header = new BasicHeader(&quot;WWW-Authenticate&quot;, resHeader);
  
    digestScheme = new DigestScheme();
    for (HeaderElement elm : header.getElements()) {      
      switch (elm.getName()) {
        case &quot;realm&quot; :
        case &quot;nonce&quot; :
          digestScheme.overrideParamter(elm.getName(), elm.getValue());
          break;
      }
    }
    digestScheme.overrideParamter(&quot;qop&quot;, &quot;auth&quot;);
    reqHeader = digestScheme.authenticate(credentials, request, context);

    // URL 呼出し - JsonPath で JSON データを検証
    this.mockMvc
      .perform(
        get(&quot;/UserDetails&quot;)
        .accept(MediaType.APPLICATION_JSON)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isOk())
      .andExpect(jsonPath(&quot;$.username&quot;).value(userName))
      .andExpect(jsonPath(&quot;$.authorities&quot;).isArray())
      .andExpect(jsonPath(&quot;$.authorities[?(@.authority == &#39;ROLE_USER&#39;)]&quot;).exists())
      .andExpect(jsonPath(&quot;$.authorities[?(@.authority == &#39;ROLE_ADMIN&#39;)]&quot;).doesNotExist());
  }

  @SuppressWarnings(&quot;unchecked&quot;)
  @Test
  public void RoleAdminTest() throws Exception {

    // ===== RoleAdmin Test Data ===== //
    String userName = &quot;admin&quot;;
    String password = &quot;admin-pass&quot;;
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userName, password);
    BasicHttpRequest request = new BasicHttpRequest(&quot;GET&quot;, &quot;/&quot;);
    BasicHttpContext context = new BasicHttpContext();
    BasicHeader header = null;
    DigestScheme digestScheme = null;
    Header reqHeader = null;
    String resHeader = &quot;&quot;;
    MvcResult mvcResult = null;


    // ===== Test 7 ===== //
    mvcResult = this.mockMvc
      .perform(
        get(&quot;/Admin&quot;)
        .accept(MediaType.TEXT_PLAIN))
      .andExpect(status().isUnauthorized())
      .andReturn();


    // ===== Test 8 ===== //
    // Response Header から Authorization Digest Request Header を作成する。
    resHeader = mvcResult.getResponse().getHeaderValue(&quot;WWW-Authenticate&quot;).toString();
    resHeader = resHeader.substring(7);
    header = new BasicHeader(&quot;WWW-Authenticate&quot;, resHeader);

    digestScheme = new DigestScheme();
    for (HeaderElement elm : header.getElements()) {      
      switch (elm.getName()) {
        case &quot;realm&quot; :
        case &quot;nonce&quot; :
          digestScheme.overrideParamter(elm.getName(), elm.getValue());
          break;
      }
    }
    digestScheme.overrideParamter(&quot;qop&quot;, &quot;auth&quot;);
    reqHeader = digestScheme.authenticate(credentials, request, context);

    // URL 呼出し
    this.mockMvc
      .perform(
        get(&quot;/Admin&quot;)
        .accept(MediaType.TEXT_PLAIN)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isOk())
      .andExpect(content().string(&quot;Admin&quot;));


    // ===== Test 9 ===== //
    mvcResult = this.mockMvc
      .perform(
        get(&quot;/UserDetails&quot;)
        .accept(MediaType.APPLICATION_JSON)
        .header(reqHeader.getName(), reqHeader.getValue()))
      .andExpect(status().isOk())
      .andReturn();
  
    // Jackson と JXPath で JSON データを検証
    String response = mvcResult.getResponse().getContentAsString();
    ObjectMapper mapper = new ObjectMapper();
    Map&amp;lt;String, Object&gt; userDetails = mapper.readValue(response, Map.class);
    JXPathContext jxContext = JXPathContext.newContext(userDetails);
  
    assertEquals(userName, jxContext.getValue(&quot;username&quot;).toString());
    assertEquals(2, jxContext.selectNodes(&quot;authorities&quot;).size());
    assertEquals(1, jxContext.selectNodes(&quot;authorities[@authority=&#39;ROLE_USER&#39;]&quot;).size());
    assertEquals(1, jxContext.selectNodes(&quot;authorities[@authority=&#39;ROLE_ADMIN&#39;]&quot;).size());
  }
}
&lt;/pre&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;RoleUser Test Data&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  一般ユーザのテストデータ
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 1&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/」の呼出しテスト&lt;br /&gt;
  認証が不要な URL にアクセスする。&lt;br /&gt;
  レスポンスコード 200:OK と共に文字列 Index を取得する。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 2&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/Home」の呼出しテストA&lt;br /&gt;
  認証が必要な URL に Authorization リクエストヘッダーなしでアクセスする。&lt;br /&gt;
  レスポンスコード 401:Unauthorized が返る。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 3&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/Home」の呼出しテストB&lt;br /&gt;
  Test 2 のレスポンスヘッダー WWW-Authenticate から realm と nonce を取得し &lt;br /&gt;
  ユーザー と パスワードを加えて Authorization リクエストヘッダー作成する。&lt;br /&gt;
  Authorization リクエストヘッダーを付けてアクセスする。&lt;br /&gt;
  レスポンスコード 200:OK と共に文字列 Home を取得する。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 4&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/Admin」の呼出しテスト&lt;br /&gt;
  Admin 権限が必要な URL に 一般ユーザでアクセスする。&lt;br /&gt;
  Test 3 で作成した Authorization リクエストヘッダーを使用する。&lt;br /&gt;
  レスポンスコード 403:Forbidden が返る。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 5&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/UserDetails」の呼出しテストA&lt;br /&gt;
  有効期限切れ nonce でアクセスする。&lt;br /&gt;
  nonce を有効期限切れにするため 3秒スリープする。&lt;br /&gt;
  Test 3 で作成した Authorization リクエストヘッダーを使用する。&lt;br /&gt;
  レスポンスコード 401:Unauthorized が返る。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 6&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/UserDetails」の呼出しテストB&lt;br /&gt;
  Test 5 のレスポンスヘッダー WWW-Authenticate から realm と nonce を取得し &lt;br /&gt;
  ユーザー と パスワードを加えて Authorization リクエストヘッダー再作成する。&lt;br /&gt;
  Authorization リクエストヘッダーを付けてアクセスする。&lt;br /&gt;
  レスポンスコード 200:OK と共にログインユーザ情報を Json 形式で取得する。&lt;br /&gt;
  JSON データを JsonPath で検証する。
&lt;/dd&gt;
&lt;/dl&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;RoleAdmin Test Data&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  管理者ユーザのテストデータ
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 7&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/Admin」の呼出しテストA&lt;br /&gt;
  認証が必要な URL に Authorization リクエストヘッダーなしでアクセスする。&lt;br /&gt;
  レスポンスコード 401:Unauthorized が返る。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 8&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/Admin」の呼出しテストB&lt;br /&gt;
  Test 7 のレスポンスヘッダー WWW-Authenticate から realm と nonce を取得し &lt;br /&gt;
  ユーザー と パスワードを加えて Authorization リクエストヘッダー作成する。&lt;br /&gt;
  Authorization リクエストヘッダーを付けてアクセスする。&lt;br /&gt;
  Admin 権限が必要な URL に 管理者ユーザでアクセスする。&lt;br /&gt;
  レスポンスコード 200:OK と共に文字列 Admin を取得する。
&lt;/dd&gt;
&lt;dt&gt;&lt;b&gt;Test 9&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  「/UserDetails」の呼出しテスト&lt;br /&gt;
  Test 8 で作成した Authorization リクエストヘッダーを使用する。&lt;br /&gt;
  レスポンスコード 200:OK と共にログインユーザ情報を Json 形式で取得する。&lt;br /&gt;
  JSON データを Jackson と JXPath で 検証する。
&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;POM ファイルの作成&lt;/h2&gt;
POM ファイルを以下の内容で作成する。
&lt;br /&gt;&lt;br /&gt;

&lt;u&gt;pom.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;project
  xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;

  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;studying-spring-security-digest&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;war&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;name&gt;studying-spring-security-digest&amp;lt;/name&gt;

  &amp;lt;properties&gt;
    &amp;lt;java.version&gt;1.7&amp;lt;/java.version&gt;
    &amp;lt;project.build.sourceEncoding&gt;UTF-8&amp;lt;/project.build.sourceEncoding&gt;
    &amp;lt;spring.version&gt;3.2.2.RELEASE&amp;lt;/spring.version&gt;
    &amp;lt;spring-security.version&gt;3.1.4.RELEASE&amp;lt;/spring-security.version&gt;
  &amp;lt;/properties&gt;

  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax.servlet&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;javax.servlet-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;3.0.1&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax.servlet.jsp&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;jsp-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;2.2&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-context&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-webmvc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-jdbc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-test&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
      &amp;lt;scope&gt;test&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-core&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-web&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-config&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;com.fasterxml.jackson.core&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;jackson-databind&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;2.2.1&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.apache.httpcomponents&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;httpclient&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;4.2.5&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;junit&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;junit&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;4.11&amp;lt;/version&gt;
      &amp;lt;scope&gt;test&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;com.jayway.jsonpath&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;json-path-assert&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;0.8.1&amp;lt;/version&gt;
      &amp;lt;scope&gt;test&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;commons-jxpath&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;commons-jxpath&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.3&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;log4j&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;log4j&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.2.17&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;

  &amp;lt;build&gt;
    &amp;lt;finalName&gt;studying-spring-security-digest&amp;lt;/finalName&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;3.1&amp;lt;/version&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;${java.version}&amp;lt;/source&gt;
          &amp;lt;target&gt;${java.version}&amp;lt;/target&gt;
          &amp;lt;encoding&gt;${project.build.sourceEncoding}&amp;lt;/encoding&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.tomcat.maven&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;tomcat7-maven-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.1&amp;lt;/version&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
&amp;lt;/project&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドと実行&lt;/h2&gt;
&lt;p&gt;
コマンドプロンプトを開きCドライブ直下の「studying-spring-security-digest」フォルダに移動後、以下の mvn コマンドを実行します。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd c:\studying-spring-security-digest
mvn clean package
mvn tomcat7:run
&lt;/pre&gt;
&lt;p&gt;
テストがパスして war ファイルが生成されれば、とりあえず OK です。&lt;br /&gt;
実際に直接ブラウザから「http://localhost:8080/studying-spring-security-digest/」にアクセスしてみるのもよいと思います。&lt;br /&gt;
Controller の URL 「/」「/Home」「/Admin」「/UserDetails」を一般ユーザと管理者ユーザで呼出してコンソールログを確認してみる。&lt;br /&gt;
設定した有効期限(3秒)のタイミングで nc がリセットされ 新しい nonce に変わっているのも確認できると思います。
&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
テストコードの中で digestScheme.authenticate() メソッドを呼出して nc をインクリメントなどして&lt;br /&gt;
Authorization リクエストヘッダーを更新しているのですが、この呼出しをコメントアウトしてもテストは通ってしまいます。&lt;br /&gt;
一度受け入れたことのある Response ダイジェスト文字列が送られてきた場合、エラーにしてほしいような気がしますが…&lt;br /&gt;
リプレイ攻撃とか大丈夫なのだろうか？&lt;br /&gt;
とりあえず、nonce の有効期限は効いているようなので問題ないかな？&lt;br /&gt;
RESTful の要件は満たさないのかもしれないけど &lt;br /&gt;
Spring Security で WebAPI の認証をする場合、Cookie / Session が無難かも？&lt;br /&gt;
もしくは、OAuth の対応に期待か？！&lt;br /&gt;
Spring MVC Test Framework は、かなりいい感じだ。&lt;br /&gt;
いろいろテストできそうだ！
&lt;/p&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.1.x/reference/basic.html#digest-processing-filter&quot;&gt;
    http://static.springsource.org/spring-security/site/docs/3.1.x/reference/basic.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework&quot;&gt;
    http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.baeldung.com/2011/11/20/basic-and-digest-authentication-for-a-restful-service-with-spring-security-3-1/&quot;&gt;
 http://www.baeldung.com/2011/11/20/basic-and-digest-authentication-for-a-restful-service-with-spring-security-3-1/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://d.hatena.ne.jp/kuwalab/20130402/p1&quot;&gt;
    http://d.hatena.ne.jp/kuwalab/20130402/p1
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://x68000.q-e-d.net/~68user/net/http-auth-2.html&quot;&gt;
    http://x68000.q-e-d.net/~68user/net/http-auth-2.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://hc.apache.org/&quot;&gt;
    http://hc.apache.org/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://code.google.com/p/json-path/&quot;&gt;
    http://code.google.com/p/json-path/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://jackson.codehaus.org/&quot;&gt;
    http://jackson.codehaus.org/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://commons.apache.org/proper/commons-jxpath/&quot;&gt;
    http://commons.apache.org/proper/commons-jxpath/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1427863495308918016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1427863495308918016'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/05/spring-security-digest.html' title='Spring Security で Digest 認証をテストする'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-1532454918396254944</id><published>2013-04-22T22:00:00.000+09:00</published><updated>2013-04-23T16:20:24.212+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><category scheme="http://www.blogger.com/atom/ns#" term="わたしもやってみた"/><title type='text'>Samba4 で Active Directory / Member Server 編 - わたしもやってみた -</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;p&gt;
今回は、Member Server を構築したいと思います。&lt;br /&gt;
そして
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory-pdc.html&quot;&gt;
 『Samba4 で Active Directory / PDC編- わたしもやってみた -』
&lt;/a&gt;
で構築した&lt;br /&gt;
Primary Domain Controller に CentOS をドメイン参加させたいと思います。&lt;br /&gt;
ドメインコントローラーから見るとコンピューターアカウントになります。&lt;br /&gt;
前提としては、 Primary Domain Controller の構築が終わっていること&lt;br /&gt;
そして Samba4 と Bind のインストール、NTP の時間合わせが済んでいることとします。&lt;br /&gt;
まだの場合は、
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory.html&quot;&gt;
 『Samba4 で Active Directory / インストール編 - わたしもやってみた -』
&lt;/a&gt;
を参考に&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;Samba4 HOWTO&lt;/a&gt; にある
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/Domain_Member&quot;&gt;Domain_Member&lt;/a&gt; をベースに&lt;br /&gt;
先人の方々の情報も踏まえつつ、わたしもやってみたいと思います。&lt;br /&gt;
ではでは Getting Started!!
&lt;/p&gt;


&lt;h2&gt;環境&lt;/h2&gt;
&lt;b&gt;Domain&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Primary Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;127.0.0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Member Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;br /&gt;VMware Player 5.0.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;mbr01&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.100&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;環境設定&lt;/h2&gt;
&lt;p&gt;ネットワークを設定する。&lt;/p&gt;

&lt;p&gt;
まず IP の設定&lt;br /&gt;
/etc/sysconfig/network-scripts ディレクトリにある ifcfg-eth0 ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.100
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;

&lt;p&gt;
次にホスト名の設定&lt;br /&gt;
/etc/sysconfig ディレクトリにある network ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=mbr01
&lt;/pre&gt;

&lt;p&gt;
そして IP とホスト名の関連付け&lt;br /&gt;
/etc ディレクトリにある hosts ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.100 mbr01 mbr01.mydomain.local
&lt;/pre&gt;

&lt;p&gt;今回は Primary Server の DNS を利用しようと思うので Bind を止めておく。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service named stop
chkconfig named off
&lt;/pre&gt;

&lt;p&gt;
そして DNS を Primary Server に設定する。&lt;br /&gt;
/etc ディレクトリにある resolv.conf を以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 192.168.0.10
&lt;/pre&gt;

&lt;p&gt;ここで一度、再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;

&lt;p&gt;ホスト名と IP 確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
hostname
ping mbr01
ping mbr01.mydomain.local
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Kerberos の設定&lt;/h2&gt;
&lt;p&gt;
Kerberos の設定ファイル krb5.conf を差し替える。&lt;br /&gt;
とりあえず、今の設定をバックアップして
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf_org
&lt;/pre&gt;

&lt;p&gt;そして以下のような内容で /etc ディレクトリに krb5.conf を作成する。&lt;/p&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[logging]
  default = FILE:/var/log/krb5libs.log
  kdc = FILE:/var/log/krb5kdc.log
  admin_server = FILE:/var/log/kadmind.log

[libdefaults]
  default_realm = MYDOMAIN.LOCAL
  dns_lookup_realm = true
  dns_lookup_kdc = true
  ticket_lifetime = 24h
  forwardable = yes

[appdefaults]
  pam = {
    debug = false
    ticket_lifetime = 36000
    renew_lifetime = 36000
    forwardable = true
    krb4_convert = false
  }
&lt;/pre&gt;

&lt;p&gt;
ここで ログインと チケットの一覧を確認しておく。&lt;br /&gt;
administrator のパスワードは、 Primary Domain Controller 構築の時に&lt;br /&gt;
samba-tool domain provision で指定したものになる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Samba4 の設定&lt;/h2&gt;
&lt;p&gt;
 Member Server  の設定をする。&lt;br /&gt;
とりあえず、samba-tool domain provision を使って&lt;br /&gt;
smb.conf を生成することにした。&lt;br /&gt;
/usr/local/samba/etc ディレクトリに smb.conf が無いことを確認する。&lt;br /&gt;
有れば削除する。&lt;br /&gt;
では、以下のコマンドで、smb.conf を生成します。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool domain provision\
  --realm=MYDOMAIN.LOCAL \
  --domain=MYDOMAIN \
  --server-role=member \
  --dns-backend=NONE \
  --adminpass &#39;passwd-123&#39;
&lt;/pre&gt;

&lt;p&gt;生成された smb.conf を以下のように変更する。&lt;/p&gt;
&lt;u&gt;/usr/local/samba/etc/smb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[global]
  workgroup = MYDOMAIN
  security = ads
  realm = MYDOMAIN.LOCAL
  encrypt passwords = yes

  netbios name = MBR01
  server role = member server
  passdb backend = samba_dsdb
  server services = s3fs, rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbind, ntp_signd, kcc, dnsupdate

  idmap config *:backend = tdb
  idmap config *:range = 70001-80000

  #idmap config MYDOMAIN:backend = ad
  #idmap config MYDOMAIN:schema_mode = rfc2307
  #idmap config MYDOMAIN:range = 500-40000

  idmap config MYDOMAIN:backend = rid
  idmap config MYDOMAIN:range = 10000 - 19999

  #winbind nss info = rfc2307
  #winbind trusted domains only = no

  winbind use default domain = yes
  winbind enum users  = yes
  winbind enum groups = yes

  template homedir = /home/%U
  template shell = /bin/bash
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;smb.conf について&lt;/span&gt;
&lt;p&gt;
 &lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;idmap config MYDOMAIN:backend = rid&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
   RID で id と gid のマップングをすることにした。&lt;br /&gt;
   MYDOMAIN ユーザの id と gid は、10000 ～ 19999 の範囲に&lt;br /&gt;
   それ以外は、70001 ～ 80000 の範囲にマップングするようにした。
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;template homedir = /home/%U&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
   Samba4 をドメインコントローラーで構成した場合、&lt;br /&gt;
   %U がユーザ名に展開されず、/home ディレクトリ 直下に %U ディレクトリができしまったのだけど&lt;br /&gt;
   Member Server の場合、これが問題なく展開された。&lt;br /&gt;
   ユーザディレクトリは /home 以下に生成されます。
  &lt;/dd&gt;
 &lt;/dl&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
今回は、Samba4 を使わず Winbind を使うことになりそうなので&lt;br /&gt;
Samba4 を止めておく。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service samba4 stop
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Winbind について&lt;/span&gt;
&lt;p&gt;
Member Server として、Samba4 を構成する場合、&lt;br /&gt;
直接 winbind を使うことになるようだ。&lt;br /&gt;
ソースは、Samba4 のものをビルドしているけど&lt;br /&gt;
これって Samba3 なの？
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;最後に Member Server としてドメイン参加する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/net ads join -U administrator
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Winbind の設定&lt;/h2&gt;
&lt;p&gt;モジュールをコピーする。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cp /usr/local/samba/lib/libnss_winbind.so /lib
ln -s /lib/libnss_winbind.so /lib/libnss_winbind.so.2
ln -s /usr/local/samba/lib/security/pam_winbind.so /lib/security
ldconfig
&lt;/pre&gt;

&lt;p&gt;この状態で authconfig を実行すれば Winbind の PAM 関連のファイルがいい感じに更新される。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
authconfig --enablewinbindauth --update
&lt;/pre&gt;

&lt;p&gt;もう一点、初回ログイン時にホームディレクトリが自動作成されるに authconfig しておく。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
authconfig --enablemkhomedir --update
&lt;/pre&gt;

&lt;p&gt;
nsswitch.conf を編集する。&lt;br /&gt;
/etc ディレクトリにある nsswitch.conf を以下の部分を変更する。
&lt;/p&gt;
&lt;u&gt;/etc/nsswitch.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
passwd: compat winbind
group:  compat winbind
...
&lt;/pre&gt;

&lt;p&gt;
やっぱり、起動スクリプトは、ほしいということで&lt;br /&gt;
yum でインストールされる samba 3.6.9 の Winbind の起動スクリプトを元に作成した。&lt;br /&gt;
変更したところは、smb.conf と winbindd のパスになる。&lt;br /&gt;
/etc/rc.d/init.d ディレクトリに 以下のような内容でファイルを作成し winbind の名前で保存する。&lt;/p&gt;
&lt;/p&gt;
&lt;u&gt;/etc/rc.d/init.d/winbind&lt;/u&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
#!/bin/sh
#
# chkconfig: - 27 73
# description: Starts and stops the Samba winbind daemon
#        #
# pidfile: /var/run/winbindd.pid
# config:  /usr/local/samba/etc/smb.conf


# Source function library.
. /etc/rc.d/init.d/functions

# Avoid using root&#39;s TMPDIR
unset TMPDIR

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ ${NETWORKING} = &quot;no&quot; ] &amp;&amp; exit 1

# Check that smb.conf exists.
[ -f /usr/local/samba/etc/smb.conf ] || exit 6

[ -f /etc/sysconfig/samba ] &amp;&amp; . /etc/sysconfig/samba

RETVAL=0


start() {
        KIND=&quot;Winbind&quot;
 echo -n $&quot;Starting $KIND services: &quot;
 daemon /usr/local/samba/sbin/winbindd &quot;$WINBINDOPTIONS&quot;
 RETVAL=$?
 echo
 [ $RETVAL -eq 0 ] &amp;&amp; touch /var/lock/subsys/winbindd || RETVAL=1
 return $RETVAL
} 

stop() {
        echo
 KIND=&quot;Winbind&quot;
 echo -n $&quot;Shutting down $KIND services: &quot;
 killproc winbindd
 RETVAL=$?
 [ $RETVAL -eq 0 ] &amp;&amp; rm -f /var/lock/subsys/winbindd
 echo &quot;&quot;
 return $RETVAL
} 

restart() {
 stop
 start
} 

reload() {
        echo -n $&quot;Reloading smb.conf file: &quot;
 killproc winbindd -HUP
 RETVAL=$?
 echo
 return $RETVAL
} 

rhstatus() {
 status winbindd
 return $?
} 

# Allow status as non-root.
if [ &quot;$1&quot; = status ]; then
       rhstatus
       exit $?
fi

# Check that we can write to it... so non-root users stop here
[ -w /usr/local/samba/etc/smb.conf ] || exit 4

case &quot;$1&quot; in
  start)
   start
 ;;
  stop)
   stop
 ;;
  restart)
   restart
 ;;
  reload)
   reload
 ;;
  status)
   rhstatus
 ;;
  condrestart)
   [ -f /var/lock/subsys/winbindd ] &amp;&amp; restart || :
 ;;
  *)
 echo $&quot;Usage: $0 {start|stop|restart|reload|status|condrestart}&quot;
 exit 2
esac

exit $?
&lt;/pre&gt;

&lt;p&gt;サービスに登録しておく。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chmod 755 /etc/rc.d/init.d/winbind
chkconfig --add winbind
chkconfig winbind on
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;smbd と nmbd について&lt;/span&gt;
&lt;p&gt;
Samba4 を Member Server として構成しファイル、プリンタ共有をする場合、&lt;br /&gt;
smbd と nmbd を直接使うことになるようだ。&lt;br /&gt;
うむむ… ここまでくると、Member Server  の場合は、&lt;br /&gt;
yum で samba3 をインストールする方が無難なのか？
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;


&lt;h2&gt;CentOS でドメインログイン&lt;/h2&gt;
&lt;p&gt;まず、Winbind を起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service winbind start
&lt;/pre&gt;

&lt;p&gt;とりあえず、ドメインユーザとグループを確認しておこう。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/wbinfo -u
/usr/local/samba/bin/wbinfo -g
&lt;/pre&gt;

&lt;p&gt;では！ドメインログイン！！&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh administrator@mbr01
&lt;/pre&gt;

&lt;p&gt;
uid と gid のマッピングはキャッシュされるようで&lt;br /&gt;
マッピングを変えてもすぐに反映されない。&lt;br /&gt;
そんな時は、以下のようにキャッシュを確認して
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/net cache list
&lt;/pre&gt;

&lt;p&gt;そして、削除！&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/net cache flush
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;
  

&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
なんだかんだで Samba4 はいい感じになっていると思う。&lt;br /&gt;
けど今後、まだ、どんどん進化していきそう。&lt;br /&gt;
とり残されないようにウォッチしていこう。&lt;br /&gt;
&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;
    https://wiki.samba.org/index.php/Samba4
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/Domain_Member&quot;&gt;
    https://wiki.samba.org/index.php/Samba4/Domain_Member
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1532454918396254944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1532454918396254944'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/04/samba4-active-directory-member-server.html' title='Samba4 で Active Directory / Member Server 編 - わたしもやってみた -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-4909456223632668873</id><published>2013-04-21T22:00:00.000+09:00</published><updated>2013-04-23T16:01:25.325+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><category scheme="http://www.blogger.com/atom/ns#" term="わたしもやってみた"/><title type='text'>Samba4 で Active Directory / BDC編 - わたしもやってみた -</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;p&gt;
今回は、Backup Domain Controller を構築したいと思います。&lt;br /&gt;
そして
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory-pdc.html&quot;&gt;
 『Samba4 で Active Directory / PDC編- わたしもやってみた -』
&lt;/a&gt;
で構築した&lt;br /&gt;
Primary Domain Controller にドメイン参加させたいと思います。&lt;br /&gt;
前提としては、Primary Domain Controller の構築が終わっていること&lt;br /&gt;
そして Samba4 と Bind のインストール、NTP の時間合わせが済んでいることになります。&lt;br /&gt;
まだの場合は、
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory.html&quot;&gt;
 『Samba4 で Active Directory / インストール編 - わたしもやってみた -』
&lt;/a&gt;
を参考に&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;Samba4 HOWTO&lt;/a&gt; にある
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/HOWTO/Join_a_domain_as_a_DC&quot;&gt;Join_a_domain_as_a_DC&lt;/a&gt; をベースに&lt;br /&gt;
先人の方々の情報も踏まえつつ、わたしもやってみたいと思います。&lt;br /&gt;
ではでは Getting Started!!
&lt;/p&gt;

&lt;h2&gt;環境&lt;/h2&gt;
&lt;b&gt;Domain&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Primary Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;127.0.0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Backup Server&lt;/b&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;br /&gt;VMware Player 5.0.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;bdc01&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.11&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;環境設定&lt;/h2&gt;
&lt;p&gt;ネットワークを設定する。&lt;/p&gt;

&lt;p&gt;
まず IP の設定&lt;br /&gt;
/etc/sysconfig/network-scripts ディレクトリにある ifcfg-eth0 ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.11
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;

&lt;p&gt;
次にホスト名の設定&lt;br /&gt;
/etc/sysconfig ディレクトリにある network ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=bdc01
&lt;/pre&gt;

&lt;p&gt;今回は Primary Server の DNS を利用しようと思うので Bind を止めておく。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service named stop
chkconfig named off
&lt;/pre&gt;

&lt;p&gt;
そして DNS を Primary Server に設定する。&lt;br /&gt;
/etc ディレクトリにある resolv.conf を以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 192.168.0.10
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;DNS について&lt;/span&gt;
&lt;p&gt;
今回、Primary Server に Primary Domain Controller と DNSを置いているけど&lt;br /&gt;
ホントは、別のサーバーにした方がいいのだろうと思う。&lt;br /&gt;
せっかくバックアップを構築していても Primary Server が落ちたら&lt;br /&gt;
DNS も死ぬので Backup Domain Controller もうまく動かなくなるのかなぁと思う。&lt;br /&gt;
とりあえず、データのバックアップは、しているのだし&lt;br /&gt;
自動フェイルオーバーまで考えなければ問題ないか？
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;ここで一度、再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;


&lt;h2&gt;Kerberos の設定&lt;/h2&gt;
&lt;p&gt;
Kerberos の設定ファイル krb5.conf を差し替える。&lt;br /&gt;
とりあえず、今の設定をバックアップして
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf_org
&lt;/pre&gt;

&lt;p&gt;そして以下のような内容で /etc ディレクトリに krb5.conf を作成する。&lt;/p&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[libdefaults]
  dns_lookup_realm = true
  dns_lookup_kdc = true
  default_realm = MYDOMAIN.LOCAL
&lt;/pre&gt;

&lt;p&gt;
ここで ログインと チケットの一覧を確認しておく。&lt;br /&gt;
administrator のパスワードは、 Primary Domain Controller 構築の時に&lt;br /&gt;
samba-tool domain provision で指定したものになる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;p&gt;


&lt;h2&gt;Samba4 の設定&lt;/h2&gt;
&lt;p&gt;
Backup Domain Controller の設定に入ります。&lt;br /&gt;
ここでのポイントは、smb.conf や その他諸々のファイルの生成に&lt;br /&gt;
samba-tool domain provision を使わないことです。&lt;br /&gt;
代わりに samba-tool domain join を使います。 &lt;br /&gt;
/usr/local/samba/etc ディレクトリに smb.conf が無いことを確認する。&lt;br /&gt;
有れば削除する。&lt;br /&gt;
念のため Samba4 を停止しておく。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service samba4 stop
&lt;/pre&gt;

&lt;p&gt;では、以下のコマンドで、smb.conf や その他諸々のファイルを生成します。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool domain join mydomain.local DC \
  -Uadministrator \
  --realm=mydomain.local \
  --dns-backend=BIND9_DLZ
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;オプションについて&lt;/span&gt;
&lt;p&gt;
--dns-backend=BIND9_DLZ を指定しているけど&lt;br /&gt;
今回は、Backup Server の DNS を使用していないので不要かも？&lt;br /&gt;
とりあえず Bind 関連のファイルを生成しておくことにした。
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;smb.conf は、以下のようになる。&lt;/p&gt;
&lt;u&gt;/usr/local/samba/etc/smb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
# Global parameters
[global]
  workgroup = MYDOMAIN
  realm = mydomain.local
  netbios name = BDC01
  server role = active directory domain controller
  server services = s3fs, rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbind, ntp_signd, kcc, dnsupdate

[netlogon]
  path = /usr/local/samba/var/locks/sysvol/mydomain.local/scripts
  read only = No

[sysvol]
  path = /usr/local/samba/var/locks/sysvol
  read only = No
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;DNS の設定&lt;/h2&gt;
&lt;p&gt;
Backup Server の IP、ホスト名を DNS に登録していきます。&lt;br /&gt;
まず、IP とホスト名の関連付けから
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool dns add \
  192.168.0.10 \
  mydomain.local \
  bdc01 A 192.168.0.11 \
  -Uadministrator
&lt;/pre&gt;

&lt;p&gt;登録できたか確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
host -t A bdc01.mydomain.local.
&lt;/pre&gt;

&lt;p&gt;
 Backup Domain Controller の objectGUID とホスト名の関連付けもする。&lt;br /&gt;
ldbsearch で objectGUID を調べる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/ldbsearch \
  -H /usr/local/samba/private/sam.ldb \
  &#39;(invocationid=*)&#39; \
  --cross-ncs objectguid
&lt;/pre&gt;

&lt;p&gt;
ここで CN=BDC01 の record を探す。&lt;br /&gt;
objectGUID が 737506d0-bfe6-40c8-815d-08c3dff7a67f だった場合&lt;br /&gt;
以下のような感じで objectGUID とホスト名の関連付けをする。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool dns add \
  192.168.0.10 \
  _msdcs.mydomain.local \
  737506d0-bfe6-40c8-815d-08c3dff7a67f \
  CNAME bdc01.mydomain.local \
  -Uadministrator
&lt;/pre&gt;

&lt;p&gt;登録できたか確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
host -t CNAME 737506d0-bfe6-40c8-815d-08c3dff7a67f._msdcs.mydomain.local.
&lt;/pre&gt;

&lt;p&gt;一応 ホスト名と ping で IP の確認もしておこう。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
hostname
ping bdc01
ping bdc01.mydomain.local
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;レプリケーションの確認&lt;/h2&gt;
&lt;p&gt;とりあえず、Primary Domain Controller のSamba4、Bind を再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh root@pdc
service named restart
service samba4 restart
exit
&lt;/pre&gt;

&lt;p&gt; Backup Domain Controller のSamba4、も再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service samba4 restart
&lt;/pre&gt;

&lt;p&gt;レプリケーションを確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool drs showrepl
&lt;/pre&gt;

&lt;p&gt;
Primary Domain Controller で追加したドメインユーザが&lt;br /&gt;
Backup Domain Controller にレプリケートされるか確認する。&lt;br /&gt;
まず、Primary Domain Controller でドメインユーザを追加する。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh root@pdc
/usr/local/samba/bin/samba-tool user add user01
exit
&lt;/pre&gt;

&lt;p&gt;そして Backup Domain Controller で確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/ldbsearch \
  -H /usr/local/samba/private/sam.ldb \
  samaccountname=user01
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;CentOS でドメインログイン&lt;/h2&gt;
&lt;p&gt;
Backup Domain Controller の場合も&lt;br /&gt;
Primary Domain Controller と同じく&lt;br /&gt;
ドメインユーザー情報に posixAccount スキーマ情報を追加して、&lt;br /&gt;
Winbind と PAM の設定が必要になるようです。&lt;br /&gt;
このあたりは、
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory-pdc.html#centos_domain_login&quot;&gt;
 『Samba4 で Active Directory / インストール編 - わたしもやってみた -』
&lt;/a&gt;
を参考に
&lt;/p&gt;
&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4909456223632668873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4909456223632668873'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/04/samba4-active-directory-bdc.html' title='Samba4 で Active Directory / BDC編 - わたしもやってみた -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-6502164230792862824</id><published>2013-04-20T22:00:00.000+09:00</published><updated>2013-04-23T16:00:40.020+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><category scheme="http://www.blogger.com/atom/ns#" term="わたしもやってみた"/><title type='text'>Samba4 で Active Directory / PDC編 - わたしもやってみた -</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;p&gt;
ここからが本番！ Primary Domain Controller を構築したいと思います。&lt;br /&gt;
前提としては、Samba4 と Bind のインストール、 NTP の時間合わせが済んでいる済んでいることになります。&lt;br /&gt;
まだの場合は、
&lt;a href=&quot;http://fummy-net.blogspot.jp/2013/04/samba4-active-directory.html&quot;&gt;
 『Samba4 で Active Directory / インストール編 - わたしもやってみた -』
&lt;/a&gt;
を参考に&lt;br /&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;Samba4 HOWTO&lt;/a&gt; にある
&lt;a href=&quot;http://wiki.samba.org/index.php/Samba4/HOWTO&quot;&gt;Samba AD DC HOWTO&lt;/a&gt; をベースに&lt;br /&gt;
先人の方々の情報も踏まえつつ、わたしもやってみたいと思います。&lt;br /&gt;
ではでは Getting Started!!
&lt;/p&gt;

&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;レルム&lt;/th&gt;
    &lt;td&gt;MYDOMAIN.LOCAL&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ドメイン&lt;/th&gt;
    &lt;td&gt;MYDOMAIN&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Host&lt;/th&gt;
    &lt;td&gt;pdc&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;IP&lt;/th&gt;
    &lt;td&gt;192.168.0.10&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Gateway&lt;/th&gt;
    &lt;td&gt;192.168.0.1&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;DNS&lt;/th&gt;
    &lt;td&gt;127.0.0.1&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;環境設定&lt;/h2&gt;
&lt;p&gt;ネットワークを設定する。&lt;/p&gt;


&lt;p&gt;
まず IP の設定&lt;br /&gt;
/etc/sysconfig/network-scripts ディレクトリにある ifcfg-eth0 ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.0.10
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
...
&lt;/pre&gt;

&lt;p&gt;
次にホスト名の設定&lt;br /&gt;
/etc/sysconfig ディレクトリにある network ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/sysconfig/network&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=pdc
&lt;/pre&gt;

&lt;p&gt;
そして IP とホスト名の関連付け&lt;br /&gt;
/etc ディレクトリにある hosts ファイルを以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.10 pdc pdc.mydomain.local
&lt;/pre&gt;

&lt;p&gt;ここで一度、再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
reboot
&lt;/pre&gt;

&lt;p&gt;ホスト名と IP 確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
hostname
ping pdc
ping pdc.mydomain.local
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Samba4 の設定&lt;/h2&gt;
&lt;p&gt;
さて… ドメインコントローラーの設定をしますかぁ&lt;br /&gt;
/usr/local/samba/etc ディレクトリに smb.conf が無いことを確認する。&lt;br /&gt;
有れば削除する。&lt;br /&gt;
Server Role は ドメインコントローラー とします。&lt;br /&gt;
バックエンドの DNS は Bind とします。&lt;br /&gt;
では、以下のコマンドで、smb.conf や その他諸々のファイルを生成します。
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/samba-tool domain provision \
  --use-rfc2307 \
  --realm=MYDOMAIN.LOCAL \
  --domain=MYDOMAIN \
  --server-role=dc \
  --dns-backend=BIND9_DLZ \
  --adminpass &#39;passwd-123&#39;
&lt;/pre&gt;
&lt;/p&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;オプションについて&lt;/span&gt;
&lt;p&gt;
ここでのミソは、--use-rfc2307 を指定しているところです。&lt;br /&gt;
これを指定しておくと CentOS で自身のドメインにログインするのに &lt;br /&gt;
必要な設定を smb.conf に出力してくれるようです。
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;とりあえず、生成された smb.conf を確認してみる。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/testparm
&lt;/pre&gt;

&lt;p&gt;Kerberos の設定ファイル krb5.conf を差し替える。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mv /etc/krb5.conf /etc/krb5.conf_org
cp /usr/local/samba/private/krb5.conf /etc/krb5.conf
&lt;/pre&gt;

&lt;p&gt;krb5.conf は以下のような感じ。&lt;/p&gt;
&lt;u&gt;/etc/krb5.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
[libdefaults]
  default_realm = MYDOMAIN.LOCAL
  dns_lookup_realm = false
  dns_lookup_kdc = true
&lt;/pre&gt;
&lt;br /&gt;


&lt;h2&gt;Bind の設定&lt;/h2&gt;
&lt;p&gt;/etc ディレクトリにある named.conf を開き以下の部分を追加、変更する。&lt;/p&gt;
&lt;u&gt;/etc/hosts&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
options {
  ...
  listen-on port 53 { any; };
  ...
  allow-query     { any; };
  ...
  tkey-gssapi-keytab &quot;/usr/local/samba/private/dns.keytab&quot;;
};
...
include &quot;/usr/local/samba/private/named.conf&quot;;
&lt;/pre&gt;

&lt;p&gt;dns.keytab の権限を変更する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chgrp named /usr/local/samba/private/dns.keytab
chmod g+r /usr/local/samba/private/dns.keytab
&lt;/pre&gt;

&lt;p&gt;/etc/rrndc.key を生成する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
rndc-confgen -a -r /dev/urandom
&lt;/pre&gt;

&lt;p&gt;
resolv.conf の設定をする。
/etc ディレクトリにある resolv.conf を開き以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/resolv.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
domain mydomain.local
nameserver 127.0.0.1
&lt;/pre&gt;

&lt;p&gt;サービスの起動設定をする。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chkconfig named on
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;確認&lt;/h2&gt;
&lt;p&gt;とりあえず、Samba4、Bind を再起動する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service samba4 restart
service named restart
&lt;/pre&gt;

&lt;p&gt;DNS を更新する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/sbin/samba_dnsupdate --verbose --all-names
&lt;/pre&gt;

&lt;p&gt;NTP と連動するために ntp_signd ディレクトリの権限を変更する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chmod 750 /usr/local/samba/var/lib/ntp_signd
&lt;/pre&gt;

&lt;p&gt;
Samba4、Kerberos、Bind の設定を確認する。&lt;br /&gt;
まず、Bind から&lt;br /&gt;
以下のコマンドでホスト、IP が返ってくるか確認する。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
host -t SRV _ldap._tcp.mydomain.local.
host -t SRV _kerberos._udp.mydomain.local.
host -t A pdc.mydomain.local.
&lt;/pre&gt;
&lt;p&gt;ここでうまくいかない場合は、named.conf、resolv.conf、hosts を再確認して DNS を再更新してみる。&lt;/p&gt;

&lt;p&gt;
次に Kerberos の確認をする。&lt;br /&gt;
以下のコマンドで ログインと チケットの一覧を確認する。&lt;br /&gt;
administrator のパスワードは、samba-tool domain provision で指定したものになる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
kinit administrator@MYDOMAIN.LOCAL
klist
&lt;/pre&gt;
&lt;p&gt;
ここでうまくいかない場合は… う～ん krb5.conf かなぁ？ &lt;br /&gt;
それとも smb.conf を削除して、Samba4 の設定からやり直し？？
&lt;/p&gt;

&lt;p&gt;
最後に Samba4 の確認をする。&lt;br /&gt;
以下のコマンドで ログインしてみる。&lt;br /&gt;
administrator のパスワードは、samba-tool domain provision で指定したものになる。&lt;br /&gt;
quit で終了できる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/smbclient //localhost/netlogon -Uadministrator
&lt;/pre&gt;
&lt;p&gt;ここでうまくいかない場合は… 今回は無かったことに…&lt;/p&gt;
&lt;br /&gt;


&lt;h2&gt;Windows XP でドメインログイン&lt;/h2&gt;
&lt;p&gt;Windows XP からドメインにログインしてみる。&lt;/p&gt;

&lt;p&gt;
コントロールパネル / ネットワーク接続 で DNS のIPを 192.168.0.10 (PDC)にする。&lt;br /&gt;
コントロールパネル / システム で コンピューター名タブを選択する。
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: none;&quot;&gt;
 &lt;li&gt;「変更」ボタンを押す。&lt;/li&gt;
 &lt;li&gt;ドメインに「MYDOMAIN.LOCAL」を入力し「OK」ボタンを押す。
  &lt;ul style=&quot;list-style-type: none;&quot;&gt;
   &lt;li&gt;ユーザ / パスワードは以下のものを入力する。&lt;/li&gt;
   &lt;ul style=&quot;list-style-type: none;&quot;&gt;
    &lt;li&gt;ユーザ名  ：Administrator&lt;/li&gt;
    &lt;li&gt;パスワード：Samba4のAdministratorのパスワード&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/ul&gt;
 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Windows を再起動する。&lt;br /&gt;
Windows のログイン画面でログイン先に MYDOMAIN を選択し Samba4 のAdministratorでログインする。
&lt;/p&gt;
&lt;br /&gt;


&lt;h2 id=&quot;centos_domain_login&quot;&gt;CentOS でドメインログイン&lt;/h2&gt;
&lt;p&gt;CentOS で自身のドメインにログインしてみる。&lt;/p&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh administrator@pdc
&lt;/pre&gt;
&lt;p&gt;
これがうまくいかない。&lt;br /&gt;
さてさて、ここからが今回の山場！&lt;br /&gt;
Winbind と posixAccount の設定をしなくては、ならないようだ。
&lt;/p&gt;

&lt;p&gt;
最初に Winbind と PAM の設定をする。 &lt;br /&gt;
まずは モジュールのコピー
&lt;/p&gt;

&lt;pre class=&quot;fummy_screen&quot;&gt;
ln -s /usr/local/samba/lib/libnss_winbind.so.2 /lib/libnss_winbind.so
ln -s /lib/libnss_winbind.so /lib/libnss_winbind.so.2
ln -s /usr/local/samba/lib/security/pam_winbind.so /lib/security
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;libnss_winbind.so.2 と pam_winbind.so について&lt;/span&gt;
&lt;p&gt;
これらのモジュールがインストールされていない場合があった。&lt;br /&gt;
きっと、再 make になるのだろうけど その際は、configure.developer を使うのだろうか？&lt;br /&gt;
configure の時に --with-ads --with-shared-modules=idmap_ad オプションを&lt;br /&gt;
指定しておくと とりあえず、インストールはされた。&lt;br /&gt;
openldap-devel がインストールされていれば、&lt;br /&gt;
特にオプションを指定しなくてもインストールされるのかもしれない。&lt;br /&gt;
ビルドスクリプトを解析するば分かるのだろうけど、そこまでは…
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;この状態で authconfig を実行すれば Winbind の PAM 関連のファイルがいい感じに更新される。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
authconfig --enablewinbindauth --update
&lt;/pre&gt;

&lt;p&gt;もう一点、初回ログイン時にホームディレクトリが自動作成されるに authconfig しておく。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
authconfig --enablemkhomedir --update
&lt;/pre&gt;
&lt;p&gt;
/etc/pam.d ディレクトリにある password-auth-ac、system-auth-ac&lt;br /&gt;
あたりがいい感じになっているはず。&lt;br /&gt;
合わせて /etc/nsswitch.conf を以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/nsswitch.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
passwd:     files winbind
group:      files winbind
shadow:     files
...
&lt;/pre&gt;

&lt;p&gt;
次に posixAccount スキーマ情報をドメインユーザに追加する。&lt;br /&gt;
ドメインユーザでCentOSにログインするには、posixAccount スキーマ情報が必要になるようだ。&lt;br /&gt;
Active Directory に登録されているドメインユーザ情報には、これがない。&lt;br /&gt;
とりあえず、administrator に posixAccount スキーマ を追加して gidNumber と uidNumber を登録してみる。&lt;br /&gt;
gidNumber と uidNumber は、ドメインユーザ情報の primaryGroupID と objectSid から決定する。&lt;br /&gt;
Samba4 には、LDAP 関連のコマンドが含まれているのでこれを利用する。&lt;br /&gt;
まず administrator のドメインユーザ情報を表示して primaryGroupID と objectSid を調べる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/ldbsearch -H /usr/local/samba/private/sam.ldb samaccountname=administrator
&lt;/pre&gt;

&lt;p&gt;
次に /root/work ディレクトリあたりに addPosixAcount.ldif 的なファイルを以下の内容で作成する。
&lt;/p&gt;

&lt;u&gt;/root/work/addPosixAcount.ldif&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
dn: cn=Administrator,cn=Users,dc=mydomain,dc=local
changetype: modify
add: objectclass
objectclass: posixAccount
-
add: gidNumber
gidNumber: 10513
-
add: uidNumber
uidNumber: 10500
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;gidNumber と uidNumber について&lt;/span&gt;
&lt;p&gt;
gidNumber は、ドメインユーザ情報の primaryGroupID を元に&lt;br /&gt;
また、uidNumber は、ドメインユーザ情報の objectSid の下４桁(ハイフン「-」で区切られた最後の部分)を元にして&lt;br /&gt;
RID のルールを想定して 10000 ～ 19999 の範囲に割り振るようにした。&lt;br /&gt;
例えば primaryGroupID が 513 であれば、10513 に objectSid の下４桁が 500 であれば 10500 にという感じになる。
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
そして ldbmodify コマンドで addPosixAcount.ldif ファイルの内容を LDAP に反映する。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/ldbmodify -H /usr/local/samba/private/sam.ldb /root/work/addPosixAcount.ldif
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;msSFU30XXX スキーマ情報&lt;/span&gt;
&lt;p&gt;
msSFU30XXX スキーマ情報の拡張がされていれば、posixAcount スキーマ情報を追加しなくても&lt;br /&gt;
CentOS でドメインにログインできるのかもしれない。&lt;br /&gt;
新規に追加したユーザーに posixAcount スキーマ情報を追加しなくてもドメインにログインできたからだ。&lt;br /&gt;
なぜか administrator では、無理だった。&lt;br /&gt;
わたしの検証環境は、msSFU30XXX スキーマ情報の拡張された状態になっていると思う。&lt;br /&gt;
いろいろ試行錯誤していて Windows のツールでもって msSFU30XXX スキーマ情報を拡張したと思っている。&lt;br /&gt;
いつかの時点までは、新規に追加したユーザーでも posixAcount スキーマ情報がなければ&lt;br /&gt;
ドメインにログインできなかったように思う。&lt;br /&gt;
それも、今となっては、定かではない。&lt;br /&gt;
とりあえず、全部の PC で uid と gid が統一できるので posixAcount スキーマ情報は、&lt;br /&gt;
追加しておく方がよいと思う。(まぁ gid は微妙ですが…)
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;smb.conf は、以下のようにしておく。&lt;/p&gt;
&lt;u&gt;/usr/local/samba/etc/smb.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
# Global parameters
[global]
  workgroup = MYDOMAIN
  realm = MYDOMAIN.LOCAL
  netbios name = PDC
  server role = active directory domain controller
  server services = s3fs, rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbind, ntp_signd, kcc, dnsupdate
  idmap_ldb:use rfc2307 = yes

  winbind use default domain = yes

  #template homedir = /home/%U
  template shell = /bin/bash

[netlogon]
  path = /usr/local/samba/var/locks/sysvol/mydomain.local/scripts
  read only = No

[sysvol]
  path = /usr/local/samba/var/locks/sysvol
  read only = No
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;smb.conf について&lt;/span&gt;
&lt;p&gt;
 &lt;dl&gt;
  &lt;dt&gt;&lt;b&gt;idmap_ldb:use rfc2307 = yes&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
   この設定が漏れていたため、なかなかドメインログインできず結構ハマった。&lt;br /&gt;
   samba-tool domain provision の時に --use-rfc2307 オプションを指定してさえいれば…
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;template homedir = /home/%U&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
   %U がユーザ名に展開されず、/home ディレクトリ 直下に %U ディレクトリができしまう…&lt;br /&gt;
   設定が抜けているのか ビルドに失敗しているのか バグ？なのか&lt;br /&gt;
   とりあえず、触らぬ神に祟り無しでデフォルトのままにしています。&lt;br /&gt;
   ユーザディレクトリは /home/MYDOMAIN 以下に生成されます。
  &lt;/dd&gt;
  &lt;dt&gt;&lt;b&gt;gidNumber&lt;/b&gt;&lt;/dt&gt;
  &lt;dd&gt;
   なぜが gid に反映されない。&lt;br /&gt;
   これもよく分かりません…&lt;br /&gt;
   まぁサーバーごとに違う gid になる訳でないのでいいかな？
  &lt;/dd&gt;
 &lt;/dl&gt;
&lt;/p&gt;
&lt;/div&gt;



&lt;p&gt;ドメインログイン前に Samba4 を再起動して&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service samba4 restart
&lt;/pre&gt;

&lt;p&gt;ログイン可能なユーザなんかを表示したりしてみて&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
getent passwd
&lt;/pre&gt;
&lt;p&gt;

&lt;p&gt;ドメインログイン！！&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ssh administrator@pdc
&lt;/pre&gt;
&lt;p&gt;

&lt;p&gt;最後に uid と gid を確認しておこう。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
id
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
これで、Active Directory を学んでいくには十分過ぎる環境が揃ったと思う。&lt;br /&gt;
微妙なところがいくつか残ったがこのあたりは追々潰していこう。&lt;br /&gt;
新しいのと入れ替えると解消されるかなぁ。&lt;br /&gt;
なぁ～んて。
&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;
    https://wiki.samba.org/index.php/Samba4
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://wiki.samba.org/index.php/Samba4/HOWTO&quot;&gt;
   http://wiki.samba.org/index.php/Samba4/HOWTO
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/Winbind&quot;&gt;
   https://wiki.samba.org/index.php/Samba4/Winbind
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://opentodo.net/2013/01/samba4-as-ad-domain-controller-on-centos-6/&quot;&gt;
 http://opentodo.net/2013/01/samba4-as-ad-domain-controller-on-centos-6/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://wiki.samba.gr.jp/mediawiki/index.php&quot;&gt;
    http://wiki.samba.gr.jp/mediawiki/index.php
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://d.hatena.ne.jp/rti7743/20110425/1303688263&quot;&gt;
    http://d.hatena.ne.jp/rti7743/20110425/1303688263
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://d.hatena.ne.jp/dicdak/20090628/1246206482&quot;&gt;
    http://d.hatena.ne.jp/dicdak/20090628/1246206482
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://web.chaperone.jp/w/index.php?samba%2Fuser&quot;&gt;
    http://web.chaperone.jp/w/index.php?samba%2Fuser
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://itpro.nikkeibp.co.jp/article/COLUMN/20070125/259692/&quot;&gt;
    http://itpro.nikkeibp.co.jp/article/COLUMN/20070125/259692/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6502164230792862824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/6502164230792862824'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/04/samba4-active-directory-pdc.html' title='Samba4 で Active Directory / PDC編 - わたしもやってみた -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-1530825838243765654</id><published>2013-04-19T22:00:00.000+09:00</published><updated>2013-04-23T15:31:24.166+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Samba4"/><category scheme="http://www.blogger.com/atom/ns#" term="わたしもやってみた"/><title type='text'>Samba4 で Active Directory / インストール編 - わたしもやってみた -</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;p&gt;
いつの間にか Samba4 がリリースされているではないか！&lt;br /&gt;
これで念願の Active Directory だ！！&lt;br /&gt;
仕事場では、あぁそれ Active Directory ね。&lt;br /&gt;
ふ～んって羨ましそうに横目にみていたのが&lt;br /&gt;
なんとわが家にも！&lt;br /&gt;
いい時代になったなぁと。
&lt;/p&gt;

&lt;p&gt;
今回は、ビルドとインストール。&lt;br /&gt;
&lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;Samba4 HOWTO&lt;/a&gt; にある
&lt;a href=&quot;https://wiki.samba.org/index.php/Build_Samba&quot;&gt;Build_Samba&lt;/a&gt; をベースに&lt;br /&gt;
先人の方々の情報も踏まえつつ、わたしもやってみたいと思います。&lt;br /&gt;
ではでは Getting Started!!
&lt;/p&gt;

&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;ContOS 6.4(CentOS-6.4-i386-minimal.iso)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Samba&lt;/th&gt;
    &lt;td&gt;4.0.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Bind&lt;/th&gt;
    &lt;td&gt;9.8.2&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;NTP&lt;/th&gt;
    &lt;td&gt;ntp-4.2.6p5&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;環境設定&lt;/h2&gt;
&lt;p&gt;とりあえず SELinux と Fire Wall を止めておく。&lt;br /&gt;
ActiveDirectoryが構築できてから設定しょう…&lt;/p&gt;

&lt;p&gt;
まずは、SELinux から&lt;br /&gt;
/etc/selinux ディレクトリにある config ファイルを開き 以下のように変更する。
&lt;/p&gt;
&lt;u&gt;/etc/selinux/config&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
...
SELINUX=disabled
...
&lt;/pre&gt;

&lt;p&gt;そして、Fire Wall&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
service iptables stop
service ip6tables stop
chkconfig iptables off
chkconfig ip6tables off
&lt;/pre&gt;

&lt;p&gt;一応、確認する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
getenforce
iptables -L
ip6tables -L
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドに必要なライブラリーを揃える&lt;/h2&gt;
&lt;p&gt;何はともあれ yum の update から&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum update
&lt;/pre&gt;

&lt;p&gt;そして、必要なライブラリーのインストール&lt;br /&gt;…結構盛りだくさん。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install gcc libacl-devel libblkid-devel gnutls-devel \
      readline-devel python-devel gdb pkgconfig krb5-workstation \
      zlib-devel setroubleshoot-server \
      setroubleshoot-plugins policycoreutils-python \
      libsemanage-python setools-libs-python setools-libs \
      popt-devel libpcap-devel sqlite-devel libidn-devel \
      libxml2-devel libacl-devel libsepol-devel libattr-devel \
      keyutils-libs-devel cyrus-sasl-devel cups-devel
&lt;/pre&gt;

&lt;p&gt;
Samba4 の HOWTO 的には以上でよいらしいのだけど configure で perl が必要だといわれる。&lt;br /&gt;
また、バックエンドの DNS に Bind を使いたい。&lt;br /&gt;
それから、--with-ads も使いたい。などその他諸々で以下のライブラリーを追加することにした。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
yum install bind bind-libs bind-utils wget\
      perl autoconf automake openldap-devel pam-devel
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドとインストール&lt;/h2&gt;
&lt;p&gt;
いよいよ、Samba4 を make する。&lt;br /&gt;
作業フォルダは /root/work とします。&lt;br /&gt;
では、ソースを取ってきて展開するところまで
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
mkdir /root/work
cd /root/work
wget http://www.samba.org/samba/ftp/stable/samba-4.0.5.tar.gz
tar -zxvf samba-4.0.5.tar.gz
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ソースの取得について&lt;/span&gt;
&lt;p&gt;
git から取ってくるのがいいらしいのだけど、チェックアウト？に時間が掛かったので&lt;br /&gt;
wget で gz にアーカイブされたものを取ってくることにした。&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;次に configure &lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /root/work/samba-4.0.5
./configure --with-ads --with-shared-modules=idmap_ad \
            --enable-debug --enable-selftest
&lt;/pre&gt;

&lt;p&gt;仕上げに make と make install &lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
make
make install
&lt;/pre&gt;

&lt;p&gt;
これで /usr/local/samba/ にインストールされる。&lt;br /&gt;
インストールできているか Version を確認してみる。
&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
/usr/local/samba/bin/smbclient --version
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;起動スクリプトの作成&lt;/h2&gt;
&lt;p&gt;
やっぱり、起動スクリプトは、ほしい。&lt;br /&gt;
ソースからインストールするとこのへんが無かったりするので大変だ！&lt;br /&gt;
/etc/rc.d/init.d ディレクトリに 以下のような内容でファイルを作成し samba4 の名前で保存する。&lt;/p&gt;
&lt;u&gt;/etc/rc.d/init.d/samba4&lt;/u&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
#! /bin/bash
#
# samba4       Bring up/down samba4 service 
#
# chkconfig: - 90 10
# description: Activates/Deactivates all samba4 interfaces configured to \
#              start at boot time.
#
### BEGIN INIT INFO
# Provides: 
# Should-Start: 
# Short-Description: Bring up/down samba4
# Description: Bring up/down samba4
### END INIT INFO
# Source function library.
. /etc/init.d/functions

if [ -f /etc/sysconfig/samba4 ]; then
 . /etc/sysconfig/samba4
fi

CWD=$(pwd)
prog=&quot;samba4&quot;

start() {
     # Attach irda device 
     echo -n $&quot;Starting $prog: &quot;
 /usr/local/samba/sbin/samba
 sleep 2
 if ps ax | grep -v &quot;grep&quot; | grep -q /samba/sbin/samba ; then success $&quot;samba4 startup&quot;; else failure $&quot;samba4 startup&quot;; fi
     echo
}
stop() {
     # Stop service.
     echo -n $&quot;Shutting down $prog: &quot;
 killall samba
 sleep 2
 if ps ax | grep -v &quot;grep&quot; | grep -q /samba/sbin/samba ; then failure $&quot;samba4 shutdown&quot;; else success $&quot;samba4 shutdown&quot;; fi
     echo
}
status() {
 /usr/local/samba/sbin/samba --show-build
}

# See how we were called.
case &quot;$1&quot; in
start)
 start
     ;;
stop)
 stop
     ;;
status)
 status irattach
 ;;
restart|reload)
 stop
 start
 ;;
*)
     echo $&quot;Usage: $0 {start|stop|restart|status}&quot;
     exit 1
esac

exit 0
&lt;/pre&gt;

&lt;p&gt;サービスに登録する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chmod 755 /etc/rc.d/init.d/samba4
chkconfig --add samba4
chkconfig samba4 on
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;NTP のインストール&lt;/h2&gt;
&lt;p&gt;
これで、終わりと思いきや、まだ NTP の設定が必要なようです。&lt;br /&gt;
Active Directory では、それぞれのPCの時間が合っていないとうまく動かないとのことです。&lt;br /&gt;
yum でインストールしたいところなのですが&lt;br /&gt;
yum で入る NTP は、signed ntp が サポートされていないらしく(Version 4.2.6以上)&lt;br /&gt;
これがないと Active Directory の時間合わせがうまく動かないようです。&lt;br /&gt;
ここも、ソースからのインストールになってしまいます。
&lt;/p&gt;

&lt;p&gt;
まずは、ソースを取ってきて展開するところまで&lt;br /&gt;
同じく作業フォルダは /root/work とします。
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /root/work
wget http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.6p5.tar.gz
tar -zxvf ntp-4.2.6p5.tar.gz
&lt;/pre&gt;

&lt;p&gt;次に configure そして make と make install&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
cd /root/work/ntp-4.2.6p5
./configure --enable-ntp-signd
make
make install
&lt;/pre&gt;

&lt;p&gt;
ここでもやはり、起動スクリプトがほしい…&lt;br /&gt;
/etc/rc.d/init.d ディレクトリに 以下のような内容でファイルを作成し ntp の名前で保存する。&lt;/p&gt;
&lt;u&gt;/etc/rc.d/init.d/ntp&lt;/u&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
#! /bin/bash
#
# ntp Bring up/down ntp service
#
#chkconfig: - 99 30
#description: Bring up/down ntp
#
### BEGIN INIT INFO
# Provides:
# Should-Start:
# Short-Description: Bring up/down ntp
# Description: Bring up/down ntp
### END INIT INFO
# Source function library.
. /etc/init.d/functions
 
CWD=$(pwd)
NTPD=/usr/local/bin/ntpd
prog=&quot;ntp&quot;
start() {
 # Attach irda device
 echo -n $&quot;Starting $prog: &quot;
 $NTPD -p /var/run/ntpd.pid
 sleep 2
 if ps ax | grep -v &quot;grep&quot; | grep -q $NTPD ; then success $&quot;ntp startup&quot;; else failure $&quot;ntp startup&quot;; fi
 echo
}
stop() {
# Stop service.
 echo -n $&quot;Shutting down $prog: &quot;
 kill -9 `cat /var/run/ntpd.pid` &gt; /dev/null 2&gt;&amp;1
 sleep 2
 if ps ax | grep -v &quot;grep&quot; | grep -q $NTPD ; then failure $&quot;ntp shutdown&quot;; else success $&quot;ntp shutdown&quot;; fi
 echo
}
# See how we were called.
case &quot;$1&quot; in
start)
 start
 ;;
stop)
 stop
 ;;
restart|reload)
 stop
 start
 ;;
*)
 echo $&quot;Usage: $0 {start|stop|restart}&quot;
 exit 1
esac

exit 0
&lt;/pre&gt;

&lt;p&gt;サービスに登録する。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
chmod 755 /etc/rc.d/init.d/ntp
chkconfig --add ntp
chkconfig ntp on
&lt;/pre&gt;

&lt;p&gt;
設定ファイルを作成する。&lt;br /&gt;
ソースからインストールすると何もないようです…&lt;br /&gt;
/etc ディレクトリに 以下のような内容でファイルを作成し ntp.conf の名前で保存する。&lt;/p&gt;
&lt;u&gt;/etc/ntp.conf&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
server 127.127.1.0
fudge 127.127.1.0 stratum 10
server 0.pool.ntp.org iburst prefer
server 1.pool.ntp.org iburst prefer
driftfile /var/lib/ntp/ntp.drift
logfile /var/log/ntp
ntpsigndsocket /usr/local/samba/var/lib/ntp_signd/
restrict default kod nomodify notrap nopeer mssntp
restrict 127.0.0.1
restrict 0.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
restrict 1.pool.ntp.org mask 255.255.255.255 nomodify notrap nopeer noquery
&lt;/pre&gt;

&lt;p&gt;早速、確認の意味も含め時刻を合わせてみる。&lt;/p&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;
ntpdate ntp.nict.jp
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;

&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
やっぱり、ソースのインストールとなるといろいろ大変だなぁというのが正直なところ。&lt;br /&gt;
起動スクリプト、コマンドパス、ファイルパス、バージョンアップ…&lt;br /&gt;
yum でインストールできる日が早く来ないかなぁと思う。&lt;br /&gt;
イケイケの Fedora に 慎重派の CentOS わたしは、そんなイメージだ。&lt;br /&gt;
CentOS の yum は、まだ、先にかなぁ。
&lt;/p&gt; 

&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4&quot;&gt;
   https://wiki.samba.org/index.php/Samba4
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Build_Samba&quot;&gt;
    https://wiki.samba.org/index.php/Build_Samba
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba_4/OS_Requirements&quot;&gt;
 https://wiki.samba.org/index.php/Samba_4/OS_Requirements
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Samba4/InitScript&quot;&gt;
    https://wiki.samba.org/index.php/Samba4/InitScript
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://wiki.samba.org/index.php/Configure_NTP&quot;&gt;
    https://wiki.samba.org/index.php/Configure_NTP
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://opentodo.net/2013/01/samba4-as-ad-domain-controller-on-centos-6/&quot;&gt;
    http://opentodo.net/2013/01/samba4-as-ad-domain-controller-on-centos-6/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://d.hatena.ne.jp/rti7743/20110425/1303688263&quot;&gt;
    http://d.hatena.ne.jp/rti7743/20110425/1303688263
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1530825838243765654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1530825838243765654'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2013/04/samba4-active-directory.html' title='Samba4 で Active Directory / インストール編 - わたしもやってみた -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-192728227924457674</id><published>2012-05-24T05:32:00.001+09:00</published><updated>2016-05-16T00:24:29.937+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Spring"/><title type='text'>SpringSecurityユーザをDBで管理、JPAでログインする</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;

&lt;p&gt;
Spring Securityを使ってデータベースに保存されたユーザ、パスワードでログインする機能を作ってみたいと思います。&lt;br /&gt;
Spring Securityで規定されたusersテーブルのスキーマをそのまま使う分には、&lt;br /&gt;
Springの定義ファイルを編集するするだけでよいのですが&lt;br /&gt;
このusersテーブルに他の項目を追加してログインセッションに置きたい場合は、カスタマイズが必要になります。&lt;br /&gt;
ログインが成功した場合、セッションキー「SPRING_SECURITY_CONTEXT」にusersテーブルの情報が置かれます。
&lt;/p&gt;
&lt;p&gt;
カスタマイズとしては、UserDetailsインターフェイス(org.springframework.security.core.userdetails)実装クラスと&lt;br /&gt;
UserDetailsServiceインターフェイス(org.springframework.security.core.userdetails)実装クラスの作成になります。&lt;br /&gt;
UserDetailsインターフェイス実装クラスは、ログイン成功時、セッションに置かれるログインユーザ情報を保持するクラスです。&lt;br /&gt;
今回は、EntityBeanで作成します。&lt;br /&gt;
UserDetailsServiceインターフェイス実装クラスは、ユーザ情報取得メソッド(loadUserByUsername)を実装します。&lt;br /&gt;
今回は、EntityManagerを使ってユーザ情報を取得します。
&lt;/p&gt;
&lt;p&gt;
今回は、usersテーブルに連絡先(contact)を追加したいと思います。&lt;br /&gt;
ではでは！Getting Started!!
&lt;/p&gt;


&lt;h2&gt;環境&lt;/h2&gt;
&lt;table class=&quot;fummy&quot;&gt;
  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows XP&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;データベース&lt;/th&gt;
    &lt;td&gt;MySQL 5.5.8&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;アプリケーションサーバー&lt;/th&gt;
    &lt;td&gt;Tomcat 7.0.27&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;Java&lt;/th&gt;
    &lt;td&gt;JDK 1.7.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th rowspan=&quot;3&quot;&gt;フレームワーク&lt;/th&gt;
    &lt;td&gt;Spring Framework 3.1.0.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Spring Security 3.1.0.RELEASE&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Hibernate 3.6.10.Final&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Maven 3.0.4&lt;br /&gt;※mvnコマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;Spring Security について&lt;/span&gt;
&lt;p&gt;
「3.1.0.RELEASE」でMavenビルドすると「invalid LOC header」エラーなるものが
発生していたのですが、Springframeworkのローカルのリポジトリ「C:\Documents and Settings\{user}\.m2\repository\org\springframework」を削除して
再度ビルドするとうまくいきました。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;

  
&lt;h2&gt;プロジェクト構成&lt;/h2&gt;
&lt;p&gt;
Cドライブ直下に「studying-spring-security」フォルダを作成し各ファイルを以下のように配置します。
&lt;/p&gt;
&lt;div id=&quot;studying-spring-security&quot; class=&quot;fummy_tree&quot;&gt;
 &lt;ul&gt;
  &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;C:\studying-spring-security&lt;/span&gt;
   &lt;ul&gt;
    &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;src&lt;/span&gt;
     &lt;ul&gt;
      &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;main&lt;/span&gt;
       &lt;ul&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;java&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;com&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;mydomain&lt;/span&gt;
             &lt;ul&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;bizlogic&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyUserDetailsService.java&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
              &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;entity&lt;/span&gt;
               &lt;ul&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyAuthority.java&lt;/span&gt;&lt;/li&gt;
                &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;MyUser.java&lt;/span&gt;&lt;/li&gt;
               &lt;/ul&gt;
              &lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;META-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext.xml&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-locale.xml&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;applicationContext-security.xml&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;persistence.xml&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;log4j.properties&lt;/span&gt;&lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;webapp&lt;/span&gt;
         &lt;ul&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;resources&lt;/span&gt;
           &lt;ul&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;style.css&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
          &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;WEB-INF&lt;/span&gt;
           &lt;ul&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;i18n&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;messages_ja.properties&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;spring&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;webmvc-config.xml&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li class=&quot;expanded&quot;&gt;&lt;span&gt;views&lt;/span&gt;
             &lt;ul&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;index.jspx&lt;/span&gt;&lt;/li&gt;
              &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;login.jspx&lt;/span&gt;&lt;/li&gt;
             &lt;/ul&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;web.xml&lt;/span&gt;&lt;/li&gt;
           &lt;/ul&gt;
          &lt;/li&gt;
         &lt;/ul&gt;
        &lt;/li&gt;
       &lt;/ul&gt;
      &lt;/li&gt;
     &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span class=&quot;img_txt&quot;&gt;pom.xml&lt;/span&gt;&lt;/li&gt;
   &lt;/ul&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
 tree1 = new YAHOO.widget.TreeView(&quot;studying-spring-security&quot;);
 tree1.render();
 tree1.subscribe(&#39;dblClickEvent&#39;,tree1.onEventEditNode);
&lt;/script&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;プロジェクト作成について&lt;/span&gt;
&lt;p&gt;
Spring Rooでプロジェクトを作成し、今回使わないもの(Tiles、Themeなど)を削除しました。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;データベースの作成&lt;/h2&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;
DROP DATABASE IF EXISTS studying_spring_security;
CREATE DATABASE IF NOT EXISTS studying_spring_security DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
&lt;/pre&gt;
&lt;p&gt;
※データベースユーザとして、パスワードなしのrootユーザが作成してあるとします。
&lt;/p&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;テーブルの作成とログインユーザの登録&lt;/h2&gt;
&lt;p&gt;
ログインユーザとして、adminとuserを作成します。&lt;br /&gt;
ログインID/パスワードはそれぞれ&lt;br /&gt;
admin/admin、user/user&lt;br /&gt;
としています。
&lt;/p&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `username` varchar(50)  NOT NULL,
  `password` varchar(255) NOT NULL,
  `enabled`  tinyint(1)   NOT NULL,
  `contact`  varchar(255) DEFAULT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
  `username`  varchar(50) NOT NULL,
  `authority` varchar(50) NOT NULL,
  UNIQUE KEY `ix_auth_username` (`username`,`authority`),
  CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
  `username`  varchar(64) NOT NULL,
  `series`    varchar(64) NOT NULL,
  `token`     varchar(64) NOT NULL,
  `last_used` timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `users` VALUES (&#39;admin&#39;, &#39;8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918&#39;, &#39;1&#39;, &#39;admin@mydomain.com&#39;);
INSERT INTO `users` VALUES (&#39;user&#39;,  &#39;04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb&#39;, &#39;1&#39;, &#39;user@mydomain.com&#39;);

INSERT INTO `authorities` VALUES (&#39;admin&#39;, &#39;ROLE_ADMIN&#39;);
INSERT INTO `authorities` VALUES (&#39;admin&#39;, &#39;ROLE_USER&#39;);
INSERT INTO `authorities` VALUES (&#39;user&#39;,  &#39;ROLE_USER&#39;);
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ServletとLoggerの設定&lt;/h2&gt;
&lt;u&gt;web.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;web-app version=&quot;3.0&quot; 
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; 
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd &quot;&gt;

  &amp;lt;display-name&gt;studying-spring-security&amp;lt;/display-name&gt;
  
  &amp;lt;context-param&gt;
    &amp;lt;param-name&gt;defaultHtmlEscape&amp;lt;/param-name&gt;
    &amp;lt;param-value&gt;true&amp;lt;/param-value&gt;
  &amp;lt;/context-param&gt;
  
  &amp;lt;context-param&gt;
    &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
    &amp;lt;param-value&gt;classpath*:META-INF/spring/applicationContext*.xml&amp;lt;/param-value&gt;
  &amp;lt;/context-param&gt;
  
  &amp;lt;listener&gt;
    &amp;lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&gt;
  &amp;lt;/listener&gt;
  
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&amp;lt;/filter-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;encoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;UTF-8&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;forceEncoding&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;true&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;CharacterEncodingFilter&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;

  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;HttpMethodFilter&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.HiddenHttpMethodFilter&amp;lt;/filter-class&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;HttpMethodFilter&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;
  
  &amp;lt;filter&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&gt;
  &amp;lt;/filter&gt;
  &amp;lt;filter-mapping&gt;
    &amp;lt;filter-name&gt;springSecurityFilterChain&amp;lt;/filter-name&gt;
    &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;
  &amp;lt;/filter-mapping&gt;
  
  &amp;lt;servlet&gt;
    &amp;lt;servlet-name&gt;studying-spring-security&amp;lt;/servlet-name&gt;
    &amp;lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&gt;
    &amp;lt;init-param&gt;
      &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;
      &amp;lt;param-value&gt;WEB-INF/spring/webmvc-config.xml&amp;lt;/param-value&gt;
    &amp;lt;/init-param&gt;
    &amp;lt;load-on-startup&gt;1&amp;lt;/load-on-startup&gt;
  &amp;lt;/servlet&gt;
  &amp;lt;servlet-mapping&gt;
    &amp;lt;servlet-name&gt;studying-spring-security&amp;lt;/servlet-name&gt;
    &amp;lt;url-pattern&gt;/&amp;lt;/url-pattern&gt;
  &amp;lt;/servlet-mapping&gt;
  
  &amp;lt;session-config&gt;
    &amp;lt;session-timeout&gt;10&amp;lt;/session-timeout&gt;
    &amp;lt;tracking-mode&gt;COOKIE&amp;lt;/tracking-mode&gt;
  &amp;lt;/session-config&gt;
&amp;lt;/web-app&gt;
&lt;/pre&gt;

&lt;u&gt;log4j.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
log4j.rootLogger=error, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=application.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;データベース接続の設定&lt;/h2&gt;
&lt;u&gt;persistence.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;persistence version=&quot;2.0&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/persistence&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd&quot;&gt;

  &amp;lt;persistence-unit name=&quot;persistenceUnit&quot; transaction-type=&quot;RESOURCE_LOCAL&quot;&gt;
    &amp;lt;provider&gt;org.hibernate.ejb.HibernatePersistence&amp;lt;/provider&gt;
    &amp;lt;properties&gt;
      &amp;lt;property name=&quot;hibernate.dialect&quot; value=&quot;org.hibernate.dialect.MySQLDialect&quot;/&gt;
      &amp;lt;property name=&quot;hibernate.hbm2ddl.auto&quot; value=&quot;validate&quot; /&gt;
      &amp;lt;property name=&quot;hibernate.ejb.naming_strategy&quot; value=&quot;org.hibernate.cfg.DefaultNamingStrategy&quot; /&gt;
      &amp;lt;property name=&quot;hibernate.connection.charSet&quot; value=&quot;UTF-8&quot; /&gt;
    &amp;lt;/properties&gt;
  &amp;lt;/persistence-unit&gt;
&amp;lt;/persistence&gt;
&lt;/pre&gt;

&lt;u&gt;applicationContext.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;beans
  xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
 xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 xsi:schemaLocation=&quot;
   http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.1.xsd&quot;&gt;

  &amp;lt;bean id=&quot;dataSource&quot;
    class=&quot;org.apache.commons.dbcp.BasicDataSource&quot; destroy-method=&quot;close&quot;&gt;
    &amp;lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.jdbc.Driver&quot;/&gt;
    &amp;lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/studying_spring_security&quot;/&gt;
    &amp;lt;property name=&quot;username&quot; value=&quot;root&quot;/&gt;
    &amp;lt;property name=&quot;password&quot; value=&quot;&quot;/&gt;
    &amp;lt;property name=&quot;testOnBorrow&quot; value=&quot;true&quot;/&gt;
    &amp;lt;property name=&quot;testOnReturn&quot; value=&quot;true&quot;/&gt;
    &amp;lt;property name=&quot;testWhileIdle&quot; value=&quot;true&quot;/&gt;
    &amp;lt;property name=&quot;timeBetweenEvictionRunsMillis&quot; value=&quot;1800000&quot;/&gt;
    &amp;lt;property name=&quot;numTestsPerEvictionRun&quot; value=&quot;3&quot;/&gt;
    &amp;lt;property name=&quot;minEvictableIdleTimeMillis&quot; value=&quot;1800000&quot;/&gt;
    &amp;lt;property name=&quot;validationQuery&quot; value=&quot;SELECT 1&quot;/&gt;
  &amp;lt;/bean&gt;

  &amp;lt;context:annotation-config/&gt;
  &amp;lt;bean id=&quot;entityManagerFactory&quot;
    class=&quot;org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean&quot;
    p:persistenceUnitName=&quot;persistenceUnit&quot; p:dataSource-ref=&quot;dataSource&quot; /&gt;
&amp;lt;/beans&gt;
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;データベース接続について&lt;/span&gt;
&lt;p&gt;
persistence.xmlファイルにデータベース接続設定を記述したいところなのですが&lt;br /&gt;
Spring Securityの標準の実装では、RememberMeトークンをデータベースに保存する場合、&lt;br /&gt;
JDBCを使用している(org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl)みたいで&lt;br /&gt;
DataSourceをSpringコンテナに登録しておく必要があるようです。&lt;br /&gt;
この部分もカスタマイズすればよいのですが、今回はパスで…
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Entityクラスの作成&lt;/h2&gt;
&lt;u&gt;MyUser.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain.entity;

import java.util.Collection;

import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.JoinColumn;

import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

@Entity
@Table(name = &quot;users&quot;)
public class MyUser implements UserDetails, CredentialsContainer{

  private static final long serialVersionUID = 1L;
  

  @Id
  private String username;
  
  private String password;
  
  private boolean enabled;

  private String contact;

  @ElementCollection(targetClass=MyAuthority.class, fetch=FetchType.EAGER)
  @CollectionTable(
    name=&quot;authorities&quot;,
    joinColumns={@JoinColumn(name=&quot;username&quot;, referencedColumnName=&quot;username&quot;)})
  @MapKeyColumn(name=&quot;authority&quot;)
  private Collection&amp;lt;GrantedAuthority&gt; authorities;

  @Transient
  private boolean accountNonExpired = true;

  @Transient
  private boolean accountNonLocked = true;

  @Transient
  private boolean credentialsNonExpired = true;


  @Override
  public void eraseCredentials() {
    password = null;
  }

  @Override
  public boolean equals(Object rhs) {
    if (rhs instanceof User) {
      return username.equals(((User) rhs).username);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return username.hashCode();
  }


  public String getContact() {
    return contact;
  }

  public void setContact(String contact) {
    this.contact = contact;
  }

  @Override
  public String getUsername() {
    return this.username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
  
  @Override
  public String getPassword() {
    return this.password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  @Override
  public boolean isEnabled() {
    return this.enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }
  
  @Override
  public boolean isAccountNonExpired() {
    return this.accountNonExpired;
  }
  
  public void setAccountNonExpired(boolean accountNonExpired) {
    this.accountNonExpired = accountNonExpired;
  }

  @Override
  public boolean isAccountNonLocked() {
    return this.accountNonLocked;
  }

  public void setAccountNonLocked(boolean accountNonLocked) {
    this.accountNonLocked = accountNonLocked;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return this.credentialsNonExpired;
  }

  public void setCredentialsNonExpired(boolean credentialsNonExpired) {
    this.credentialsNonExpired = credentialsNonExpired;
  }

  @Override
  public Collection&amp;lt;GrantedAuthority&gt; getAuthorities() {
    return this.authorities;
  }
  
  public void setAuthorities(Collection&amp;lt;GrantedAuthority&gt; authorities) {
    this.authorities = authorities;
  }
}
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;MyUserクラスについて&lt;/span&gt;
&lt;p&gt;
equals()メソッド、hashCode()メソッドは、User(org.springframework.security.core.userdetails)クラスを参考に実装しました。&lt;br /&gt;
CredentialsContainerインターフェイスの実装も同様に参考にしました。&lt;br /&gt;
…toString()メソッドはパスです。
&lt;/p&gt;
&lt;/div&gt;

&lt;u&gt;MyAuthority.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain.entity;

import javax.persistence.Embeddable;

import org.springframework.security.core.GrantedAuthority;

@Embeddable
public class MyAuthority implements GrantedAuthority{

  private static final long serialVersionUID = 1L;
  
  private String authority;

  @Override
  public String getAuthority() {
    return this.authority;
  }

  public void setAuthority(String authority) {
    this.authority = authority;
  }

  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }

    if (obj instanceof MyAuthority) {
      return authority.equals(((MyAuthority) obj).authority);
    }

    return false;
  }

  public int hashCode() {
    return this.authority.hashCode();
  }

  public String toString() {
    return this.authority;
  }
}
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;MyAuthorityクラスについて&lt;/span&gt;
&lt;p&gt;
equals()メソッド、hashCode()メソッド、toString()メソッドは、&lt;br /&gt;
SimpleGrantedAuthority(org.springframework.security.core.authority)クラスを参考に実装しました。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;UserDetailsServiceクラスの作成&lt;/h2&gt;
&lt;u&gt;MyUserDetailsService.java&lt;/u&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.mydomain.bizlogic;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.mydomain.entity.MyUser;

@Service
public class MyUserDetailsService implements UserDetailsService {
  
  @PersistenceContext
  private EntityManager em;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

    try {
      String jpql = &quot;SELECT u FROM User u Where username = :username&quot;;
      Query query = this.em.createQuery(jpql, MyUser.class);
      query.setParameter(&quot;username&quot;, username);
      MyUser user = (MyUser) query.getSingleResult();

      return user;
    } catch(NoResultException exp) {
      throw new UsernameNotFoundException(&quot;ユーザが見つかりません&quot;);
    }
  }
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring Securityの設定&lt;/h2&gt;
&lt;p&gt;
/login、/resourcesには、アクセス制限を設定せず、それ以外のURLにアクセス制限を設定します。
&lt;/p&gt;
&lt;p&gt;
&amp;lt;authentication-provider&gt;タグと&amp;lt;remember-me&gt;タグのuser-service-ref属性に&lt;br /&gt;
作成したUserDetailsServiceインターフェイス実装クラスを指定します。
&lt;/p&gt;
&lt;p&gt;
RememberMeトークンをデータベースに保存する場合、&amp;lt;remember-me&gt;タグのdata-source-ref属性に&lt;br /&gt;
Springコンテナに登録されたDataSourceを指定します。
&lt;/p&gt;

&lt;u&gt;applicationContext-security.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;beans:beans
  xmlns=&quot;http://www.springframework.org/schema/security&quot; 
  xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd&quot;&gt;

  &amp;lt;http auto-config=&quot;true&quot; use-expressions=&quot;true&quot;&gt;
    &amp;lt;form-login login-processing-url=&quot;/resources/j_spring_security_check&quot;
      login-page=&quot;/login&quot; authentication-failure-url=&quot;/login?login_error=t&quot; 
      default-target-url=&quot;/&quot; always-use-default-target=&quot;true&quot;/&gt;
    &amp;lt;logout logout-url=&quot;/resources/j_spring_security_logout&quot; logout-success-url=&quot;/&quot;/&gt;

    &amp;lt;intercept-url pattern=&quot;/login*&quot; access=&quot;permitAll&quot; /&gt;
    &amp;lt;intercept-url pattern=&quot;/resources/**&quot; access=&quot;permitAll&quot; /&gt;
    &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;hasRole(&#39;ROLE_USER&#39;)&quot; /&gt;
        
    &amp;lt;remember-me data-source-ref=&quot;dataSource&quot; user-service-ref=&quot;myUserDetailsService&quot;/&gt;
  &amp;lt;/http&gt;

  &amp;lt;authentication-manager alias=&quot;authenticationManager&quot;&gt;
    &amp;lt;authentication-provider user-service-ref=&quot;myUserDetailsService&quot;&gt;
      &amp;lt;password-encoder hash=&quot;sha-256&quot;/&gt;
    &amp;lt;/authentication-provider&gt;
  &amp;lt;/authentication-manager&gt;
    
  &amp;lt;beans:bean id=&quot;myUserDetailsService&quot; class=&quot;com.mydomain.bizlogic.MyUserDetailsService&quot;/&gt;
&amp;lt;/beans:beans&gt;
&lt;/pre&gt;

&lt;u&gt;applicationContext-locale.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;beans
  xmlns=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd&quot;&gt;

  &amp;lt;bean id=&quot;messageSource&quot;
    class=&quot;org.springframework.context.support.ReloadableResourceBundleMessageSource&quot;
    p:basenames=&quot;WEB-INF/i18n/messages&quot; p:fallbackToSystemLocale=&quot;false&quot;
    p:fileEncodings=&quot;UTF-8&quot; p:defaultEncoding=&quot;UTF-8&quot; /&gt;
&amp;lt;/beans&gt;
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;エラーメッセージの日本語化について&lt;/span&gt;
&lt;p&gt;
SpringSecurity関連のクラスがSpringコンテナに登録される際に
ReloadableResourceBundleMessageSourceクラスなどからmessages_ja.propertiesファイルが読み込めないと
エラーメッセージが日本語化できないようです。&lt;br /&gt;
webmvc-config.xmlファイルが読み込まれる時点では、
SpringSecurity関連クラスのSpringコンテナへの登録が終わっているようなので
このファイルでReloadableResourceBundleMessageSourceクラスなどの設定をしていると
SpringSecurityのデフォルトのエラーメッセージを日本語で上書きされないみたいです。
&lt;/p&gt;
&lt;/div&gt;

&lt;u&gt;messages_ja.properties&lt;/u&gt;
&lt;pre class=&quot;brush: plain&quot;&gt;
AbstractUserDetailsAuthenticationProvider.badCredentials=ログインID、パスワードが違います。
&lt;/pre&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;native2asciiについて&lt;/span&gt;
&lt;p&gt;
ReloadableResourceBundleMessageSourceクラスを使用するとnative2asciiで変換を行わなくてもよいようです。
&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Spring MVCの設定&lt;/h2&gt;
&lt;u&gt;webmvc-config.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;beans
  xmlns=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
  xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd&quot;&gt;

  &amp;lt;mvc:annotation-driven /&gt;
  &amp;lt;mvc:resources location=&quot;/resources/&quot; mapping=&quot;/resources/**&quot; /&gt;
  &amp;lt;mvc:view-controller path=&quot;/login&quot; /&gt;
  &amp;lt;mvc:view-controller path=&quot;/&quot; view-name=&quot;index&quot; /&gt;

  &amp;lt;bean id=&quot;internalResourceViewResolver&quot;
    class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;
    p:viewClass=&quot;org.springframework.web.servlet.view.JstlView&quot;
    p:prefix=&quot;/WEB-INF/views/&quot; p:suffix=&quot;.jspx&quot; /&gt;
&amp;lt;/beans&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;Viewファイルの作成&lt;/h2&gt;
&lt;u&gt;login.jspx&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;html version=&quot;2.0&quot;
  xmlns:jsp=&quot;http://java.sun.com/JSP/Page&quot;
  xmlns:c=&quot;http://java.sun.com/jsp/jstl/core&quot;
  xmlns:fn=&quot;http://java.sun.com/jsp/jstl/functions&quot;
  xmlns:spring=&quot;http://www.springframework.org/tags&quot;
  xmlns:form=&quot;http://www.springframework.org/tags/form&quot;&gt;

  &amp;lt;jsp:output doctype-root-element=&quot;HTML&quot; doctype-system=&quot;about:legacy-compat&quot; /&gt;
  &amp;lt;jsp:directive.page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; /&gt;
  
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot;/&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;ログイン&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;h3&gt;ログイン&amp;lt;/h3&gt;
      &amp;lt;c:if test=&quot;${not empty param.login_error}&quot;&gt;
      &amp;lt;div class=&quot;errors&quot;&gt;
        &amp;lt;p&gt;&amp;lt;c:out value=&quot;${SPRING_SECURITY_LAST_EXCEPTION.message}&quot; /&gt;&amp;lt;/p&gt;
      &amp;lt;/div&gt;
      &amp;lt;/c:if&gt;
        &amp;lt;spring:url value=&quot;/resources/j_spring_security_check&quot; var=&quot;form_url&quot; /&gt;
        &amp;lt;form name=&quot;f&quot; action=&quot;${fn:escapeXml(form_url)}&quot; method=&quot;POST&quot;&gt;
        &amp;lt;table&gt;
          &amp;lt;tbody&gt;
            &amp;lt;tr&gt;
              &amp;lt;th&gt;&amp;lt;label for=&quot;j_username&quot;&gt;ログインユーザ&amp;lt;/label&gt;&amp;lt;/th&gt;
              &amp;lt;td&gt;&amp;lt;input id=&quot;j_username&quot; type=&#39;text&#39; name=&#39;j_username&#39; style=&quot;width:98%&quot; /&gt;&amp;lt;/td&gt;
            &amp;lt;/tr&gt;
            &amp;lt;tr&gt;
              &amp;lt;th&gt;&amp;lt;label for=&quot;j_password&quot;&gt;パスワード&amp;lt;/label&gt;&amp;lt;/th&gt;
              &amp;lt;td&gt;&amp;lt;input id=&quot;j_password&quot; type=&#39;password&#39; name=&#39;j_password&#39; style=&quot;width:100px&quot; /&gt;
              &amp;lt;input id=&quot;proceed&quot; type=&quot;submit&quot; value=&quot;ログイン&quot; /&gt;&amp;lt;/td&gt;
            &amp;lt;/tr&gt;
            &amp;lt;tr&gt;
              &amp;lt;td&gt;&amp;lt;/td&gt;
              &amp;lt;th&gt;
                &amp;lt;input id=&quot;_spring_security_remember_me&quot; 
                  type=&#39;checkbox&#39; name=&#39;_spring_security_remember_me&#39; value=&quot;true&quot;/&gt;
                &amp;lt;label for=&quot;_spring_security_remember_me&quot;&gt;ログインしたままにする&amp;lt;/label&gt;
              &amp;lt;/th&gt;
            &amp;lt;/tr&gt;
          &amp;lt;/tbody&gt;
        &amp;lt;/table&gt;
        &amp;lt;/form&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;エラーメッセージの表示について&lt;/span&gt;
&lt;p&gt;
SpringSecurityのエラーメッセージはセッションに保存されているみたいなので
&amp;lt;jsp:directive.page  session=&quot;false&quot; /&gt; としているとエラーメッセージが表示されなくようです。
&lt;/p&gt;
&lt;/div&gt;

&lt;u&gt;index.jspx&lt;/u&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;html version=&quot;2.0&quot;
  xmlns:jsp=&quot;http://java.sun.com/JSP/Page&quot;
  xmlns:c=&quot;http://java.sun.com/jsp/jstl/core&quot;
  xmlns:sec=&quot;http://www.springframework.org/security/tags&quot;&gt;

  &amp;lt;jsp:output doctype-root-element=&quot;HTML&quot; doctype-system=&quot;about:legacy-compat&quot; /&gt;
  &amp;lt;jsp:directive.page contentType=&quot;text/html;charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot; /&gt;
  
  &amp;lt;head&gt;
    &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
    &amp;lt;c:url value=&quot;/resources/style.css&quot; var=&quot;style&quot;/&gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${style}&quot; /&gt;
    &amp;lt;title&gt;ようこそ&amp;lt;/title&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;div id=&quot;page&quot;&gt;
      &amp;lt;h3&gt;ようこそ&amp;lt;/h3&gt;
      &amp;lt;p&gt;
        &amp;lt;sec:authentication property=&quot;principal.username&quot; htmlEscape=&quot;true&quot;/&gt;さん&amp;lt;br /&gt;
        あなたの連絡先は&amp;lt;sec:authentication property=&quot;principal.contact&quot; htmlEscape=&quot;true&quot;/&gt;です。&amp;lt;br /&gt;
        &amp;lt;sec:authorize access=&quot;hasRole(&#39;ROLE_ADMIN&#39;)&quot;&gt;※管理者権限が設定されています。&amp;lt;/sec:authorize&gt;
      &amp;lt;/p&gt;
      &amp;lt;c:url value=&quot;/resources/j_spring_security_logout&quot; var=&quot;logout_url&quot;/&gt;
      &amp;lt;a href=&quot;${logout_url}&quot;&gt;ログアウト&amp;lt;/a&gt;
    &amp;lt;/div&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;

&lt;u&gt;style.css&lt;/u&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
body {
  margin: 0px;
  padding: 0px;
  font: 13px Arial, Helvetica, sans-serif;
  color: #212121;
}

h1 {
  margin-top: 0px;
  font-size: 2.4em;
}

p {
  margin-bottom: 1.8em;
  line-height: 160%;
}

table {
  margin: 0px auto;
}

th, td {
  text-align: left;
}

div.errors {
  background-color: #FFEBE8;
  border: solid 1px #DD3C10;
  width: 300px;
  margin: 3px auto;
  padding: 3px;
}

#page {
  width: 900px;
  margin: 0px auto;
  padding: 30px 0px;
  text-align: center;
}
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;POMファイルの作成&lt;/h2&gt;
&lt;u&gt;pom.xml&lt;/u&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&amp;lt;project 
  xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;
    http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
    
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;studying-spring-security-02&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;war&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;name&gt;studying-spring-security-02&amp;lt;/name&gt;
  
  &amp;lt;properties&gt;
    &amp;lt;java.version&gt;1.7&amp;lt;/java.version&gt;
    &amp;lt;project.build.sourceEncoding&gt;UTF-8&amp;lt;/project.build.sourceEncoding&gt;
    &amp;lt;spring.version&gt;3.1.0.RELEASE&amp;lt;/spring.version&gt;
    &amp;lt;spring-security.version&gt;3.0.7.RELEASE&amp;lt;/spring-security.version&gt;
  &amp;lt;/properties&gt;
  
  &amp;lt;repositories&gt;
    &amp;lt;repository&gt;
      &amp;lt;id&gt;spring-maven-release&amp;lt;/id&gt;
      &amp;lt;name&gt;Spring Maven Release Repository&amp;lt;/name&gt;
      &amp;lt;url&gt;http://maven.springframework.org/release&amp;lt;/url&gt;
    &amp;lt;/repository&gt;
  &amp;lt;/repositories&gt;
  
  &amp;lt;pluginRepositories&gt;
    &amp;lt;pluginRepository&gt;
      &amp;lt;id&gt;spring-maven-release&amp;lt;/id&gt;
      &amp;lt;name&gt;Spring Maven Release Repository&amp;lt;/name&gt;
      &amp;lt;url&gt;http://maven.springframework.org/release&amp;lt;/url&gt;
    &amp;lt;/pluginRepository&gt;    &amp;lt;pluginRepository&gt;
      &amp;lt;id&gt;apache.snapshots&amp;lt;/id&gt;
      &amp;lt;url&gt;http://repository.apache.org/snapshots/&amp;lt;/url&gt;
    &amp;lt;/pluginRepository&gt;
  &amp;lt;/pluginRepositories&gt;
  
  &amp;lt;dependencies&gt;     
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;javaee-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;6.0&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;javax.servlet&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;jstl&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.2&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-context&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-webmvc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-jdbc&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-orm&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-core&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-config&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-web&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.springframework.security&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;spring-security-taglibs&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;${spring-security.version}&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.hibernate&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;hibernate-entitymanager&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;3.6.10.Final&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;commons-pool&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;commons-pool&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.5.6&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;commons-dbcp&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;commons-dbcp&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.3&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;mysql&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;mysql-connector-java&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;5.1.18&amp;lt;/version&gt;
    &amp;lt;/dependency&gt;

    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;junit&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;junit&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;4.10&amp;lt;/version&gt;
      &amp;lt;scope&gt;test&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;

  &amp;lt;build&gt;
    &amp;lt;finalName&gt;studying-spring-security-02&amp;lt;/finalName&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.3.2&amp;lt;/version&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;${java.version}&amp;lt;/source&gt;
          &amp;lt;target&gt;${java.version}&amp;lt;/target&gt;
          &amp;lt;encoding&gt;${project.build.sourceEncoding}&amp;lt;/encoding&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.tomcat.maven&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;tomcat7-maven-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.0-SNAPSHOT&amp;lt;/version&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
&amp;lt;/project&gt;
&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;


&lt;h2&gt;ビルドと実行&lt;/h2&gt;
&lt;p&gt;
コマンドプロンプトを開きCドライブ直下の「studying-spring-security」フォルダに移動後、mvnコマンドを実行します。
&lt;/p&gt;
&lt;pre class=&quot;brush: bash;gutter: false;&quot;&gt;
cd c\studying-spring-security
mvn clean package
mvn tomcat7:run
&lt;/pre&gt;
&lt;p&gt;
Webブラウザを開き「http://localhost:8080/studying-spring-security/」にアクセスします。&lt;br /&gt;
ログインユーザ/パスワードは admin/admin と user/user になります。
&lt;/p&gt;

&lt;div class=&quot;fummy_blockquote&quot;&gt;
&lt;span class=&quot;fummy_blockquote_title&quot;&gt;tomcat7-maven-pluginについて&lt;/span&gt;
&lt;p&gt;
現時点での最新は「2.0-beta-1」だと思われるのですが、このVersionでtomcatを実行すると&lt;br /&gt;
「Unable to determine URL for WEB-INF/classes」的なエラー発生してしまいます。&lt;br /&gt;
これは、tomcat7-maven-pluginが使用しているtomcat(7.0.25)が原因とのことです。&lt;br /&gt;
エラーは出るもののとりあえずは動いてそうなので問題なさそうなのですが、&lt;br /&gt;
エラーが出ているのがなんとなく嫌な感じなのでSNAPSHOTにしてみました。&lt;br /&gt;
なにぶんSNAPSHOTなので日によって挙動が違うかもしれません…&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;
SpringSecurityは、もっとガチガチで入り込む余地があまりないような印象だったのですが&lt;br /&gt;
さわってみた感じ割と挟みどころがいろいろ用意されているようです。&lt;br /&gt;
全然使い込んではないのでなんとも言えませんが…&lt;br /&gt;
とりあえずSpringは期待できそうだ。！&lt;br /&gt;
Webの開発をJavaでとなるとSpringの今後に懸かってますね。！&lt;br /&gt;
…JavaでWebかぁ～&lt;br /&gt;
ないのかな？
&lt;/p&gt;


&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://news.mynavi.jp/articles/2010/03/25/spring3/003.html&quot;&gt;
    http://news.mynavi.jp/articles/2010/03/25/spring3/003.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://sites.google.com/site/soracane/home/springnitsuite/spring-security/spring-securityno-settei-houhou&quot;&gt;
    http://sites.google.com/site/soracane/home/springnitsuite/spring-security/spring-securityno-settei-houhou
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://ameblo.jp/spring-beginner/entry-10239724907.html&quot;&gt;
 http://ameblo.jp/spring-beginner/entry-10239724907.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://oajamfibia.wordpress.com/2011/07/05/writing-a-custom-jpa-userdetailservice/&quot;&gt;
    http://oajamfibia.wordpress.com/2011/07/05/writing-a-custom-jpa-userdetailservice/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://krams915.blogspot.jp/2010/12/spring-security-mvc-integration_18.html&quot;&gt;
    http://krams915.blogspot.jp/2010/12/spring-security-mvc-integration_18.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://www.codercorp.com/blog/spring/security-spring/writing-custom-userdetailsservice-for-spring-security.html&quot;&gt;
    http://www.codercorp.com/blog/spring/security-spring/writing-custom-userdetailsservice-for-spring-security.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://stackoverflow.com/questions/9580031/javax-naming-namenotfoundexception-resource-web-inf-classes-not-found-with-mav&quot;&gt;
    http://stackoverflow.com/questions/9580031/javax-naming-namenotfoundexception-resource-web-inf-classes-not-found-with-mav
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/192728227924457674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/192728227924457674'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2012/05/springsecuritydbjpa.html' title='SpringSecurityユーザをDBで管理、JPAでログインする'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-2493773143265077474</id><published>2011-10-17T16:00:00.041+09:00</published><updated>2011-10-18T01:02:50.062+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="air"/><category scheme="http://www.blogger.com/atom/ns#" term="flex"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><title type='text'>mavenでAIRする</title><content type='html'>&lt;h2&gt;はじめに&lt;/h2&gt;今回は、flexmojos-maven-pluginでFlex-AIRアプリを作ろう！というのがテーマです。&lt;br /&gt;
このMavenプラグインを使ってうれしいことは&lt;br /&gt;
&lt;ol&gt;  &lt;li&gt;無償である。&lt;/li&gt;
  &lt;li&gt;MavenリポジトリにアップされているFlex-SDKを利用できるのでダウンロード、インストールなどが不要である。&lt;/li&gt;
&lt;/ol&gt;などなどです。&lt;br /&gt;
それでは、Getting Started!!&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;環境&lt;/h2&gt;&lt;table class=&quot;fummy&quot;&gt;  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;OS&lt;/th&gt;
    &lt;td&gt;Windows XP&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;ビルドツール&lt;/th&gt;
    &lt;td&gt;Maven 3.0.3  ※mvnコマンドが実行できるように設定しておきます。&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
&lt;h2&gt;作業フォルダ作成&lt;/h2&gt;とりあえずCドライブ直下にプロジェクトフォルダを作成します。&lt;br /&gt;
コマンドプロンプトを開いて以下のコマンドを実行します。&lt;br /&gt;
&lt;pre class=&quot;brush: bash;gutter: false;&quot;&gt;cd c:\
mkdir maven-air
cd maven-air
&lt;/pre&gt;作成した作業フォルダ作成に移動しておきます。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;pom.xmlファイルを作る&lt;/h2&gt;コマンドプロンプトから以下のコマンドを実行します。&lt;br /&gt;
&lt;pre class=&quot;brush: bash;gutter: false;&quot;&gt;mvn archetype:generate ^
-DarchetypeRepository=http://repository.sonatype.org/content/groups/flexgroup ^
-DarchetypeGroupId=org.sonatype.flexmojos ^
-DarchetypeArtifactId=flexmojos-archetypes-application ^
-DarchetypeVersion=4.0-beta-3
&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;archetypeVersionについて&lt;/span&gt;&lt;br /&gt;
現時点での最新と思われる 4.0-beta-3 を指定します。&lt;br /&gt;
&lt;/div&gt;groupId、artifactId、versionは以下のようにします。&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;  &lt;tr valign=&quot;top&quot;&gt;
    &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;groupId&lt;/th&gt;
    &lt;td&gt;mydomain.com&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;artifactId&lt;/th&gt;
    &lt;td&gt;my-air&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th&gt;version&lt;/th&gt;
    &lt;td&gt;1.0-SNAPSHOT&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
コマンド実行後、プロジェクトフォルダとpomファイルが生成されます。&lt;br /&gt;
次に生成されたpomファイルを以下のような感じで編集します。&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
     
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;

  &amp;lt;groupId&gt;mydomain.com&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;my-air&amp;lt;/artifactId&gt;
  &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
  &lt;packaging&gt;swf&lt;/packaging&gt;

  &lt;name&gt;my-air Flex&lt;/name&gt;

  &lt;properties&gt;
    &lt;application.name&gt;Main&lt;/application.name&gt;
    &lt;flex.framework.version&gt;4.5.0.17855&lt;/flex.framework.version&gt;
    &lt;keystore.file&gt;${basedir}/src/main/resources/sign.p12&lt;/keystore.file&gt;
    &lt;keystore.password&gt;geheim&lt;/keystore.password&gt;
  &lt;/properties&gt;
  
  &lt;build&gt;
    &amp;lt;sourceDirectory&gt;src/main/flex&amp;lt;/sourceDirectory&gt;
    &amp;lt;testSourceDirectory&gt;src/test/flex&amp;lt;/testSourceDirectory&gt;
    &lt;plugins&gt; 
      &lt;plugin&gt;
        &amp;lt;groupId&gt;org.sonatype.flexmojos&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;flexmojos-maven-plugin&amp;lt;/artifactId&gt;
        &lt;version&gt;4.0-beta-3&lt;/version&gt;
        &lt;extensions&gt;true&lt;/extensions&gt;
        &lt;configuration&gt;
          &amp;lt;flexBuilderCompatibility&gt;true&amp;lt;/flexBuilderCompatibility&gt;
          &amp;lt;sourceFile&gt;${application.name}.mxml&amp;lt;/sourceFile&gt;
          &amp;lt;descriptorTemplate&gt;${basedir}/src/main/flex/${application.name}-app.xml&amp;lt;/descriptorTemplate&gt;
          &lt;keystore&gt;${keystore.file}&lt;/keystore&gt;
          &lt;storepass&gt;${keystore.password}&lt;/storepass&gt;
          &amp;lt;includeFileSets&gt;
            &amp;lt;fileSet&gt;
              &lt;directory&gt;src/main/resources/embedded&lt;/directory&gt;
              &lt;includes&gt;
                &lt;include&gt;*.*&lt;/include&gt;
              &lt;/includes&gt;
            &amp;lt;/fileSet&gt;
          &amp;lt;/includeFileSets&gt;
        &lt;/configuration&gt;
        &lt;executions&gt;
          &lt;execution&gt;
            &lt;goals&gt;
              &lt;goal&gt;sign-air&lt;/goal&gt;
            &lt;/goals&gt;
          &lt;/execution&gt;
        &lt;/executions&gt;
      &lt;/plugin&gt;
    &lt;/plugins&gt;
  &lt;/build&gt;

  &lt;dependencies&gt;
    &lt;dependency&gt;
      &amp;lt;groupId&gt;com.adobe.flex.framework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;flex-framework&amp;lt;/artifactId&gt;
      &lt;version&gt;${flex.framework.version}&lt;/version&gt;
      &lt;type&gt;pom&lt;/type&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &amp;lt;groupId&gt;com.adobe.flexunit&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;flexunit&amp;lt;/artifactId&gt;
      &lt;version&gt;0.85&lt;/version&gt;
      &lt;type&gt;swc&lt;/type&gt;
      &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &amp;lt;groupId&gt;com.adobe.flex.framework&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;air-framework&amp;lt;/artifactId&gt;
      &lt;version&gt;${flex.framework.version}&lt;/version&gt;
      &lt;type&gt;pom&lt;/type&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &amp;lt;groupId&gt;com.adobe.flex&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;compiler&amp;lt;/artifactId&gt;
      &lt;version&gt;${flex.framework.version}&lt;/version&gt;
      &lt;type&gt;pom&lt;/type&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &amp;lt;groupId&gt;com.adobe.flex.compiler&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;adt&amp;lt;/artifactId&gt;
      &lt;version&gt;${flex.framework.version}&lt;/version&gt;
    &lt;/dependency&gt;
  &lt;/dependencies&gt;

  &lt;repositories&gt;
    &lt;repository&gt;
      &lt;id&gt;flexmojos&lt;/id&gt;
      &lt;url&gt;http://repository.sonatype.org/content/groups/flexgroup&lt;/url&gt;
    &lt;/repository&gt;
  &lt;/repositories&gt;

  &amp;lt;pluginRepositories&gt;
    &amp;lt;pluginRepository&gt;
      &lt;id&gt;flexmojos&lt;/id&gt;
      &lt;url&gt;http://repository.sonatype.org/content/groups/flexgroup&lt;/url&gt;
    &amp;lt;/pluginRepository&gt;
  &amp;lt;/pluginRepositories&gt;
 
  &lt;profiles&gt;
    &lt;profile&gt;
      &lt;id&gt;createCertificateKey&lt;/id&gt;
      &lt;activation&gt;
        &lt;file&gt;
          &lt;missing&gt;${basedir}/src/main/resources/sign.p12&lt;/missing&gt;
        &lt;/file&gt;
      &lt;/activation&gt;
      &lt;build&gt;
        &lt;plugins&gt;
          &lt;plugin&gt;
            &amp;lt;groupId&gt;org.codehaus.mojo&amp;lt;/groupId&gt;
            &amp;lt;artifactId&gt;exec-maven-plugin&amp;lt;/artifactId&gt;
            &lt;version&gt;1.1.1&lt;/version&gt;        
            &lt;executions&gt;
              &lt;execution&gt;
                &lt;id&gt;generate-cert&lt;/id&gt;
                &lt;phase&gt;generate-resources&lt;/phase&gt;
                &lt;goals&gt;
                  &lt;goal&gt;exec&lt;/goal&gt;
                &lt;/goals&gt;
                  &lt;configuration&gt;
                    &lt;executable&gt;java&lt;/executable&gt;
                    &amp;lt;workingDirectory&gt;${basedir}/src/main/resources&amp;lt;/workingDirectory&gt;
                    &lt;arguments&gt;
                      &lt;argument&gt;-classpath&lt;/argument&gt;
                      &amp;lt;classpath/&gt;
                      &lt;argument&gt;com.adobe.air.ADT&lt;/argument&gt;
                      &lt;argument&gt;-certificate&lt;/argument&gt;
                      &lt;argument&gt;-cn&lt;/argument&gt;
                      &lt;argument&gt;AirPackage&lt;/argument&gt;
                      &lt;argument&gt;2048-RSA&lt;/argument&gt;
                      &lt;argument&gt;sign.p12&lt;/argument&gt;
                      &lt;argument&gt;geheim&lt;/argument&gt;
                    &lt;/arguments&gt;
                  &lt;/configuration&gt;
              &lt;/execution&gt;
            &lt;/executions&gt;
          &lt;/plugin&gt;
        &lt;/plugins&gt;
      &lt;/build&gt;
    &lt;/profile&gt;
  &lt;/profiles&gt;
 
&amp;lt;/project&gt;
&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;CertificateKeyファイルについて&lt;/span&gt;&lt;br /&gt;
ビルドの度にCertificateKeyファイルを作ってAIRパッケージするとAIRインストール時にエラーが出る。&lt;br /&gt;
なのでprofile設定を利用してCertificateKeyファイルが無い時にだけ生成するようにします。&lt;br /&gt;
(同じCertificateKeyファイルを使ってパッケージしないといけないようだ)&lt;br /&gt;
sign-airゴールの設定をうまくやればこのprofile設定は不要かもしれません。&lt;br /&gt;
わたしはよく分からなかったのでexec-maven-pluginでCertificateKeyファイルを生成する方法にしました。&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;テンプレートファイル作成&lt;/h2&gt;AIRパッケージのためのテンプレートファイル作成します。&lt;br /&gt;
ファイル名は 『Main-app.xml』 として、『c:\maven-air\my-air\src\main\flex』 フォルダに置きます。&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Main-app.xml&lt;/u&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;application xmlns=&quot;http://ns.adobe.com/air/application/1.5.3&quot;&gt;
  &lt;id&gt;com.mydomain.MyApp&lt;/id&gt;
  &lt;filename&gt;Main&lt;/filename&gt;
  &lt;name&gt;My Application&lt;/name&gt;
  &lt;version&gt;1.0&lt;/version&gt;
  &amp;lt;initialWindow&gt;
    &lt;content&gt;my-air-1.0-SNAPSHOT.swf&lt;/content&gt;
    &lt;visible&gt;true&lt;/visible&gt;
    &lt;width&gt;800&lt;/width&gt;
    &lt;height&gt;600&lt;/height&gt;
  &amp;lt;/initialWindow&gt;
&lt;/application&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;h2&gt;プロジェクト構成の確認&lt;/h2&gt;確認の前に embedded フォルダを作成します。&lt;br /&gt;
コマンドプロンプトを開いて以下のコマンドを実行します。&lt;br /&gt;
&lt;pre class=&quot;brush: bash;gutter: false;&quot;&gt;&amp;nbsp;
mkdir C:\maven-air\my-air\src\main\resources\embedded
&amp;nbsp;
&lt;/pre&gt;この時点で以下のようなファイル構成になっているか確認します。&lt;br /&gt;
&lt;ul class=&quot;fummy_tree&quot;&gt;  &lt;li&gt;C:\maven-air&lt;br /&gt;
    &lt;ul&gt;      &lt;li&gt;my-air&lt;br /&gt;
        &lt;ul&gt;          &lt;li&gt;src&lt;br /&gt;
            &lt;ul&gt;              &lt;li&gt;main&lt;br /&gt;
                &lt;ul&gt;                  &lt;li&gt;flex&lt;br /&gt;
                    &lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;Main.mxml&lt;/li&gt;&lt;/ul&gt;                    &lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;Main-app.xml&lt;/li&gt;&lt;/ul&gt;                  &lt;/li&gt;
                  &lt;li&gt;resources&lt;br /&gt;
                    &lt;ul&gt;&lt;li&gt;embedded&lt;/li&gt;&lt;/ul&gt;                  &lt;/li&gt;
                &lt;/ul&gt;              &lt;/li&gt;
              &lt;li&gt;test&lt;br /&gt;
                &lt;ul&gt;                  &lt;li&gt;flex&lt;br /&gt;
                    &lt;ul&gt;                      &lt;li&gt;mydomain&lt;br /&gt;
                        &lt;ul&gt;                          &lt;li&gt;com&lt;br /&gt;
                            &lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;TestApp.as&lt;/li&gt;&lt;/ul&gt;                          &lt;/li&gt;
                        &lt;/ul&gt;                      &lt;/li&gt;
                    &lt;/ul&gt;                  &lt;/li&gt;
                &lt;/ul&gt;              &lt;/li&gt;
            &lt;/ul&gt;          &lt;/li&gt;
          &lt;li class=&quot;fummy_tree_txt&quot;&gt;pom.xml&lt;/li&gt;
        &lt;/ul&gt;      &lt;/li&gt;
    &lt;/ul&gt;  &lt;/li&gt;
&lt;/ul&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;sign-airゴールについて&lt;/span&gt;&lt;br /&gt;
embedded フォルダを作るのを忘れるとビルドの時のsign-airゴール実行に&lt;br /&gt;
NullPointerExceptionが出るので気をつけましょう。&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;ビルド！！&lt;/h2&gt;いよいよビルドの瞬間がやって参りました。&lt;br /&gt;
うまくいきますかねぇ？&lt;br /&gt;
ドキドキしますね！&lt;br /&gt;
それでは pom.xml ファイルのあるプロジェクトフォルダに移動してコマンドプロンプトから以下のコマンドを実行！！！&lt;br /&gt;
&lt;pre class=&quot;brush: bash;gutter: false;&quot;&gt;cd c:\maven-air\my-air
mvn clean package -Dmaven.test.skip=true
&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;ここに来て残念なお知らせが・・・&lt;/span&gt;&lt;br /&gt;
テスト実行時、 Flash Playerか見つからない等のエラーが出てしまいます。&lt;br /&gt;
（わたしの環境だけなのかもしれませんが・・・）&lt;br /&gt;
PATH変数やmvnコマンドの引数にFlashPlayer.exeを使いしてみたりしたのですがうまくいきませんでした。&lt;br /&gt;
なので今回、テストは見送りです。&lt;br /&gt;
わたしの設定の仕方が間違っているのか、プラグインがおかしいのかよく分かりません。&lt;br /&gt;
まぁオープンソースなのでそんなものです。&lt;br /&gt;
いつかソースコードを眺めてみたいと思っています。&lt;br /&gt;
&lt;/div&gt;コマンド実行後、targetフォルダ直下に my-air-1.0-SNAPSHOT.air ファイルが出力されます。&lt;br /&gt;
このファイルをダブルクリックするとAIRインストーラーが起動します。&lt;br /&gt;
あとは適当にインスールするフォルダを選択して完了です。&lt;br /&gt;
&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;AIRアプリのインストールに失敗する場合&lt;/span&gt;&lt;br /&gt;
同じAIRアプリを違うCertificateKeyファイルでパッケージするができないようです。&lt;br /&gt;
そんな時は、コントロールパネルの『プログラムの追加と削除』から一旦AIRアプリを削除してから&lt;br /&gt;
再度my-air-1.0-SNAPSHOT.air ファイルを実行してみよう。&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;おわりに&lt;/h2&gt;とりあえずは、無償でFlex-AIRアプリがそこそこお手軽に作れる環境が整ったとは思いますが&lt;br /&gt;
いくつか問題点は残りました。(テストなど)&lt;br /&gt;
ここがFlashBuilderを購入するか悩むところです。&lt;br /&gt;
仕事ならばFlashBuilder購入なのでしょうが&lt;br /&gt;
趣味程度ならflexmojos-maven-pluginで十分なのでしょうかね？&lt;br /&gt;
むむ～&lt;br /&gt;
flexmojos-maven-pluginのソース読む元気ないし&lt;br /&gt;
FlashBuilderの安いものなら３万円くらいなので買おうかしら？&lt;br /&gt;
&lt;br /&gt;
&lt;dl&gt;&lt;dt&gt;&lt;b&gt;参考URL&lt;/b&gt;&lt;/dt&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://blog.flex-mojos.info/&quot;&gt;
    http://blog.flex-mojos.info/
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://flexmojos.sonatype.org/getting-started.html&quot;&gt;
    http://flexmojos.sonatype.org/getting-started.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://docs.sonatype.org/display/FLEXMOJOS/Home&quot;&gt;
    https://docs.sonatype.org/display/FLEXMOJOS/Home
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://docs.sonatype.org/display/FLEXMOJOS/Building+an+AIR+Application&quot;&gt;
    https://docs.sonatype.org/display/FLEXMOJOS/Building+an+AIR+Application
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;https://repository.sonatype.org/content/sites/maven-sites/flexmojos/latest/plugin-info.html&quot;&gt;
    https://repository.sonatype.org/content/sites/maven-sites/flexmojos/latest/plugin-info.html
  &lt;/a&gt;
&lt;/dd&gt;
&lt;dd&gt;
  &lt;a href=&quot;http://code.google.com/p/jianwikis/wiki/MavenForFlexAirProject#Flex_AIR_Maven_Project&quot;&gt;
    http://code.google.com/p/jianwikis/wiki/MavenForFlexAirProject#Flex_AIR_Maven_Project
  &lt;/a&gt;
&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2493773143265077474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2493773143265077474'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2011/10/mavenair.html' title='mavenでAIRする'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-436858914722825572</id><published>2009-12-06T09:57:00.015+09:00</published><updated>2009-12-06T10:43:38.926+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (1/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;はじめに&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
EJBのコンテナ管理トランザクション(ContainerManagedTransactions-CMT)とは&lt;br /&gt;
どんなものなのか試してみようというのが今回のテーマです。&lt;br /&gt;
ステートレスのローカルオブジェクトのセッション Bean で検証をします。&lt;br /&gt;
主に@TransactionAttributeアノテーションによってTransactionAttributeTypeが&lt;br /&gt;
REQUIREDとREQUIRES_NEWに指定されたメソッドがコミットされる様子と&lt;br /&gt;
ロールバックされる様子を観察したいと思います。&lt;br /&gt;
インターセプターを使ってトランザクション内に例外を発生させてロールバックするようにします。&lt;br /&gt;
確認のためにセッションIDをアクセスログとして保存し表示する検証用Webアプリを作成します。&lt;br /&gt;
検証用Webアプリはシンプルにしたいのでフレームワークを使わずServletとJSPで作成します。&lt;br /&gt;&lt;br /&gt;
以下の内容を検証します。&lt;br /&gt;
&lt;ol&gt; &lt;li&gt;REQUIREDに指定されたメソッド同士は同じトランザクションとして処理されるのか？ &lt;br /&gt;
  トランザクションに参加するいずれかのメソッドで例外が発生した場合、&lt;br /&gt;
  その トランザクションに参加するすべてのメソッドでの処理がロールバックされるか検証します。&lt;br /&gt;
 &lt;/li&gt;
 &lt;li&gt;REQUIRES_NEWに指定されたメソッドは別トランザクションを開始して処理されるのか？ &lt;br /&gt;
  別トランザクションのメソッドで例外が発生した場合でも REQUIRES_NEWに指定された&lt;br /&gt;
  メソッドでの処理がコミットされていれば、その処理はロールバックされないことを検証します。&lt;br /&gt;
  (別トランザクションのメソッドでの処理はロールバックされる) &lt;br /&gt;
 &lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/436858914722825572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/436858914722825572'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/110.html' title='コンテナ管理トランザクションであそぶ - (1/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-4251445267453832054</id><published>2009-12-06T09:56:00.045+09:00</published><updated>2009-12-06T17:58:36.930+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (2/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;準備する&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;検証環境&lt;/span&gt;&lt;br /&gt;
&lt;table class=&quot;fummy&quot;&gt;&lt;tr valign=&quot;top&quot;&gt;     &lt;th width=&quot;200px&quot; nowrap=&quot;nowrap&quot;&gt;アプリケーションサーバー &lt;/th&gt;     &lt;td&gt;GlassFish v2.1&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;th&gt;データベースサーバー&lt;/th&gt;     &lt;td&gt;Apache Derby&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;th&gt;OS&lt;/th&gt;     &lt;td&gt;Windows XP&lt;/td&gt;   &lt;/tr&gt;
&lt;tr&gt;     &lt;th&gt;ビルドツール&lt;/th&gt;     &lt;td&gt;Maven 2&lt;br /&gt;
※mvnコマンドが実行できるように設定しておいてください。&lt;/td&gt;   &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
適当な場所に作業フォルダー(C:\mywork)を作り、フォルダとファイルを配置します。&lt;br /&gt;
一括ビルドプロジェクト(build)、EARファイルプロジェクト(build-ear)、&lt;br /&gt;
EJBプロジェクト(ejb-module)、Webアプリプロジェクト(webtest-client)を作成します。 &lt;br /&gt;
&lt;br /&gt;
&lt;ul class=&quot;fummy_tree&quot;&gt;&lt;li&gt;C:\mywork&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;build&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;pom.xml&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;build-ear&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;pom.xml&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;ejb-module&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;src&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;main&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;java&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;com&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;mydomain&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;ejb&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;Action.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;ActionImpl.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;Message.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;MessageUtil.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;Rollback.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;SubAction.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;SubActionImpl.java&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;resources&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;META-INF&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;ejb-jar.xml&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;persistence.xml&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;pom.xml&lt;/li&gt;&lt;br /&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;webtest-client&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;src&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;main&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;java&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;com&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;mydomain&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;ejb&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;Client.java&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;PageData.java&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;webapp&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;WEB-INF&lt;br /&gt;
&lt;ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;web.xml&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li class=&quot;fummy_tree_txt&quot;&gt;index.jsp&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;li class=&quot;fummy_tree_txt&quot;&gt;pom.xml&lt;/li&gt;&lt;br /&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4251445267453832054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4251445267453832054'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/310.html' title='コンテナ管理トランザクションであそぶ - (2/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-7381761351464419049</id><published>2009-12-06T09:56:00.035+09:00</published><updated>2009-12-06T11:46:44.685+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (3/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;DataSourceの設定をする&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
asadmin create-jdbc-connection-poolコマンドを使ってコネクションプールを作成します。&lt;br /&gt;&lt;br /&gt;
コマンドプロンプトを開き以下のように入力します。&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;asadmin create-jdbc-connection-pool ^
  --datasourceclassname org.apache.derby.jdbc.ClientDataSource ^
  --restype javax.sql.DataSource ^
  --property user=sa:password=sa:databaseName=example ^
  MyJDBCPool
&lt;/pre&gt;コネクションプールのプロパティに接続文字列を追加します。&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;asadmin set ^
  domain.resources.jdbc-connection-pool.MyJDBCPool.property.url=jdbc:derby://localhost:1527/example
&lt;/pre&gt;コネクションプールのプロパティに接続文字列属性を追加します。&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;asadmin set ^
  domain.resources.jdbc-connection-pool.MyJDBCPool.property.connectionAttributes=;create=true
&lt;/pre&gt;コネクションの接続を確認します。&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;asadmin ping-connection-pool MyJDBCPool&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;
asadmin create-jdbc-resourceコマンドを使ってコネクションプールをJNDIに登録します。&lt;br /&gt;&lt;br /&gt;
コマンドプロンプトを開き以下のように入力します。&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;asadmin create-jdbc-resource ^
  --connectionpoolid MyJDBCPool ^
  jdbc/MyJDBCResource
&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;GlassFish管理コマンド&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
asadmin コマンドを使用するためには 『GLASSFISHインストールフォルダ/bin』 フォルダを&lt;br /&gt;
PATH変数に追加しておかないといけません。&lt;br /&gt;
コントロールパネルのシステムの詳細設定タブから環境変数を編集します。&lt;br /&gt;
それからGlassFishサーバーとJavaDBを起動します。&lt;br /&gt;
WebのGlassFish管理コンソールからコネクションプールをJDBCリソースに登録する場合は&lt;br /&gt;
『jdbc/MyJDBCResource』 でJNDIから参照できるようにします。&lt;br /&gt;
&lt;/div&gt;Persistence Unitを定義します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;persistence.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;persistence version=&quot;1.0&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/persistence&quot; 
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/persistence 
                      http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd&quot;&gt;
  &amp;lt;persistence-unit name=&quot;example-pu&quot;&gt;
  &amp;lt;provider&gt;oracle.toplink.essentials.PersistenceProvider&amp;lt;/provider&gt;
    &amp;lt;jta-data-source&gt;jdbc/MyJDBCResource&amp;lt;/jta-data-source&gt;
    &amp;lt;properties&gt;
      &amp;lt;property name=&quot;toplink.ddl-generation&quot; value=&quot;drop-and-create-tables&quot; /&gt;
    &amp;lt;/properties&gt;
  &amp;lt;/persistence-unit&gt;
&amp;lt;/persistence&gt;&lt;/pre&gt;&lt;table class=&quot;fummy_list&quot;&gt;  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;Persistence UnitをJNDIのグローバルネームスペース 『example-pu』 に登録します。&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/7381761351464419049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/7381761351464419049'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/210.html' title='コンテナ管理トランザクションであそぶ - (3/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-4958058855371864155</id><published>2009-12-06T09:55:00.015+09:00</published><updated>2009-12-06T18:50:27.890+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (4/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;ビジネスBeanを作成する&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
ステートレス Session Beanを作成します。&lt;br /&gt;
ビジネスインターフェイスとセッションBeanを２組作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;Action.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import java.util.List;
import javax.ejb.*;

@Local
public interface Action
{
    public void regist(String message, int rollbak);
    
    public void insert(Message message);
    
    public void decorateInsert(Message message);

    public List&amp;lt;Message&gt; getMessages();
    
    public void delete();
}&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;ActionImpl.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import java.util.*;
import javax.ejb.*;
import javax.persistence.*;
import static com.mydomain.ejb.MessageUtil.*;

@Stateless
public class ActionImpl implements Action
{
    @PersistenceContext(unitName=&quot;example-pu&quot;)
    private EntityManager em;
    
    @EJB
    private SubAction subAction;

    public void regist(String text, int rollback)          
    {       
        this.decorateInsert(createMessage(text, 2, rollback));
        this.insert(createMessage(text, 3, rollback));
        this.subAction.decorateInsert(createMessage(text, 5, rollback));
        this.subAction.insert(createMessage(text, 6, rollback));
        try
        {
            this.subAction.requiresNewInsert(createMessage(text, 8, rollback));
        }
        catch(Exception exp){}

        Message chkMessage = createMessage(text, 1, rollback);
        doRollback(chkMessage);
    }

    public void insert(Message message)          
    {
        setLogMessage(message, this.getClass(), &quot;insert&quot;);
        this.em.persist(message);
        
        Message decMessage = createMessage(message);
        setNextLogMessage(decMessage);
        this.decorateInsert(decMessage);
        
        doRollback(message);
    }
    
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void decorateInsert(Message message)            
    {
        setLogMessage(message, this.getClass(), &quot;decorateInsert&quot;);
        message.setText(message.getText() + &quot;*&quot;);
        this.em.persist(message);

        doRollback(message);
    }

    public List&amp;lt;Message&gt; getMessages()
    {
        String sql = &quot;SELECT m FROM message AS m ORDER BY m.no&quot;;
        return this.em.createQuery(sql).getResultList();
    }

    public void delete()
    {
        this.em.createQuery(&quot;DELETE FROM message&quot;).executeUpdate();
    }
}&lt;/pre&gt;&lt;table class=&quot;fummy_list&quot;&gt;  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;regist()メソッド&lt;br /&gt;
     サーブレットから呼出されてセッションIDをアクセスログとして登録するメインの処理を開始します。&lt;br /&gt;
     処理の最後にMessageUtil.doRollback()メソッドを呼出して例外発生の判定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;insert()メソッド&lt;br /&gt;
     セッションIDを登録します。&lt;br /&gt;
     処理の最後にMessageUtil.doRollback()メソッドを呼出して例外発生の判定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;decorateInsert()メソッド&lt;br /&gt;
     セッションIDに『*』 アスタリスクを付けて登録します。&lt;br /&gt;
     REQUIRES_NEW を指定して別トランザクションとして処理されるようにします。&lt;br /&gt;
     処理の最後にMessageUtil.doRollback()メソッドを呼出して例外発生の判定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;SubAction.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import javax.ejb.Local;

@Local
public interface SubAction
{
    public void insert(Message message);

    public void decorateInsert(Message message);
    
    public void requiresNewInsert(Message message);
}&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;SubActionImpl.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import javax.ejb.*;
import javax.persistence.*;
import static com.mydomain.ejb.MessageUtil.*;

@Stateless(name=&quot;subActionImpl&quot;)                 
public class SubActionImpl implements SubAction
{
    @PersistenceContext(unitName=&quot;example-pu&quot;)
    private EntityManager em;

    public void insert(Message message)          
    {
        setLogMessage(message, this.getClass(), &quot;insert&quot;);
        this.em.persist(message);

        Message decMessage = createMessage(message);
        setNextLogMessage(decMessage);
        this.decorateInsert(decMessage);

        doRollback(message);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void decorateInsert(Message message)            
    {
        setLogMessage(message, this.getClass(), &quot;decorateInsert&quot;);
        message.setText(message.getText() + &quot;*&quot;);
        this.em.persist(message);

        doRollback(message);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void requiresNewInsert(Message message)         
    {
        String sessionId = message.getText();
        message.setText(sessionId + &quot;+&quot;);
        this.em.persist(message);

        Message decMessage = createMessage(message);
        setNextLogMessage(decMessage);
        decMessage.setText(sessionId);
        this.insert(decMessage);
    }
}&lt;/pre&gt;&lt;table class=&quot;fummy_list&quot;&gt;  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;このクラスのEJB名(EJBコンテナ内での名前)をsubActionImpl に設定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;insert()メソッド&lt;br /&gt;
     セッションIDを登録します。&lt;br /&gt;
     処理の最後にMessageUtil.doRollback()メソッドを呼出して例外発生の判定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;decorateInsert()メソッド&lt;br /&gt;
     セッションIDに『*』 アスタリスクを付けて登録します。&lt;br /&gt;
     REQUIRES_NEW を指定して別トランザクションとして処理されるようにします。&lt;br /&gt;
     処理の最後にMessageUtil.doRollback()メソッドを呼出して例外発生の判定します。&lt;br /&gt;
    &lt;/td&gt;
  &lt;tr&gt;
    &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;
    &lt;td&gt;requiresNewInsert()メソッド&lt;br /&gt;
     セッションIDに『+』 プラスを付けて登録します。&lt;br /&gt;
     REQUIRES_NEW を指定して別トランザクションとして処理されるようにします。&lt;br /&gt;
     処理の最後にインターセプターによって例外発生の判定をします。&lt;br /&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4958058855371864155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/4958058855371864155'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/410.html' title='コンテナ管理トランザクションであそぶ - (4/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-1309330843640524712</id><published>2009-12-06T09:55:00.013+09:00</published><updated>2009-12-06T17:19:32.437+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (5/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;インターセプターを作成する&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
インターセプターを作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;Rollback.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import javax.interceptor.*;
import static com.mydomain.ejb.MessageUtil.*;

public class Rollback
{
    @AroundInvoke
    public Object action(InvocationContext ctx) throws Exception
    {
        Object[] params = ctx.getParameters();
        Message message = (Message)params[0];
        setLogMessage(message, ctx.getTarget().getClass(), ctx.getMethod().getName());
        
        Object retObj = ctx.proceed();                     
        
        doRollback(message);
        
        return retObj;
    }
}&lt;/pre&gt;同じクラスの同じメソッドであっても呼出される条件によってインターセプトが適用されない場合もあったので&lt;br /&gt;
SubActionImpl クラスの requiresNewInsert() メソッドのみにインターセプトを設定しています。&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;ejb-jar.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;ejb-jar version=&quot;3.0&quot;
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; 
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; 
  xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee 
                      http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd&quot;&gt;
  &amp;lt;interceptors&gt;
    &amp;lt;interceptor&gt;
      &amp;lt;interceptor-class&gt;com.mydomain.ejb.Rollback&amp;lt;/interceptor-class&gt;         
    &amp;lt;/interceptor&gt;
  &amp;lt;/interceptors&gt;
  &amp;lt;assembly-descriptor&gt;
    &amp;lt;interceptor-binding&gt;
      &amp;lt;ejb-name&gt;subActionImpl&amp;lt;/ejb-name&gt;         
      &amp;lt;interceptor-class&gt;com.mydomain.ejb.Rollback&amp;lt;/interceptor-class&gt;
      &amp;lt;method&gt;
        &amp;lt;method-name&gt;requiresNewInsert&amp;lt;/method-name&gt;
        &amp;lt;method-params&gt;
          &amp;lt;method-param&gt;com.mydomain.ejb.Message&amp;lt;/method-param&gt;
        &amp;lt;/method-params&gt;
      &amp;lt;/method&gt;
    &amp;lt;/interceptor-binding&gt;
  &amp;lt;/assembly-descriptor&gt;
&amp;lt;/ejb-jar&gt;&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;&lt;span class=&quot;fummy_blockquote_title&quot;&gt;インターセプトされないメソッドの呼出し&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
EJBインスタンスを通して直接呼出される場合はインターセプトが適用されるようですが &lt;br /&gt;
メソッドの処理の中で呼出される場合にはインターセプトが 適用されないみたいでした。&lt;br /&gt;
今回の検証用Webアプリの例でいうとActionImplクラスのinsert()メソッドとdecorateInsert()メソッドは&lt;br /&gt;
regist()メソッドから呼出されるためインターセプトは適用されないようです。 &lt;br /&gt;
(regist()メソッドはサーブレットから直接呼出されるのでインターセプトが適用できるようです。) &lt;br /&gt;
またSubActionImplクラスのinsert()メソッドとdecorateInsert()メソッドは&lt;br /&gt;
ActionImplクラスのregist()メソッドから直接呼出される場合にはインターセプトが適用されるようですが、 &lt;br /&gt;
requiresNewInsert()メソッドの処理の中から呼出されたinsert()メソッド、 &lt;br /&gt;
insert()メソッドの処理の中から呼出されたdecorateInsert()メソッドには インターセプトが適用されないようでした。&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1309330843640524712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/1309330843640524712'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/510.html' title='コンテナ管理トランザクションであそぶ - (5/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-145312803947747714</id><published>2009-12-06T09:54:00.015+09:00</published><updated>2009-12-06T18:55:26.197+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (6/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;エンティティBeanを作成する&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
エンティティBeanを作成します。&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;Message.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import java.io.Serializable;
import javax.persistence.*;

@Entity(name=&quot;message&quot;)
public class Message implements Serializable
{
    private static final long serialVersionUID = 1L;

    public Message(){}

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    private long id;
    public long getId(){ return this.id; }
    public void setId(long id){ this.id = id; }

    private String text;
    public String getText(){ return this.text; }
    public void setText(String text){ this.text = text; }

    @Column(name=&quot;number&quot;)
    private int no;
    public int getNo(){ return this.no; }
    public void setNo(int no){ this.no = no; }

    @Column(name=&quot;rollback_point&quot;)
    private int rollback;
    public int getRollback(){ return this.rollback; }
    public void setRollback(int rollback){ this.rollback = rollback; }

    private String biko;
    public String getBiko(){ return this.biko; }
    public void setBiko(String biko){ this.biko = biko; }
}&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
エンティティBeanの生成と例外の生成をするユーティリティクラスを作成します。&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;MessageUtil.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

public class MessageUtil
{    
    public static Message createMessage(Message message)
    {
        return createMessage(
            message.getText(), message.getNo(), message.getRollback(), message.getBiko());
    }

    public static Message createMessage(String text, int no, int rollback)
    {
        return createMessage(text, no, rollback, &quot;&quot;);
    }
    
    public static Message createMessage(String text, int no, int rollback, String biko)
    {
        Message message = new Message();
        message.setText(text);
        message.setNo(no);
        message.setRollback(rollback);
        message.setBiko(biko);
        return message;
    }

    public static void setLogMessage(Message message, Class cls, String method)
    {
        String biko = cls.getSimpleName() + &quot;:&quot; + method + &quot;( )&quot;;
        message.setBiko(message.getBiko() + biko);
    }
    
    public static void setNextLogMessage(Message message)
    {
        message.setBiko(message.getBiko() + &quot;=&amp;gt;&quot;);
        message.setNo(message.getNo()+1);
    }
    
    public static boolean doRollback(Message message)      
    {
        if(message.getRollback() == message.getNo())
        {
            Object obj = null;
            obj.toString();
        }
        return false;
    }
}&lt;/pre&gt;&lt;table class=&quot;fummy_list&quot;&gt;&lt;tr&gt;     &lt;th class=&quot;fummy_list_tip&quot;&gt;&lt;/th&gt;     &lt;td&gt;doRollback()メソッド&lt;br /&gt;
例外発生の判定をします。&lt;br /&gt;
例外はjava.lang.NullPointerExceptionをthrowします。&lt;br /&gt;
&lt;/td&gt;   &lt;/tr&gt;
&lt;/table&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/145312803947747714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/145312803947747714'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/610.html' title='コンテナ管理トランザクションであそぶ - (6/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-5544281459760651748</id><published>2009-12-06T09:54:00.014+09:00</published><updated>2009-12-06T18:55:19.559+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (7/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;検証用Webアプリを作成する&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
サーブレットとviewページを作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;Client.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;

public class Client extends HttpServlet
{
    @EJB
    private Action action;
    
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        HttpSession session = req.getSession();
        try
        {
            this.action.delete();
            String strRollback = req.getParameterMap().containsKey(&quot;rollback&quot;)
                ? req.getParameter(&quot;rollback&quot;).toString() : &quot;0&quot;;
            int rollback = Pattern.matches(&quot;[^0-9]&quot;, strRollback)
                ? 0 : Integer.parseInt(strRollback);
            try
            {
                this.action.regist(session.getId(), rollback);
            }
            catch(Exception exp){}
            req.setAttribute(&quot;messages&quot;, this.action.getMessages());
            this.setMethodList(req, rollback);
        }
        catch(Exception ex)
        {
            session.invalidate();
            ex.printStackTrace();
            System.out.println(&quot;webclient servlet test failed&quot;);
            throw new ServletException(ex);
        }
    }
    
    protected void setMethodList(HttpServletRequest req, int rollback)
    {
        PageData pageData = new PageData();
        List&amp;lt;Map&amp;lt;String,String&gt;&gt; methodList = pageData.getMethodList();
        req.setAttribute(&quot;methodList&quot;, methodList);

        List&amp;lt;String&gt; rollbacks = new ArrayList&amp;lt;String&gt;();
        for(int ct=0; ct&amp;lt;methodList.size(); ct++)
        {
            rollbacks.add((rollback == ct) ? &quot;checked&quot; : &quot;&quot;);
        }
        req.setAttribute(&quot;rollbacks&quot;, rollbacks);
    }
}&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;PageData.java&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;package com.mydomain.ejb;

import java.io.Serializable;
import java.util.*;

public class PageData implements Serializable
{
    private static final long serialVersionUID = 1L;

    public PageData(){}
    
    public List&amp;lt;Map&amp;lt;String,String&gt;&gt; getMethodList()
    {
        List&amp;lt;Map&amp;lt;String,String&gt;&gt; methods = new ArrayList&amp;lt;Map&amp;lt;String,String&gt;&gt;();
        String spc = &quot;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&quot;;
        String pls = spc + spc + spc + &quot;+&quot;;

        this.addMethodRow(methods, &quot;&quot;, &quot;&quot;);
        this.addMethodRow(methods, &quot;+-&gt;regist( )&quot;, pls);
        this.addMethodRow(methods, spc + &quot;+-&gt;decorateInsert( )&quot;, pls);
        this.addMethodRow(methods, spc + &quot;+-&gt;insert( )&quot;, pls);
        this.addMethodRow(methods, spc + &quot;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;+-&gt;decorateInsert( )&quot;, pls);
        this.addMethodRow(methods, spc + &quot;+--------------------------&quot;, &quot;---&gt;decorateInsert( )&quot;);
        this.addMethodRow(methods, spc + &quot;+--------------------------&quot;, &quot;---&gt;insert( )&quot;);
        this.addMethodRow(methods, &quot;&quot;, spc + &quot;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;+-&gt;decorateInsert( )&quot;);
        this.addMethodRow(methods, spc + &quot;+--------------------------&quot;, &quot;---&gt;requiresNewInsert( )&quot;);
        this.addMethodRow(methods, &quot;&quot;, spc + &quot;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;+-&gt;insert( )&quot;);
        this.addMethodRow(methods, &quot;&quot;, spc + spc + &quot;+-&gt;decorateInsert( )&quot;);
        
        return methods;
    }

    protected void addMethodRow(List&amp;lt;Map&amp;lt;String,String&gt;&gt; methods, String act, String sub)
    {
        Map&amp;lt;String,String&gt; row = new HashMap&amp;lt;String,String&gt;();
        row.put(&quot;act&quot;, act);
        row.put(&quot;sub&quot;, sub);
        row.put(&quot;rollback&quot;, Integer.toString(methods.size()));
        methods.add(row);
    }
}
&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;index.jsp&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;%@ page language=&quot;java&quot; pageEncoding=&quot;UTF-8&quot; %&gt;
&amp;lt;%@ page contentType=&quot;text/html; charset=UTF-8&quot; %&gt;
&amp;lt;%@ page import=&quot;java.util.*,com.mydomain.ejb.*&quot; %&gt;
&amp;lt;jsp:include page=&quot;/servlet&quot; /&gt;
&amp;lt;%
List&amp;lt;String&gt; rollbacks = (List&amp;lt;String&gt;)request.getAttribute(&quot;rollbacks&quot;);
List&amp;lt;Map&amp;lt;String,String&gt;&gt; methodList 
  = (List&amp;lt;Map&amp;lt;String,String&gt;&gt;)request.getAttribute(&quot;methodList&quot;); 
%&gt;
&amp;lt;html&gt;
  &amp;lt;head&gt;
    &amp;lt;title&gt;Access Log&amp;lt;/title&gt;
    &amp;lt;style type=&quot;text/css&quot;&gt;
      table.list, table  {font-size: 9pt;}
      table.list th {background:#FFCC66;}
      table.list td {background:#FFFFCC}
    &amp;lt;/style&gt;
  &amp;lt;/head&gt;
  &amp;lt;body&gt;
    &amp;lt;form method=&quot;post&quot;&gt;
      &amp;lt;table&gt;
        &amp;lt;tr&gt;
          &amp;lt;td&gt;
            &amp;lt;table style=&quot;border: solid 1px;&quot;&gt;
              &amp;lt;tr&gt;
                &amp;lt;th&gt;No.&amp;lt;/th&gt;
                &amp;lt;th&gt;&amp;lt;/th&gt;
                &amp;lt;th align=&quot;left&quot; style=&quot;text-decoration: underline;&quot;&gt;ActionImpl&amp;lt;/th&gt;
                &amp;lt;th style=&quot;text-decoration: underline;&quot;&gt;SubActionImpl&amp;lt;/th&gt;
              &amp;lt;/tr&gt;
              &amp;lt;% for(int ct=1; ct&amp;lt;rollbacks.size(); ct++){ %&gt;
              &amp;lt;tr&gt;
                &amp;lt;td&gt;&amp;lt;%= ct %&gt;&amp;lt;/td&gt;
                &amp;lt;td align=&quot;right&quot;&gt;
                  Rollback
                  &amp;lt;input type=&quot;radio&quot; name=&quot;rollback&quot; value=&quot;&amp;lt;%= ct %&gt;&quot; &amp;lt;%= rollbacks.get(ct) %&gt;&gt;
                &amp;lt;/td&gt;
                &amp;lt;td&gt;&amp;lt;%= methodList.get(ct).get(&quot;act&quot;) %&gt;&amp;lt;/td&gt;
                &amp;lt;td&gt;&amp;lt;%= methodList.get(ct).get(&quot;sub&quot;) %&gt;&amp;lt;/td&gt;
              &amp;lt;/tr&gt;
              &amp;lt;% } %&gt;
              &amp;lt;tr&gt;
                &amp;lt;td&gt;&amp;lt;/td&gt;
                &amp;lt;td align=&quot;right&quot;&gt;
                  Commit
                  &amp;lt;input type=&quot;radio&quot; name=&quot;rollback&quot; value=&quot;0&quot;  &amp;lt;%= rollbacks.get(0) %&gt;&gt;
                &amp;lt;/td&gt;
                &amp;lt;td colspan=&quot;3&quot;&gt;&amp;lt;/td&gt;
              &amp;lt;/tr&gt;
            &amp;lt;/table&gt;
            &amp;lt;input type=&quot;submit&quot; value=&quot;access&quot;&gt;
          &amp;lt;/td&gt;
        &amp;lt;/tr&gt;
        &amp;lt;tr&gt;
          &amp;lt;td&gt;
            &amp;lt;table class=&quot;list&quot;&gt;
              &amp;lt;tr&gt;
                &amp;lt;th&gt;No.&amp;lt;/th&gt;
                &amp;lt;th&gt;Session ID&amp;lt;br /&gt;&amp;lt;/th&gt;
                &amp;lt;th&gt;Class:Method&amp;lt;/th&gt;
              &amp;lt;/tr&gt;
              &amp;lt;% for(Message row : (List&amp;lt;Message&gt;)request.getAttribute(&quot;messages&quot;)){ %&gt;
              &amp;lt;tr&gt;
                &amp;lt;td&gt;&amp;lt;%= row.getNo() %&gt;&amp;lt;/td&gt;
                &amp;lt;td&gt;&amp;lt;%= row.getText() %&gt;&amp;lt;/td&gt;
                &amp;lt;td&gt;&amp;lt;%= row.getBiko() %&gt;&amp;lt;/td&gt;
              &amp;lt;/tr&gt;
              &amp;lt;% } %&gt;
            &amp;lt;/table&gt;
          &amp;lt;/td&gt;
        &amp;lt;/tr&gt;
      &amp;lt;/table&gt;
    &amp;lt;/form&gt;
  &amp;lt;/body&gt;
&amp;lt;/html&gt;
&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;
web.xmlファイルを作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;web.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;lt;web-app version=&quot;2.5&quot; 
  xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; 
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee 
                      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot;&gt;
  &amp;lt;welcome-file-list&gt;
    &amp;lt;welcome-file&gt;index.jsp&amp;lt;/welcome-file&gt;
  &amp;lt;/welcome-file-list&gt;  
  &amp;lt;servlet&gt;
    &amp;lt;servlet-name&gt;Servlet&amp;lt;/servlet-name&gt;
    &amp;lt;servlet-class&gt;com.mydomain.ejb.Client&amp;lt;/servlet-class&gt;
    &amp;lt;load-on-startup&gt;0&amp;lt;/load-on-startup&gt;
  &amp;lt;/servlet&gt;
  &amp;lt;servlet-mapping&gt;
    &amp;lt;servlet-name&gt;Servlet&amp;lt;/servlet-name&gt;
    &amp;lt;url-pattern&gt;/servlet&amp;lt;/url-pattern&gt;
  &amp;lt;/servlet-mapping&gt;
&amp;lt;/web-app&gt;&lt;/pre&gt;&lt;div class=&quot;fummy_blockquote&quot;&gt;サーブレットで@EJBアノテーションを使用するためServlet2.5のスキーマを宣言します。 &lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5544281459760651748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/5544281459760651748'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/710.html' title='コンテナ管理トランザクションであそぶ - (7/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4586301750364911142.post-2908069843553662013</id><published>2009-12-06T09:53:00.004+09:00</published><updated>2009-12-06T12:11:51.810+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="コンテナ管理トランザクションであそぶ"/><title type='text'>コンテナ管理トランザクションであそぶ - (8/10) -</title><content type='html'>&lt;span class=&quot;fummy_title&quot;&gt;ビルドする&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
ejb-jarファイル、warファイル、earファイル作成ビルドファイルを作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;ejb-module/pom.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;project&gt;
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;ejb-module&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;ejb&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;build&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;1.6&amp;lt;/source&gt;
          &amp;lt;target&gt;1.6&amp;lt;/target&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-ejb-plugin&amp;lt;/artifactId&gt;
        &amp;lt;configuration&gt;
          &amp;lt;ejbVersion&gt;3.0&amp;lt;/ejbVersion&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.apache.openejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;javaee-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;5.0-2&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.apache.openejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;openejb-core&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;3.1.2&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;
&amp;lt;/project&gt;&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;webtest-client/pom.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;project&gt;
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;webtest-client&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;war&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;build&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;groupId&gt;org.apache.maven.plugins&amp;lt;/groupId&gt;
        &amp;lt;artifactId&gt;maven-compiler-plugin&amp;lt;/artifactId&gt;
        &amp;lt;configuration&gt;
          &amp;lt;source&gt;1.6&amp;lt;/source&gt;
          &amp;lt;target&gt;1.6&amp;lt;/target&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.apache.openejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;javaee-api&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;5.0-2&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;org.apache.openejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;openejb-core&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;3.1.2&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;ejb-module&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.0&amp;lt;/version&gt;
      &amp;lt;scope&gt;provided&amp;lt;/scope&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;
&amp;lt;/project&gt;&lt;/pre&gt;&lt;span class=&quot;fummy_block_title&quot;&gt;build-ear/pom.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;project&gt;
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;enterprise-bean-example&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;ear&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;build&gt;
    &amp;lt;plugins&gt;
      &amp;lt;plugin&gt;
        &amp;lt;artifactId&gt;maven-ear-plugin&amp;lt;/artifactId&gt;
        &amp;lt;version&gt;2.4&amp;lt;/version&gt;
        &amp;lt;configuration&gt;
          &amp;lt;modules&gt;
            &amp;lt;ejbModule&gt;
              &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
              &amp;lt;artifactId&gt;ejb-module&amp;lt;/artifactId&gt;
            &amp;lt;/ejbModule&gt;
            &amp;lt;webModule&gt;
              &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
              &amp;lt;artifactId&gt;webtest-client&amp;lt;/artifactId&gt;
              &amp;lt;contextRoot&gt;webtest-client&amp;lt;/contextRoot&gt;
            &amp;lt;/webModule&gt;
          &amp;lt;/modules&gt;
        &amp;lt;/configuration&gt;
      &amp;lt;/plugin&gt;
    &amp;lt;/plugins&gt;
  &amp;lt;/build&gt;
  &amp;lt;dependencies&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;ejb-module&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.0&amp;lt;/version&gt;
      &amp;lt;type&gt;ejb&amp;lt;/type&gt;
    &amp;lt;/dependency&gt;
    &amp;lt;dependency&gt;
      &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
      &amp;lt;artifactId&gt;webtest-client&amp;lt;/artifactId&gt;
      &amp;lt;version&gt;1.0&amp;lt;/version&gt;
      &amp;lt;type&gt;war&amp;lt;/type&gt;
    &amp;lt;/dependency&gt;
  &amp;lt;/dependencies&gt;
&amp;lt;/project&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;
一括ビルドpomファイルを作成します。&lt;br /&gt;&lt;br /&gt;
&lt;span class=&quot;fummy_block_title&quot;&gt;build/pom.xml&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_code&quot;&gt;&amp;lt;project&gt;
  &amp;lt;modelVersion&gt;4.0.0&amp;lt;/modelVersion&gt;
  &amp;lt;groupId&gt;com.mydomain.ejb&amp;lt;/groupId&gt;
  &amp;lt;artifactId&gt;build&amp;lt;/artifactId&gt;
  &amp;lt;packaging&gt;pom&amp;lt;/packaging&gt;
  &amp;lt;version&gt;1.0&amp;lt;/version&gt;
  &amp;lt;modules&gt;
      &amp;lt;module&gt;../ejb-module/pom.xml&amp;lt;/module&gt;
      &amp;lt;module&gt;../webtest-client/pom.xml&amp;lt;/module&gt;
      &amp;lt;module&gt;../build-ear/pom.xml&amp;lt;/module&gt;
   &amp;lt;/modules&gt;
&amp;lt;/project&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;
コマンドプロンプトを開き一括ビルドpomファイルのあるフォルダに移動して一括ビルドpomファイルを実行します。&lt;br /&gt;&lt;br /&gt;
&lt;pre class=&quot;fummy_screen&quot;&gt;cd C:\mywork\build
mvn clean install&lt;/pre&gt;生成されたenterprise-bean-example-1.0.earを 『GLASSFISHインストールフォルダ/domains/domain1/autodeploy』フォルダーにコピーします。 &lt;br /&gt;
&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2908069843553662013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4586301750364911142/posts/default/2908069843553662013'/><link rel='alternate' type='text/html' href='http://fummy-net.blogspot.com/2009/12/810.html' title='コンテナ管理トランザクションであそぶ - (8/10) -'/><author><name>fummy</name><uri>http://www.blogger.com/profile/03699154879074064971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>