2

我正在将一个 struts 2.2.3.1 portlet 应用程序 (jsr168) 迁移到新版本 2.3.3。在大多数情况下,一切仍然正常,但有一个明显的问题。

我正在使用 package.properties 资源包来处理我的 jsp 中的标题和标签等内容。当我第一次点击操作时,会从资源包中检索消息并按预期显示。但是,当我对某个操作发出第二个请求时,会显示消息键,这通常意味着找不到资源。

(第一次执行操作和任何后续请求之间的主要区别似乎是,在第一个请求中,仅处理事件 portlet 阶段,而不是呈现 portlet 阶段。任何后续请求都使用这两个阶段。)

我花了很长时间才弄清楚发生了什么,但大致是这样的:

我开始逐步浏览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
  • 默认文本提供者

第二次请求值栈

  • --> TestPortletAction
  • 默认文本提供者
  • DirectRenderFromEventAction
  • 默认文本提供者

版本 2.3.3

第一个请求值栈

  • --> TestPortletAction
  • 默认文本提供者

第二次请求值栈

  • DirectRenderFromEventAction
  • 默认文本提供者
  • --> TestPortletAction
  • 默认文本提供者

因此,由于某种原因,在新版本中,在处理完渲染阶段后,动作类不在堆栈顶部。这意味着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. 第二个DefaultTextProvider如何以及在何处被推入堆栈?(真的有理由拥有其中两个吗?)
  2. DirectRenderFromEventAction如何以及在何处被推入堆栈?
  3. 新版本的什么变化导致了这种情况?
  4. 这是错误还是预期行为?

一个可能的解决方案(确实可以解决它)是使用 ognl 显式调用操作的getText方法,但我认为这并不理想,它确实意味着更改一堆 jsp 文件。

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

可以在此处找到演示该问题的示例项目(这是一个 Maven 项目):

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

我使用tomcat 6.0.25pluto 1.1.7作为我的开发环境。

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 回答