<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <title>conversation</title>
    <link rel="stylesheet" href="/css/style.css" type="text/css" media="all" />
    
    
  </head>
  <body>
    <div class="header">
      <div id="pane.left-header" class="pane left">
        <div id="org.seasar.kvasir.cms.toolbox.customPop-2" class="pop org.seasar.kvasir.cms.toolbox.customPop">
          <div class="body"><div class="wiki">
<ul>
<li><a href="/" class="pageNotFound">（サイトトップ）</a></li>
<li><a href="/download">ダウンロード</a></li>
<li><a href="/news/" class="pageNotFound">新着情報</a></li>
<li><a href="/docs/" class="pageNotFound">ドキュメント</a></li>
</ul>
</div>
</div>
        </div>
      </div>
      <div id="pane.right-header" class="pane right">
        <div id="org.seasar.kvasir.cms.toolbox.loginPop-1" class="pop org.seasar.kvasir.cms.toolbox.loginPop">
          <div class="body">
<form method="post" action="/plugins/toolbox/login.do/docs/0.9.x/ref/conversation">
    <div class="row">
      <span class="label">Name</span>
      <span class="textinput"><input type="text" name="name" size="12" value="" /></span>
    </div>
    <div class="row">
      <span class="label">Password</span>
      <span class="textinput"><input type="password" name="password" size="12" value="" /></span>
    </div>
    <div class="row">
      <span class="submit"><input type="submit" value="Login" /></span>
    </div>
</form>
</div>
        </div><div id="org.seasar.kvasir.cms.pop.loginUserPop-1" class="pop org.seasar.kvasir.cms.pop.loginUserPop">
          <div class="body"></div>
        </div>
      </div>
    </div>
    <div class="page-frame">
      <div id="pane.left-sidebar" class="pane sidebar-frame sidebar subcontent">
        <div id="org.seasar.kvasir.cms.toolbox.searchPop-1" class="pop org.seasar.kvasir.cms.toolbox.searchPop">
          <h2 class="title">Search</h2>
          <div class="body"><form class="simple" method="get" action="/plugins/toolbox/search.do">
  <input type="text" name="query" size="12" />
  <input type="submit" value="Go!" />
</form>
</div>
        </div><div id="org.seasar.kvasir.cms.toolbox.customPop-1" class="pop org.seasar.kvasir.cms.toolbox.customPop">
          <h2 class="title">メニュー</h2>
          <div class="body"><ul>
  <li><a href="/" class="pageNotFound">（サイトトップ）</a></li>
  <li><a href="/download">ダウンロード</a></li>
  <li><a href="/news">新着情報</a></li>
  
    
    
      <li>
        <a href="/docs/1.0.x">ドキュメント（1.0.x系）</a>
        
      </li><li>
        <a href="/docs/0.9.x">ドキュメント（0.9.x系）</a>
        
      </li>
    
  
  <li><a href="/roadmap">ロードマップ</a></li>
  <li><a href="https://www.seasar.org/issues/browse/YMIR">既知の不具合を閲覧する・不具合を報告する</a></li>
  <li><a href="https://www.seasar.org/svn/ymir/">SVNリポジトリ</a></li>
  <li><a href="http://svn.seasar.org/browse/?root=ymir">SVNリポジトリを閲覧</a></li>
</ul>
</div>
        </div>
      </div>
      <div id="pane.main" class="pane main">
      
        <div id="org.seasar.kvasir.cms.toolbox.breadcrumbsPop-1" class="pop org.seasar.kvasir.cms.toolbox.breadcrumbsPop">
          
          <div class="body"><ul>
  <li class="first"><a href="/">(TOP)</a></li><li><a href="/docs">ドキュメント</a></li><li><a href="/docs/0.9.x">ドキュメント（0.9.x系）</a></li><li><a href="/docs/0.9.x/ref">リファレンスマニュアル</a></li><li>conversation</li>
</ul>
</div>
        </div><div id="org.seasar.kvasir.cms.pop.contentPop-1" class="pop org.seasar.kvasir.cms.pop.contentPop">
          <h2 class="title">conversation</h2>
          <div class="body">
<ul class="information">
  
  <li>2008-01-23 19:32:38</li>
</ul>
<div class="content"><div class="document">
<p>conversationはJBoss Seamのconversationから着想を得た機能で、sessionよりも小さい単位で一連の処理を扱うためのものです。</p>
<p>Ymirのconversationには以下の特徴があります。</p>
<ul>
<li>conversationは識別子として名前を持ちます。</li>
<li>conversationにはオブジェクトをバインドすることができます。バインドされたオブジェクトは、conversationが存在する間有効です。このスコープをconversationスコープと呼びます。</li>
<li>conversationには複数のPageクラスを関連付けることができます。関連付けられたPageクラス毎にphaseという文字列を割り当てることができます。</li>
<li>conversationに関連付けられたPageクラスには、どのphaseから遷移可能かという制約を付与することができます。許可されたphase以外から遷移した場合は不正遷移を表す例外（IllegalTransitionException）がスローされます。また、phaseに関する制約があったとしても、今のphaseから同じphaseに遷移することは許されています（例：自画面に帰ってくるような遷移）。</li>
<li>conversationは開始指示によって開始されます。conversationに参加していない状態で開始指示なしにconversationに参加しようとした場合や、あるconversationから開始指示なしに別のconversationに遷移した場合は、IllegalTransitionExceptionがスローされます。</li>
<li>conversationは終了指示によって終了されます。終了するとconversationは削除され、conversationにバインドされたオブジェクトも自動的にアンバインドされます。ただし、終了せずに別のconversationを開始することも許されています。この場合でも自動的にそれまでのconversationは削除されます。</li>
<li>conversationは基本的に同一sessionにつき同時に1つしか存在できませんが、あるconversationから別のconversation（これをsub conversationと言います）を開始して終了後に元のconversationに戻ってくるようにすることは可能です。</li>
<li>conversationに参加している状態で、conversationに関連付けられていないPageに遷移することは可能です。この場合、その遷移自体行なわれないのと同じことになります。従って、元のconversationに遷移を戻すこともできます。</li>
</ul>
<p>以下、コメントの入力→確認→登録完了という一連の処理についてconversationを定義する例を通してconversation機能の使い方を説明します。</p>
<div class="section" id="conversatione381abpagee38292e996a2e980a3e4bb98e38191e3828b">
<h3><a name="conversatione381abpagee38292e996a2e980a3e4bb98e38191e3828b">conversationにPageを関連付ける</a></h3>
<p>conversationにPageを関連付けるには、次の例のようにPageクラスに@Conversationアノテーションを付与します。</p>
<p>なお、投稿完了画面を表示するためのCompletedPageクラスには@Conversationアノテーションを付与していませんが、これはこのページへはConfirmPageにてconversationを終了してから遷移するような作りにしているためです。</p>
<pre class="literal-block">
@Conversation(name = &quot;comment&quot;, phase = &quot;input&quot;)
public class InputPage extends InputPageBase {
    ...
    @Begin
    public void _get() {
        ...
    }
    ...
}

...

@Conversation(name = &quot;comment&quot;, phase = &quot;confirm&quot;, followAfter = &quot;input&quot;)
public class ConfirmPage extends ConfirmPageBase {
    ...
    @End
    public String _post_ok() {
        // コメントの投稿処理
        ....
        return &quot;redirect:comment_completed.html&quot;;
    }
    ...
}

...

public class CompletedPage extends CompletedPageBase {
    ...
}
</pre>
<p>nameプロパティにはconversationの名前を指定します。上の例ではコメント投稿処理なので「comment」という名前にしています。</p>
<p>phaseプロパティにはこのPageがconversation中のどういうフェーズを表すのかを文字列で指定します。phaseプロパティは省略可能ですが、極力指定しておくことをお勧めします。</p>
<p>conversation中でページ遷移を決まった順序で行なわせたい場合は、followAfterプロパティで直前のphaseを1つまたは複数指定します。followAfterプロパティが指定されると、指定されたphaseからの遷移しか許可しないようになります。上の例では、confirmフェーズはinputフェーズからしか遷移できないように指定しています。</p>
</div>
<div class="section" id="conversatione381aee9968be5a78be381a8e7b582e4ba86">
<h3><a name="conversatione381aee9968be5a78be381a8e7b582e4ba86">conversationの開始と終了</a></h3>
<p>conversationは開始指示がないと開始できないため、開始指示を追加します。開始指示は@Beginアノテーションで行ないます。</p>
<pre class="literal-block">
public class InputPage extends InputPageBase {
    ...
    @Begin
    public void _get() {
        ...
    }
    ...
}
</pre>
<p>@Beginアノテーションはアクションメソッドに付与します。<tt class="docutils literal"><span class="pre">_get</span></tt>メソッドに@Beginアノテーションを付与していますので、普通にInputPageのURLに遷移することでcomment conversationが開始されます。</p>
<p>conversationを終了するには終了指示として@Endアノテーションを付与します。</p>
<pre class="literal-block">
public class ConfirmPage extends ConfirmPageBase {
    ...
    @End
    public String _post_ok() {
        // コメントの投稿処理
        ....
        return &quot;redirect:comment_completed.html&quot;;
    }
    ...
}
</pre>
<p>この例では、投稿確認ページで「OK」ボタンが押された場合に<tt class="docutils literal"><span class="pre">_post_ok</span></tt>アクションが呼び出され、コメントの投稿処理が終わった後にconversationが終了するようにしています。</p>
<p>ここで注意してほしいのは、conversationの終了処理は@Endアノテーションが付与されているアクションの実行が完了した時点で行なわれるということです。このようにしているのは、@Endアノテーションが付与されているアクションの処理中にもconversationスコープのオブジェクトを利用できるようにするためです。なお、アクションの実行中に実行時例外等がスローされて処理が中断された場合でもconversationは終了します。</p>
<p>このため、アクションの中で何らかの判定を行なってその結果でconversationを終了させるかどうかを決定したい場合は工夫が必要です。</p>
<p>一番簡単なのは、アクションの中でconversationを終了させるかどうか判定し、終了させる場合は終了用のアクションにリダイレクトするようにしておき、そのアクションに@Endアノテーションを付与する方法です。</p>
<p>このような画面構成が取れない場合は、アクションの呼び出し前にYmirのconstraintの仕組みで判定を行なう方法を使うことができます。具体的には、「conversationの終了条件を満たしていること」という制約を表す専用のConstraintクラスとアノテーションを作成し、そのConstraintクラスの中で判定を行なうようにします。または、Pageクラスに<a class="reference pageNotFound" href="/doc/ref/validation#e3839ae383bce382b8e59bbae69c89e381aee38390e383aae38387e383bce382b7e383a7e383b3">ページ固有のバリデーション</a>としてconversationの終了条件を満たしていることを確認するためのメソッドを作成し、該当アクション呼び出し時にだけこのバリデーションが呼ばれるようにしておくという方法もあります。</p>
<p>この他にも、Seasar2のインターセプタとして判定処理を実装し、該当アクションにそのインターセプタを掛けることでも条件分岐を実現することができます。</p>
</div>
<div class="section" id="conversatione382b9e382b3e383bce38397">
<h3><a name="conversatione382b9e382b3e383bce38397">conversationスコープ</a></h3>
<p>conversationスコープにオブジェクトをバインドしたい場合や、conversationスコープからオブジェクトを取り出したい場合は@Inアノテーションと@Outアノテーションを使用します。</p>
<pre class="literal-block">
@In(ConversationScope.class)
public void setComment(CommentDto comment) {
    comment_ = comment;
}

...

@Out(ConversationScope.class)
public CommentDto getComment() {
    return comment_;
}
</pre>
<p>@Inアノテーションや@Outアノテーションの詳細については「<a class="reference" href="/doc/ref/objectScope">オブジェクトスコープ</a>」を参照して下さい。</p>
</div>
<div class="section" id="sub-conversation">
<h3><a name="sub-conversation">sub conversation</a></h3>
<p>例えばコメント投稿の画面遷移の途中で、認証が済んでいなければユーザ認証画面を経由させてから投稿画面に戻す、というような遷移を実現したいとします。このように、あるconversaion中から一時的に別のconversationに遷移させ、そのconversaionが終了してから元のconversationに復帰させたい場合はsub conversationを使います。</p>
<p>sub conversationに遷移させるには@BeginSubConversationアノテーションを使用します。</p>
<pre class="literal-block">
@Conversation(name = &quot;comment&quot;, phase = &quot;start&quot;)
public class StartPage extends StartPageBase {
    ...
    @BeginSubConversation(reenter = &quot;redirect:comment_input.html&quot;)
    public String _post_logininput() {
        return &quot;redirect:login_input.html&quot;;
    }
    ...
}
</pre>
<p>上の例では、コメント投稿開始ページ（StartPage）にて「ログイン」ボタンが押され、_post_logininputアクションが呼び出されて処理が完了したタイミングでログイン処理用のconverstionを開始するようになります。なお、sub conversationの開始処理もconversationの終了処理と同様、アクションの処理が完了した時点で行なわれますが、アクションの処理中に例外がスローされるとsub conversationは開始されません。</p>
<p>@BeginSubConversationのreenterプロパティには、sub conversationが終了した時に元々のconversationに帰ってくるための遷移先をYmirのアクションの文字列の返り値と同じ形式で指定して下さい。上の例では、ログイン処理が終わった後にコメントの入力画面（comment_input.html）に遷移するように指定しています。</p>
<p>sub conversationを終了するには特別な処理は必要ありません。普通のconversationを終了するのと同じように@Endアノテーションで終了させるだけです。</p>
<pre class="literal-block">
@Conversation(name = &quot;login&quot;, phase = &quot;input&quot;)
public class InputPage extends InputPageBase {
    ...
    @End
    public String _post_login() {
        // ログインのために必要な処理
        ...
        return &quot;redirect:login_completed.html&quot;;
    }
    ...
}
</pre>
<p>上の例では、ログインフォーム画面（InputPage）にて<tt class="docutils literal"><span class="pre">_post_login</span></tt>アクションが呼び出された場合にlogin conversationが終了します。終了後の遷移先は通常はアクションの返り値に従ってlogin_completed.htmlになりますが、現在のconversationがsub conversationである場合はアクションの返り値は無視され、代わりにsub conversation開始時に@BeginSubConversationアノテーションで指定されたreenterプロパティの値が使用されます。従って、上の例ではcomment_input.htmlに遷移することになります。</p>
<p>なお終了後の遷移先を差し替える都合上、sub conversationを終了させる可能性のある@Endアノテーションは返り値がStringまたはObject型であるアクションメソッドに付与する必要があります。返り値がStringでもObjectでもないアクションメソッドに@Endを付与してそのアクションメソッド経由でsub conversationを終了させようとした場合はRuntimeExceptionがスローされます。</p>
</div>
</div>
</div>
</div>
        </div>
      
    </div>
    </div>
    <div class="footer-delimiter"></div>
    <div id="pane.footer" class="pane footer">
      <div id="org.seasar.kvasir.cms.pop.poweredByPop-1" class="pop org.seasar.kvasir.cms.pop.poweredByPop">
        <div class="body">Powered by Kvasir/Sora</div>
      </div>
    </div>
  </body>
</html>
