Author: dan.j.allen
Date: 2009-05-26 11:38:57 -0400 (Tue, 26 May 2009)
New Revision: 10991
Added:
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIRestrictView.java
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIViewAction.java
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/ActionSourceHandler.java
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/DeferredValueExpressionHandler.java
modules/trunk/faces/src/main/java/org/jboss/seam/faces/lifecycle/ViewActionExecutor.java
modules/trunk/faces/src/main/resources/META-INF/seam-faces.taglib.xml
Modified:
modules/trunk/faces/pom.xml
modules/trunk/faces/src/main/resources/META-INF/faces-config.xml
Log:
add UIViewAction (page actions) component
add UIRestriceView (page security restrictions) component
add facelets helper classes
Modified: modules/trunk/faces/pom.xml
===================================================================
--- modules/trunk/faces/pom.xml 2009-05-26 15:37:39 UTC (rev 10990)
+++ modules/trunk/faces/pom.xml 2009-05-26 15:38:57 UTC (rev 10991)
@@ -59,6 +59,13 @@
<groupId>${seam.groupId}</groupId>
<artifactId>seam-international</artifactId>
</dependency>
+
+ <!-- QUESTION do we want this dependency? -->
+ <dependency>
+ <groupId>${seam.groupId}</groupId>
+ <artifactId>seam-security</artifactId>
+ <optional>true</optional>
+ </dependency>
<dependency>
<groupId>${seam.groupId}</groupId>
Added:
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIRestrictView.java
===================================================================
--- modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIRestrictView.java
(rev 0)
+++
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIRestrictView.java 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,65 @@
+package org.jboss.seam.faces.component;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIComponentBase;
+
+/**
+ * @author Dan Allen
+ */
+public class UIRestrictView extends UIComponentBase
+{
+
+ // ------------------------------------------------------ Manifest Constants
+
+ /**
+ * <p>
+ * The standard component type for this component.
+ * </p>
+ */
+ public static final String COMPONENT_TYPE =
"org.jboss.seam.faces.RestrictView";
+
+ /**
+ * <p>
+ * The standard component type for this component.
+ * </p>
+ */
+ public static final String COMPONENT_FAMILY =
"org.jboss.seam.faces.RestrictView";
+
+ /**
+ * Properties that are tracked by state saving.
+ */
+ enum PropertyKeys {
+ require
+ }
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>
+ * Create a new {@link UIRestrictView} instance with default property values.
+ * </p>
+ */
+ public UIRestrictView()
+ {
+ super();
+ setRendererType(null);
+ }
+
+ // -------------------------------------------------------------- Properties
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_FAMILY;
+ }
+
+ public ValueExpression getRequire()
+ {
+ return (ValueExpression) getStateHelper().get(PropertyKeys.require);
+ }
+
+ public void setRequire(ValueExpression require)
+ {
+ getStateHelper().put(PropertyKeys.require, require);
+ }
+}
Added: modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIViewAction.java
===================================================================
--- modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIViewAction.java
(rev 0)
+++
modules/trunk/faces/src/main/java/org/jboss/seam/faces/component/UIViewAction.java 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,77 @@
+package org.jboss.seam.faces.component;
+
+import javax.el.MethodExpression;
+import javax.faces.component.UIComponentBase;
+
+/**
+ * @author Dan Allen
+ */
+public class UIViewAction extends UIComponentBase
+{
+
+ // ------------------------------------------------------ Manifest Constants
+
+ /**
+ * <p>
+ * The standard component type for this component.
+ * </p>
+ */
+ public static final String COMPONENT_TYPE =
"org.jboss.seam.faces.ViewAction";
+
+ /**
+ * <p>
+ * The standard component type for this component.
+ * </p>
+ */
+ public static final String COMPONENT_FAMILY =
"org.jboss.seam.faces.ViewAction";
+
+ /**
+ * Properties that are tracked by state saving.
+ */
+ enum PropertyKeys {
+ onPostback,
+ execute
+ }
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>
+ * Create a new {@link UIViewAction} instance with default property values.
+ * </p>
+ */
+ public UIViewAction()
+ {
+ super();
+ setRendererType(null);
+ }
+
+ // -------------------------------------------------------------- Properties
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_FAMILY;
+ }
+
+ public MethodExpression getExecute()
+ {
+ return (MethodExpression) getStateHelper().get(PropertyKeys.execute);
+ }
+
+ public void setExecute(MethodExpression execute)
+ {
+ getStateHelper().put(PropertyKeys.execute, execute);
+ }
+
+ public boolean isOnPostback()
+ {
+ return (Boolean) getStateHelper().eval(PropertyKeys.onPostback, false);
+ }
+
+ public void setOnPostback(boolean onPostback)
+ {
+ getStateHelper().put(PropertyKeys.onPostback, onPostback);
+ }
+
+}
Added:
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/ActionSourceHandler.java
===================================================================
---
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/ActionSourceHandler.java
(rev 0)
+++
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/ActionSourceHandler.java 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,91 @@
+package org.jboss.seam.faces.facelets;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.el.MethodExpression;
+import javax.faces.view.facelets.ComponentConfig;
+import javax.faces.view.facelets.ComponentHandler;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRule;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.Metadata;
+import javax.faces.view.facelets.MetadataTarget;
+import javax.faces.view.facelets.TagAttribute;
+import javax.faces.view.facelets.TagAttributeException;
+
+/**
+ * An adaptor class for Facelets that allows the method expression attribute to
+ * be accepted rather than interpreted as a value expression. This class should
+ * be supplemented to support all such tags that require a method expression.
+ *
+ * @author Dan Allen
+ */
+public class ActionSourceHandler extends ComponentHandler
+{
+ public static final String EXECUTE_ATTRIBUTE_NAME = "execute";
+
+ public ActionSourceHandler(ComponentConfig config)
+ {
+ super(config);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected MetaRuleset createMetaRuleset(Class type)
+ {
+ return super.createMetaRuleset(type).addRule(new ExecuteRule());
+ }
+
+ private class ExecuteRule extends MetaRule
+ {
+ @Override
+ public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget
meta)
+ {
+ if (EXECUTE_ATTRIBUTE_NAME.equals(name) &&
MethodExpression.class.equals(meta.getPropertyType(name)))
+ {
+ return new MethodExpressionMetadata(meta.getWriteMethod(name), attribute,
Object.class, new Class[0]);
+ }
+
+ return null;
+ }
+ }
+
+ private static class MethodExpressionMetadata extends Metadata
+ {
+ private final Method method;
+
+ private final TagAttribute attribute;
+
+ private Class returnType;
+
+ private Class[] argumentTypes;
+
+ public MethodExpressionMetadata(Method method, TagAttribute attribute, Class
returnType, Class[] argumentTypes)
+ {
+ this.method = method;
+ this.attribute = attribute;
+ this.returnType = returnType;
+ this.argumentTypes = argumentTypes;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance)
+ {
+ MethodExpression expr = attribute.getMethodExpression(ctx, returnType,
argumentTypes);
+
+ try
+ {
+ // this invocation is to assign the method expression to the property of the
component
+ method.invoke(instance, expr);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new TagAttributeException(attribute, e.getCause());
+ }
+ catch (Exception e)
+ {
+ throw new TagAttributeException(attribute, e);
+ }
+ }
+ }
+}
Added:
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/DeferredValueExpressionHandler.java
===================================================================
---
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/DeferredValueExpressionHandler.java
(rev 0)
+++
modules/trunk/faces/src/main/java/org/jboss/seam/faces/facelets/DeferredValueExpressionHandler.java 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,81 @@
+package org.jboss.seam.faces.facelets;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.el.ValueExpression;
+import javax.faces.view.facelets.ComponentConfig;
+import javax.faces.view.facelets.ComponentHandler;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.MetaRule;
+import javax.faces.view.facelets.MetaRuleset;
+import javax.faces.view.facelets.Metadata;
+import javax.faces.view.facelets.MetadataTarget;
+import javax.faces.view.facelets.TagAttribute;
+import javax.faces.view.facelets.TagAttributeException;
+
+public class DeferredValueExpressionHandler extends ComponentHandler
+{
+ public static final String REQUIRE_ATTRIBUTE_NAME = "require";
+
+ public DeferredValueExpressionHandler(ComponentConfig config)
+ {
+ super(config);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected MetaRuleset createMetaRuleset(Class type)
+ {
+ return super.createMetaRuleset(type).addRule(new RequiredRule());
+ }
+
+ private class RequiredRule extends MetaRule
+ {
+ @Override
+ public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget
meta)
+ {
+ if (REQUIRE_ATTRIBUTE_NAME.equals(name) &&
ValueExpression.class.equals(meta.getPropertyType(name)))
+ {
+ return new ValueExpressionMetadata(meta.getWriteMethod(name), attribute,
Boolean.class);
+ }
+
+ return null;
+ }
+ }
+
+ private static class ValueExpressionMetadata extends Metadata
+ {
+ private final Method method;
+
+ private final TagAttribute attribute;
+
+ private Class returnType;
+
+ public ValueExpressionMetadata(Method method, TagAttribute attribute, Class
returnType)
+ {
+ this.method = method;
+ this.attribute = attribute;
+ this.returnType = returnType;
+ }
+
+ public void applyMetadata(FaceletContext ctx, Object instance)
+ {
+ ValueExpression expr = attribute.getValueExpression(ctx, returnType);
+
+ try
+ {
+ // this invocation is to assign the value expression to the property of the
component
+ method.invoke(instance, expr);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new TagAttributeException(attribute, e.getCause());
+ }
+ catch (Exception e)
+ {
+ throw new TagAttributeException(attribute, e);
+ }
+ }
+ }
+}
Added:
modules/trunk/faces/src/main/java/org/jboss/seam/faces/lifecycle/ViewActionExecutor.java
===================================================================
---
modules/trunk/faces/src/main/java/org/jboss/seam/faces/lifecycle/ViewActionExecutor.java
(rev 0)
+++
modules/trunk/faces/src/main/java/org/jboss/seam/faces/lifecycle/ViewActionExecutor.java 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,228 @@
+package org.jboss.seam.faces.lifecycle;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.el.ELException;
+import javax.el.MethodExpression;
+import javax.faces.FacesException;
+import javax.faces.application.NavigationHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+
+import org.jboss.seam.bridge.ManagerBridge;
+import org.jboss.seam.faces.component.UIRestrictView;
+import org.jboss.seam.faces.component.UIViewAction;
+import org.jboss.seam.security.Identity;
+import org.jboss.webbeans.log.Log;
+import org.jboss.webbeans.log.Logging;
+
+//@ListenerFor(systemEventClass = PreRenderViewEvent.class, sourceClass =
UIViewRoot.class)
+public class ViewActionExecutor implements SystemEventListener
+{
+ private static final Log log = Logging.getLog(ViewActionExecutor.class);
+
+ public boolean isListenerForSource(Object source)
+ {
+ return source instanceof UIViewRoot;
+ }
+
+ public void processEvent(SystemEvent event) throws AbortProcessingException
+ {
+ processViewActions();
+ }
+
+ protected void processViewActions()
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ UIViewRoot initialViewRoot = context.getViewRoot();
+
+ // TEMPORARY needs to be organized better
+
+ // collect first so as not to introduce a hard dependency on Identity if tag is not
in use
+ Collection<UIRestrictView> restrictions =
collectionViewRestrictions(initialViewRoot);
+ if (!restrictions.isEmpty())
+ {
+ if (Identity.isSecurityEnabled())
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Processing view restrictions before render view");
+ }
+
+ Identity identity =
ManagerBridge.getProvider().getCurrentManager().getInstanceByType(Identity.class);
+ try
+ {
+ for (UIRestrictView restriction : restrictions)
+ {
+ if (restriction.getRequire() != null)
+ {
+ identity.checkRestriction(restriction.getRequire());
+ }
+ else
+ {
+ identity.checkPermission(initialViewRoot.getViewId(),
"render");
+ }
+ }
+ }
+ // FIXME damn this is ugly, but JCDI is wrapping exceptions
+ catch (Exception e)
+ {
+ Throwable cause = e;
+ if (e instanceof InvocationTargetException)
+ {
+ cause = e.getCause();
+ }
+
+ context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, cause));
+ // FIXME this is lame; there should be some other way to stop view
rendering
+ context.getViewRoot().setRendered(false);
+ throw new AbortProcessingException("View restriction criteria was not
met.");
+ //return;
+ }
+ }
+ }
+ // END TEMPORARY
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Processing view actions before render view");
+ }
+
+ NavigationHandler navHandler = context.getApplication().getNavigationHandler();
+ boolean postback = context.isPostback();
+
+ if (!postback && context.isValidationFailed())
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Validation flagged as failed. Calling navigation handler
without executing view actions.");
+ }
+ navHandler.handleNavigation(context, null, null);
+ return;
+ }
+
+ Collection<UIViewAction> actions = collectViewActions(initialViewRoot,
postback);
+ for (UIViewAction action : actions)
+ {
+ String outcome = null;
+ String fromAction = null;
+
+ MethodExpression execute = action.getExecute();
+ // QUESTION shouldn't this be an illegal state otherwise??
+ if (execute != null)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Executing view action expression {0}",
execute.getExpressionString());
+ }
+ try
+ {
+ Object returnVal = execute.invoke(context.getELContext(), null);
+ outcome = (returnVal != null ? returnVal.toString() : null);
+ fromAction = execute.getExpressionString();
+ }
+ catch (ELException e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error(e.getMessage(), e);
+ }
+ throw new FacesException(execute.getExpressionString() + ": " +
e.getMessage(), e);
+ }
+ }
+
+ navHandler.handleNavigation(context, fromAction, outcome);
+
+ // QUESTION In either of these two cases, should an AbortProcessingEvent
exception be thrown?
+ if (context.getResponseComplete())
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Response marked as complete during view action processing.
Short-circuiting remaining actions.");
+ }
+ // FIXME this is lame; there should be some other way to stop view rendering
+ context.getViewRoot().setRendered(false);
+ break;
+ }
+ else if
(!initialViewRoot.getViewId().equals(context.getViewRoot().getViewId()))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Detected change in view ID during view action processing.
Short-circuiting remaining actions.");
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Pick out the UIRestrictView components from the metadata facet's children. If
no
+ * matches are found, an unmodifiable empty list is returned.
+ */
+ protected Collection<UIRestrictView> collectionViewRestrictions(UIViewRoot
viewRoot)
+ {
+ return collectMetadataComponents(viewRoot, new
UIComponentFilter<UIRestrictView>() {
+
+ @Override
+ public boolean accept(UIComponent candidate)
+ {
+ return candidate instanceof UIRestrictView;
+ }
+
+ });
+ }
+
+ /**
+ * Pick out the UIViewAction components from the metadata facet's children. If
this is a postback,
+ * only select UIViewAction components that are to be executed on a postback. If no
matches
+ * are found, an unmodifiable empty list is returned.
+ */
+ protected Collection<UIViewAction> collectViewActions(UIViewRoot viewRoot, final
boolean postback)
+ {
+ return collectMetadataComponents(viewRoot, new
UIComponentFilter<UIViewAction>() {
+
+ @Override
+ public boolean accept(UIComponent candidate)
+ {
+ return candidate instanceof UIViewAction && (!postback ||
((UIViewAction) candidate).isOnPostback());
+ }
+
+ });
+ }
+
+ protected <C extends UIComponent> Collection<C>
collectMetadataComponents(UIViewRoot viewRoot, UIComponentFilter<C>
componentFilter)
+ {
+ UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME);
+
+ if (metadataFacet == null)
+ {
+ return Collections.<C>emptyList();
+ }
+
+ Collection<C> matches = new ArrayList<C>();
+ for (UIComponent candidate : metadataFacet.getChildren())
+ {
+ if (componentFilter.accept(candidate))
+ {
+ matches.add((C) candidate);
+ }
+ }
+
+ return matches;
+ }
+
+ protected abstract class UIComponentFilter<C extends UIComponent>
+ {
+ public abstract boolean accept(UIComponent candidate);
+ }
+
+}
Modified: modules/trunk/faces/src/main/resources/META-INF/faces-config.xml
===================================================================
--- modules/trunk/faces/src/main/resources/META-INF/faces-config.xml 2009-05-26 15:37:39
UTC (rev 10990)
+++ modules/trunk/faces/src/main/resources/META-INF/faces-config.xml 2009-05-26 15:38:57
UTC (rev 10991)
@@ -5,8 +5,12 @@
version="2.0"
id="seam3">
+ <name>seam3</name>
+
<ordering>
- <after>webbeans</after>
+ <after>
+ <name>webbeans</name>
+ </after>
</ordering>
<factory>
@@ -18,8 +22,12 @@
<el-resolver>org.jboss.seam.el.SeamELResolver</el-resolver>
<el-resolver>org.jboss.seam.faces.el.SeamFacesELResolver</el-resolver>
<system-event-listener>
+
<system-event-listener-class>org.jboss.seam.faces.lifecycle.ViewActionExecutor</system-event-listener-class>
<system-event-class>javax.faces.event.PreRenderViewEvent</system-event-class>
+ </system-event-listener>
+ <system-event-listener>
<system-event-listener-class>org.jboss.seam.faces.lifecycle.TransferStatusMessagesListener</system-event-listener-class>
+
<system-event-class>javax.faces.event.PreRenderViewEvent</system-event-class>
</system-event-listener>
</application>
@@ -28,5 +36,15 @@
<phase-listener>org.jboss.seam.faces.lifecycle.SeamPhaseListener</phase-listener>
-->
</lifecycle>
+
+ <component>
+ <component-type>org.jboss.seam.faces.RestrictView</component-type>
+
<component-class>org.jboss.seam.faces.component.UIRestrictView</component-class>
+ </component>
+
+ <component>
+ <component-type>org.jboss.seam.faces.ViewAction</component-type>
+
<component-class>org.jboss.seam.faces.component.UIViewAction</component-class>
+ </component>
</faces-config>
Added: modules/trunk/faces/src/main/resources/META-INF/seam-faces.taglib.xml
===================================================================
--- modules/trunk/faces/src/main/resources/META-INF/seam-faces.taglib.xml
(rev 0)
+++ modules/trunk/faces/src/main/resources/META-INF/seam-faces.taglib.xml 2009-05-26
15:38:57 UTC (rev 10991)
@@ -0,0 +1,23 @@
+<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd"
+ version="2.0">
+ <
namespace>http://jboss.com/products/seam/faces</namespace>
+
+ <tag>
+ <tag-name>restrictView</tag-name>
+ <component>
+ <component-type>org.jboss.seam.faces.RestrictView</component-type>
+
<handler-class>org.jboss.seam.faces.component.DeferredValueExpressionHandler</handler-class>
+ </component>
+ </tag>
+
+ <tag>
+ <tag-name>viewAction</tag-name>
+ <component>
+ <component-type>org.jboss.seam.faces.ViewAction</component-type>
+
<handler-class>org.jboss.seam.faces.component.ActionSourceHandler</handler-class>
+ </component>
+ </tag>
+
+</facelet-taglib>
\ No newline at end of file