Seam SVN: r8490 - in trunk/src/main/org/jboss/seam/security: management and 1 other directories.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-22 11:58:20 -0400 (Tue, 22 Jul 2008)
New Revision: 8490
Modified:
trunk/src/main/org/jboss/seam/security/Identity.java
trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java
trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java
Log:
fix JpaPermissionStore lookup, minor
Modified: trunk/src/main/org/jboss/seam/security/Identity.java
===================================================================
--- …
[View More]trunk/src/main/org/jboss/seam/security/Identity.java 2008-07-22 07:59:22 UTC (rev 8489)
+++ trunk/src/main/org/jboss/seam/security/Identity.java 2008-07-22 15:58:20 UTC (rev 8490)
@@ -600,6 +600,7 @@
if (!securityEnabled) return true;
if (systemOp) return true;
if (permissionMapper == null) return false;
+ if (target == null) return false;
return permissionMapper.resolvePermission(target, action);
}
Modified: trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java 2008-07-22 07:59:22 UTC (rev 8489)
+++ trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java 2008-07-22 15:58:20 UTC (rev 8490)
@@ -683,7 +683,7 @@
catch (NoResultException ex)
{
return null;
- }
+ }
}
public List<String> listUsers()
Modified: trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java 2008-07-22 07:59:22 UTC (rev 8489)
+++ trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java 2008-07-22 15:58:20 UTC (rev 8490)
@@ -28,7 +28,7 @@
@Install(precedence = BUILT_IN)
public class PermissionManager implements Serializable
{
- public static final String PERMISSION_STORE_COMPONENT_NAME = "org.jboss.seam.security.permission.jpaPermissionStore";
+ public static final String PERMISSION_STORE_COMPONENT_NAME = "org.jboss.seam.security.jpaPermissionStore";
public static final String PERMISSION_PERMISSION_NAME = "seam.permission";
[View Less]
16 years, 6 months
Seam SVN: r8489 - trunk/doc/Seam_Reference_Guide/en-US.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-22 03:59:22 -0400 (Tue, 22 Jul 2008)
New Revision: 8489
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Security.xml
Log:
completed section on identifier strategies, described permission management
Modified: trunk/doc/Seam_Reference_Guide/en-US/Security.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-22 07:36:36 UTC (rev 8488)
+++ trunk/doc/…
[View More]Seam_Reference_Guide/en-US/Security.xml 2008-07-22 07:59:22 UTC (rev 8489)
@@ -4026,12 +4026,12 @@
By default, multiple permissions for the same target object and recipient will be persisted as a single database record,
with the <literal>action</literal> property/column containing a comma-separated list of the granted actions. To reduce
the amount of physical storage required to persist a large number of permissions, it is possible to use a bitmasked
- integer value (instead of a comma-separated list) to store the list of actions.
+ integer value (instead of a comma-separated list) to store the list of permission actions.
</para>
<para>
For example, if recipient "Bob" is granted both the <literal>view</literal> and <literal>comment</literal> permissions
- for a particular <literal>MemberImage</literal> instance, then by default the <literal>action</literal> property of the
+ for a particular <literal>MemberImage</literal> (an entity bean) instance, then by default the <literal>action</literal> property of the
permission entity will contain "<literal>view,comment</literal>", representing the two granted permission actions.
Alternatively, if using bitmasked values for the permission actions, as defined like so:
</para>
@@ -4048,6 +4048,10 @@
for a large number of allowable actions for any particular target class, the storage required for the permission records
is greatly reduced by using bitmasked actions.
</para>
+
+ <para>
+ Obviously, it is very important that the <literal>mask</literal> values specified are powers of 2.
+ </para>
</sect4>
<sect4>
@@ -4103,37 +4107,77 @@
</para>
<programlisting><![CDATA[@Identifier(name = "customer")
-public class Customer {
- ]]></programlisting>
+public class Customer {]]></programlisting>
<para>
The identifier for the following class will be "<literal>customerAction</literal>":
</para>
<programlisting><![CDATA[@Name("customerAction")
-public class CustomerAction {
- ]]></programlisting>
+public class CustomerAction { ]]></programlisting>
<para>
Finally, the identifier for the following class will be "<literal>Customer</literal>":
</para>
- <programlisting><![CDATA[public class Customer {
- ]]></programlisting>
+ <programlisting><![CDATA[public class Customer { ]]></programlisting>
</sect4>
<sect4>
<title>EntityIdentifierStrategy</title>
- </sect4>
-
- <sect4>
- <title>Overview</title>
<para>
- So how does persistent permission data actually look when stored in the database?
+ This identifier strategy is used to generate unique identifiers for entity beans. It does so by
+ concatenating the entity name (or otherwise configured name) with a string representation of the
+ primary key value of the entity. The rules for generating the name section of the identifier are
+ similar to <literal>ClassIdentifierStrategy</literal>. The primary key value (i.e. the
+ <emphasis>id</emphasis> of the entity) is obtained using the <literal>PersistenceProvider</literal>
+ component, which is able to correctly determine the value regardless of which persistence implementation
+ is used within the Seam application. For entities not annotated with <literal>@Entity</literal>, it is
+ necessary to explicitly configure the identifier strategy on the entity class itself, for example:
</para>
+
+ <programlisting><![CDATA[@Identifier(value = EntityIdentifierStrategy.class)
+public class Customer { ]]></programlisting>
+
+ <para>
+ For an example of the type of identifier values generated, assume we have the following entity class:
+ </para>
+
+ <programlisting><![CDATA[@Entity
+public class Customer {
+ private Integer id;
+ private String firstName;
+ private String lastName;
+
+ @Id
+ public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+
+ public String getFirstName() { return firstName; }
+ public void setFirstName(String firstName) { this.firstName = firstName; }
+
+ public String getLastName() { return lastName; }
+ public void setLastName(String lastName) { this.lastName = lastName; }
+}]]></programlisting>
+
+ <para>
+ For a <literal>Customer</literal> instance with an <literal>id</literal> value of <literal>1</literal>,
+ the value of the identifier would be "<literal>Customer:1</literal>". If the entity class is annotated
+ with an explicit identifier name, like so:
+ </para>
+
+ <programlisting><![CDATA[@Entity
+@Identifier(name = "cust")
+public class Customer { ]]></programlisting>
+
+ <para>
+ Then a <literal>Customer</literal> with an <literal>id</literal> value of <literal>123</literal>
+ would have an identifier value of "<literal>cust:123</literal>".
+ </para>
</sect4>
+
</sect3>
</sect2>
@@ -4145,9 +4189,337 @@
<para>
In much the same way that Seam Security provides an Identity Management API for the management of users and roles,
- it also provides a Permissions Management API for the management of persistent user permissions.
+ it also provides a Permissions Management API for the management of persistent user permissions, via the
+ <literal>PermissionManager</literal> component.
</para>
+
+ <sect2>
+ <title>PermissionManager</title>
+
+ <para>
+ The <literal>PermissionManager</literal> component is an application-scoped Seam component that provides a number of
+ methods for managing permissions. Before it can be used, it must be configured with a permission store (although by
+ default it will attempt to use <literal>JpaPermissionStore</literal> if it is available). To explicitly configure a
+ custom permission store, specify the <literal>permission-store</literal> property in components.xml:
+ </para>
+
+ <programlisting><![CDATA[
+<security:permission-manager permission-store="#{ldapPermissionStore}"/>
+ ]]></programlisting>
+
+ <para>
+ The following table describes each of the available methods provided by <literal>PermissionManager</literal>:
+ </para>
+
+ <table>
+ <title>PermissionManager API methods</title>
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Return type</para>
+ </entry>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target, String action)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of <literal>Permission</literal> objects representing all of the permissions that
+ have been granted for the specified target and action.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of <literal>Permission</literal> objects representing all of the permissions that
+ have been granted for the specified target and action.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermission(Permission permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Persists (grants) the specified <literal>Permission</literal> to the backend permission store.
+ Returns true if the operation was successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Persists (grants) the specified list of <literal>Permission</literal>s to the backend permission store.
+ Returns true if the operation was successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermission(Permission permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Removes (revokes) the specified <literal>Permission</literal> from the backend permission store.
+ Returns true if the operation was successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Removes (revokes) the specified list of <literal>Permission</literal>s from the backend permission store.
+ Returns true if the operation was successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<String></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listAvailableActions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of the available actions for the specified target object. The actions that this
+ method returns are dependent on the <literal>@Permission</literal> annotations configured on the
+ target object's class.
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2>
+ <title>Permission checks for PermissionManager operations</title>
+
+ <para>
+ Invoking the methods of <literal>PermissionManager</literal> requires that currently authenticated user
+ has the appropriate authorization to perform the operation.
+ </para>
+
+ <table>
+ <title>Permission Management Security Permissions</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="1*" />
+ <colspec colnum="2" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Permission Target</para>
+ </entry>
+ <entry align="center">
+ <para>Permission Action</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>
+ <para>
+ <literal>listPermissions()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ The specified <literal>target</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.read-permissions</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>grantPermission()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ The target of the specified <literal>Permission</literal>, or each of the targets
+ for the specified list of <literal>Permission</literal>s (depending on which method is
+ called).
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.grant-permission</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>grantPermission()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ The target of the specified <literal>Permission</literal>.
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.grant-permission</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>grantPermissions()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Each of the targets of the specified list of <literal>Permission</literal>s.
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.grant-permission</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>revokePermission()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ The target of the specified <literal>Permission</literal>.
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.revoke-permission</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>revokePermissions()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Each of the targets of the specified list of <literal>Permission</literal>s.
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.revoke-permission</literal>
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+
</sect1>
<sect1>
[View Less]
16 years, 6 months
Seam SVN: r8488 - trunk/src/main/org/jboss/seam/security/permission.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-22 03:36:36 -0400 (Tue, 22 Jul 2008)
New Revision: 8488
Modified:
trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java
Log:
permission target should be an Object
Modified: trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java 2008-07-21 15:12:41 UTC (rev 8487)
+++…
[View More] trunk/src/main/org/jboss/seam/security/permission/PermissionManager.java 2008-07-22 07:36:36 UTC (rev 8488)
@@ -83,7 +83,7 @@
this.permissionStore = permissionStore;
}
- public List<Permission> listPermissions(String target, String action)
+ public List<Permission> listPermissions(Object target, String action)
{
if (target == null) return null;
Identity.instance().checkPermission(target, PERMISSION_READ);
[View Less]
16 years, 6 months
Seam SVN: r8487 - trunk/doc/Seam_Reference_Guide/en-US.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-21 11:12:41 -0400 (Mon, 21 Jul 2008)
New Revision: 8487
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Security.xml
Log:
identifier policy
Modified: trunk/doc/Seam_Reference_Guide/en-US/Security.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-21 14:06:45 UTC (rev 8486)
+++ trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-21 15:12:41 UTC (rev 8487)
@@ …
[View More]-4075,9 +4075,59 @@
is capable of generating a unique identifier for the specified target class. The second method,
<literal>getIdentifier()</literal> returns the unique identifier value for the specified target object.
</para>
+
+ <para>
+ Seam provides two <literal>IdentifierStrategy</literal> implementations, <literal>ClassIdentifierStrategy</literal>
+ and <literal>EntityIdentifierStrategy</literal> (see next sections for details).
+ </para>
+
+ <para>
+ To explicitly configure a specific identifier strategy to use for a particular class, it should be annotated with
+ <literal>org.jboss.seam.annotations.security.permission.Identifier</literal>, and the value should be set to
+ a concrete implementation of the <literal>IdentifierStrategy</literal> interface. An optional <literal>name</literal>
+ property can also be specified, the effect of which is dependent upon the actual <literal>IdentifierStrategy</literal>
+ implementation used.
+ </para>
</sect4>
<sect4>
+ <title>ClassIdentifierStrategy</title>
+
+ <para>
+ This identifier strategy is used to generate unique identifiers for classes, and will use the value of the
+ <literal>name</literal> (if specified) in the <literal>@Identifier</literal> annotation. If there is no
+ <literal>name</literal> property provided, then it will attempt to use the component name of the class
+ (if the class is a Seam component), or as a last resort it will create an identifier based on the name
+ of the class (excluding the package name). For example, the identifier for the following class will
+ be "<literal>customer</literal>":
+ </para>
+
+ <programlisting><![CDATA[@Identifier(name = "customer")
+public class Customer {
+ ]]></programlisting>
+
+ <para>
+ The identifier for the following class will be "<literal>customerAction</literal>":
+ </para>
+
+ <programlisting><![CDATA[@Name("customerAction")
+public class CustomerAction {
+ ]]></programlisting>
+
+ <para>
+ Finally, the identifier for the following class will be "<literal>Customer</literal>":
+ </para>
+
+ <programlisting><![CDATA[public class Customer {
+ ]]></programlisting>
+
+ </sect4>
+
+ <sect4>
+ <title>EntityIdentifierStrategy</title>
+ </sect4>
+
+ <sect4>
<title>Overview</title>
<para>
[View Less]
16 years, 7 months
Seam SVN: r8486 - in trunk/src/main/org/jboss/seam: security/permission and 1 other directory.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-21 10:06:45 -0400 (Mon, 21 Jul 2008)
New Revision: 8486
Modified:
trunk/src/main/org/jboss/seam/annotations/security/permission/Identifier.java
trunk/src/main/org/jboss/seam/security/permission/IdentifierPolicy.java
Log:
IdentifierPolicy not required
Modified: trunk/src/main/org/jboss/seam/annotations/security/permission/Identifier.java
===================================================================
--- trunk/src/main/org/jboss/seam/…
[View More]annotations/security/permission/Identifier.java 2008-07-20 13:53:49 UTC (rev 8485)
+++ trunk/src/main/org/jboss/seam/annotations/security/permission/Identifier.java 2008-07-21 14:06:45 UTC (rev 8486)
@@ -22,7 +22,7 @@
@Retention(RUNTIME)
@Inherited
public @interface Identifier
-{
- Class<? extends IdentifierStrategy> value();
+{
+ Class<? extends IdentifierStrategy> value() default IdentifierStrategy.class;
String name() default "";
}
Modified: trunk/src/main/org/jboss/seam/security/permission/IdentifierPolicy.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/permission/IdentifierPolicy.java 2008-07-20 13:53:49 UTC (rev 8485)
+++ trunk/src/main/org/jboss/seam/security/permission/IdentifierPolicy.java 2008-07-21 14:06:45 UTC (rev 8486)
@@ -56,26 +56,28 @@
{
Class<? extends IdentifierStrategy> strategyClass =
target.getClass().getAnnotation(Identifier.class).value();
- try
+
+ if (strategyClass != IdentifierStrategy.class)
{
- strategy = strategyClass.newInstance();
- strategies.put(target.getClass(), strategy);
+ try
+ {
+ strategy = strategyClass.newInstance();
+ strategies.put(target.getClass(), strategy);
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException("Error instantiating IdentifierStrategy for object " + target, ex);
+ }
}
- catch (Exception ex)
- {
- throw new RuntimeException("Error instantiating IdentifierStrategy for object " + target, ex);
- }
}
- else
+
+ for (IdentifierStrategy s : registeredStrategies)
{
- for (IdentifierStrategy s : registeredStrategies)
+ if (s.canIdentify(target.getClass()))
{
- if (s.canIdentify(target.getClass()))
- {
- strategy = s;
- strategies.put(target.getClass(), strategy);
- break;
- }
+ strategy = s;
+ strategies.put(target.getClass(), strategy);
+ break;
}
}
}
[View Less]
16 years, 7 months
Seam SVN: r8485 - trunk/doc/Seam_Reference_Guide/en-US.
by seam-commits@lists.jboss.org
Author: shane.bryzak(a)jboss.com
Date: 2008-07-20 09:53:49 -0400 (Sun, 20 Jul 2008)
New Revision: 8485
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Security.xml
Log:
some more additions
Modified: trunk/doc/Seam_Reference_Guide/en-US/Security.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-19 15:48:26 UTC (rev 8484)
+++ trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-20 13:53:49 UTC (rev 8485)
@…
[View More]@ -3062,7 +3062,7 @@
</mediaobject>
<para>
- Each of these classes are explained in more detail in the following sections.
+ The relevant classes are explained in more detail in the following sections.
</para>
<sect3>
@@ -3090,7 +3090,9 @@
<para>
It is very simple to implement your own permission resolver. The <literal>PermissionResolver</literal>
- interface defines only two methods that must be implemented:
+ interface defines only two methods that must be implemented, as shown by the following table. By deploying
+ your own <literal>PermissionResolver</literal> implementation in your Seam project, it will be automatically
+ scanned during deployment and registered with the default <literal>ResolverChain</literal>.
</para>
<table>
@@ -3138,7 +3140,7 @@
</para>
</entry>
</row>
-
+
<row>
<entry>
<para>
@@ -3152,7 +3154,7 @@
</entry>
<entry>
<para>
- This method should iterate through the specified set of objects, and remove any that would otherwise
+ This method should remove any objects from the specified set, that would otherwise
return <literal>false</literal> if passed to the <literal>hasPermission()</literal> method with the
same <literal>action</literal> parameter value.
</para>
@@ -3177,7 +3179,9 @@
<para>
The following sequence diagram shows the interaction between the components of the permission framework during a
- permission check (explanation follows):
+ permission check (explanation follows). A permission check can originate from a number of possible sources,
+ for example - the security interceptor, the <literal>s:hasPermission</literal> EL function, or via an API
+ call to <literal>Identity.checkPermission</literal>:
</para>
<mediaobject>
@@ -3193,19 +3197,22 @@
</sect2>
<sect2>
- <title>Rule-based Permissions</title>
+ <title>RuleBasedPermissionResolver</title>
+ <para>
+ One of the built-in permission resolvers provided by Seam, <literal>RuleBasedPermissionResolver</literal>
+ allows permissions to be evaluated based on a set of Drools (JBoss Rules) security rules. A couple of the
+ advantages of using a rule engine are 1) a centralized location for the business logic that is used to
+ evaluate user permissions, and 2) speed - Drools uses very efficient algorithms for evaluating large
+ numbers of complex rules involving multiple conditions.
+ </para>
+
<sect3>
- <title>Configuration</title>
-
- </sect3>
-
- <sect3>
<title>Requirements</title>
<para>
- If using the rule-based permission features provided by Seam Security, the following jar files are required to be
- distributed with your project:
+ If using the rule-based permission features provided by Seam Security, the following jar files are required by Drools
+ to be distributed with your project:
</para>
<itemizedlist>
@@ -3226,208 +3233,220 @@
</listitem>
</itemizedlist>
- </sect3>
+ </sect3>
- </sect2>
+ <sect3>
+ <title>Configuration</title>
+
+ <para>
+ The configuration for <literal>RuleBasedPermissionResolver</literal> requires that a Drools rule base is first
+ configured in <literal>components.xml</literal>. By default, it expects that the rule base is named
+ <literal>securityRules</literal>, as per the following example:
+ </para>
+
+ <programlisting role="XML"><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:core="http://jboss.com/products/seam/core"
+ xmlns:security="http://jboss.com/products/seam/security"
+ xmlns:drools="http://jboss.com/products/seam/drools"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation=
+ "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
+ http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd
+ http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd"
+ http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd">
+
+ <drools:rule-base name="securityRules">
+ <drools:rule-files>
+ <value>/META-INF/security.drl</value>
+ </drools:rule-files>
+ </drools:rule-base>
+
+ </components>]]></programlisting>
+
+ <para>
+ The default rule base name can be overridden by specifying the <literal>security-rules</literal>
+ property for <literal>RuleBasedPermissionResolver</literal>:
+ </para>
+
+ <programlisting><![CDATA[
+ <security:rule-based-permission-resolver security-rules="#{prodSecurityRules}"/>]]></programlisting>
- </sect1>
-
- <sect1>
- <title>Writing Security Rules</title>
-
- <para>
- Up to this point there has been a lot of mention of permissions, but no information about how permissions
- are actually defined or granted. This section completes the picture, by explaining how permission
- checks are processed, and how to implement permission checks for a Seam application.
- </para>
-
- <sect2>
- <title>Permissions Overview</title>
-
- <para>
- So how does the security API know whether a user has the <literal>customer:modify</literal> permission
- for a specific customer? Seam Security provides quite a novel method for determining user permissions,
- based on JBoss Rules. A couple of the advantages of using a rule engine are 1) a centralized location
- for the business logic that is behind each user permission, and 2) speed - JBoss Rules uses very efficient
- algorithms for evaluating large numbers of complex rules involving multiple conditions.
- </para>
-
- </sect2>
-
- <sect2>
- <title>Configuring a rules file</title>
-
- <para>
- Seam Security expects to find a <literal>RuleBase</literal> component called <literal>securityRules</literal>
- which it uses to evaluate permission checks. This is configured in <literal>components.xml</literal> as follows:
- </para>
-
- <programlisting role="XML"><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
- xmlns:core="http://jboss.com/products/seam/core"
- xmlns:security="http://jboss.com/products/seam/security"
- xmlns:drools="http://jboss.com/products/seam/drools"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation=
- "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
- http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd
- http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd"
- http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd">
-
- <drools:rule-base name="securityRules">
- <drools:rule-files>
- <value>/META-INF/security.drl</value>
- </drools:rule-files>
- </drools:rule-base>
-
-</components>]]></programlisting>
-
- <para>
- Once the <literal>RuleBase</literal> component is configured, it's time to write the security rules.
- </para>
- </sect2>
-
- <sect2>
- <title>Creating a security rules file</title>
- <para>
- For this step you need to create a file called <literal>security.drl</literal> in the
- <literal>/META-INF</literal> directory of your application's jar file. In actual fact this file can be called
- anything you want, and exist in any location as long as it is configured appropriately in
- <literal>components.xml</literal>.
- </para>
-
- <para>
- So what should the security rules file contain? At this stage it might be a good idea to at least skim
- through the JBoss Rules documentation, however to get started here's an extremely simple example:
- </para>
-
- <programlisting><![CDATA[package MyApplicationPermissions;
-
-import org.jboss.seam.security.PermissionCheck;
-import org.jboss.seam.security.Role;
-
-rule CanUserDeleteCustomers
+ <para>
+ Once the <literal>RuleBase</literal> component is configured, it's time to write the security rules.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Writing Security Rules</title>
+
+ <para>
+ The first step to writing security rules is to create a new rule file in the <literal>/META-INF</literal>
+ directory of your application's jar file. Usually this file would be named something like
+ <literal>security.drl</literal>, however you can name it whatever you like as long as it is configured
+ correspondingly in <literal>components.xml</literal>.
+ </para>
+
+ <para>
+ So what should the security rules file contain? At this stage it might be a good idea to at least skim
+ through the Drools documentation, however to get started here's an extremely simple example:
+ </para>
+
+ <programlisting><![CDATA[package MyApplicationPermissions;
+
+ import org.jboss.seam.security.permission.PermissionCheck;
+ import org.jboss.seam.security.Role;
+
+ rule CanUserDeleteCustomers
+ when
+ c: PermissionCheck(target == "customer", action == "delete")
+ Role(name == "admin")
+ then
+ c.grant();
+ end]]></programlisting>
+
+ <para>
+ Let's break this down step by step. The first thing we see is the package declaration. A package in Drools
+ is essentially a collection of rules. The package name can be anything you want - it doesn't relate
+ to anything else outside the scope of the rule base.
+ </para>
+
+ <para>
+ The next thing we can notice is a couple of import statements for the <literal>PermissionCheck</literal>
+ and <literal>Role</literal> classes. These imports inform the rules engine that we'll be referencing
+ these classes within our rules.
+ </para>
+
+ <para>
+ Finally we have the code for the rule. Each rule within a package should be given a unique name (usually
+ describing the purpose of the rule). In this case our rule is called <literal>CanUserDeleteCustomers</literal>
+ and will be used to check whether a user is allowed to delete a customer record.
+ </para>
+
+ <para>
+ Looking at the body of the rule definition we can notice two distinct sections. Rules have what is known
+ as a left hand side (LHS) and a right hand side (RHS). The LHS consists of the conditional part of the
+ rule, i.e. a list of conditions which must be satisfied for the rule to fire. The LHS is represented by
+ the <literal>when</literal> section. The RHS is the consequence, or action section of the rule that will
+ only be fired if all of the conditions in the LHS are met. The RHS is represented by the
+ <literal>then</literal> section. The end of the rule is denoted by the <literal>end</literal> line.
+ </para>
+
+ <para>
+ If we look at the LHS of the rule, we see two conditions listed there. Let's examine the first condition:
+ </para>
+
+ <programlisting><![CDATA[c: PermissionCheck(target == "customer", action == "delete")]]></programlisting>
+
+ <para>
+ In plain english, this condition is stating that there must exist a <literal>PermissionCheck</literal> object
+ with a <literal>target</literal> property equal to "customer", and an <literal>action</literal> property equal
+ to "delete" within the working memory.
+ </para>
+
+ <para>
+ So what is the working memory? Also known as a "stateful session" in Drools terminology, the working memory
+ is a session-scoped object that contains the contextual information that is required by the rules engine to
+ make a decision about a permission check. Each time the <literal>hasPermission()</literal> method is called,
+ a temporary <literal>PermissionCheck</literal> object, or <emphasis>Fact</emphasis>, is inserted into the
+ working memory. This <literal>PermissionCheck</literal> corresponds exactly to the permission that is being
+ checked, so for example if you call <literal>hasPermission("account", "create")</literal> then a
+ <literal>PermissionCheck</literal> object with a <literal>target</literal> equal to "account" and
+ <literal>action</literal> equal to "create" will be inserted into the working memory for the duration of the
+ permission check.
+ </para>
+
+ <para>
+ Besides the <literal>PermissionCheck</literal> facts, there is also a <literal>org.jboss.seam.security.Role</literal>
+ fact for each of the roles that the authenticated user is a member of. These <literal>Role</literal> facts
+ are synchronized with the user's authenticated roles at the beginning of every permission check. As a consequence,
+ any <literal>Role</literal> object that is inserted into the working memory during the course of a permission
+ check will be removed before the next permission check occurs, if the authenticated user is not actually a member of
+ that role. Besides the <literal>PermissionCheck</literal> and <literal>Role</literal> facts, the working
+ memory also contains the <literal>java.security.Principal</literal> object that was created as a result of
+ the authentication process.
+ </para>
+
+ <para>
+ It is also possible to insert additional long-lived facts into the working memory by calling
+ <literal>RuleBasedPermissionResolver.instance().getSecurityContext().insert()</literal>,
+ passing the object as a parameter. The exception to this is <literal>Role</literal> objects, which as
+ already discussed are synchronized at the start of each permission check.
+ </para>
+
+ <para>
+ Getting back to our simple example, we can also notice that the first line of our LHS is prefixed with
+ <literal>c:</literal>. This is a variable binding, and is used to refer back to the object that is
+ matched by the condition (in this case, the <literal>PermissionCheck</literal>). Moving on to the
+ second line of our LHS, we see this:
+ </para>
+
+ <programlisting><![CDATA[Role(name == "admin")]]></programlisting>
+
+ <para>
+ This condition simply states that there must be a <literal>Role</literal> object with a
+ <literal>name</literal> of "admin" within the working memory. As already mentioned, user roles are inserted into
+ the working memory at the beginning of each permission check. So, putting both conditions together, this
+ rule is essentially saying "I will fire if you are checking for the <literal>customer:delete</literal>
+ permission and the user is a member of the <literal>admin</literal> role".
+ </para>
+
+ <para>
+ So what is the consequence of the rule firing? Let's take a look at the RHS of the rule:
+ </para>
+
+ <programlisting><![CDATA[c.grant()]]></programlisting>
+
+ <para>
+ The RHS consists of Java code, and in this case is invoking the <literal>grant()</literal>
+ method of the <literal>c</literal> object, which as already mentioned is a variable binding
+ for the <literal>PermissionCheck</literal> object. Besides the <literal>name</literal> and
+ <literal>action</literal> properties of the <literal>PermissionCheck</literal> object, there
+ is also a <literal>granted</literal> property which is initially set to <literal>false</literal>.
+ Calling <literal>grant()</literal> on a <literal>PermissionCheck</literal> sets the
+ <literal>granted</literal> property to <literal>true</literal>, which means that the permission
+ check was successful, allowing the user to carry out whatever action the permission check was
+ intended for.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Non-String permission targets</title>
+
+ <para>
+ So far we have only seen permission checks for String-literal permission targets. It is of course also
+ possible to write security rules for permission targets of more complex types. For example, let's say that you wish
+ to write a security rule to allow your users to create blog comments. The following rule demonstrates
+ how this may be expressed, by requiring the target of the permission check to be an instance of
+ <literal>MemberBlog</literal>, and also requiring that the currently authenticated user is a member of the
+ <literal>user</literal> role:
+ </para>
+
+ <programlisting><![CDATA[rule CanCreateBlogComment
+ no-loop
+ activation-group "permissions"
when
- c: PermissionCheck(name == "customer", action == "delete")
- Role(name == "admin")
+ blog: MemberBlog()
+ check: PermissionCheck(target == blog, action == "create", granted == false)
+ Role(name == "user")
then
- c.grant();
-end;]]></programlisting>
-
- <para>
- Let's break this down. The first thing we see is the package declaration. A package in JBoss Rules
- is essentially a collection of rules. The package name can be anything you want - it doesn't relate
- to anything else outside the scope of the rule base.
- </para>
-
- <para>
- The next thing we can notice is a couple of import statements for the <literal>PermissionCheck</literal>
- and <literal>Role</literal> classes. These imports inform the rules engine that we'll be referencing
- these classes within our rules.
- </para>
-
- <para>
- Finally we have the code for the rule. Each rule within a package should be given a unique name (usually
- describing the purpose of the rule). In this case our rule is called <literal>CanUserDeleteCustomers</literal>
- and will be used to check whether a user is allowed to delete a customer record.
- </para>
-
- <para>
- Looking at the body of the rule definition we can notice two distinct sections. Rules have what is known
- as a left hand side (LHS) and a right hand side (RHS). The LHS consists of the conditional part of the
- rule, i.e. a list of conditions which must be satisfied for the rule to fire. The LHS is represented by
- the <literal>when</literal> section. The RHS is the consequence, or action section of the rule that will
- only be fired if all of the conditions in the LHS are met. The RHS is represented by the
- <literal>then</literal> section. The end of the rule is denoted by the <literal>end;</literal> line.
- </para>
-
- <para>
- If we look at the LHS of the rule, we see two conditions listed there. Let's examine the first condition:
- </para>
-
- <programlisting><![CDATA[c: PermissionCheck(name == "customer", action == "delete")]]></programlisting>
-
- <para>
- In plain english, this condition is stating that there must exist a <literal>PermissionCheck</literal> object
- with a <literal>name</literal> property equal to "customer", and an <literal>action</literal> property equal
- to "delete" within the working memory.
- </para>
-
- <para>
- So what is the working memory? Also known as a "stateful session" in Drools terminology, the working memory
- is a session-scoped object that contains the contextual information that is required by the rules engine to
- make a decision about a permission check. Each time the <literal>hasPermission()</literal> method is called,
- a temporary <literal>PermissionCheck</literal> object, or <emphasis>Fact</emphasis>, is inserted into the
- working memory. This <literal>PermissionCheck</literal> corresponds exactly to the permission that is being
- checked, so for example if you call <literal>hasPermission("account", "create", null)</literal> then a
- <literal>PermissionCheck</literal> object with a <literal>name</literal> equal to "account" and
- <literal>action</literal> equal to "create" will be inserted into the working memory for the duration of the
- permission check.
- </para>
+ check.grant();
+end
+]]></programlisting>
- <para>
- Besides the <literal>PermissionCheck</literal> facts, there is also a <literal>org.jboss.seam.security.Role</literal>
- fact for each of the roles that the authenticated user is a member of. These <literal>Role</literal> facts
- are synchronized with the user's authenticated roles at the beginning of every permission check. As a consequence,
- any <literal>Role</literal> object that is inserted into the working memory during the course of a permission
- check will be removed before the next permission check occurs, if the authenticated user is not a member of
- that role. Besides the <literal>PermissionCheck</literal> and <literal>Role</literal> facts, the working
- memory also contains the <literal>java.security.Principal</literal> object that was created during
- the authentication process.
- </para>
-
- <para>
- It is also possible to insert additional long-lived facts into the working memory by calling
- <literal>((RuleBasedIdentity) RuleBasedIdentity.instance()).getSecurityContext().insert()</literal>,
- passing the object as a parameter. The exception to this is <literal>Role</literal> objects, which as
- already discussed are synchronized at the start of each permission check.
- </para>
+ </sect3>
- <para>
- Getting back to our simple example, we can also notice that the first line of our LHS is prefixed with
- <literal>c:</literal>. This is a variable binding, and is used to refer back to the object that is
- matched by the condition. Moving onto the second line of our LHS, we see this:
- </para>
-
- <programlisting><![CDATA[Role(name == "admin")]]></programlisting>
-
- <para>
- This condition simply states that there must be a <literal>Role</literal> object with a
- <literal>name</literal> of "admin" within the working memory. As mentioned, user roles are inserted into
- the working memory at the beginning of each permission check. So, putting both conditions together, this
- rule is essentially saying "I will fire if you are checking for the <literal>customer:delete</literal>
- permission and the user is a member of the <literal>admin</literal> role".
- </para>
-
- <para>
- So what is the consequence of the rule firing? Let's take a look at the RHS of the rule:
- </para>
-
- <programlisting><![CDATA[c.grant()]]></programlisting>
-
- <para>
- The RHS consists of Java code, and in this case is invoking the <literal>grant()</literal>
- method of the <literal>c</literal> object, which as already mentioned is a variable binding
- for the <literal>PermissionCheck</literal> object. Besides the <literal>name</literal> and
- <literal>action</literal> properties of the <literal>PermissionCheck</literal> object, there
- is also a <literal>granted</literal> property which is initially set to <literal>false</literal>.
- Calling <literal>grant()</literal> on a <literal>PermissionCheck</literal> sets the
- <literal>granted</literal> property to <literal>true</literal>, which means that the permission
- check was successful, allowing the user to carry out whatever action the permission check was
- intended for.
- </para>
-
<sect3>
<title>Wildcard permission checks</title>
<para>
It is possible to implement a wildcard permission check (which allows all actions for a given permission
- name), by omitting the <literal>action</literal> constraint for the <literal>PermissionCheck</literal> in
+ target), by omitting the <literal>action</literal> constraint for the <literal>PermissionCheck</literal> in
your rule, like this:
</para>
<programlisting><![CDATA[rule CanDoAnythingToCustomersIfYouAreAnAdmin
when
- c: PermissionCheck(name == "customer")
+ c: PermissionCheck(target == "customer")
Role(name == "admin")
then
c.grant();
@@ -3437,12 +3456,649 @@
<para>
This rule allows users with the <literal>admin</literal> role to perform <emphasis>any</emphasis> action for
any <literal>customer</literal> permission check.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2>
+ <title>PersistentPermissionResolver</title>
+
+ <para>
+ Another built-in permission resolver provided by Seam, <literal>PersistentPermissionResolver</literal>
+ allows permissions to be loaded from persistent storage, such as a relational database. This permission
+ resolver provides ACL style instance-based security, allowing for specific object permissions to be assigned
+ to individual users and roles. It also allows for persistent, arbitrarily-named permission targets (not
+ necessarily object/class based) to be assigned in the same way.
+ </para>
+
+ <sect3>
+ <title>Configuration</title>
+
+ <para>
+ Before it can be used, <literal>PersistentPermissionResolver</literal> must be configured with a
+ valid <literal>PermissionStore</literal> in <literal>components.xml</literal>. If not configured,
+ it will attempt to use the default permission store, <literal>JpaIdentityStore</literal> (see section
+ further down for details). To use a permission store other than the default, configure the
+ <literal>permission-store</literal> property as follows:
+ </para>
+
+ <programlisting><![CDATA[ <security:persistent-permission-resolver permission-store="#{myCustomPermissionStore}"/>]]></programlisting>
+
+ </sect3>
+
+ <sect3>
+ <title>Permission Stores</title>
+
+ <para>
+ A permission store is required for <literal>PersistentPermissionResolver</literal> to connect to the
+ backend storage where permissions are persisted. Seam provides one <literal>PermissionStore</literal>
+ implementation out of the box, <literal>JpaPermissionStore</literal>, which is used to store
+ permissions inside a relational database. It is possible to write your own permission store
+ by implementing the <literal>PermissionStore</literal> interface, which defines the following
+ methods:
</para>
+
+ <table>
+ <title>PermissionStore interface</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Return type</para>
+ </entry>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions granted for the specified target object.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target, String action)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions with the specified action, granted for the specified target object.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Set<Object> targets, String action)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions with the specified action, granted for the specified set of
+ target objects.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermission(Permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should persist the specified <literal>Permission</literal> object to the backend
+ storage, returning true if successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should persist all of the <literal>Permission</literal> objects contained in the
+ specified <literal>List</literal>, returning true if successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermission(Permission permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should remove the specified <literal>Permission</literal> object from persistent storage.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should remove all of the <literal>Permission</literal> objects in the specified list
+ from persistent storage.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<String></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listAvailableActions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a list of all the available actions (as Strings) for the class of the
+ specified target object. It is used in conjunction with permission management to build the user
+ interface for granting specific class permissions (see section further down).
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
</sect3>
+
+ <sect3>
+ <title>JpaPermissionStore</title>
+
+ <para>
+ This is the default <literal>PermissionStore</literal> implementation (and the only one provided by Seam), which
+ uses a relational database to store permissions. Before it can be used it must be configured with either one or
+ two entity classes for storing user and role permissions. These entity classes must be annotated with a special
+ set of security annotations to configure which properties of the entity correspond to various aspects of the
+ permissions being stored.
+ </para>
+
+ <para>
+ If you wish to use the same entity (i.e. a single database table) to store both user and role permissions, then
+ only the <literal>user-permission-class</literal> property is required to be configured. If you wish to use
+ separate tables for storing user and role permissions, then in addition to the <literal>user-permission-class</literal>
+ property you must also configure the <literal>role-permission-class</literal> property.
+ </para>
+
+ <para>For example, to configure a single entity class to store both user and role permissions:</para>
+
+ <programlisting><![CDATA[ <security:jpa-permission-store user-permission-class="com.acme.model.AccountPermission"/>]]></programlisting>
+
+ <para>To configure separate entity classes for storing user and role permissions:</para>
+
+ <programlisting><![CDATA[ <security:jpa-permission-store user-permission-class="com.acme.model.UserPermission"
+ role-permission-class="com.acme.model.RolePermission"/>]]></programlisting>
+
+ <sect4>
+ <title>Permission annotations</title>
+
+ <para>
+ As mentioned, the entity classes that contain the user and role permissions must be configured with a
+ special set of annotations, contained within the <literal>org.jboss.seam.annotations.security.permission</literal> package.
+ The following table lists each of these annotations along with a description of how they are used:
+ </para>
+
+ <table>
+ <title>Entity Permission annotations</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Annotation</para>
+ </entry>
+ <entry align="center">
+ <para>Target</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionTarget</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the permission target. The property
+ should be of type <literal>java.lang.String</literal>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionAction</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the permission action. The property
+ should be of type <literal>java.lang.String</literal>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionUser</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the recipient user for the permission. It should
+ be of type <literal>java.lang.String</literal> and contain the user's username.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionRole</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the recipient role for the permission. It should
+ be of type <literal>java.lang.String</literal> and contain the role name.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionDiscriminator</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation should be used when the same entity/table is used to store both user and role permissions. It identifies
+ the property of the entity that is used to discriminate between user and role permissions. By default, if the column
+ value contains the string literal <literal>user</literal>, then the record will be treated as a user permission. If it
+ contains the string literal <literal>role</literal>, then it will be treated as a role permission. It is also possible
+ to override these defaults by specifying the <literal>userValue</literal> and <literal>roleValue</literal> properties
+ within the annotation. For example, to use <literal>u</literal> and <literal>r</literal> instead of <literal>user</literal>
+ and <literal>role</literal>, the annotation would be written like this:
+ </para>
+
+ <programlisting><![CDATA[ @PermissionDiscriminator(userValue = "u", roleValue = "r")]]></programlisting>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect4>
+
+ <sect4>
+ <title>Example Entity</title>
+
+ <para>
+ Here is an example of an entity class that is used to store both user and role permissions. The following class can be found
+ inside the SeamSpace example:
+ </para>
+
+ <programlisting><![CDATA[
+@Entity
+public class AccountPermission implements Serializable {
+ private Integer permissionId;
+ private String recipient;
+ private String target;
+ private String action;
+ private String discriminator;
+
+ @Id @GeneratedValue
+ public Integer getPermissionId() {
+ return permissionId;
+ }
+
+ public void setPermissionId(Integer permissionId) {
+ this.permissionId = permissionId;
+ }
+
+ @PermissionUser @PermissionRole
+ public String getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ @PermissionTarget
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ @PermissionAction
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ @PermissionDiscriminator
+ public String getDiscriminator() {
+ return discriminator;
+ }
+
+ public void setDiscriminator(String discriminator) {
+ this.discriminator = discriminator;
+ }
+}
+ ]]></programlisting>
+
+ <para>
+ As can be seen in the above example, the <literal>getDiscriminator()</literal> method has been annotated
+ with the <literal>@PermissionDiscriminator</literal> annotation, to allow <literal>JpaPermissionStore</literal> to
+ determine which records represent user permissions and which represent role permissions. In addition, it
+ can also be seen that the <literal>getRecipient()</literal> method is annotated with both
+ <literal>@PermissionUser</literal> and <literal>@PermissionRole</literal> annotations. This is perfectly valid,
+ and simply means that the <literal>recipient</literal> property of the entity will either contain the name
+ of the user or the name of the role, depending on the value of the <literal>discriminator</literal> property.
+ </para>
+
+ </sect4>
+
+ <sect4>
+ <title>Class-specific Permission Configuration</title>
+
+ <para>
+ A further set of class-specific annotations can be used to configure a specific set of allowable permissions
+ for a target class. These permissions can be found in the <literal>org.jboss.seam.annotation.security.permission</literal>
+ package:
+ </para>
+
+ <table>
+ <title>Class Permission Annotations</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Annotation</para>
+ </entry>
+ <entry align="center">
+ <para>Target</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@Permissions</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>TYPE</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ A container annotation, this annotation may contain an array of <literal>@Permission</literal> annotations.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@Permission</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>TYPE</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation defines a single allowable permission action for the target class. Its <literal>action</literal>
+ property must be specified, and an optional <literal>mask</literal> property may also be specified if permission
+ actions are to be persisted as bitmasked values (see next section).
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Here's an example of the above annotations in action. The following class can also be found in the SeamSpace example:
+ </para>
+
+ <programlisting><![CDATA[@Permissions({
+ @Permission(action = "view"),
+ @Permission(action = "comment")
+})
+@Entity
+public class MemberImage implements Serializable {]]></programlisting>
+ <para>
+ This example demonstrates how two allowable permission actions, <literal>view</literal> and <literal>comment</literal>
+ can be declared for the entity class <literal>MemberImage</literal>.
+ </para>
+
+ </sect4>
+
+ <sect4>
+ <title>Permission masks</title>
+
+ <para>
+ By default, multiple permissions for the same target object and recipient will be persisted as a single database record,
+ with the <literal>action</literal> property/column containing a comma-separated list of the granted actions. To reduce
+ the amount of physical storage required to persist a large number of permissions, it is possible to use a bitmasked
+ integer value (instead of a comma-separated list) to store the list of actions.
+ </para>
+
+ <para>
+ For example, if recipient "Bob" is granted both the <literal>view</literal> and <literal>comment</literal> permissions
+ for a particular <literal>MemberImage</literal> instance, then by default the <literal>action</literal> property of the
+ permission entity will contain "<literal>view,comment</literal>", representing the two granted permission actions.
+ Alternatively, if using bitmasked values for the permission actions, as defined like so:
+ </para>
+
+ <programlisting><![CDATA[@Permissions({
+ @Permission(action = "view", mask = 1),
+ @Permission(action = "comment", mask = 2)
+})
+@Entity
+public class MemberImage implements Serializable {]]></programlisting>
+
+ <para>
+ The <literal>action</literal> property will instead simply contain "3" (with both the 1 bit and 2 bit switched on). Obviously
+ for a large number of allowable actions for any particular target class, the storage required for the permission records
+ is greatly reduced by using bitmasked actions.
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Identifier Policy</title>
+
+ <para>
+ When storing or looking up permissions, <literal>JpaPermissionStore</literal> must be able to uniquely identify specific
+ object instances to effectively operate on its permissions. To achieve this, an <emphasis>identifier strategy</emphasis>
+ may be assigned to each target class for the generation of unique identifier values. Each identifier strategy
+ implementation knows how to generate unique identifiers for a particular type of class, and it is a simple matter to
+ create new identifier strategies.
+ </para>
+
+ <para>
+ The <literal>IdentifierStrategy</literal> interface is very simple, declaring only two methods:
+ </para>
+
+ <programlisting><![CDATA[public interface IdentifierStrategy {
+ boolean canIdentify(Class targetClass);
+ String getIdentifier(Object target);
+}]]></programlisting>
+
+ <para>
+ The first method, <literal>canIdentify()</literal> simply returns <literal>true</literal> if the identifier strategy
+ is capable of generating a unique identifier for the specified target class. The second method,
+ <literal>getIdentifier()</literal> returns the unique identifier value for the specified target object.
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Overview</title>
+
+ <para>
+ So how does persistent permission data actually look when stored in the database?
+ </para>
+ </sect4>
+ </sect3>
+
</sect2>
</sect1>
+
+ <sect1>
+ <title>Permission Management</title>
+
+ <para>
+ In much the same way that Seam Security provides an Identity Management API for the management of users and roles,
+ it also provides a Permissions Management API for the management of persistent user permissions.
+ </para>
+
+ </sect1>
<sect1>
<title>SSL Security</title>
[View Less]
16 years, 7 months
Seam SVN: r8484 - trunk/src/wicket/org/jboss/seam/wicket/ioc.
by seam-commits@lists.jboss.org
Author: pete.muir(a)jboss.org
Date: 2008-07-19 11:48:26 -0400 (Sat, 19 Jul 2008)
New Revision: 8484
Added:
trunk/src/wicket/org/jboss/seam/wicket/ioc/StatelessInterceptor.java
Removed:
trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java
Modified:
trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java
trunk/src/wicket/…
[View More]org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java
Log:
Do more at deployment time
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -3,7 +3,7 @@
import org.jboss.seam.wicket.WicketComponent;
-public class BijectionInterceptor<T> implements Interceptor<T>
+public class BijectionInterceptor<T> implements StatelessInterceptor<T>
{
public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -20,21 +20,12 @@
import org.jboss.seam.pageflow.Pageflow;
import org.jboss.seam.persistence.PersistenceContexts;
-public class ConversationInterceptor<T> implements Interceptor<T>
+public class ConversationInterceptor<T> implements StatelessInterceptor<T>
{
- private boolean isConversationManagement(InvocationContext<T> invocationContext)
- {
- return invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) ||
- invocationContext.getAccessibleObject().isAnnotationPresent(End.class) ||
- invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) ||
- invocationContext.getAccessibleObject().isAnnotationPresent(EndTask.class) ||
- invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class);
- }
-
public void beforeInvoke(InvocationContext<T> invocationContext)
{
- if (isConversationManagement(invocationContext))
+ if (invocationContext.getComponent().isConversationManagementMethod(invocationContext.getAccessibleObject()))
{
if ( isMissingJoin(invocationContext) )
{
@@ -51,7 +42,7 @@
public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
{
- if (isConversationManagement(invocationContext))
+ if (invocationContext.getComponent().isConversationManagementMethod(invocationContext.getAccessibleObject()))
{
beginConversationIfNecessary(invocationContext, result);
endConversationIfNecessary(invocationContext, result);
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -3,7 +3,7 @@
import org.jboss.seam.annotations.RaiseEvent;
import org.jboss.seam.core.Events;
-public class EventInterceptor<T> implements Interceptor<T>
+public class EventInterceptor<T> implements StatelessInterceptor<T>
{
public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
Deleted: trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -1,15 +0,0 @@
-package org.jboss.seam.wicket.ioc;
-
-import java.io.Serializable;
-
-
-public interface Interceptor<T> extends Serializable
-{
-
- public void beforeInvoke(InvocationContext<T> invocationContext);
-
- public Object afterInvoke(InvocationContext<T> invocationContext, Object result);
-
- public Exception handleException(InvocationContext<T> invocationContext, Exception exception);
-
-}
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -11,7 +11,6 @@
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
-import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.Modifier;
@@ -138,7 +137,7 @@
CtClass instrumentedComponent = classPool.get(InstrumentedComponent.class.getName());
implementation.addInterface(instrumentedComponent);
CtMethod getHandlerMethod = CtNewMethod.getter("getHandler", handlerField);
- CtMethod getEnclosingInstance = CtNewMethod.make("public " + InstrumentedComponent.class.getName() +" getEnclosingInstance() { return " + WicketHandler.class.getName() + ".getEnclosingInstance(this, 10); }", implementation);
+ CtMethod getEnclosingInstance = CtNewMethod.make("public " + InstrumentedComponent.class.getName() +" getEnclosingInstance() { return handler.getEnclosingInstance(this); }", implementation);
implementation.addMethod(getEnclosingInstance);
implementation.addMethod(getHandlerMethod);
@@ -230,16 +229,6 @@
src += "java.lang.reflect.Constructor constructor = this.getClass().getDeclaredConstructor(parameterTypes);";
return src;
}
-
- private static String createParametersArray(CtBehavior behavior) throws NotFoundException
- {
- String src = "Object[] parameters = new Object[" + behavior.getParameterTypes().length + "];";
- for (int i = 0; i < behavior.getParameterTypes().length; i++)
- {
- src += "parameters[" + i + "] = $" + i + ";";
- }
- return src;
- }
private static boolean isInstrumentable(CtClass clazz)
{
Copied: trunk/src/wicket/org/jboss/seam/wicket/ioc/StatelessInterceptor.java (from rev 8481, trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java)
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/StatelessInterceptor.java (rev 0)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/StatelessInterceptor.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -0,0 +1,15 @@
+package org.jboss.seam.wicket.ioc;
+
+import java.io.Serializable;
+
+
+public interface StatelessInterceptor<T> extends Serializable
+{
+
+ public void beforeInvoke(InvocationContext<T> invocationContext);
+
+ public Object afterInvoke(InvocationContext<T> invocationContext, Object result);
+
+ public Exception handleException(InvocationContext<T> invocationContext, Exception exception);
+
+}
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-19 15:48:10 UTC (rev 8483)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-19 15:48:26 UTC (rev 8484)
@@ -2,8 +2,8 @@
import java.io.Serializable;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.List;
import org.jboss.seam.wicket.WicketComponent;
@@ -23,7 +23,6 @@
this.type = type;
}
- private List<Interceptor> interceptors;
private Class<?> type;
private transient WicketComponent component;
private int reentrant = 0;
@@ -37,18 +36,6 @@
return component;
}
- private List<Interceptor> getInterceptors()
- {
- if (interceptors == null)
- {
- interceptors = new ArrayList<Interceptor>();
- interceptors.add(new BijectionInterceptor());
- interceptors.add(new ConversationInterceptor());
- interceptors.add(new EventInterceptor());
- }
- return interceptors;
- }
-
public void beforeInvoke(Object target, Method calledMethod)
{
doBeforeInvoke(new InvocationContext(calledMethod, target, getComponent()));
@@ -74,7 +61,7 @@
{
if (reentrant == 0)
{
- for (Interceptor interceptor : getInterceptors())
+ for (StatelessInterceptor interceptor : (List<StatelessInterceptor>) getComponent().getInterceptors())
{
interceptor.beforeInvoke(invocationContext);
}
@@ -97,7 +84,7 @@
reentrant--;
if (reentrant == 0)
{
- for (Interceptor interceptor : getInterceptors())
+ for (StatelessInterceptor interceptor : (List<StatelessInterceptor>) getComponent().getInterceptors())
{
exception = interceptor.handleException(invocationContext, exception);
}
@@ -110,9 +97,9 @@
reentrant--;
if (reentrant == 0)
{
- for (int i = interceptors.size() - 1; i >= 0; i--)
+ for (int i = getComponent().getInterceptors().size() - 1; i >= 0; i--)
{
- result = interceptors.get(i).afterInvoke(invocationContext, result);
+ result = ((StatelessInterceptor) getComponent().getInterceptors().get(i)).afterInvoke(invocationContext, result);
}
}
return result;
@@ -123,14 +110,14 @@
return reentrant > 0;
}
- public static InstrumentedComponent getEnclosingInstance(Object bean, int level)
+ public InstrumentedComponent getEnclosingInstance(Object bean)
{
- Class enclosingType = bean.getClass().getEnclosingClass();
+ Class enclosingType = getComponent().getClass();
if (enclosingType != null)
{
try
{
- java.lang.reflect.Field enclosingField = bean.getClass().getDeclaredField("this$" + level);
+ Field enclosingField = bean.getClass().getDeclaredField(getComponent().getEnclosingInstanceVariableName());
enclosingField.setAccessible(true);
Object enclosingInstance = enclosingField.get(bean);
if (enclosingInstance instanceof InstrumentedComponent)
@@ -138,19 +125,9 @@
return (InstrumentedComponent) enclosingInstance;
}
}
- catch (Exception e)
- {
- if (level == 0)
- {
- return null;
- }
- else
- {
- return getEnclosingInstance(bean, level -1);
- }
- }
+ catch (Exception e) {}
}
return null;
}
-
+
}
[View Less]
16 years, 7 months
Seam SVN: r8483 - trunk/src/wicket/org/jboss/seam/wicket.
by seam-commits@lists.jboss.org
Author: pete.muir(a)jboss.org
Date: 2008-07-19 11:48:10 -0400 (Sat, 19 Jul 2008)
New Revision: 8483
Modified:
trunk/src/wicket/org/jboss/seam/wicket/WicketComponent.java
Log:
Do more at deployment time
Modified: trunk/src/wicket/org/jboss/seam/wicket/WicketComponent.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/WicketComponent.java 2008-07-19 15:02:45 UTC (rev 8482)
+++ trunk/src/wicket/org/jboss/seam/wicket/…
[View More]WicketComponent.java 2008-07-19 15:48:10 UTC (rev 8483)
@@ -3,30 +3,44 @@
import static org.jboss.seam.ScopeType.STATELESS;
import static org.jboss.seam.ScopeType.UNSPECIFIED;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.jboss.seam.Component;
import org.jboss.seam.Namespace;
import org.jboss.seam.RequiredException;
import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Out;
+import org.jboss.seam.annotations.RaiseEvent;
+import org.jboss.seam.annotations.bpm.BeginTask;
+import org.jboss.seam.annotations.bpm.EndTask;
+import org.jboss.seam.annotations.bpm.StartTask;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Expressions;
import org.jboss.seam.core.Init;
import org.jboss.seam.log.Log;
+import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
-import org.jboss.seam.util.Reflections;
import org.jboss.seam.wicket.ioc.BijectedAttribute;
import org.jboss.seam.wicket.ioc.BijectedField;
import org.jboss.seam.wicket.ioc.BijectedMethod;
+import org.jboss.seam.wicket.ioc.BijectionInterceptor;
+import org.jboss.seam.wicket.ioc.ConversationInterceptor;
+import org.jboss.seam.wicket.ioc.EventInterceptor;
import org.jboss.seam.wicket.ioc.InjectedAttribute;
import org.jboss.seam.wicket.ioc.InjectedField;
+import org.jboss.seam.wicket.ioc.StatelessInterceptor;
public class WicketComponent<T>
{
@@ -60,14 +74,23 @@
}
}
- private static Log log = Logging.getLog(WicketComponent.class);
+ private static LogProvider log = Logging.getLogProvider(WicketComponent.class);
private Class<? extends T> type;
+ private Class<?> enclosingType;
+ private String enclosingInstanceVariableName;
+
private List<BijectedAttribute<In>> inAttributes = new ArrayList<BijectedAttribute<In>>();
private List<BijectedAttribute<Out>> outAttributes = new ArrayList<BijectedAttribute<Out>>();
private List<InjectedLogger> loggerFields = new ArrayList<InjectedLogger>();
+ private Set<AccessibleObject> conversationManagementMembers = new HashSet<AccessibleObject>();
+
+ private List<StatelessInterceptor<T>> interceptors = new ArrayList<StatelessInterceptor<T>>();
+
+ boolean anyMethodHasRaiseEvent = false;
+
public Class<?> getType()
{
return type;
@@ -86,6 +109,26 @@
}
}
+
+ private void initInterceptors()
+ {
+ // TODO Add a check to see whether we really need this
+ interceptors.add(new BijectionInterceptor());
+ if (!conversationManagementMembers.isEmpty())
+ {
+ interceptors.add(new ConversationInterceptor());
+ }
+ if (anyMethodHasRaiseEvent)
+ {
+ interceptors.add(new EventInterceptor());
+ }
+ }
+
+ public List<StatelessInterceptor<T>> getInterceptors()
+ {
+ return interceptors;
+ }
+
public static String getContextVariableName(Class<?> type)
{
return type.getName() + ".wicketComponent";
@@ -99,8 +142,29 @@
public WicketComponent(Class<? extends T> type)
{
this.type = type;
- log.info("Class: #0", type);
+ this.enclosingType = type.getEnclosingClass();
+ if (enclosingType != null)
+ {
+ Class<?> c = enclosingType.getEnclosingClass();
+ int i = 0;
+ while (c != null)
+ {
+ c = c.getEnclosingClass();
+ i++;
+ }
+ this.enclosingInstanceVariableName = "this$" + i;
+ log.info("Class: " + type + ", enclosed by " + enclosingType);
+ log.trace("[" + type + "] enclosing instance variable: " + enclosingInstanceVariableName);
+ }
+ else
+ {
+ log.info("Class: " + type);
+ }
+
scan();
+
+ initInterceptors();
+
Contexts.getApplicationContext().set(getName(), this);
}
@@ -115,6 +179,10 @@
{
add(field);
}
+ for(Constructor<T> constructor : clazz.getDeclaredConstructors())
+ {
+ add(constructor);
+ }
}
public void outject(T target)
@@ -188,6 +256,18 @@
}
}
+ private void add(Constructor<T> constructor)
+ {
+ if ( constructor.isAnnotationPresent(Begin.class) ||
+ constructor.isAnnotationPresent(End.class) ||
+ constructor.isAnnotationPresent(StartTask.class) ||
+ constructor.isAnnotationPresent(BeginTask.class) ||
+ constructor.isAnnotationPresent(EndTask.class) )
+ {
+ conversationManagementMembers.add(constructor);
+ }
+ }
+
private void add(Method method)
{
if ( method.isAnnotationPresent(In.class) )
@@ -218,6 +298,20 @@
});
}
+
+ if ( method.isAnnotationPresent(Begin.class) ||
+ method.isAnnotationPresent(End.class) ||
+ method.isAnnotationPresent(StartTask.class) ||
+ method.isAnnotationPresent(BeginTask.class) ||
+ method.isAnnotationPresent(EndTask.class) )
+ {
+ conversationManagementMembers.add(method);
+ }
+
+ if (method.isAnnotationPresent(RaiseEvent.class))
+ {
+ anyMethodHasRaiseEvent = true;
+ }
}
private void add(Field field)
@@ -341,10 +435,26 @@
}
}
+ public Class<?> getEnclosingType()
+ {
+ return enclosingType;
+ }
+
+ public String getEnclosingInstanceVariableName()
+ {
+ return enclosingInstanceVariableName;
+ }
+
@Override
public String toString()
{
return "WicketComponent(" + type + ")";
}
+ public boolean isConversationManagementMethod(AccessibleObject member)
+ {
+ return member!=null &&
+ conversationManagementMembers.contains(member);
+ }
+
}
[View Less]
16 years, 7 months
Seam SVN: r8482 - in trunk: examples/wicket/src/web/org/jboss/seam/example/wicket and 2 other directories.
by seam-commits@lists.jboss.org
Author: pete.muir(a)jboss.org
Date: 2008-07-19 11:02:45 -0400 (Sat, 19 Jul 2008)
New Revision: 8482
Added:
trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java
Modified:
trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBooking.java
trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBookingAction.java
trunk/examples/wicket/src/action/org/jboss/seam/…
[View More]example/wicket/action/Register.java
trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/RegisterAction.java
trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Book.java
trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Confirm.java
trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Hotel.java
trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Main.java
trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Register.java
trunk/src/wicket/org/jboss/seam/wicket/SeamWebApplication.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java
Log:
Add support for conversation control, task/process control and events
Modified: trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBooking.java
===================================================================
--- trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBooking.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBooking.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -6,7 +6,7 @@
@Local
public interface HotelBooking
{
- public Hotel selectHotel(Long hotelId);
+ public void selectHotel(Hotel hotel);
public void bookHotel();
Modified: trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBookingAction.java
===================================================================
--- trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBookingAction.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/HotelBookingAction.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -10,14 +10,12 @@
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
-import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.security.Restrict;
-import org.jboss.seam.core.Events;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.log.Log;
@@ -42,21 +40,15 @@
@In(create=true)
private StatusMessages statusMessages;
-
- @In
- private Events events;
@Logger
private Log log;
private boolean bookingValid;
- // TODO Use outjection
- @Begin
- public Hotel selectHotel(Long hotelId)
+ public void selectHotel(Hotel hotel)
{
- hotel = em.find(Hotel.class, hotelId);
- return hotel;
+ this.hotel = hotel;
}
public void bookHotel()
@@ -99,7 +91,6 @@
em.persist(booking);
statusMessages.add("Thank you, #{user.name}, your confimation number for #{hotel.name} is #{booking.id}");
log.info("New booking: #{booking.id} for #{user.username}");
- events.raiseTransactionSuccessEvent("bookingConfirmed");
}
@End
Modified: trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/Register.java
===================================================================
--- trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/Register.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/Register.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -8,8 +8,6 @@
{
public void register();
public void invalid();
- public String getVerify();
- public void setVerify(String verify);
public boolean isRegistered();
public void destroy();
Modified: trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/RegisterAction.java
===================================================================
--- trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/RegisterAction.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/action/org/jboss/seam/example/wicket/action/RegisterAction.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -29,6 +29,7 @@
@In(create=true)
private StatusMessages statusMessages;
+ @In
private String verify;
private boolean registered;
@@ -66,14 +67,7 @@
{
return registered;
}
- public String getVerify()
- {
- return verify;
- }
- public void setVerify(String verify)
- {
- this.verify = verify;
- }
+
@Remove
public void destroy() {}
Modified: trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Book.java
===================================================================
--- trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Book.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Book.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -32,6 +32,7 @@
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.model.PropertyModel;
+import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.wicket.action.Booking;
@@ -114,6 +115,7 @@
{
@Override
+ @End
public void onClick()
{
setResponsePage(Main.class);
Modified: trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Confirm.java
===================================================================
--- trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Confirm.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Confirm.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -10,6 +10,7 @@
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.RaiseEvent;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.example.wicket.action.Booking;
@@ -45,6 +46,7 @@
body.add(new Link("confirm")
{
@Override
+ @RaiseEvent("bookingConfirmed")
public void onClick()
{
hotelBooking.confirm();
Modified: trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Hotel.java
===================================================================
--- trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Hotel.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Hotel.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -4,6 +4,7 @@
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.link.Link;
+import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.wicket.action.HotelBooking;
@@ -12,6 +13,7 @@
public class Hotel extends WebPage
{
+ @In
private org.jboss.seam.example.wicket.action.Hotel hotel;
@In(create=true)
@@ -33,14 +35,13 @@
body.add(new Link("cancel")
{
@Override
+ @End
public void onClick()
{
- hotelBooking.cancel();
setResponsePage(Main.class);
}
});
- initHotel();
body.add(new HotelViewPanel("hotel", hotel));
add(body);
}
@@ -48,17 +49,8 @@
@Override
protected void onBeforeRender()
{
- initHotel();
super.onBeforeRender();
}
- private void initHotel()
- {
- if (hotel == null)
- {
- hotel = hotelBooking.selectHotel(getPageParameters().getLong("hotelId"));
- }
- }
-
}
Modified: trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Main.java
===================================================================
--- trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Main.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Main.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -31,17 +31,18 @@
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.model.PropertyModel;
+import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.wicket.action.Booking;
import org.jboss.seam.example.wicket.action.BookingList;
import org.jboss.seam.example.wicket.action.Hotel;
+import org.jboss.seam.example.wicket.action.HotelBooking;
import org.jboss.seam.example.wicket.action.HotelSearching;
import org.jboss.seam.security.Identity;
import org.jboss.seam.wicket.SeamPropertyModel;
@@ -58,6 +59,9 @@
@In(create=true)
private BookingList bookingList;
+
+ @In(create=true)
+ private HotelBooking hotelBooking;
private DataView hotelDataView;
private DataView bookedHotelDataView;
@@ -113,12 +117,24 @@
@Override
protected void populateItem(Item item)
{
- Hotel hotel = (Hotel) item.getModelObject();
+ final Hotel hotel = (Hotel) item.getModelObject();
item.add(new Label("hotelName", hotel.getName()));
item.add(new Label("hotelAddress", hotel.getAddress()));
item.add(new Label("hotelCityStateCountry", hotel.getCity() + ", " + hotel.getState() + ", " + hotel.getCountry()));
item.add(new Label("hotelZip", hotel.getZip()));
- item.add(new BookmarkablePageLink("viewHotel", org.jboss.seam.example.wicket.Hotel.class).setParameter("hotelId", hotel.getId()));
+ //item.add(new BookmarkablePageLink("viewHotel", org.jboss.seam.example.wicket.Hotel.class).setParameter("hotelId", hotel.getId()));
+ item.add(new Link("viewHotel")
+ {
+
+ @Override
+ @Begin
+ public void onClick()
+ {
+ hotelBooking.selectHotel(hotel);
+ setResponsePage(org.jboss.seam.example.wicket.Hotel.class);
+ }
+
+ });
}
};
Modified: trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Register.java
===================================================================
--- trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Register.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/examples/wicket/src/web/org/jboss/seam/example/wicket/Register.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -10,7 +10,9 @@
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
+import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Out;
import org.jboss.seam.example.wicket.action.User;
import org.jboss.seam.wicket.SeamPropertyModel;
@@ -36,6 +38,19 @@
public class RegisterForm extends Form
{
+ @Out(scope=ScopeType.EVENT, required=false)
+ private String verify;
+
+ public String getVerify()
+ {
+ return verify;
+ }
+
+ public void setVerify(String verify)
+ {
+ this.verify = verify;
+ }
+
public RegisterForm(String id)
{
super(id);
@@ -70,14 +85,7 @@
return user;
}
}));
- add(new FormInputBorder("verifyDecorate", "Verify Password", verify, new SeamPropertyModel("verify")
- {
- @Override
- public Object getTarget()
- {
- return register;
- }
- }));
+ add(new FormInputBorder("verifyDecorate", "Verify Password", verify, new PropertyModel(this, "verify")));
add(new EqualPasswordInputValidator(password, verify));
}
Modified: trunk/src/wicket/org/jboss/seam/wicket/SeamWebApplication.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/SeamWebApplication.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/src/wicket/org/jboss/seam/wicket/SeamWebApplication.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -91,18 +91,13 @@
@Override
protected CharSequence encode(RequestCycle requestCycle, IBookmarkablePageRequestTarget requestTarget)
{
- if (requestCycle.getRequest().getParameter("cid") != null)
+ // TODO Do this nicely
+ StringBuilder stringBuilder = new StringBuilder(super.encode(requestCycle, requestTarget));
+ if (Manager.instance().isLongRunningConversation())
{
- // TODO Do this nicely
- StringBuilder stringBuilder = new StringBuilder(super.encode(requestCycle, requestTarget));
- if (Manager.instance().isReallyLongRunningConversation())
- {
- stringBuilder.append("&" + Manager.instance().getConversationIdParameter() + "=" + Conversation.instance().getId());
- }
- return stringBuilder.subSequence(0, stringBuilder.length());
-
+ stringBuilder.append("&" + Manager.instance().getConversationIdParameter() + "=" + Conversation.instance().getId());
}
- return super.encode(requestCycle, requestTarget);
+ return stringBuilder.subSequence(0, stringBuilder.length());
}
};
Added: trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java (rev 0)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -0,0 +1,283 @@
+package org.jboss.seam.wicket.ioc;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.jboss.seam.annotations.ApplicationException;
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.End;
+import org.jboss.seam.annotations.FlushModeType;
+import org.jboss.seam.annotations.bpm.BeginTask;
+import org.jboss.seam.annotations.bpm.EndTask;
+import org.jboss.seam.annotations.bpm.StartTask;
+import org.jboss.seam.core.ConversationEntries;
+import org.jboss.seam.core.ConversationEntry;
+import org.jboss.seam.core.ConversationPropagation;
+import org.jboss.seam.core.Interpolator;
+import org.jboss.seam.core.Manager;
+import org.jboss.seam.navigation.ConversationIdParameter;
+import org.jboss.seam.navigation.Pages;
+import org.jboss.seam.pageflow.Pageflow;
+import org.jboss.seam.persistence.PersistenceContexts;
+
+public class ConversationInterceptor<T> implements Interceptor<T>
+{
+
+ private boolean isConversationManagement(InvocationContext<T> invocationContext)
+ {
+ return invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(End.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(EndTask.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class);
+ }
+
+ public void beforeInvoke(InvocationContext<T> invocationContext)
+ {
+ if (isConversationManagement(invocationContext))
+ {
+ if ( isMissingJoin(invocationContext) )
+ {
+ throw new IllegalStateException("begin method invoked from a long-running conversation, try using @Begin(join=true) on method: " + invocationContext.getMember().getName());
+ }
+
+
+ /*if ( redirectToExistingConversation(invocationContext.getMethod()) )
+ {
+ return null;
+ }*/
+ }
+ }
+
+ public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
+ {
+ if (isConversationManagement(invocationContext))
+ {
+ beginConversationIfNecessary(invocationContext, result);
+ endConversationIfNecessary(invocationContext, result);
+ }
+ return result;
+ }
+
+ public Exception handleException(InvocationContext<T> invocationContext, Exception exception)
+ {
+ if ( isEndConversationRequired(exception) )
+ {
+ endConversation(false);
+ }
+ return exception;
+ }
+
+ private boolean isEndConversationRequired(Exception e)
+ {
+ Class<? extends Exception> clazz = e.getClass();
+ return clazz.isAnnotationPresent(ApplicationException.class)
+ && clazz.getAnnotation(ApplicationException.class).end();
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean redirectToExistingConversation(Method method)
+ {
+ if ( !Manager.instance().isLongRunningConversation() )
+ {
+ String id = null;
+ ConversationPropagation propagation = ConversationPropagation.instance();
+ String conversation = propagation != null ? propagation.getConversationName() : null;
+
+ if ( method.isAnnotationPresent(Begin.class) )
+ {
+ id = method.getAnnotation(Begin.class).id();
+ }
+ else if ( method.isAnnotationPresent(BeginTask.class) )
+ {
+ id = method.getAnnotation(BeginTask.class).id();
+ }
+ else if ( method.isAnnotationPresent(StartTask.class) )
+ {
+ id = method.getAnnotation(StartTask.class).id();
+ }
+
+ if ( id!=null && !"".equals(id) )
+ {
+ id = Interpolator.instance().interpolate(id);
+ ConversationEntry ce = ConversationEntries.instance().getConversationEntry(id);
+ if (ce==null)
+ {
+ Manager.instance().updateCurrentConversationId(id);
+ }
+ else
+ {
+ return ce.redirect();
+ }
+ }
+ else if (conversation != null && !"".equals(conversation))
+ {
+ ConversationIdParameter param = Pages.instance().getConversationIdParameter(conversation);
+ if (param != null)
+ {
+ ConversationEntry ce = ConversationEntries.instance().getConversationEntry(param.getConversationId());
+ if (ce != null)
+ {
+ return ce.redirect();
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean isMissingJoin(InvocationContext invocationContext) {
+ return Manager.instance().isLongRunningOrNestedConversation() && (
+ (
+ invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) &&
+ !invocationContext.getAccessibleObject().getAnnotation(Begin.class).join() &&
+ !invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested()
+ ) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class)
+ );
+ }
+
+ @SuppressWarnings("deprecation")
+ private void beginConversationIfNecessary(InvocationContext invocationContext, Object result)
+ {
+
+ boolean simpleBegin =
+ invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class) ||
+ invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) ||
+ ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) && invocationContext.getAccessibleObject().getAnnotation(Begin.class).ifOutcome().length==0 );
+ if ( simpleBegin )
+ {
+ if ( result!=null || ( invocationContext.getMethod() != null && invocationContext.getMethod().getReturnType().equals(void.class)) || invocationContext.getConstructor() != null )
+ {
+ boolean nested = false;
+ if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) )
+ {
+ nested = invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested();
+ }
+ beginConversation( nested, getProcessDefinitionName(invocationContext) );
+ setFlushMode(invocationContext); //TODO: what if conversation already exists? Or a nested conversation?
+ }
+ }
+ else if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) )
+ {
+ String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(Begin.class).ifOutcome();
+ if ( outcomes.length==0 || Arrays.asList(outcomes).contains(result) )
+ {
+ beginConversation(
+ invocationContext.getAccessibleObject().getAnnotation(Begin.class).nested(),
+ getProcessDefinitionName(invocationContext)
+ );
+ setFlushMode(invocationContext); //TODO: what if conversation already exists? Or a nested conversation?
+ }
+ }
+
+ }
+
+ private void setFlushMode(InvocationContext<T> invocationContext)
+ {
+ FlushModeType flushMode;
+ if (invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class))
+ {
+ flushMode = invocationContext.getAccessibleObject().getAnnotation(Begin.class).flushMode();
+ }
+ else if (invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class))
+ {
+ flushMode = invocationContext.getAccessibleObject().getAnnotation(BeginTask.class).flushMode();
+ }
+ else if (invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class))
+ {
+ flushMode = invocationContext.getAccessibleObject().getAnnotation(StartTask.class).flushMode();
+ }
+ else
+ {
+ return;
+ }
+
+ PersistenceContexts.instance().changeFlushMode(flushMode);
+ }
+
+ private String getProcessDefinitionName(InvocationContext invocationContext) {
+ if ( invocationContext.getAccessibleObject().isAnnotationPresent(Begin.class) )
+ {
+ return invocationContext.getAccessibleObject().getAnnotation(Begin.class).pageflow();
+ }
+ if ( invocationContext.getAccessibleObject().isAnnotationPresent(BeginTask.class) )
+ {
+ return invocationContext.getAccessibleObject().getAnnotation(BeginTask.class).pageflow();
+ }
+ if ( invocationContext.getAccessibleObject().isAnnotationPresent(StartTask.class) )
+ {
+ return invocationContext.getAccessibleObject().getAnnotation(StartTask.class).pageflow();
+ }
+ //TODO: let them pass a pageflow name as a request parameter
+ return "";
+ }
+
+ private void beginConversation(boolean nested, String pageflowName)
+ {
+ if ( !Manager.instance().isLongRunningOrNestedConversation() )
+ {
+ Manager.instance().beginConversation( );
+ beginNavigation(pageflowName);
+ }
+ else if (nested)
+ {
+ Manager.instance().beginNestedConversation();
+ beginNavigation(pageflowName);
+ }
+ }
+
+ private void beginNavigation(String pageflowName)
+ {
+ if ( !pageflowName.equals("") )
+ {
+ Pageflow.instance().begin(pageflowName);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void endConversationIfNecessary(InvocationContext<T> invocationContext, Object result)
+ {
+ boolean isEndAnnotation = invocationContext.getAccessibleObject().isAnnotationPresent(End.class);
+ boolean isEndTaskAnnotation = invocationContext.getAccessibleObject().isAnnotationPresent(EndTask.class);
+
+ boolean beforeRedirect = ( isEndAnnotation && invocationContext.getAccessibleObject().getAnnotation(End.class).beforeRedirect() ) ||
+ ( isEndTaskAnnotation && invocationContext.getAccessibleObject().getAnnotation(EndTask.class).beforeRedirect() );
+
+ boolean simpleEnd =
+ ( isEndAnnotation && invocationContext.getAccessibleObject().getAnnotation(End.class).ifOutcome().length==0 ) ||
+ ( isEndTaskAnnotation && invocationContext.getAccessibleObject().getAnnotation(EndTask.class).ifOutcome().length==0 );
+ if ( simpleEnd )
+ {
+ if ( result!=null || invocationContext.getConstructor() != null || (invocationContext.getMethod() != null && invocationContext.getMethod().getReturnType().equals(void.class)) ) //null outcome interpreted as redisplay
+ {
+ endConversation(beforeRedirect);
+ }
+ }
+ else if ( isEndAnnotation )
+ {
+ String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(End.class).ifOutcome();
+ if ( Arrays.asList(outcomes).contains(result) )
+ {
+ endConversation(beforeRedirect);
+ }
+ }
+ else if ( isEndTaskAnnotation )
+ {
+ //TODO: fix minor code duplication
+ String[] outcomes = invocationContext.getAccessibleObject().getAnnotation(EndTask.class).ifOutcome();
+ if ( Arrays.asList(outcomes).contains(result) )
+ {
+ endConversation(beforeRedirect);
+ }
+ }
+ }
+
+ private void endConversation(boolean beforeRedirect)
+ {
+ Manager.instance().endConversation(beforeRedirect);
+ }
+
+}
Property changes on: trunk/src/wicket/org/jboss/seam/wicket/ioc/ConversationInterceptor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java (rev 0)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -0,0 +1,41 @@
+package org.jboss.seam.wicket.ioc;
+
+import org.jboss.seam.annotations.RaiseEvent;
+import org.jboss.seam.core.Events;
+
+public class EventInterceptor<T> implements Interceptor<T>
+{
+
+ public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
+ {
+ if ( result!=null || invocationContext.getConstructor() != null || (invocationContext.getMethod() != null && invocationContext.getMethod().getReturnType().equals(void.class)) )
+ {
+ if ( invocationContext.getAccessibleObject().isAnnotationPresent(RaiseEvent.class) )
+ {
+ String[] types = invocationContext.getAccessibleObject().getAnnotation(RaiseEvent.class).value();
+ if ( types.length==0 )
+ {
+ Events.instance().raiseEvent( invocationContext.getMember().getName() );
+ }
+ else
+ {
+ for (String type: types )
+ {
+ Events.instance().raiseEvent(type);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public void beforeInvoke(InvocationContext<T> invocationContext)
+ {
+
+ }
+
+ public Exception handleException(InvocationContext<T> invocationContext, Exception exception)
+ {
+ return exception;
+ }
+}
\ No newline at end of file
Property changes on: trunk/src/wicket/org/jboss/seam/wicket/ioc/EventInterceptor.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -1,6 +1,8 @@
package org.jboss.seam.wicket.ioc;
+import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import org.jboss.seam.wicket.WicketComponent;
@@ -32,14 +34,49 @@
{
return constructor;
}
+
public Method getMethod()
{
return method;
}
+
+ public Member getMember()
+ {
+ if (method != null)
+ {
+ return method;
+ }
+ else if (constructor != null)
+ {
+ return constructor;
+ }
+ else
+ {
+ throw new IllegalStateException("No member");
+ }
+ }
+
+ public AccessibleObject getAccessibleObject()
+ {
+ if (method != null)
+ {
+ return method;
+ }
+ else if (constructor != null)
+ {
+ return constructor;
+ }
+ else
+ {
+ throw new IllegalStateException("No member");
+ }
+ }
+
public T getBean()
{
return bean;
}
+
public WicketComponent<T> getComponent()
{
return component;
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -152,7 +152,7 @@
CtMethod newMethod = CtNewMethod.copy(method, newName, implementation, null);
implementation.addMethod(newMethod);
- method.setBody(createBody(implementation, newMethod));
+ method.setBody(createBody(implementation, method, newMethod));
log.trace("instrumented method " + method.getName());
}
}
@@ -180,10 +180,10 @@
return classLoader;
}
- private static String createBody(CtClass clazz, CtMethod method) throws NotFoundException
+ private static String createBody(CtClass clazz, CtMethod method, CtMethod newMethod) throws NotFoundException
{
- String src = "{" + createMethodObject(method) + "handler.beforeInvoke(this, method);" + createMethodDelegation(method) + "return ($r) handler.afterInvoke(this, method, ($w) result);}";
- log.trace("Creating method " + clazz.getName() + "." + method.getName() + "(" + method.getSignature() + ")" + src);
+ String src = "{" + createMethodObject(method) + "handler.beforeInvoke(this, method);" + createMethodDelegation(newMethod) + "return ($r) handler.afterInvoke(this, method, ($w) result);}";
+ log.trace("Creating method " + clazz.getName() + "." + newMethod.getName() + "(" + newMethod.getSignature() + ")" + src);
return src;
}
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-19 12:43:17 UTC (rev 8481)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-19 15:02:45 UTC (rev 8482)
@@ -26,7 +26,6 @@
private List<Interceptor> interceptors;
private Class<?> type;
private transient WicketComponent component;
- private boolean callInProgress;
private int reentrant = 0;
private WicketComponent getComponent()
@@ -44,6 +43,8 @@
{
interceptors = new ArrayList<Interceptor>();
interceptors.add(new BijectionInterceptor());
+ interceptors.add(new ConversationInterceptor());
+ interceptors.add(new EventInterceptor());
}
return interceptors;
}
@@ -93,6 +94,7 @@
private Exception doHandleException(InvocationContext invocationContext, Exception exception)
{
+ reentrant--;
if (reentrant == 0)
{
for (Interceptor interceptor : getInterceptors())
[View Less]
16 years, 7 months
Seam SVN: r8481 - trunk/src/wicket/org/jboss/seam/wicket/ioc.
by seam-commits@lists.jboss.org
Author: pete.muir(a)jboss.org
Date: 2008-07-19 08:43:17 -0400 (Sat, 19 Jul 2008)
New Revision: 8481
Added:
trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java
Removed:
trunk/src/wicket/org/jboss/seam/wicket/ioc/RootInterceptor.java
Modified:
trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java
trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
trunk/src/wicket/org/…
[View More]jboss/seam/wicket/ioc/WicketHandler.java
Log:
Add support for intercepting exceptions
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java 2008-07-17 18:49:20 UTC (rev 8480)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/BijectionInterceptor.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -3,18 +3,17 @@
import org.jboss.seam.wicket.WicketComponent;
-public class BijectionInterceptor<T> extends RootInterceptor<T>
+public class BijectionInterceptor<T> implements Interceptor<T>
{
- @Override
- public void afterInvoke(InvocationContext<T> invocationContext)
+ public Object afterInvoke(InvocationContext<T> invocationContext, Object result)
{
invocationContext.getComponent().outject(invocationContext.getBean());
invocationContext.getComponent().disinject(invocationContext.getBean());
disinjectEnclosingInstances(invocationContext);
+ return result;
}
- @Override
public void beforeInvoke(InvocationContext<T> invocationContext)
{
try
@@ -28,12 +27,17 @@
}
}
+ public Exception handleException(InvocationContext<T> invocationContext, Exception exception)
+ {
+ return exception;
+ }
+
private static <T> void injectEnclosingInstances(InvocationContext<T> invocationContext)
{
InstrumentedComponent enclosingInstance = invocationContext.getInstrumentedComponent().getEnclosingInstance();
while (enclosingInstance != null)
{
- if (!enclosingInstance.getHandler().isCallInProgress())
+ if (!enclosingInstance.getHandler().isReentrant())
{
WicketComponent.getInstance(enclosingInstance.getClass()).inject(enclosingInstance);
enclosingInstance = enclosingInstance.getEnclosingInstance();
@@ -50,7 +54,7 @@
InstrumentedComponent enclosingInstance = invocationContext.getInstrumentedComponent().getEnclosingInstance();
while (enclosingInstance != null)
{
- if (!enclosingInstance.getHandler().isCallInProgress())
+ if (!enclosingInstance.getHandler().isReentrant())
{
WicketComponent.getInstance(enclosingInstance.getClass()).disinject(enclosingInstance);
enclosingInstance = enclosingInstance.getEnclosingInstance();
Copied: trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java (from rev 8477, trunk/src/wicket/org/jboss/seam/wicket/ioc/RootInterceptor.java)
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java (rev 0)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/Interceptor.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -0,0 +1,15 @@
+package org.jboss.seam.wicket.ioc;
+
+import java.io.Serializable;
+
+
+public interface Interceptor<T> extends Serializable
+{
+
+ public void beforeInvoke(InvocationContext<T> invocationContext);
+
+ public Object afterInvoke(InvocationContext<T> invocationContext, Object result);
+
+ public Exception handleException(InvocationContext<T> invocationContext, Exception exception);
+
+}
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java 2008-07-17 18:49:20 UTC (rev 8480)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/InvocationContext.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -20,7 +20,7 @@
this.component = component;
}
- public InvocationContext(T bean, WicketComponent<T> component)
+ public InvocationContext(Constructor<T> constructor, T bean, WicketComponent<T> component)
{
// TODO Write the constructor discovery code
this.constructor = constructor;
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-17 18:49:20 UTC (rev 8480)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -6,10 +6,12 @@
import javassist.CannotCompileException;
import javassist.ClassPool;
+import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
+import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.Modifier;
@@ -131,6 +133,8 @@
Initializer handlerInitializer = Initializer.byCall(handlerClass, "create");
implementation.addField(handlerField, handlerInitializer);
+ CtClass exception = classPool.get(Exception.class.getName());
+
CtClass instrumentedComponent = classPool.get(InstrumentedComponent.class.getName());
implementation.addInterface(instrumentedComponent);
CtMethod getHandlerMethod = CtNewMethod.getter("getHandler", handlerField);
@@ -142,34 +146,13 @@
{
if (!Modifier.isStatic(method.getModifiers()))
{
- String methodName = method.getName();
if (!("getHandler".equals(method.getName()) || "getEnclosingInstance".equals(method.getName())))
- {
- String methodSignature = "";
- for (int i = 0; i < method.getParameterTypes().length; i++)
- {
- if (i > 0)
- {
- methodSignature += ",";
- }
- methodSignature += method.getParameterTypes()[i].getName() + ".class";
- }
- String methodCall = "this.getClass().getDeclaredMethod(\""+ methodName + "\", methodParameters)";
- String methodParameters;
- if (methodSignature.length() > 0)
- {
- methodParameters = "Class[] methodParameters = {" + methodSignature + "};";
- }
- else
- {
- methodParameters = "Class[] methodParameters = new Class[0];";
- }
- log.trace("Method call: " + methodCall);
+ {
+ String newName = implementation.makeUniqueName(method.getName());
- method.insertBefore(methodParameters + "handler.beforeInvoke(this, " + methodCall + ");");
- method.insertBefore("handler.setCallInProgress(true);");
- method.insertAfter(methodParameters + "handler.afterInvoke(this, " + methodCall + ");");
- method.insertAfter("handler.setCallInProgress(false);", true);
+ CtMethod newMethod = CtNewMethod.copy(method, newName, implementation, null);
+ implementation.addMethod(newMethod);
+ method.setBody(createBody(implementation, newMethod));
log.trace("instrumented method " + method.getName());
}
}
@@ -178,21 +161,85 @@
{
if (constructor.isConstructor())
{
- constructor.insertBeforeBody("handler.beforeInvoke(this);");
- constructor.insertBeforeBody("handler.setCallInProgress(true);");
- constructor.insertAfter("handler.afterInvoke(this);");
- constructor.insertAfter("handler.setCallInProgress(false);");
- log.trace("instrumented constructor " + constructor.getName());
+ {
+ String constructorObject = createConstructorObject(constructor);
+ constructor.insertBeforeBody(constructorObject + "handler.beforeInvoke(this, constructor);");
+ constructor.addCatch("{" + constructorObject + "throw new RuntimeException(handler.handleException(this, constructor, e));}", exception, "e");
+ constructor.insertAfter(constructorObject + "handler.afterInvoke(this, constructor);");
+ log.trace("instrumented constructor " + constructor.getName());
+ }
}
}
}
classes.add(implementation.getName());
+
}
public ClassLoader getClassLoader()
{
return classLoader;
}
+
+ private static String createBody(CtClass clazz, CtMethod method) throws NotFoundException
+ {
+ String src = "{" + createMethodObject(method) + "handler.beforeInvoke(this, method);" + createMethodDelegation(method) + "return ($r) handler.afterInvoke(this, method, ($w) result);}";
+ log.trace("Creating method " + clazz.getName() + "." + method.getName() + "(" + method.getSignature() + ")" + src);
+ return src;
+ }
+
+ private static String createMethodDelegation(CtMethod method) throws NotFoundException
+ {
+ CtClass returnType = method.getReturnType();
+ if (returnType.equals(CtClass.voidType))
+ {
+ return "Object result = null; " + wrapInExceptionHandler(method.getName() + "($$);");
+ }
+ else
+ {
+ String src = returnType.getName() + " result;";
+ src += wrapInExceptionHandler("result = " + method.getName() + "($$);");
+ return src;
+ }
+ }
+
+ private static String wrapInExceptionHandler(String src)
+ {
+ return "try {" + src + "} catch (Exception e) { throw new RuntimeException(handler.handleException(this, method, e)); }";
+ }
+
+ private static String createParameterTypesArray(CtBehavior behavior) throws NotFoundException
+ {
+ String src = "Class[] parameterTypes = new Class[" + behavior.getParameterTypes().length + "];";
+ for (int i = 0; i < behavior.getParameterTypes().length; i++)
+ {
+ src += "parameterTypes[" + i + "] = " + behavior.getParameterTypes()[i].getName() + ".class;";
+ }
+ return src;
+ }
+
+ private static String createMethodObject(CtMethod method) throws NotFoundException
+ {
+ String src = createParameterTypesArray(method);
+ src += "java.lang.reflect.Method method = this.getClass().getDeclaredMethod(\""+ method.getName() + "\", parameterTypes);";
+ return src;
+ }
+
+ private static String createConstructorObject(CtConstructor constructor) throws NotFoundException
+ {
+ String src = createParameterTypesArray(constructor);
+ src += "java.lang.reflect.Constructor constructor = this.getClass().getDeclaredConstructor(parameterTypes);";
+ return src;
+ }
+
+ private static String createParametersArray(CtBehavior behavior) throws NotFoundException
+ {
+ String src = "Object[] parameters = new Object[" + behavior.getParameterTypes().length + "];";
+ for (int i = 0; i < behavior.getParameterTypes().length; i++)
+ {
+ src += "parameters[" + i + "] = $" + i + ";";
+ }
+ return src;
+ }
private static boolean isInstrumentable(CtClass clazz)
{
Deleted: trunk/src/wicket/org/jboss/seam/wicket/ioc/RootInterceptor.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/RootInterceptor.java 2008-07-17 18:49:20 UTC (rev 8480)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/RootInterceptor.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -1,13 +0,0 @@
-package org.jboss.seam.wicket.ioc;
-
-import java.io.Serializable;
-
-
-public abstract class RootInterceptor<T> implements Serializable
-{
-
- public abstract void beforeInvoke(InvocationContext<T> invocationContext);
-
- public abstract void afterInvoke(InvocationContext<T> invocationContext);
-
-}
Modified: trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java
===================================================================
--- trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-17 18:49:20 UTC (rev 8480)
+++ trunk/src/wicket/org/jboss/seam/wicket/ioc/WicketHandler.java 2008-07-19 12:43:17 UTC (rev 8481)
@@ -23,7 +23,7 @@
this.type = type;
}
- private List<RootInterceptor> interceptors;
+ private List<Interceptor> interceptors;
private Class<?> type;
private transient WicketComponent component;
private boolean callInProgress;
@@ -38,11 +38,11 @@
return component;
}
- private List<RootInterceptor> getInterceptors()
+ private List<Interceptor> getInterceptors()
{
if (interceptors == null)
{
- interceptors = new ArrayList<RootInterceptor>();
+ interceptors = new ArrayList<Interceptor>();
interceptors.add(new BijectionInterceptor());
}
return interceptors;
@@ -50,30 +50,30 @@
public void beforeInvoke(Object target, Method calledMethod)
{
- beforeInvoke(new InvocationContext(calledMethod, target, getComponent()));
+ doBeforeInvoke(new InvocationContext(calledMethod, target, getComponent()));
}
- public void afterInvoke(Object target, Method calledMethod)
+ public Object afterInvoke(Object target, Method calledMethod, Object result)
{
- afterInvoke(new InvocationContext(calledMethod, target, getComponent()));
+ return doAfterInvoke(new InvocationContext(calledMethod, target, getComponent()), result);
}
- public void beforeInvoke(Object target)
+ public void beforeInvoke(Object target, Constructor constructor)
{
getComponent().initialize(target);
- beforeInvoke(new InvocationContext(target, getComponent()));
+ doBeforeInvoke(new InvocationContext(constructor, target, getComponent()));
}
- public void afterInvoke(Object target)
+ public void afterInvoke(Object target, Constructor constructor)
{
- afterInvoke(new InvocationContext(target, getComponent()));
+ doAfterInvoke(new InvocationContext(constructor, target, getComponent()), null);
}
- private void beforeInvoke(InvocationContext invocationContext)
+ private void doBeforeInvoke(InvocationContext invocationContext)
{
- if (reentrant ==0)
+ if (reentrant == 0)
{
- for (RootInterceptor interceptor : getInterceptors())
+ for (Interceptor interceptor : getInterceptors())
{
interceptor.beforeInvoke(invocationContext);
}
@@ -81,30 +81,44 @@
reentrant++;
}
- private void afterInvoke(InvocationContext invocationContext)
+ public Exception handleException(Object target, Method method, Exception exception)
{
- reentrant--;
+ return doHandleException(new InvocationContext(method, target, getComponent()), exception);
+ }
+
+ public Exception handleException(Object target, Constructor constructor, Exception exception)
+ {
+ return doHandleException(new InvocationContext(constructor, target, getComponent()), exception);
+ }
+
+ private Exception doHandleException(InvocationContext invocationContext, Exception exception)
+ {
if (reentrant == 0)
{
- for (RootInterceptor interceptor : getInterceptors())
+ for (Interceptor interceptor : getInterceptors())
{
- interceptor.afterInvoke(invocationContext);
+ exception = interceptor.handleException(invocationContext, exception);
}
}
+ return exception;
}
-
- public boolean isCallInProgress()
+
+ private Object doAfterInvoke(InvocationContext invocationContext, Object result)
{
- if (callInProgress == false)
+ reentrant--;
+ if (reentrant == 0)
{
- reentrant = 0;
+ for (int i = interceptors.size() - 1; i >= 0; i--)
+ {
+ result = interceptors.get(i).afterInvoke(invocationContext, result);
+ }
}
- return callInProgress;
+ return result;
}
-
- public void setCallInProgress(boolean callInProgress)
+
+ public boolean isReentrant()
{
- this.callInProgress = callInProgress;
+ return reentrant > 0;
}
public static InstrumentedComponent getEnclosingInstance(Object bean, int level)
[View Less]
16 years, 7 months