Author: shane.bryzak(a)jboss.com
Date: 2008-10-07 23:50:08 -0400 (Tue, 07 Oct 2008)
New Revision: 9220
Added:
trunk/src/main/org/jboss/seam/util/TypedBeanProperty.java
Modified:
trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java
trunk/src/main/org/jboss/seam/util/AnnotatedBeanProperty.java
Log:
JBSEAM-3466
Modified: trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java 2008-10-07
21:08:44 UTC (rev 9219)
+++ trunk/src/main/org/jboss/seam/security/management/JpaIdentityStore.java 2008-10-08
03:50:08 UTC (rev 9220)
@@ -4,6 +4,8 @@
import static org.jboss.seam.annotations.Install.BUILT_IN;
import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -36,6 +38,7 @@
import org.jboss.seam.log.Logging;
import org.jboss.seam.security.Identity;
import org.jboss.seam.util.AnnotatedBeanProperty;
+import org.jboss.seam.util.TypedBeanProperty;
/**
* The default identity store implementation, uses JPA as its persistence mechanism.
@@ -54,6 +57,8 @@
public static final String EVENT_PRE_PERSIST_USER =
"org.jboss.seam.security.management.prePersistUser";
public static final String EVENT_USER_AUTHENTICATED =
"org.jboss.seam.security.management.userAuthenticated";
+ public static final String EVENT_PRE_PERSIST_USER_ROLE =
"org.jboss.seam.security.management.prePersistUserRole";
+
private static final LogProvider log = Logging.getLogProvider(JpaIdentityStore.class);
protected FeatureSet featureSet;
@@ -62,6 +67,9 @@
private Class userClass;
private Class roleClass;
+ private Class xrefClass;
+ private TypedBeanProperty xrefUserProperty;
+ private TypedBeanProperty xrefRoleProperty;
private AnnotatedBeanProperty<UserPrincipal> userPrincipalProperty;
private AnnotatedBeanProperty<UserPassword> userPasswordProperty;
@@ -119,14 +127,7 @@
userEnabledProperty = new AnnotatedBeanProperty(userClass, UserEnabled.class);
userFirstNameProperty = new AnnotatedBeanProperty(userClass, UserFirstName.class);
userLastNameProperty = new AnnotatedBeanProperty(userClass, UserLastName.class);
-
- if (roleClass != null)
- {
- roleNameProperty = new AnnotatedBeanProperty(roleClass, RoleName.class);
- roleGroupsProperty = new AnnotatedBeanProperty(roleClass, RoleGroups.class);
- roleConditionalProperty = new AnnotatedBeanProperty(roleClass,
RoleConditional.class);
- }
-
+
if (!userPrincipalProperty.isSet())
{
throw new IdentityManagementException("Invalid userClass " +
userClass.getName() +
@@ -137,12 +138,53 @@
{
throw new IdentityManagementException("Invalid userClass " +
userClass.getName() +
" - required annotation @UserRoles not found on any Field or
Method.");
- }
+ }
- if (roleClass != null && !roleNameProperty.isSet())
+ if (roleClass != null)
{
- throw new IdentityManagementException("Invalid roleClass " +
roleClass.getName() +
- " - required annotation @RoleName not found on any Field or Method.");
+ if (!roleNameProperty.isSet())
+ {
+ throw new IdentityManagementException("Invalid roleClass " +
roleClass.getName() +
+ " - required annotation @RoleName not found on any Field or
Method.");
+ }
+
+ roleNameProperty = new AnnotatedBeanProperty(roleClass, RoleName.class);
+ roleGroupsProperty = new AnnotatedBeanProperty(roleClass, RoleGroups.class);
+ roleConditionalProperty = new AnnotatedBeanProperty(roleClass,
RoleConditional.class);
+
+ Type type = userRolesProperty.getPropertyType();
+ if (type instanceof ParameterizedType &&
+ Collection.class.isAssignableFrom((Class) ((ParameterizedType)
type).getRawType()))
+ {
+ Type genType = Object.class;
+
+ for (Type t : ((ParameterizedType) type).getActualTypeArguments())
+ {
+ genType = t;
+ break;
+ }
+
+ // If the @UserRoles property isn't a collection of <roleClass>,
then assume the relationship
+ // is going through a cross-reference table
+ if (!genType.equals(roleClass))
+ {
+ xrefClass = (Class) genType;
+ xrefUserProperty = new TypedBeanProperty(xrefClass, userClass);
+ xrefRoleProperty = new TypedBeanProperty(xrefClass, roleClass);
+
+ if (!xrefUserProperty.isSet())
+ {
+ throw new IdentityManagementException("Error configuring
JpaIdentityStore - it looks like " +
+ "you're using a cross-reference table, however the user
property cannot be determined.");
+ }
+
+ if (!xrefRoleProperty.isSet())
+ {
+ throw new IdentityManagementException("Error configuring
JpaIdentityStore - it looks like " +
+ "you're using a cross-reference table, however the role
property cannot be determined.");
+ }
+ }
+ }
}
}
@@ -259,11 +301,11 @@
if (userRoles == null)
{
// This should either be a Set, or a List...
- if (Set.class.isAssignableFrom(userRolesProperty.getPropertyClass()))
+ if (Set.class.isAssignableFrom((Class) userRolesProperty.getPropertyType()))
{
userRoles = new HashSet();
}
- else if (List.class.isAssignableFrom(userRolesProperty.getPropertyClass()))
+ else if (List.class.isAssignableFrom((Class)
userRolesProperty.getPropertyType()))
{
userRoles = new ArrayList();
}
@@ -275,7 +317,30 @@
return false;
}
- ((Collection) userRolesProperty.getValue(user)).add(roleToGrant);
+ if (xrefClass == null)
+ {
+ // If this is a Many-To-Many relationship, simply add the role
+ ((Collection) userRolesProperty.getValue(user)).add(roleToGrant);
+ }
+ else
+ {
+ // Otherwise we need to insert a cross-reference entity instance
+ try
+ {
+ Object xref = xrefClass.newInstance();
+ xrefUserProperty.setValue(xref, user);
+ xrefRoleProperty.setValue(xref, roleToGrant);
+
+ Events.instance().raiseEvent(EVENT_PRE_PERSIST_USER_ROLE, xref);
+
+ ((Collection) userRolesProperty.getValue(user)).add(mergeEntity(xref));
+ }
+ catch (Exception ex)
+ {
+ throw new IdentityManagementException("Error creating cross-reference
role record.", ex);
+ }
+ }
+
mergeEntity(user);
return true;
@@ -294,9 +359,27 @@
{
throw new NoSuchRoleException("Could not revoke role, role '" +
role + "' does not exist");
}
-
- boolean success = ((Collection)
userRolesProperty.getValue(user)).remove(roleToRevoke);
+
+ boolean success = false;
+ if (xrefClass == null)
+ {
+ success = ((Collection) userRolesProperty.getValue(user)).remove(roleToRevoke);
+ }
+ else
+ {
+ Collection roles = ((Collection) userRolesProperty.getValue(user));
+
+ for (Object xref : roles)
+ {
+ if (xrefRoleProperty.getValue(xref).equals(roleToRevoke))
+ {
+ success = roles.remove(xref);
+ break;
+ }
+ }
+ }
+
if (success) mergeEntity(user);
return success;
}
@@ -322,11 +405,11 @@
if (roleGroups == null)
{
// This should either be a Set, or a List...
- if (Set.class.isAssignableFrom(roleGroupsProperty.getPropertyClass()))
+ if (Set.class.isAssignableFrom((Class) roleGroupsProperty.getPropertyType()))
{
roleGroups = new HashSet();
}
- else if (List.class.isAssignableFrom(roleGroupsProperty.getPropertyClass()))
+ else if (List.class.isAssignableFrom((Class)
roleGroupsProperty.getPropertyType()))
{
roleGroups = new ArrayList();
}
Modified: trunk/src/main/org/jboss/seam/util/AnnotatedBeanProperty.java
===================================================================
--- trunk/src/main/org/jboss/seam/util/AnnotatedBeanProperty.java 2008-10-07 21:08:44 UTC
(rev 9219)
+++ trunk/src/main/org/jboss/seam/util/AnnotatedBeanProperty.java 2008-10-08 03:50:08 UTC
(rev 9220)
@@ -4,6 +4,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.Type;
/**
* A convenience class for working with an annotated property (either a field or method)
of
@@ -17,7 +18,7 @@
private Method propertyGetter;
private Method propertySetter;
private String name;
- private Class propertyClass;
+ private Type propertyType;
private T annotation;
private boolean isFieldProperty;
@@ -70,7 +71,7 @@
{
this.propertyGetter = Reflections.getGetterMethod(cls, this.name);
this.propertySetter = Reflections.getSetterMethod(cls, this.name);
- this.propertyClass = this.propertyGetter.getReturnType();
+ this.propertyType = this.propertyGetter.getGenericReturnType();
isFieldProperty = false;
set = true;
}
@@ -88,7 +89,7 @@
this.propertyField = propertyField;
isFieldProperty = true;
this.name = propertyField.getName();
- this.propertyClass = propertyField.getDeclaringClass();
+ this.propertyType = propertyField.getGenericType();
}
public void setValue(Object bean, Object value)
@@ -125,9 +126,9 @@
return annotation;
}
- public Class getPropertyClass()
+ public Type getPropertyType()
{
- return propertyClass;
+ return propertyType;
}
public boolean isSet()
Added: trunk/src/main/org/jboss/seam/util/TypedBeanProperty.java
===================================================================
--- trunk/src/main/org/jboss/seam/util/TypedBeanProperty.java (rev
0)
+++ trunk/src/main/org/jboss/seam/util/TypedBeanProperty.java 2008-10-08 03:50:08 UTC (rev
9220)
@@ -0,0 +1,109 @@
+package org.jboss.seam.util;
+
+import java.beans.Introspector;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class TypedBeanProperty
+{
+ private Field propertyField;
+ private Method propertyGetter;
+ private Method propertySetter;
+
+ private String name;
+
+ private boolean isFieldProperty;
+ private boolean set = false;
+
+ public TypedBeanProperty(Class<?> cls, Class type)
+ {
+ // First check declared fields
+ for (Field f : cls.getDeclaredFields())
+ {
+ if (f.getGenericType().equals(type))
+ {
+ setupFieldProperty(f);
+ set = true;
+ return;
+ }
+ }
+
+ // Then check public fields, in case it's inherited
+ for (Field f : cls.getFields())
+ {
+ if (f.getGenericType().equals(type))
+ {
+ setupFieldProperty(f);
+ set = true;
+ return;
+ }
+ }
+
+ // Then check public methods (we ignore private methods)
+ for (Method m : cls.getMethods())
+ {
+ if (m.getGenericReturnType().equals(type))
+ {
+ String methodName = m.getName();
+
+ if ( m.getName().startsWith("get") )
+ {
+ this.name = Introspector.decapitalize( m.getName().substring(3) );
+ }
+ else if ( methodName.startsWith("is") )
+ {
+ this.name = Introspector.decapitalize( m.getName().substring(2) );
+ }
+
+ if (this.name != null)
+ {
+ this.propertyGetter = Reflections.getGetterMethod(cls, this.name);
+ this.propertySetter = Reflections.getSetterMethod(cls, this.name);
+ isFieldProperty = false;
+ set = true;
+ }
+ else
+ {
+ throw new IllegalStateException("Invalid accessor method, must start
with 'get' or 'is'. " +
+ "Method: " + m + " in class: " + cls);
+ }
+ }
+ }
+ }
+
+ private void setupFieldProperty(Field propertyField)
+ {
+ this.propertyField = propertyField;
+ isFieldProperty = true;
+ this.name = propertyField.getName();
+ }
+
+ public void setValue(Object bean, Object value)
+ {
+ if (isFieldProperty)
+ {
+ Reflections.setAndWrap(propertyField, bean, value);
+ }
+ else
+ {
+ Reflections.invokeAndWrap(propertySetter, bean, value);
+ }
+ }
+
+ public Object getValue(Object bean)
+ {
+ if (isFieldProperty)
+ {
+ return Reflections.getAndWrap(propertyField, bean);
+ }
+ else
+ {
+ return Reflections.invokeAndWrap(propertyGetter, bean);
+ }
+ }
+
+ public boolean isSet()
+ {
+ return set;
+ }
+}