1

我有一个复合组件来动态调用 bean 动作:

<composite:interface>
    <composite:attribute name="actionMethod" method-signature="java.lang.String action()"/>
</composite:interface>

<composite:implementation>

    <p:menubar autoDisplay="false" styleClass="subMenu">
        <p:menuitem>
             <h:commandButton action="#{cc.attrs.actionMethod}" value="#{bundle.CreateSaveLink}" styleClass="smallButton button buttonSave"/>
        </p:menuitem>
    </p:menubar>

</composite:implementation>

而且我还定义了一个 ActionListener 类来实现安全性:

public class SecurityActionListener extends ActionListenerImpl implements ActionListener {


    private static final Logger log = Logger.getLogger(SecurityActionListener.class);
    private String isCasEnabled;

    public SecurityActionListener() {
        isCasEnabled = PropertyUtility.isCasEnabled();
    }


    @SuppressWarnings("unused")
    @Override
    public void processAction(final ActionEvent event) {

        if(!isCasEnabled.equals("true")) {
            super.processAction(event);
            return;
        }


        final FacesContext context = FacesContext.getCurrentInstance();
        final Application application = context.getApplication();
        final ConfigurableNavigationHandler navHandler = (ConfigurableNavigationHandler) application.getNavigationHandler();

        // Action stuff
        final UIComponent source = event.getComponent();
        final ActionSource actionSource = (ActionSource) source;
        MethodBinding binding;

        binding = actionSource.getAction();
        final String expr = binding.getExpressionString();
        if (!expr.startsWith("#")) {
            super.processAction(event);
            return;
        }

        final int idx = expr.indexOf('.');
        if (idx <0) {
            log.error("Errore nella formattazione della chiamata al metodo: " + expr + ". No '.' found");
            return;
        } 

        final String target = expr.substring(0, idx).substring(2);
        final String t = expr.substring(idx + 1);
        String method = t.substring(0, (t.length() - 1));

        final int idxParams = method.indexOf('(');
        if (idxParams >=0) {
            method = method.substring(0,idxParams);
        }

        final MethodExpression expression = new MethodExpressionMethodBindingAdapter(binding);
        final ELContext elContext = context.getELContext();
        final ExpressionFactory factory = context.getApplication().getExpressionFactory();

        final ValueExpression ve = factory.createValueExpression(elContext, "#{" + target + '}', Object.class);
        final Object result = ve.getValue(elContext);

        // Check if the target method is a secured method
        // and check security accordingly
        final Method[] methods = result.getClass().getMethods();
        for (final Method meth : methods) {
            if (meth.getName().equals(method)) {
                if (meth.isAnnotationPresent(CustomSecurity.class)) {
                    final CustomSecurity securityAnnotation = meth.getAnnotation(CustomSecurity.class);
                    log.debug("Function to check security on: " + securityAnnotation.value()); 
                    SecurityUtility.checkSecurity(securityAnnotation.value());
                } else {
                    super.processAction(event);
                }
                break;
            }
        }

        log.warn("No method: " + method + " found in: " + methods + ", for object: " + result);

    }

}

如果 commandButton 上的操作以标准方式定义:

<h:commandButton action="#{bean.action}" value="test" />

一切正常,我能够通过其 ActionEvent 在侦听器中检测 bean 和动作,但是使用复合组件和此代码我没有关于实际动作参数的信息

如果我使用大括号表示法,我也会遇到同样的问题:#{beanName['action']}。在这种情况下,在调试模式下,我可以看到对象 TagMethodExpression 与 MethodExpressionImpl 和 VariableMapperImpl 绑定,其中有映射 beanName -> "real_bean_name"

如果 ActionEvent 是由复合组件或大括号表示法生成的,有没有办法通过 ActionEvent 获取 bean 和 action?

谢谢!

4

1 回答 1

1

作为解决方案不是很干净,但我解决了使用 ActionSource2、MethodExpression 和 Reflection 访问 TagMethodExpression 私有字段...

final ActionSource2 actionSource = (ActionSource2) source;
MethodExpression expression;
expression = actionSource.getActionExpression();
String exp = expression.getExpressionString();

String trimmed = exp.trim();

// remove ending bracket
trimmed = trimmed.substring(0, trimmed.lastIndexOf("']"));

int openBracket = trimmed.lastIndexOf("['");

String beanName = trimmed.substring(0, openBracket);
StringBuilder builder = new StringBuilder(beanName);
builder.append('.');
builder.append(trimmed.substring(openBracket + 2, trimmed.length()));

String ret = builder.toString();

if (expression instanceof TagMethodExpression) {
    try {

        TagMethodExpression tme = (TagMethodExpression) expression;
        Field f = tme.getClass().getDeclaredField("orig");
        f.setAccessible(true);
        MethodExpression me = (MethodExpression) f.get(tme);
        Field ff = me.getClass().getDeclaredField("varMapper");
        ff.setAccessible(true);
        VariableMapperImpl vmi = (VariableMapperImpl) ff.get(me);
        ValueExpression beanResolveVariable = vmi.resolveVariable(beanName);
        String toString = beanResolveVariable.toString();
        Pattern pattern = Pattern.compile("value=\"#\\{(.*?)\\}");
        Matcher matcher = pattern.matcher(toString);

        if(matcher.find()){
            String realBeanName = matcher.group(1);
            ret = ret.replace(beanName, realBeanName);
        }
    } catch (NoSuchFieldException ex) {
        log.error(LogUtility.getStrExc(ex));
    } catch (SecurityException ex) {
        log.error(LogUtility.getStrExc(ex));
    } catch (IllegalAccessException ex) {
        log.error(LogUtility.getStrExc(ex));
    }
}
...
于 2013-03-20T13:08:57.657 回答