[seam-commits] Seam SVN: r10991 - in modules/trunk/faces: src/main/java/org/jboss/seam/faces and 4 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Tue May 26 11:38:58 EDT 2009


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




More information about the seam-commits mailing list