2

Struts 2.2.3.1 ポートレット アプリケーション (jsr168) を新しいバージョン 2.3.3 に移行中です。ほとんどの場合、すべてが同じように機能しますが、明らかな問題が 1 つあります。

JSP のタイトルやラベルなどに package.properties リソース バンドルを使用しています。最初にアクションを実行すると、メッセージがリソース バンドルから取得され、期待どおりに表示されます。ただし、アクションに対して 2 番目の要求を行うと、メッセージ キーが表示されます。これは通常、リソースが見つからなかったことを意味します。

(最初にアクションをヒットしたときと後続のリクエストの主な違いは、最初のリクエストではイベント ポートレット フェーズのみが処理され、ポートレットのレンダリング フェーズは処理されないことです。フォローアップ リクエストでは、両方のフェーズが使用されます。)

何が起こっているのかを理解するのにかなりの時間がかかりましたが、大まかに何が起こっているのかを以下に示します。

org.apache.struts2.components.Textをステップ実行することから始めました。このクラスは、jsp で struts テキスト タグが検出されたときに使用されます。

end()メソッドは、 TextProviderHelperの静的getText()メソッドを使用します。これが私が問題の根本を見つけた場所です:

    for (Object o : stack.getRoot()) {
        if (o instanceof TextProvider) {
            tp = (TextProvider) o;
            msg = tp.getText(key, null, args, stack);

            break;
        }
    }

上記のコードは、 TextProviderが見つかるまで値スタックを反復処理します。ActionSupportはTextProviderを実装、struts はアクションをスタックの最上位またはその近くに配置しようとするため、通常、アクションはプロバイダーになります。

両方のバージョンの異なる時点での値スタック内のオブジェクトを次に示します。

バージョン 2.2.3.1

最初のリクエスト値スタック

  • --> TestPortletAction
  • DefaultTextProvider

2 番目の要求値スタック

  • --> TestPortletAction
  • DefaultTextProvider
  • DirectRenderFromEventAction
  • DefaultTextProvider

バージョン 2.3.3

最初のリクエスト値スタック

  • --> TestPortletAction
  • DefaultTextProvider

2 番目の要求値スタック

  • DirectRenderFromEventAction
  • DefaultTextProvider
  • --> TestPortletAction
  • DefaultTextProvider

そのため、何らかの理由で、新しいバージョンでは、レンダリング フェーズが処理された後、アクション クラスがスタックの一番上にありません。これは、TextProviderHelperがDefaultTextProviderを使用することを意味します。これは、リソース バンドルを見つけようとしますが、決して成功しません。

DefaultTextProviderがスタックにプッシュされる方法を調査しました。

Jsr168ディスパッチャーserviceAction () は、ActionProxyFactoryを呼び出して、アクション プロキシを作成します。プロキシを返す直前に、prepare () の呼び出しが行われ、次にDefaultActionInvocationinitメソッドが呼び出されます。init は、スタックを作成するcreateContextMap () を呼び出します。

stack = valueStackFactory.createValueStack();

使用されるコンストラクター:

protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
    setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
    push(prov);
}

ああ、それは私がこれまでに問題に取り組んできた限りです。残っている質問は次のとおりです。

  1. 2 番目のDefaultTextProviderがスタックにプッシュされる方法と場所は? (そして、それらを2つ持つ本当の理由はありますか?)
  2. DirectRenderFromEventActionがスタックにプッシュされる方法と場所は?
  3. 新しいバージョンのどのような変更が原因ですか?
  4. これはバグですか、それとも意図した動作ですか?

考えられる解決策 (これで解決します) は、 ognlを使用してアクションのgetTextメソッドを明示的に呼び出すことですが、これは理想的ではないと思います。また、大量の jsp ファイルを変更することを意味します。

<s:property value = "%{getText('some.key')}"/>

この問題を示すサンプル プロジェクトは、次の場所にあります (Maven プロジェクトです)。

http://new-value-stack-order.googlecode.com/svn

開発環境としてpluto 1.1.7でtomcat 6.0.25を使用しています。

4

1 に答える 1

0

理解した。答えはPortletStateInterceptorにあります

2.2.3.1

@SuppressWarnings("unchecked")
private void restoreStack(ActionInvocation invocation) {
    RenderRequest request = (RenderRequest) invocation.getInvocationContext().get(REQUEST);
    if (StringUtils.isNotEmpty(request.getParameter(EVENT_ACTION))) {
        if(!isProperPrg(invocation)) {
            if (LOG.isDebugEnabled()) LOG.debug("Restoring value stack from event phase");
            ValueStack oldStack = (ValueStack) invocation.getInvocationContext().getSession().get(
            STACK_FROM_EVENT_PHASE);
            if (oldStack != null) {
                CompoundRoot oldRoot = oldStack.getRoot();
                ValueStack currentStack = invocation.getStack();
                CompoundRoot root = currentStack.getRoot();
                root.addAll(0, oldRoot);
                if (LOG.isDebugEnabled()) LOG.debug("Restored stack");
            }
        }
        else {
            if (LOG.isDebugEnabled()) LOG.debug("Won't restore stack from event phase since it's a proper PRG request");
        }
    }
}

2.3.3

@SuppressWarnings("unchecked")
private void restoreStack(ActionInvocation invocation) {
    RenderRequest request = (RenderRequest) invocation.getInvocationContext().get(REQUEST);
    if (StringUtils.isNotEmpty(request.getParameter(EVENT_ACTION))) {
        if(!isProperPrg(invocation)) {
            if (LOG.isDebugEnabled()) LOG.debug("Restoring value stack from event phase");
            ValueStack oldStack = (ValueStack) invocation.getInvocationContext().getSession().get(
            STACK_FROM_EVENT_PHASE);
            if (oldStack != null) {
                CompoundRoot oldRoot = oldStack.getRoot();
                ValueStack currentStack = invocation.getStack();
                CompoundRoot root = currentStack.getRoot();
                root.addAll(oldRoot);
                if (LOG.isDebugEnabled()) LOG.debug("Restored stack");
            }
        }
        else {
            if (LOG.isDebugEnabled()) LOG.debug("Won't restore stack from event phase since it's a proper PRG request");
        }
    }
}

root.addAll()に注意してください。古いバージョンでは、現在のスタックの先頭に古いスタックが追加されます。新しいバージョンでは、それを最後に追加します。

これは実際には2.1.3で修正された古いバグでした。Jiraで新しい問題を作成します。

于 2012-05-14T15:17:53.277 に答える