我有一个复合组件可以动态调用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和action,但对于复合组件和这段代码,我没有关于实际action参数的信息
如果我使用大括号表示法,我也会遇到同样的问题:#{beanName['action']}。在这种情况下,在调试模式下,我可以看到对象TagMethodExpression与MethodExpressionImpl和VariableMapperImpl绑定,其中有映射beanName->"real_bean_name"
如果它是由复合组件或大括号表示法生成的,那么有没有一种方法可以通过ActionEvent获取bean和action?
谢谢!
不是很干净的解决方案,但我使用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));
}
}
...