Package org.jboss.seam.security.management.picketlink

Source Code of org.jboss.seam.security.management.picketlink.JpaIdentityStore

package org.jboss.seam.security.management.picketlink;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.jboss.seam.security.annotations.management.IdentityProperty;
import org.jboss.seam.security.annotations.management.PropertyType;
import org.jboss.seam.security.management.IdentityObjectImpl;
import org.jboss.seam.security.management.IdentityObjectRelationshipImpl;
import org.jboss.seam.security.management.IdentityObjectRelationshipTypeImpl;
import org.jboss.seam.security.management.IdentityObjectTypeImpl;
import org.jboss.seam.solder.properties.Property;
import org.jboss.seam.solder.properties.query.AnnotatedPropertyCriteria;
import org.jboss.seam.solder.properties.query.NamedPropertyCriteria;
import org.jboss.seam.solder.properties.query.PropertyCriteria;
import org.jboss.seam.solder.properties.query.PropertyQueries;
import org.jboss.seam.solder.properties.query.TypedPropertyCriteria;
import org.jboss.seam.solder.reflection.Reflections;
import org.picketlink.idm.common.exception.IdentityException;
import org.picketlink.idm.impl.api.SimpleAttribute;
import org.picketlink.idm.impl.store.FeaturesMetaDataImpl;
import org.picketlink.idm.spi.configuration.IdentityStoreConfigurationContext;
import org.picketlink.idm.spi.configuration.metadata.IdentityObjectAttributeMetaData;
import org.picketlink.idm.spi.exception.OperationNotSupportedException;
import org.picketlink.idm.spi.model.IdentityObject;
import org.picketlink.idm.spi.model.IdentityObjectAttribute;
import org.picketlink.idm.spi.model.IdentityObjectCredential;
import org.picketlink.idm.spi.model.IdentityObjectRelationship;
import org.picketlink.idm.spi.model.IdentityObjectRelationshipType;
import org.picketlink.idm.spi.model.IdentityObjectType;
import org.picketlink.idm.spi.search.IdentityObjectSearchCriteria;
import org.picketlink.idm.spi.store.FeaturesMetaData;
import org.picketlink.idm.spi.store.IdentityObjectSearchCriteriaType;
import org.picketlink.idm.spi.store.IdentityStoreInvocationContext;
import org.picketlink.idm.spi.store.IdentityStoreSession;

/**
* IdentityStore implementation that allows identity related data to be
* persisted in a database via JPA
* @author Shane Bryzak
*/
public class JpaIdentityStore implements org.picketlink.idm.spi.store.IdentityStore, Serializable
{
   private static final long serialVersionUID = 7729139146633529501L;
  
   public static final String OPTION_IDENTITY_CLASS_NAME = "identityEntityClassName";
   public static final String OPTION_CREDENTIAL_CLASS_NAME = "credentialEntityClassName";
   public static final String OPTION_RELATIONSHIP_CLASS_NAME = "relationshipEntityClassName";
   public static final String OPTION_ROLE_TYPE_CLASS_NAME = "roleTypeEntityClassName";
   public static final String OPTION_ATTRIBUTE_CLASS_NAME = "attributeEntityClassName";
  
   private static final String DEFAULT_USER_IDENTITY_TYPE = "USER";
   private static final String DEFAULT_ROLE_IDENTITY_TYPE = "ROLE";
   private static final String DEFAULT_GROUP_IDENTITY_TYPE = "GROUP";  
  
   private static final String DEFAULT_RELATIONSHIP_TYPE_MEMBERSHIP = "MEMBERSHIP";
   private static final String DEFAULT_RELATIONSHIP_TYPE_ROLE = "ROLE";

   // Property keys
  
   private static final String PROPERTY_IDENTITY_ID = "IDENTITY_ID";
   private static final String PROPERTY_IDENTITY_NAME = "IDENTITY_NAME";
   private static final String PROPERTY_IDENTITY_TYPE = "IDENTITY_TYPE";
   private static final String PROPERTY_IDENTITY_TYPE_NAME = "IDENTITY_TYPE_NAME";
   private static final String PROPERTY_CREDENTIAL_VALUE = "CREDENTIAL_VALUE";
   private static final String PROPERTY_CREDENTIAL_TYPE = "CREDENTIAL_TYPE";
   private static final String PROPERTY_CREDENTIAL_TYPE_NAME = "CREDENTIAL_TYPE_NAME";
   private static final String PROPERTY_CREDENTIAL_IDENTITY = "CREDENTIAL_IDENTITY";
   private static final String PROPERTY_RELATIONSHIP_FROM = "RELATIONSHIP_FROM";
   private static final String PROPERTY_RELATIONSHIP_TO = "RELATIONSHIP_TO";
   private static final String PROPERTY_RELATIONSHIP_TYPE = "RELATIONSHIP_TYPE";
   private static final String PROPERTY_RELATIONSHIP_TYPE_NAME = "RELATIONSHIP_TYPE_NAME";
   private static final String PROPERTY_RELATIONSHIP_NAME = "RELATIONSHIP_NAME";

   private static final String PROPERTY_ROLE_TYPE_NAME = "RELATIONSHIP_NAME_NAME";
  
   private static final String PROPERTY_ATTRIBUTE_NAME = "ATTRIBUTE_NAME";
   private static final String PROPERTY_ATTRIBUTE_VALUE = "ATTRIBUTE_VALUE";
   private static final String PROPERTY_ATTRIBUTE_IDENTITY = "ATTRIBUTE_IDENTITY"
   private static final String PROPERTY_ATTRIBUTE_TYPE = "ATTRIBUTE_TYPE";
  
   private static final String ATTRIBUTE_TYPE_TEXT = "text";
   private static final String ATTRIBUTE_TYPE_BOOLEAN = "boolean";
   private static final String ATTRIBUTE_TYPE_DATE = "date";
   private static final String ATTRIBUTE_TYPE_INT = "int";
   private static final String ATTRIBUTE_TYPE_LONG = "long";
   private static final String ATTRIBUTE_TYPE_FLOAT = "float";
   private static final String ATTRIBUTE_TYPE_DOUBLE = "double";
  
   private class EntityToSpiConverter
   {
      private static final String IDENTITY_TYPE_CACHE_PREFIX = "identity_type:";
      private static final String RELATIONSHIP_TYPE_CACHE_PREFIX = "relationship_type:";
     
      private Map<Object,Object> cache = new HashMap<Object,Object>();
     
      private Property<?> identityIdProperty = modelProperties.get(PROPERTY_IDENTITY_ID);
      private Property<?> identityNameProperty = modelProperties.get(PROPERTY_IDENTITY_NAME);
      private Property<?> identityTypeProperty = modelProperties.get(PROPERTY_IDENTITY_TYPE);
      private Property<?> identityTypeNameProperty = modelProperties.get(PROPERTY_IDENTITY_TYPE_NAME);
      private Property<?> relationshipTypeNameProperty = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE_NAME);
     
      public IdentityObject convertToIdentityObject(Object entity)
      {
         if (!identityClass.isAssignableFrom(entity.getClass()))
         {
            throw new IllegalArgumentException("Invalid identity entity");
         }
        
         if (cache.containsKey(entity))
         {
            return (IdentityObject) cache.get(entity);
         }
         else
         {        
            IdentityObject obj = new IdentityObjectImpl(
               identityIdProperty.getValue(entity).toString(),
               identityNameProperty.getValue(entity).toString(),
               convertToIdentityObjectType(identityTypeProperty.getValue(entity)));
            cache.put(entity, obj);
           
            return obj;           
         }
      }
     
      public IdentityObjectType convertToIdentityObjectType(Object value)
      {
         if (value instanceof String)
         {
            String key = IDENTITY_TYPE_CACHE_PREFIX + (String) value;
            if (cache.containsKey(key)) return (IdentityObjectType) cache.get(key);
           
            IdentityObjectType type = new IdentityObjectTypeImpl((String) value);
            cache.put(key, type);
            return type;
         }
         else
         {
            if (cache.containsKey(value)) return (IdentityObjectType) cache.get(value);
            IdentityObjectType type = new IdentityObjectTypeImpl(
                  (String) identityTypeNameProperty.getValue(value));
            cache.put(value, type);
            return type;
         }
      }
     
      public IdentityObjectRelationshipType convertToRelationshipType(Object value)
      {
         if (value instanceof String)
         {
            String key = RELATIONSHIP_TYPE_CACHE_PREFIX + (String) value;
            if (cache.containsKey(key)) return (IdentityObjectRelationshipType) cache.get(key);
           
            IdentityObjectRelationshipType type = new IdentityObjectRelationshipTypeImpl((String) value);
            cache.put(key, type);
            return type;
         }
         else
         {
            if (cache.containsKey(value)) return (IdentityObjectRelationshipType) cache.get(value);
            IdentityObjectRelationshipType type = new IdentityObjectRelationshipTypeImpl(
                  (String) relationshipTypeNameProperty.getValue(value));
            cache.put(value, type);
            return type;
         }
      }
   }
  
  
   private String id;
     
   // Entity classes  
   private Class<?> identityClass;
   private Class<?> credentialClass;
   private Class<?> relationshipClass;  
   private Class<?> attributeClass;
   private Class<?> roleTypeClass;
  
   private String userIdentityType = DEFAULT_USER_IDENTITY_TYPE;
   private String roleIdentityType = DEFAULT_ROLE_IDENTITY_TYPE;
   private String groupIdentityType = DEFAULT_GROUP_IDENTITY_TYPE;
  
   private String relationshipTypeMembership = DEFAULT_RELATIONSHIP_TYPE_MEMBERSHIP;
   private String relationshipTypeRole = DEFAULT_RELATIONSHIP_TYPE_ROLE;
  
   /**
    * Model properties
    */
   private Map<String,Property<Object>> modelProperties = new HashMap<String,Property<Object>>();  
  
   /**
    * Attribute properties
    */
   private Map<String,Property<Object>> attributeProperties = new HashMap<String,Property<Object>>();
  
   private FeaturesMetaData featuresMetaData;
  
   private class PropertyTypeCriteria implements PropertyCriteria
   {
      private PropertyType pt;
     
      public PropertyTypeCriteria(PropertyType pt)
      {
         this.pt = pt;
      }
     
      public boolean fieldMatches(Field f)
      {
         return f.isAnnotationPresent(IdentityProperty.class) &&
            f.getAnnotation(IdentityProperty.class).value().equals(pt);
      }

      public boolean methodMatches(Method m)
      {
         return m.isAnnotationPresent(IdentityProperty.class) &&
            m.getAnnotation(IdentityProperty.class).value().equals(pt);
      }     
   }
  
   public JpaIdentityStore(String id)
   {
      this.id = id;
   }
  
   public void bootstrap(IdentityStoreConfigurationContext configurationContext)
      throws IdentityException
   {          
      String clsName = configurationContext.getStoreConfigurationMetaData()
         .getOptionSingleValue(OPTION_IDENTITY_CLASS_NAME);

      if (clsName == null)
      {
         throw new IdentityException("Error bootstrapping JpaIdentityStore - identity entity class cannot be null");
      }
     
      try
      {
         identityClass = Reflections.classForName(clsName);
      }
      catch (ClassNotFoundException e)
      {
         throw new IdentityException("Error bootstrapping JpaIdentityStore - invalid identity entity class: " + clsName);
      }
     
      if (identityClass == null)
      {
         throw new IdentityException(
               "Error initializing JpaIdentityStore - identityClass not set");
      }
     
      clsName = configurationContext.getStoreConfigurationMetaData()
         .getOptionSingleValue(OPTION_CREDENTIAL_CLASS_NAME);
     
      if (clsName != null)
      {
         try
         {
            credentialClass = Class.forName(clsName);
         }
         catch (ClassNotFoundException e)
         {
            throw new IdentityException("Error bootstrapping JpaIdentityStore - invalid credential entity class: " + clsName);
         }
      }
     
      clsName = configurationContext.getStoreConfigurationMetaData()
         .getOptionSingleValue(OPTION_RELATIONSHIP_CLASS_NAME);
     
      try
      {
         relationshipClass = Class.forName(clsName);
      }
      catch (ClassNotFoundException e)
      {
         throw new IdentityException("Error bootstrapping JpaIdentityStore - invalid relationship entity class: " + clsName);
      }     
     
      boolean namedRelationshipsSupported = false;
     
      clsName = configurationContext.getStoreConfigurationMetaData()
         .getOptionSingleValue(OPTION_ROLE_TYPE_CLASS_NAME);
     
      if (clsName != null)
      {
         try
         {
            roleTypeClass = Class.forName(clsName);
            namedRelationshipsSupported = true;
         }
         catch (ClassNotFoundException e)
         {
            throw new IdentityException("Error bootstrapping JpaIdentityStore - invalid role type entity class: " + clsName);
         }
      }
     
      clsName = configurationContext.getStoreConfigurationMetaData()
         .getOptionSingleValue(OPTION_ATTRIBUTE_CLASS_NAME);
      if (clsName != null)
      {
         try
         {
            attributeClass = Class.forName(clsName);
         }
         catch (ClassNotFoundException e)
         {
            throw new IdentityException("Error bootstrapping JpaIdentityStore - invalid attribute entity class: " + clsName);
         }
      }
     
      configureIdentityId();
      configureIdentityName();
      configureIdentityType();
     
      configureCredentials();
      configureRelationships();
      configureAttributes();  
     
      if (namedRelationshipsSupported)
      {
         configureRoleTypeName();
      }
     
      featuresMetaData = new FeaturesMetaDataImpl(
            configurationContext.getStoreConfigurationMetaData(),
            new HashSet<IdentityObjectSearchCriteriaType>(),
            false,
            namedRelationshipsSupported,
            new HashSet<String>()
            );           
   }  
  
   protected void configureIdentityId() throws IdentityException
   {
      List<Property<Object>> props = PropertyQueries.createQuery(identityClass)
         .addCriteria(new AnnotatedPropertyCriteria(Id.class))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_IDENTITY_ID, props.get(0));
      }
      else
      {
         throw new IdentityException("Error initializing JpaIdentityStore - no Identity ID found.");
      }
   }
     
   protected void configureIdentityName() throws IdentityException
   {     
      List<Property<Object>> props = PropertyQueries.createQuery(identityClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.NAME))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_IDENTITY_NAME, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous identity name property in identity class " + identityClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(identityClass, "username", "userName", "name");
         if (p != null)
         {
            modelProperties.put(PROPERTY_IDENTITY_NAME, p);
         }
         else
         {
            // Last resort - check whether the entity class exposes a single String property
            // if so, let's assume it's the identity name
            props = PropertyQueries.createQuery(identityClass)
               .addCriteria(new TypedPropertyCriteria(String.class))
               .getResultList();
            if (props.size() == 1)
            {
               modelProperties.put(PROPERTY_IDENTITY_NAME, props.get(0));
            }
         }
      }

      if (!modelProperties.containsKey(PROPERTY_IDENTITY_NAME))
      {
         throw new IdentityException("Error initializing JpaIdentityStore - no valid identity name property found.");
      }
   }
  
   protected void configureIdentityType() throws IdentityException
   {     
      List<Property<Object>> props = PropertyQueries.createQuery(identityClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.TYPE))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_IDENTITY_TYPE, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous identity type property in identity class " + identityClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(identityClass, "identityObjectType",
               "identityType", "identityObjectTypeName", "identityTypeName",
               "typeName", "discriminator", "accountType", "userType", "type");
         if (p != null)
         {
            modelProperties.put(PROPERTY_IDENTITY_TYPE, props.get(0));
         }
         else
         {
            // Last resort - let's check all properties, and try to find one
            // with an entity type that has "type" in its name
            props = PropertyQueries.createQuery(identityClass).getResultList();
            search: for (Property<Object> typeProp : props)
            {
               if (typeProp.getJavaClass().isAnnotationPresent(Entity.class) &&
                     (typeProp.getJavaClass().getSimpleName().contains("type") ||
                           typeProp.getJavaClass().getSimpleName().contains("Type")))
               {
                  // we have a potential match, let's check if this entity has a name property
                  Property<Object> nameProp = findNamedProperty(typeProp.getJavaClass(),
                        "identityObjectTypeName", "identityTypeName", "typeName", "name");
                  if (nameProp != null)
                  {
                     modelProperties.put(PROPERTY_IDENTITY_TYPE, typeProp);
                     modelProperties.put(PROPERTY_IDENTITY_TYPE_NAME, nameProp);
                     break search;
                  }
               }
            }
         }        
      }     
     
      Property<?> typeProp = modelProperties.get(PROPERTY_IDENTITY_TYPE);
     
      if (typeProp == null)
      {
         throw new IdentityException("Error initializing JpaIdentityStore - no valid identity type property found.");
      }
     
      if (!String.class.equals(typeProp.getJavaClass()) &&
            !modelProperties.containsKey(PROPERTY_IDENTITY_TYPE_NAME))
      {
         // We're not dealing with a simple type name - validate the lookup type
         Property<Object> nameProp = findNamedProperty(typeProp.getJavaClass(),
               "identityObjectTypeName", "identityTypeName", "typeName", "name");
         if (nameProp != null)
         {
            modelProperties.put(PROPERTY_IDENTITY_TYPE_NAME, nameProp);
         }
         else
         {
            throw new IdentityException("Error initializing JpaIdentityStore - no valid identity type name property found.");
         }
      }
   }
  
   protected Property<Object> findNamedProperty(Class<?> targetClass, String... allowedNames)
   {
      List<Property<Object>> props = PropertyQueries.createQuery(targetClass)
         .addCriteria(new TypedPropertyCriteria(String.class))
         .addCriteria(new PropertyTypeCriteria(PropertyType.NAME))
         .getResultList();
     
      if (props.size() == 1)
      {
         return props.get(0);
      }
      else
      {
         props = PropertyQueries.createQuery(targetClass)
            .addCriteria(new TypedPropertyCriteria(String.class))
            .addCriteria(new NamedPropertyCriteria(allowedNames))
            .getResultList();
        
         for (String name : allowedNames)
         {
            for (Property<Object> prop : props)
            {
               if (name.equals(prop.getName())) return prop;
            }
         }
      }     
     
      return null;
   }
  
   protected void configureCredentials() throws IdentityException
   {
      // If a credential entity has been explicitly configured, scan it
      if (credentialClass != null)
      {
         List<Property<Object>> props = PropertyQueries.createQuery(credentialClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.VALUE))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_CREDENTIAL_VALUE, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous credential value property in credential class " +
                  credentialClass.getName());
         }
         else
         {
            // Try scanning for a credential property also
            props = PropertyQueries.createQuery(credentialClass)
               .addCriteria(new PropertyTypeCriteria(PropertyType.CREDENTIAL))
               .getResultList();
            if (props.size() == 1)
            {
               modelProperties.put(PROPERTY_CREDENTIAL_VALUE, props.get(0));
            }
            else if (props.size() > 1)
            {
               throw new IdentityException(
                     "Ambiguous credential value property in credential class " +
                     credentialClass.getName());
            }
            else
            {
               Property<Object> p = findNamedProperty(credentialClass, "credentialValue",
                     "password", "passwordHash", "credential", "value");
               if (p != null) modelProperties.put(PROPERTY_CREDENTIAL_VALUE, p);
            }
         } 
        
         // Scan for the credential identity property
         props = PropertyQueries.createQuery(credentialClass)
            .addCriteria(new TypedPropertyCriteria(identityClass))
            .getResultList();
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_CREDENTIAL_IDENTITY, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous identity property in credential class " +
                  credentialClass.getName());
         }
         else
         {
            // Scan for a named identity property
            props = PropertyQueries.createQuery(credentialClass)
               .addCriteria(new NamedPropertyCriteria("identity", "identityObject"))
               .getResultList();
            if (!props.isEmpty())
            {
               modelProperties.put(PROPERTY_CREDENTIAL_IDENTITY, props.get(0));
            }
            else
            {
               throw new IdentityException("Error initializing JpaIdentityStore - no credential identity property found.");
            }
         }
      }
      else
      {
         // The credentials may be stored in the identity class        
         List<Property<Object>> props = PropertyQueries.createQuery(identityClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.CREDENTIAL))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_CREDENTIAL_VALUE, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous credential property in identity class " +
                  identityClass.getName());
         }
         else
         {        
            Property<Object> p = findNamedProperty(identityClass, "credentialValue",
                  "password", "passwordHash", "credential", "value");
            if (p != null) modelProperties.put(PROPERTY_CREDENTIAL_VALUE, p);
         }
      }
           
      if (!modelProperties.containsKey(PROPERTY_CREDENTIAL_VALUE))
      {
         throw new IdentityException("Error initializing JpaIdentityStore - no credential value property found.");
      }           
           
      // Scan for a credential type property
      List<Property<Object>> props = PropertyQueries.createQuery(credentialClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.TYPE))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_CREDENTIAL_TYPE, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous credential type property in credential class " +
               credentialClass.getName());
      }
      else
      {
         props = PropertyQueries.createQuery(credentialClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.CREDENTIAL_TYPE))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_CREDENTIAL_TYPE, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous credential type property in credential class " +
                  credentialClass.getName());           
         }
         else
         {        
            Property<Object> p = findNamedProperty(credentialClass, "credentialType",
                  "identityObjectCredentialType", "type");
            if (p != null) modelProperties.put(PROPERTY_CREDENTIAL_TYPE, p);
         }
      }     

      Property<?> typeProp = modelProperties.get(PROPERTY_CREDENTIAL_TYPE);     
     
      // If the credential type property isn't a String, then validate the lookup type
      if (!String.class.equals(typeProp.getJavaClass()))
      {
         Property<Object> nameProp = findNamedProperty(typeProp.getJavaClass(),
               "credentialObjectTypeName", "credentialTypeName", "typeName", "name");
         if (nameProp != null)
         {
            modelProperties.put(PROPERTY_CREDENTIAL_TYPE_NAME, nameProp);
         }
         else
         {
            throw new IdentityException("Error initializing JpaIdentityStore - no valid credential type name property found.");
         }
      }      
   }
  
   protected void configureRelationships() throws IdentityException
   {
      if (relationshipClass == null)
      {
         throw new IdentityException("Error initializing JpaIdentityStore - relationshipClass not set.");
      }
     
      List<Property<Object>> props = PropertyQueries.createQuery(relationshipClass)
         .addCriteria(new TypedPropertyCriteria(identityClass))
         .addCriteria(new PropertyTypeCriteria(PropertyType.RELATIONSHIP_FROM))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_RELATIONSHIP_FROM, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous relationshipFrom property in relationship class " +
               relationshipClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(relationshipClass, "relationshipFrom",
               "fromIdentityObject", "fromIdentity");
         if (p != null)
         {
            modelProperties.put(PROPERTY_RELATIONSHIP_FROM, p);
         }
         else
         {
            // Last resort - search for a property with a type of identityClass
            // and a "from" in its name
            props = PropertyQueries.createQuery(relationshipClass)
               .addCriteria(new TypedPropertyCriteria(identityClass))
               .getResultList();
           
            for (Property<Object> prop : props)
            {
               if (prop.getName().contains("from"))
               {
                  modelProperties.put(PROPERTY_RELATIONSHIP_FROM, prop);
                  break;
               }
            }
         }        
      }
 
     
      props = PropertyQueries.createQuery(relationshipClass)
         .addCriteria(new TypedPropertyCriteria(identityClass))
         .addCriteria(new PropertyTypeCriteria(PropertyType.RELATIONSHIP_TO))
         .getResultList();
  
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_RELATIONSHIP_TO, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous relationshipTo property in relationship class " +
               relationshipClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(relationshipClass, "relationshipTo",
               "toIdentityObject", "toIdentity");
         if (p != null)
         {
            modelProperties.put(PROPERTY_RELATIONSHIP_TO, p);
         }
         else
         {
            // Last resort - search for a property with a type of identityClass
            // and a "to" in its name
            props = PropertyQueries.createQuery(relationshipClass)
               .addCriteria(new TypedPropertyCriteria(identityClass))
               .getResultList();
           
            for (Property<Object> prop : props)
            {
               if (prop.getName().contains("to"))
               {
                  modelProperties.put(PROPERTY_RELATIONSHIP_TO, prop);
                  break;
               }
            }
         }        
      }     
     
      props = PropertyQueries.createQuery(relationshipClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.TYPE))
         .getResultList();
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_RELATIONSHIP_TYPE, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous relationshipType property in relationship class " +
               relationshipClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(relationshipClass,
               "identityRelationshipType", "relationshipType", "type");
         if (p != null)
         {
            modelProperties.put(PROPERTY_RELATIONSHIP_TYPE, p);
         }
         else
         {
            props = PropertyQueries.createQuery(relationshipClass)
               .getResultList();
            for (Property<Object> prop : props)
            {
               if (prop.getName().contains("type"))
               {
                  modelProperties.put(PROPERTY_RELATIONSHIP_TYPE, prop);
                  break;
               }
            }
         }
      }
     
      props = PropertyQueries.createQuery(relationshipClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.NAME))
         .addCriteria(new TypedPropertyCriteria(String.class))
         .getResultList();
     
      if (props.size() == 1)
      {
         modelProperties.put(PROPERTY_RELATIONSHIP_NAME, props.get(0));
      }
      else if (props.size() > 1)
      {
         throw new IdentityException(
               "Ambiguous relationship name property in relationship class " +
               relationshipClass.getName());
      }
      else
      {
         Property<Object> p = findNamedProperty(relationshipClass,
               "relationshipName", "name");
         if (p != null)
         {
            modelProperties.put(PROPERTY_RELATIONSHIP_NAME, p);
         }
      }
     
      if (!modelProperties.containsKey(PROPERTY_RELATIONSHIP_FROM))
      {
         throw new IdentityException(
            "Error initializing JpaIdentityStore - no valid relationship from property found.");
      }
     
      if (!modelProperties.containsKey(PROPERTY_RELATIONSHIP_TO))
      {
         throw new IdentityException(
            "Error initializing JpaIdentityStore - no valid relationship to property found.");
      }
     
      if (!modelProperties.containsKey(PROPERTY_RELATIONSHIP_TYPE))
      {
         throw new IdentityException(
            "Error initializing JpaIdentityStore - no valid relationship type property found.");
      }
     
      if (!modelProperties.containsKey(PROPERTY_RELATIONSHIP_NAME))
      {
         throw new IdentityException(
            "Error initializing JpaIdentityStore - no valid relationship name property found.");
      }
     
      Class<?> typeClass = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE).getJavaClass();
      if (!String.class.equals(typeClass))
      {
         props = PropertyQueries.createQuery(typeClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.NAME))
            .addCriteria(new TypedPropertyCriteria(String.class))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_RELATIONSHIP_TYPE_NAME, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous relationship type name property in class " +
                  typeClass.getName());
         }
         else
         {
            Property<Object> p = findNamedProperty(typeClass, "relationshipTypeName",
                  "typeName", "name");
            if (p != null)
            {
               modelProperties.put(PROPERTY_RELATIONSHIP_TYPE_NAME, p);
            }
         }
        
         if (!modelProperties.containsKey(PROPERTY_RELATIONSHIP_TYPE_NAME))
         {
            throw new IdentityException(
                  "Error initializing JpaIdentityStore - no valid relationship type name property found");
         }
      }     
   }
  
   protected void configureAttributes() throws IdentityException
   {
      // If an attribute class has been configured, scan it for attributes
      if (attributeClass != null)
      {
         List<Property<Object>> props = PropertyQueries.createQuery(attributeClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.NAME))
            .addCriteria(new TypedPropertyCriteria(String.class))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_ATTRIBUTE_NAME, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                "Ambiguous attribute name property in class " +
                attributeClass.getName());
         }
         else
         {
            Property<Object> prop = findNamedProperty(attributeClass,
                  "attributeName", "name");
            if (prop != null) modelProperties.put(PROPERTY_ATTRIBUTE_NAME, prop);
         }
        
         props = PropertyQueries.createQuery(attributeClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.VALUE))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_ATTRIBUTE_VALUE, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous attribute value property in class " +
                  attributeClass.getName());
         }
         else
         {
            Property<Object> prop = findNamedProperty(attributeClass,
                  "attributeValue", "value");
            if (prop != null) modelProperties.put(PROPERTY_ATTRIBUTE_VALUE, prop);
         }
        
         props = PropertyQueries.createQuery(attributeClass)
            .addCriteria(new TypedPropertyCriteria(identityClass))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_ATTRIBUTE_IDENTITY, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous identity property in attribute class " +
                  attributeClass.getName());
         }
         else
         {
            throw new IdentityException("Error initializing JpaIdentityStore - " +
                  "no attribute identity property found.");
         }
        
         props = PropertyQueries.createQuery(attributeClass)
            .addCriteria(new PropertyTypeCriteria(PropertyType.TYPE))
            .getResultList();
        
         if (props.size() == 1)
         {
            modelProperties.put(PROPERTY_ATTRIBUTE_TYPE, props.get(0));
         }
         else if (props.size() > 1)
         {
            throw new IdentityException(
                  "Ambiguous attribute type property in class " +
                  attributeClass.getName());
         }
      }

      // Scan for additional attributes in the identity class also
      List<Property<Object>> props = PropertyQueries.createQuery(identityClass)
         .addCriteria(new PropertyTypeCriteria(PropertyType.ATTRIBUTE))
         .getResultList();
  
      for (Property<Object> p : props)
      {
         attributeProperties.put(
               p.getAnnotatedElement().getAnnotation(IdentityProperty.class).attributeName(),
               p);
      }
     
      // scan any entity classes referenced by the identity class also
      props = PropertyQueries.createQuery(identityClass)
         .getResultList();
     
      for (Property<Object> p : props)
      {
         if (!p.isReadOnly() && p.getJavaClass().isAnnotationPresent(Entity.class))
         {
            List<Property<Object>> pp = PropertyQueries.createQuery(p.getJavaClass())
               .addCriteria(new PropertyTypeCriteria(PropertyType.ATTRIBUTE))
               .getResultList();
           
            for (Property<Object> attributeProperty : pp)
            {
               attributeProperties.put(
                     attributeProperty.getAnnotatedElement().getAnnotation(IdentityProperty.class).attributeName(),
                     attributeProperty);                       
            }
         }
      }
   }
  
   protected void configureRoleTypeName()
   {
      Property<Object> relationshipNameProp = findNamedProperty(roleTypeClass, "name");
      if (relationshipNameProp != null)
      {        
         modelProperties.put(PROPERTY_ROLE_TYPE_NAME, relationshipNameProp);
      }
   }
  
   protected class AttributeValue
   {
      private String encoded;
      private String type;
     
      public AttributeValue(String encoded, String type)
      {
         this.encoded = encoded;
         this.type = type;
      }
     
      public String getEncoded()
      {
         return encoded;
      }
     
      public String getType()
      {
         return type;
      }
   }
  
   public String getUserIdentityType()
   {
      return userIdentityType;
   }
  
   public void setUserIdentityType(String userIdentityType)
   {
      this.userIdentityType = userIdentityType;
   }
  
   public String getRoleIdentityType()
   {
      return roleIdentityType;
   }
  
   public void setRoleIdentityType(String roleIdentityType)
   {
      this.roleIdentityType = roleIdentityType;
   }
  
   public String getGroupIdentityType()
   {
      return groupIdentityType;
   }
  
   public void setGroupIdentityType(String groupIdentityType)
   {
      this.groupIdentityType = groupIdentityType;
   }
  
   public String getRelationshipTypeMembership()
   {
      return relationshipTypeMembership;
   }
  
   public void setRelationshipTypeMembership(String relationshipTypeMembership)
   {
      this.relationshipTypeMembership = relationshipTypeMembership;
   }
  
   public String getRelationshipTypeRole()
   {
      return relationshipTypeRole;
   }
  
   public void setRelationshipTypeRole(String relationshipTypeRole)
   {
      this.relationshipTypeRole = relationshipTypeRole;
  
  
   public IdentityStoreSession createIdentityStoreSession(
         Map<String, Object> sessionOptions) throws IdentityException
   {
      EntityManager em = (EntityManager) sessionOptions.get("ENTITY_MANAGER");
     
      return new JpaIdentityStoreSessionImpl(em);
   }

   public IdentityObject createIdentityObject(
         IdentityStoreInvocationContext invocationCtx, String name,
         IdentityObjectType identityObjectType) throws IdentityException
   {
      return createIdentityObject(invocationCtx, name, identityObjectType, null);
   }
  
   protected Object lookupIdentityType(String identityType, EntityManager em)
   {     
      try
      {
         Property<Object> typeNameProp = modelProperties.get(PROPERTY_IDENTITY_TYPE_NAME);
        
         // If there is no identity type table, just return the name
         if (typeNameProp == null) return identityType;
        
         Object val = em.createQuery(
               "select t from " + typeNameProp.getDeclaringClass().getName() +
               " t where t." + typeNameProp.getName() +
                " = :identityType")
               .setParameter("identityType", identityType)
               .getSingleResult();
         return val;
      }
      catch (NoResultException ex)
      {
         return null;
      }     
   }

   public IdentityObject createIdentityObject(
         IdentityStoreInvocationContext ctx, String name,
         IdentityObjectType identityObjectType, Map<String, String[]> attributes)
         throws IdentityException
   {
      try
      {
         Object identityInstance = identityClass.newInstance();
         modelProperties.get(PROPERTY_IDENTITY_NAME).setValue(identityInstance, name);
        
         Property<Object> typeProp = modelProperties.get(PROPERTY_IDENTITY_TYPE);
        
         if (String.class.equals(typeProp.getJavaClass()))
         {
            typeProp.setValue(identityInstance, identityObjectType.getName());
         }
         else
         {
            typeProp.setValue(identityInstance, lookupIdentityType(identityObjectType.getName(),
                  getEntityManager(ctx)));
         }
        
         EntityManager em = getEntityManager(ctx);
        
         em.persist(identityInstance);
        
         Object id = modelProperties.get(PROPERTY_IDENTITY_ID).getValue(identityInstance);
         IdentityObject obj = new IdentityObjectImpl(
               (id != null ? id.toString() : null),
               name, identityObjectType);
        
         if (attributes != null)
         {
            List<IdentityObjectAttribute> attribs = new ArrayList<IdentityObjectAttribute>();
            for (String key : attributes.keySet())
            {
               for (String value : attributes.get(key))
               {
                  attribs.add(new SimpleAttribute(key, value));
               }
            }
           
            updateAttributes(ctx, obj, attribs.toArray(new IdentityObjectAttribute[attribs.size()]));
         }

         return obj;
      }
      catch (Exception ex)
      {
         throw new IdentityException("Error creating identity object", ex);
      }   
   }

   public IdentityObjectRelationship createRelationship(
         IdentityStoreInvocationContext invocationCtx,
         IdentityObject fromIdentity, IdentityObject toIdentity,
         IdentityObjectRelationshipType relationshipType,
         String relationshipName, boolean createNames) throws IdentityException
   {
      try
      {
         EntityManager em = getEntityManager(invocationCtx);
        
         Object relationship = relationshipClass.newInstance();
        
         modelProperties.get(PROPERTY_RELATIONSHIP_FROM).setValue(relationship,
               lookupIdentity(fromIdentity, em));
         modelProperties.get(PROPERTY_RELATIONSHIP_TO).setValue(relationship,
               lookupIdentity(toIdentity, em));
        
         Property<Object> type = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE);
         if (String.class.equals(modelProperties.get(PROPERTY_RELATIONSHIP_TYPE).getJavaClass()))
         {
            type.setValue(relationship, relationshipType.getName());
         }
         else
         {
            type.setValue(relationship, lookupRelationshipType(relationshipType, em));
         }
        
         modelProperties.get(PROPERTY_RELATIONSHIP_NAME).setValue(relationship,
               relationshipName);
        
         em.persist(relationship);
        
         return new IdentityObjectRelationshipImpl(fromIdentity, toIdentity,
               relationshipName, relationshipType);
      }
      catch (Exception ex)
      {
         throw new IdentityException("Exception creating relationship", ex);
      }
   }
  
   protected Object lookupIdentity(IdentityObject obj, EntityManager em)
   {
      Property<?> identityNameProp = modelProperties.get(PROPERTY_IDENTITY_NAME);
      Property<?> identityTypeProp = modelProperties.get(PROPERTY_IDENTITY_TYPE);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(identityClass);
      Root<?> root = criteria.from(identityClass);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(identityNameProp.getName()), obj.getName()));
      predicates.add(builder.equal(root.get(identityTypeProp.getName()), lookupIdentityType(obj.getIdentityType().getName(), em)));
     
      // TODO add criteria for identity type
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      return em.createQuery(criteria).getSingleResult();
   }
  
   protected Object lookupCredentialTypeEntity(String name, EntityManager em)
   {
      Property<?> credentialTypeNameProp = modelProperties.get(PROPERTY_CREDENTIAL_TYPE_NAME);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(credentialTypeNameProp.getDeclaringClass());
      Root<?> root = criteria.from(credentialTypeNameProp.getDeclaringClass());
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(credentialTypeNameProp.getName()), name));     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));

      return em.createQuery(criteria).getSingleResult();
   }
  
   protected Object lookupRelationshipType(IdentityObjectRelationshipType relationshipType, EntityManager em)
   {
      Property<?> relationshipTypeNameProp = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE_NAME);     
     
      if (relationshipTypeNameProp != null)
      {
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(relationshipTypeNameProp.getDeclaringClass());
         Root<?> root = criteria.from(relationshipTypeNameProp.getDeclaringClass());
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(relationshipTypeNameProp.getName()), relationshipType.getName()));     
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));

         return em.createQuery(criteria).getSingleResult();
      }
      else
      {
         return relationshipType.getName();
      }
   }

   public String createRelationshipName(IdentityStoreInvocationContext ctx,
         String name) throws IdentityException, OperationNotSupportedException
   {
      try
      {
         Property<Object> roleTypeNameProp = modelProperties.get(PROPERTY_ROLE_TYPE_NAME);
        
         Object roleTypeInstance = roleTypeClass.newInstance();
         roleTypeNameProp.setValue(roleTypeInstance, name);
        
         EntityManager em = getEntityManager(ctx);
        
         em.persist(roleTypeInstance);
         return name;
      }
      catch (Exception ex)
      {
         throw new IdentityException("Error creating relationship name", ex);
      }
   }
  
   public EntityManager getEntityManager(IdentityStoreInvocationContext invocationContext)
   {
      return ((JpaIdentityStoreSessionImpl) invocationContext.getIdentityStoreSession()).getEntityManager();
   }

   public IdentityObject findIdentityObject(IdentityStoreInvocationContext invocationContext, String id)
         throws IdentityException
   {
      try
      {
        Object identity = getEntityManager(invocationContext).createQuery("select i from " +
              identityClass.getName() + " i where i." +
              modelProperties.get(PROPERTY_IDENTITY_ID).getName() +
              " = :id")
              .setParameter("id", id)
              .getSingleResult();
       
        IdentityObjectType type = modelProperties.containsKey(PROPERTY_IDENTITY_TYPE_NAME) ?
              new IdentityObjectTypeImpl(
                    modelProperties.get(PROPERTY_IDENTITY_TYPE_NAME).getValue(
                          modelProperties.get(PROPERTY_IDENTITY_TYPE).getValue(identity)).toString()) :
              new IdentityObjectTypeImpl(modelProperties.get(PROPERTY_IDENTITY_TYPE).getValue(identity).toString());
       
       
        return new IdentityObjectImpl(
                  modelProperties.get(PROPERTY_IDENTITY_ID).getValue(identity).toString(),
                  modelProperties.get(PROPERTY_IDENTITY_NAME).getValue(identity).toString(),
                  type);
      }
      catch (NoResultException ex)
      {
         return null;
      }
   }

   public IdentityObject findIdentityObject(
         IdentityStoreInvocationContext invocationContext, String name,
         IdentityObjectType identityObjectType) throws IdentityException
   {
      try
      {
         Object identityType = modelProperties.containsKey(PROPERTY_IDENTITY_TYPE_NAME) ?
               lookupIdentityType(identityObjectType.getName(), getEntityManager(invocationContext)) :
                  identityObjectType.getName();
        
         Object identity = getEntityManager(invocationContext).createQuery("select i from " +
              identityClass.getName() + " i where i." +
              modelProperties.get(PROPERTY_IDENTITY_NAME).getName() +
              " = :name and i." + modelProperties.get(PROPERTY_IDENTITY_TYPE).getName() +
              " = :type")
              .setParameter("name", name)
              .setParameter("type", identityType)             
              .getSingleResult();
       
        return new IdentityObjectImpl(
                  modelProperties.get(PROPERTY_IDENTITY_ID).getValue(identity).toString(),
                  modelProperties.get(PROPERTY_IDENTITY_NAME).getValue(identity).toString(),
                  identityObjectType);
      }
      catch (NoResultException ex)
      {
         return null;
      }
   }

   public Collection<IdentityObject> findIdentityObject(
         IdentityStoreInvocationContext ctx,
         IdentityObjectType identityType, IdentityObjectSearchCriteria searchCriteria)
         throws IdentityException
   {
      List<IdentityObject> objs = new ArrayList<IdentityObject>();
     
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(identityClass);
     
      Root<?> root = criteria.from(identityClass);

      Property<?> identityTypeProp = modelProperties.get(PROPERTY_IDENTITY_TYPE);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
     
      if (identityType != null)
      {
         predicates.add(builder.equal(root.get(identityTypeProp.getName()),
               lookupIdentityType(identityType.getName(), em)));
      }
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      List<?> results = em.createQuery(criteria).getResultList();
     
      EntityToSpiConverter converter = new EntityToSpiConverter();
     
      for (Object result : results)
      {              
         objs.add(converter.convertToIdentityObject(result));
      }
     
      return objs;
   }

   public String getId()
   {
      return id;
   }

   public Set<String> getRelationshipNames(IdentityStoreInvocationContext ctx,
         IdentityObjectSearchCriteria searchCriteria) throws IdentityException,
         OperationNotSupportedException
   {
      Set<String> names = new HashSet<String>();
     
      Property<Object> roleTypeNameProp = modelProperties.get(PROPERTY_ROLE_TYPE_NAME);
     
      if (roleTypeClass != null)
      {
         EntityManager em = getEntityManager(ctx);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(roleTypeClass);
         criteria.from(roleTypeClass);
        
         List<?> results = em.createQuery(criteria).getResultList();
         for (Object result : results)
         {
            names.add(roleTypeNameProp.getValue(result).toString());
         }
      }     

      return names;
   }

   public Set<String> getRelationshipNames(IdentityStoreInvocationContext ctx,
         IdentityObject identity, IdentityObjectSearchCriteria searchCriteria)
         throws IdentityException, OperationNotSupportedException
   {
      Set<String> names = new HashSet<String>();
     
      if (!featuresMetaData.isNamedRelationshipsSupported()) return names;
     
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(relationshipClass);
      Root<?> root = criteria.from(relationshipClass);
     
      Property<?> identityToProperty = modelProperties.get(PROPERTY_RELATIONSHIP_TO);
      Property<?> relationshipNameProperty = modelProperties.get(PROPERTY_RELATIONSHIP_NAME);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(identityToProperty.getName()),
            lookupIdentity(identity, em)));
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      List<?> results = em.createQuery(criteria).getResultList();
      for (Object result : results)
      {
         names.add((String) relationshipNameProperty.getValue(result));
      }
     
      return names;
   }

   public Map<String, String> getRelationshipProperties(
         IdentityStoreInvocationContext ctx,
         IdentityObjectRelationship relationship) throws IdentityException,
         OperationNotSupportedException
   {
      throw new OperationNotSupportedException("getRelationshipProperties() not supported");
   }

   public FeaturesMetaData getSupportedFeatures()
   {     
      return featuresMetaData;
   }

   public void removeIdentityObject(
         IdentityStoreInvocationContext ctx, IdentityObject identity)
         throws IdentityException
   {
      removeRelationships(ctx, identity, null, false);
     
      EntityManager em = getEntityManager(ctx);
     
      Property<?> nameProperty = modelProperties.get(PROPERTY_IDENTITY_NAME);
      Property<?> typeProperty = modelProperties.get(PROPERTY_IDENTITY_TYPE);     
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
     
      CriteriaQuery<?> criteria = builder.createQuery(identityClass);
      Root<?> root = criteria.from(identityClass);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(nameProperty.getName()),
            identity.getName()));
      predicates.add(builder.equal(root.get(typeProperty.getName()),
            lookupIdentityType(identity.getIdentityType().getName(), em)));
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      try
      {
         Object instance = em.createQuery(criteria).getSingleResult();
                 
         // If there is a credential class, delete any credentials
         if (credentialClass != null)
         {
            Property<?> credentialIdentityProp = modelProperties.get(PROPERTY_CREDENTIAL_IDENTITY);
           
            criteria = builder.createQuery(credentialClass);
            root = criteria.from(credentialClass);
           
            predicates = new ArrayList<Predicate>();
            predicates.add(builder.equal(root.get(credentialIdentityProp.getName()),
                  instance));
            criteria.where(predicates.toArray(new Predicate[predicates.size()]));
           
            List<?> results = em.createQuery(criteria).getResultList();
            for (Object result : results)
            {
               em.remove(result);
            }
         }
        
         // If there is an attribute class, delete any attributes
         if (attributeClass != null)
         {
            Property<?> attributeIdentityProperty = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
            criteria = builder.createQuery(attributeClass);
            root = criteria.from(attributeClass);
           
            predicates = new ArrayList<Predicate>();
            predicates.add(builder.equal(root.get(attributeIdentityProperty.getName()),
                  instance));
            criteria.where(predicates.toArray(new Predicate[predicates.size()]));
           
            List<?> results = em.createQuery(criteria).getResultList();
            for (Object result : results)
            {
               em.remove(result);
            }
         }
        
         em.remove(instance);
      }
      catch (NoResultException ex)
      {
         throw new IdentityException(String.format(
               "Exception removing identity object - [%s] not found.",
               identity), ex);
      }
   }

   public void removeRelationship(IdentityStoreInvocationContext ctx,
         IdentityObject fromIdentity, IdentityObject toIdentity,
         IdentityObjectRelationshipType relationshipType,
         String relationshipName) throws IdentityException
   {
      Property<?> fromProperty = modelProperties.get(PROPERTY_RELATIONSHIP_FROM);
      Property<?> toProperty = modelProperties.get(PROPERTY_RELATIONSHIP_TO);
      Property<?> relationshipTypeProp = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE);
     
      EntityManager em = getEntityManager(ctx);

      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(identityClass);
      Root<?> root = criteria.from(identityClass);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(fromProperty.getName()),
            lookupIdentity(fromIdentity, em)));
      predicates.add(builder.equal(root.get(toProperty.getName()),
            lookupIdentity(toIdentity, em)));
      predicates.add(builder.equal(root.get(relationshipTypeProp.getName()),
            lookupRelationshipType(relationshipType, em)));
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      Object relationship = em.createQuery(criteria).getSingleResult();
      em.remove(relationship);
   }

   public String removeRelationshipName(IdentityStoreInvocationContext ctx,
         String name) throws IdentityException, OperationNotSupportedException
   {
      Property<?> nameProp = modelProperties.get(PROPERTY_ROLE_TYPE_NAME);
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(roleTypeClass);
      Root<?> root = criteria.from(roleTypeClass);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
      predicates.add(builder.equal(root.get(nameProp.getName()), name));
      criteria.where(predicates.toArray((new Predicate[0])));
      Object roleType = em.createQuery(criteria).getSingleResult();
      em.remove(roleType);
     
      return null;
   }

   public void removeRelationships(
         IdentityStoreInvocationContext ctx,
         IdentityObject identity1, IdentityObject identity2, boolean named)
         throws IdentityException
   {
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(relationshipClass);
      Root<?> root = criteria.from(relationshipClass);
     
      Property<?> relationshipFromProp = modelProperties.get(PROPERTY_RELATIONSHIP_FROM);
      Property<?> relationshipToProp = modelProperties.get(PROPERTY_RELATIONSHIP_TO);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
     
      if (identity1 != null)
      {
         predicates.add(builder.equal(root.get(relationshipFromProp.getName()),
               lookupIdentity(identity1, em)));
      }
     
      if (identity2 != null)
      {
         predicates.add(builder.equal(root.get(relationshipToProp.getName()),
               lookupIdentity(identity2, em)));
      }
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      List<?> results = em.createQuery(criteria).getResultList();
      for (Object result : results)
      {
         em.remove(result);
      }
     
      criteria = builder.createQuery(relationshipClass);
      criteria.from(relationshipClass);
     
      predicates = new ArrayList<Predicate>();
     
      if (identity2 != null)
      {
         predicates.add(builder.equal(root.get(relationshipFromProp.getName()),
               lookupIdentity(identity2, em)));
      }
     
      if (identity1 != null)
      {
         predicates.add(builder.equal(root.get(relationshipToProp.getName()),
               lookupIdentity(identity1, em)));
      }
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      results = em.createQuery(criteria).getResultList();
      for (Object result : results)
      {
         em.remove(result);
      }
   }

   public Set<IdentityObjectRelationship> resolveRelationships(
         IdentityStoreInvocationContext ctx,
         IdentityObject fromIdentity, IdentityObject toIdentity,
         IdentityObjectRelationshipType relationshipType)
         throws IdentityException
   {
      Set<IdentityObjectRelationship> relationships = new HashSet<IdentityObjectRelationship>();
     
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(relationshipClass);
      Root<?> root = criteria.from(relationshipClass);
     
      Property<?> relationshipFromProp = modelProperties.get(PROPERTY_RELATIONSHIP_FROM);
      Property<?> relationshipToProp = modelProperties.get(PROPERTY_RELATIONSHIP_TO);
      Property<?> relationshipTypeProp = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE);
      Property<?> relationshipNameProp = modelProperties.get(PROPERTY_RELATIONSHIP_NAME);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
     
      if (fromIdentity != null)
      {
         predicates.add(builder.equal(root.get(relationshipFromProp.getName()),
            lookupIdentity(fromIdentity, em)));
      }
     
      if (toIdentity != null)
      {
         predicates.add(builder.equal(root.get(relationshipToProp.getName()),
            lookupIdentity(toIdentity, em)));
      }
     
      if (relationshipType != null)
      {
         predicates.add(builder.equal(root.get(relationshipTypeProp.getName()),
               lookupRelationshipType(relationshipType, em)));
      }
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      List<?> results = em.createQuery(criteria).getResultList();
     
      EntityToSpiConverter converter = new EntityToSpiConverter();
     
      for (Object result : results)
      {
         IdentityObjectRelationship relationship = new IdentityObjectRelationshipImpl(
               converter.convertToIdentityObject(relationshipFromProp.getValue(result)),
               converter.convertToIdentityObject(relationshipToProp.getValue(result)),
               (String) relationshipNameProp.getValue(result),
               converter.convertToRelationshipType(relationshipTypeProp.getValue(result))        
         );
        
         relationships.add(relationship);
      }
     
      return relationships;
   }

   public Set<IdentityObjectRelationship> resolveRelationships(
         IdentityStoreInvocationContext ctx, IdentityObject identity,
         IdentityObjectRelationshipType relationshipType, boolean parent,
         boolean named, String name) throws IdentityException
   {
      Set<IdentityObjectRelationship> relationships = new HashSet<IdentityObjectRelationship>();
     
      EntityManager em = getEntityManager(ctx);
     
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<?> criteria = builder.createQuery(relationshipClass);
      Root<?> root = criteria.from(relationshipClass);
     
      Property<?> relationshipFromProp = modelProperties.get(PROPERTY_RELATIONSHIP_FROM);
      Property<?> relationshipToProp = modelProperties.get(PROPERTY_RELATIONSHIP_TO);
      Property<?> relationshipTypeProp = modelProperties.get(PROPERTY_RELATIONSHIP_TYPE);
      Property<?> relationshipNameProp = modelProperties.get(PROPERTY_RELATIONSHIP_NAME);
     
      List<Predicate> predicates = new ArrayList<Predicate>();
     
      if (parent)
      {
         predicates.add(builder.equal(root.get(relationshipFromProp.getName()),
               lookupIdentity(identity, em)));
      }
      else
      {
         predicates.add(builder.equal(root.get(relationshipToProp.getName()),
               lookupIdentity(identity, em)));
      }
           
      if (relationshipType != null)
      {
         predicates.add(builder.equal(root.get(relationshipTypeProp.getName()),
               lookupRelationshipType(relationshipType, em)));
      }
     
      if (named)
      {
         if (name != null)
         {
            predicates.add(builder.equal(root.get(relationshipNameProp.getName()),
               name));
         }
         else
         {
            predicates.add(builder.isNotNull(root.get(relationshipNameProp.getName())));
         }
      }
     
      criteria.where(predicates.toArray(new Predicate[predicates.size()]));
     
      List<?> results = em.createQuery(criteria).getResultList();
     
      EntityToSpiConverter converter = new EntityToSpiConverter();
     
      for (Object result : results)
      {
         IdentityObjectRelationship relationship = new IdentityObjectRelationshipImpl(
               converter.convertToIdentityObject(relationshipFromProp.getValue(result)),
               converter.convertToIdentityObject(relationshipToProp.getValue(result)),
               (String) relationshipNameProp.getValue(result),
               converter.convertToRelationshipType(relationshipTypeProp.getValue(result))        
         );
        
         relationships.add(relationship);
      }
     
      return relationships;
   }

   public void updateCredential(IdentityStoreInvocationContext ctx,
         IdentityObject identityObject, IdentityObjectCredential credential)
         throws IdentityException
   {
      EntityManager em = getEntityManager(ctx);
     
      Property<Object> credentialValue = modelProperties.get(PROPERTY_CREDENTIAL_VALUE);
     
      if (credentialClass != null)
      {
         Property<Object> credentialIdentity = modelProperties.get(PROPERTY_CREDENTIAL_IDENTITY);
         Property<Object> credentialType = modelProperties.get(PROPERTY_CREDENTIAL_TYPE);
         Object identity = lookupIdentity(identityObject, em);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(credentialClass);
         Root<?> root = criteria.from(credentialClass);
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(credentialIdentity.getName()),
               identity));
        
         if (credentialType != null)
         {
            if (String.class.equals(credentialType.getJavaClass()))
            {
               predicates.add(builder.equal(root.get(credentialType.getName()),
                     credential.getType().getName()));
            }
            else
            {
               predicates.add(builder.equal(root.get(credentialType.getName()),
                     lookupCredentialTypeEntity(credential.getType().getName(), em)));
            }
         }
        
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));
        
         List<?> results = em.createQuery(criteria).getResultList();
        
         if (results.isEmpty())
         {
            // The credential doesn't exist, let's create it
            try
            {
               Object newCredential = credentialClass.newInstance();
               credentialIdentity.setValue(newCredential, identity);
               credentialValue.setValue(newCredential, credential.getValue());
               credentialType.setValue(newCredential,
                     lookupCredentialTypeEntity(credential.getType().getName(), em));
              
               em.persist(newCredential);
            }
            catch (IllegalAccessException ex)
            {
               throw new IdentityException("Error updating credential - could " +
                     "not create credential instance", ex);
            }
            catch (InstantiationException ex)
            {
               throw new IdentityException("Error updating credential - could " +
                     "not create credential instance", ex);
            }
         }
         else
         {
            // TODO there shouldn't be multiple credentials with the same type,
            // but if there are, we need to deal with it somehow.. for now just use the first one
           
            Object result = results.get(0);
            credentialValue.setValue(result, credential.getValue());
           
            em.merge(result);
         }
      }
      else
      {
         // The credential is stored in the identity class, update it there
        
         Property<Object> credentialProp = modelProperties.get(PROPERTY_CREDENTIAL_VALUE);
         Object identity = lookupIdentity(identityObject, em);
        
         credentialProp.setValue(identity, credential.getValue());
        
         em.merge(identity);        
      }

   }

   public boolean validateCredential(IdentityStoreInvocationContext ctx,
         IdentityObject identityObject, IdentityObjectCredential credential)
         throws IdentityException
   {
      EntityManager em = getEntityManager(ctx);

      Property<?> credentialValue = modelProperties.get(PROPERTY_CREDENTIAL_VALUE);
     
      // Either credentials are stored in their own class...
      if (credentialClass != null)
      {
         Property<?> credentialIdentity = modelProperties.get(PROPERTY_CREDENTIAL_IDENTITY);
         Property<?> credentialType = modelProperties.get(PROPERTY_CREDENTIAL_TYPE);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(credentialClass);
         Root<?> root = criteria.from(credentialClass);
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(credentialIdentity.getName()),
               lookupIdentity(identityObject, em)));
        
         if (credentialType != null)
         {
            if (String.class.equals(credentialType.getJavaClass()))
            {
               predicates.add(builder.equal(root.get(credentialType.getName()),
                     credential.getType().getName()));
            }
            else
            {
               predicates.add(builder.equal(root.get(credentialType.getName()),
                     lookupCredentialTypeEntity(credential.getType().getName(), em)));
            }
         }
        
         criteria.where(predicates.toArray(new Predicate[0]));
        
         List<?> results = em.createQuery(criteria).getResultList();
        
         if (results.isEmpty()) return false;
        
         // TODO this only supports plain text passwords
        
         for (Object result : results)
         {
            Object val = credentialValue.getValue(result);
            if (val.equals(credential.getValue())) return true;
         }
      }
      // or they're stored in the identity class
      else
      {
         Property<?> identityNameProp = modelProperties.get(PROPERTY_IDENTITY_NAME);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(credentialValue.getDeclaringClass());
        
         Root<?> root = criteria.from(credentialValue.getDeclaringClass());
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(identityNameProp.getName()),
               identityObject.getName()));
        
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));
        
         Object result = em.createQuery(criteria).getSingleResult();
        
         Object val = credentialValue.getValue(result);
         if (val.equals(credential.getValue())) return true;
      }

      return false;
   }

   public void addAttributes(IdentityStoreInvocationContext ctx,
         IdentityObject identityObject, IdentityObjectAttribute[] attributes)
         throws IdentityException
   {
      try
      {
         EntityManager em = getEntityManager(ctx);
        
         Object identity = lookupIdentity(identityObject, em);
        
         // TODO support for attributeProperties
        
         if (attributeClass != null)
         {
            Property<Object> attributeIdentityProp = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
            Property<Object> attributeNameProp = modelProperties.get(PROPERTY_ATTRIBUTE_NAME);
            Property<Object> attributeValueProp = modelProperties.get(PROPERTY_ATTRIBUTE_VALUE);
           
            for (IdentityObjectAttribute attrib : attributes)
            {
               if (attrib.getSize() == 1)
               {
                  Object attribute = attributeClass.newInstance();
                  attributeIdentityProp.setValue(attribute, identity);
                  attributeNameProp.setValue(attribute, attrib.getName());
                  attributeValueProp.setValue(attribute, attrib.getValue());
                  em.persist(attribute);
               }
               else
               {
                  for (Object value : attrib.getValues())
                  {
                     Object attribute = attributeClass.newInstance();
                     attributeIdentityProp.setValue(attribute, identity);
                     attributeNameProp.setValue(attribute, attrib.getName());
                     attributeValueProp.setValue(attribute, value);
                     em.persist(attribute);  
                  }
               }
            }
         }
      }
      catch (Exception e)
      {
         throw new IdentityException("Error while adding attributes.", e);
      }     
   }

   public IdentityObjectAttribute getAttribute(IdentityStoreInvocationContext ctx,
         IdentityObject identity, String name) throws IdentityException
   {
      EntityManager em = getEntityManager(ctx);
     
      Property<?> attributeProperty = attributeProperties.get(name);
      if (attributeProperty != null)
      {
         // TODO implement attribute search for attributes scattered across the model
        
        
         return new SimpleAttribute(name);
      }
      else
      {
         // If there is no attributeClass set, we have nowhere else to look - return an empty attribute
         if (attributeClass == null) return new SimpleAttribute(name);
        
         Property<?> attributeIdentityProp = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
         Property<?> attributeNameProp = modelProperties.get(PROPERTY_ATTRIBUTE_NAME);
         Property<?> attributeValueProp = modelProperties.get(PROPERTY_ATTRIBUTE_VALUE);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(attributeClass);
         Root<?> root = criteria.from(attributeClass);
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(attributeIdentityProp.getName()),
               lookupIdentity(identity, em)));
         predicates.add(builder.equal(root.get(attributeNameProp.getName()),
               name));
        
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));
        
         List<?> results = em.createQuery(criteria).getResultList();
        
         if (results.size() == 0)
         {
            // No results found, return an empty attribute value
            return new SimpleAttribute(name);           
         }
         else if (results.size() == 1)
         {
            return new SimpleAttribute(name, attributeValueProp.getValue(results.get(0)));
         }
         else
         {
            Collection<Object> values = new ArrayList<Object>();
            for (Object result : results)
            {
               values.add(attributeValueProp.getValue(result));              
            }
           
            return new SimpleAttribute(name, values.toArray());
         }
      }
   }

   public Map<String, IdentityObjectAttribute> getAttributes(
         IdentityStoreInvocationContext ctx,
         IdentityObject identityObject) throws IdentityException
   {
      Map<String, IdentityObjectAttribute> attributes = new HashMap<String,IdentityObjectAttribute>();
     
      EntityManager em = getEntityManager(ctx);
     
      Object identity = lookupIdentity(identityObject, em);
     
      // TODO iterate through attributeProperties
     
      if (attributeClass != null)
      {
         Property<?> attributeIdentityProp = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
         Property<?> attributeNameProp = modelProperties.get(PROPERTY_ATTRIBUTE_NAME);
         Property<?> attributeValueProp = modelProperties.get(PROPERTY_ATTRIBUTE_VALUE);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(attributeClass);
         Root<?> root = criteria.from(attributeClass);
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(attributeIdentityProp.getName()),
               identity));
        
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));
        
         List<?> results = em.createQuery(criteria).getResultList();

         for (Object result : results)
         {
            String name = attributeNameProp.getValue(result).toString();
            Object value = attributeValueProp.getValue(result);
           
            if (attributes.containsKey(name))
            {
               IdentityObjectAttribute attr = attributes.get(name);
               attr.addValue(value);
            }
            else
            {
               attributes.put(name, new SimpleAttribute(name, value));
            }
         }
      }
     
      return attributes;
   }

   public void removeAttributes(IdentityStoreInvocationContext ctx,
         IdentityObject identityObject, String[] attributeNames)
         throws IdentityException
   {     
      EntityManager em = getEntityManager(ctx);
     
      Object identity = lookupIdentity(identityObject, em);
           
      if (attributeClass != null)
      {
         Property<?> attributeIdentityProp = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
         Property<?> attributeNameProp = modelProperties.get(PROPERTY_ATTRIBUTE_NAME);
        
         CriteriaBuilder builder = em.getCriteriaBuilder();
         CriteriaQuery<?> criteria = builder.createQuery(attributeClass);
         Root<?> root = criteria.from(attributeClass);
        
         List<Predicate> predicates = new ArrayList<Predicate>();
         predicates.add(builder.equal(root.get(attributeIdentityProp.getName()),
               identity));
        
         criteria.where(predicates.toArray(new Predicate[predicates.size()]));
        
         List<?> results = em.createQuery(criteria).getResultList();
        
         for (Object result : results)
         {
            String name = attributeNameProp.getValue(result).toString();
            for (String n : attributeNames)
            {
               if (name != null && name.equals(n))
               {
                  em.remove(result);
                  break;
               }
            }           
         }
      }
   }

   public void updateAttributes(IdentityStoreInvocationContext ctx,
         IdentityObject identityObject, IdentityObjectAttribute[] attributes)
         throws IdentityException
   {    
      try
      {
         EntityManager em = getEntityManager(ctx);
        
         Object identity = lookupIdentity(identityObject, em);
        
         if (attributeClass != null)
         {
            Property<Object> attributeIdentityProp = modelProperties.get(PROPERTY_ATTRIBUTE_IDENTITY);
            Property<Object> attributeNameProp = modelProperties.get(PROPERTY_ATTRIBUTE_NAME);
            Property<Object> attributeValueProp = modelProperties.get(PROPERTY_ATTRIBUTE_VALUE);
            Property<Object> attributeTypeProp = modelProperties.get(PROPERTY_ATTRIBUTE_TYPE);
           
            for (IdentityObjectAttribute attrib : attributes)
            {
               CriteriaBuilder builder = em.getCriteriaBuilder();
               CriteriaQuery<?> criteria = builder.createQuery(attributeClass);
               Root<?> root = criteria.from(attributeClass);
              
               List<Predicate> predicates = new ArrayList<Predicate>();
               predicates.add(builder.equal(root.get(attributeIdentityProp.getName()),
                     identity));
               predicates.add(builder.equal(root.get(attributeNameProp.getName()),
                     attrib.getName()));
              
               criteria.where(predicates.toArray(new Predicate[predicates.size()]));
              
               List<?> results = em.createQuery(criteria).getResultList();
     
               // All existing attribute values should be overwritten, so we
               // will first remove them, then add the new values
              
               if (!results.isEmpty())
               {
                  for (Object result : results)
                  {
                     em.remove(result);
                  }
               }
              
               for (Object value : attrib.getValues())
               {
                  Object attribute = attributeClass.newInstance();
                  attributeIdentityProp.setValue(attribute, identity);
                  attributeNameProp.setValue(attribute, attrib.getName());

                  // If there is an attribute type property, then determine the value type
                  // TODO this is messy, refactor it by abstracting into a utility class
                  if (attributeTypeProp != null)
                  {
                     if (String.class.equals(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, value.toString());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_TEXT);
                     }
                     else if (Boolean.class.equals(value.getClass()) || Boolean.TYPE.equals(value.getClass()))
                     { 
                        attributeValueProp.setValue(attribute, Boolean.toString((Boolean) value));
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_BOOLEAN);
                     }
                     else if (Date.class.isAssignableFrom(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, "" + ((Date) value).getTime());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_DATE);
                     }
                     else if (Integer.class.equals(value.getClass()) || Integer.TYPE.equals(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, ((Integer) value).toString());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_INT);
                     }
                     else if (Long.class.equals(value.getClass()) || Long.TYPE.equals(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, ((Long) value).toString());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_LONG);
                     }
                     else if (Float.class.equals(value.getClass()) || Float.TYPE.equals(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, ((Float) value).toString());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_FLOAT);
                     }
                     else if (Double.class.equals(value.getClass()) || Double.TYPE.equals(value.getClass()))
                     {
                        attributeValueProp.setValue(attribute, ((Double) value).toString());
                        attributeTypeProp.setValue(attribute, ATTRIBUTE_TYPE_DOUBLE);
                     }
                     else
                     {
                        throw new IdentityException("Could not persist attribute value - unsupported attribute value type " +
                              value.getClass());
                     }
                  }
                  else
                  {
                     attributeValueProp.setValue(attribute, value.toString());
                  }
                 
                  em.persist(attribute);  
               }
            }
         }
      }
      catch (Exception e)
      {
         throw new IdentityException("Error while updating attributes.", e);
      }
   }

   public IdentityStoreSession createIdentityStoreSession()
         throws IdentityException
   {
      return createIdentityStoreSession(null);
   }

   public Collection<IdentityObject> findIdentityObject(
         IdentityStoreInvocationContext invocationCxt, IdentityObject identity,
         IdentityObjectRelationshipType relationshipType, boolean parent,
         IdentityObjectSearchCriteria criteria) throws IdentityException
   {
      List<IdentityObject> objs = new ArrayList<IdentityObject>();
     
      System.out.println("*** Invoked unimplemented method findIdentityObject()");
     
      // TODO Auto-generated method stub
      return objs;
   }
  
   public IdentityObject findIdentityObjectByUniqueAttribute(
         IdentityStoreInvocationContext invocationCtx,
         IdentityObjectType identityObjectType,
         IdentityObjectAttribute attribute) throws IdentityException
   {
      // TODO Auto-generated method stub
      return null;
   }  
  
   public Map<String, IdentityObjectAttributeMetaData> getAttributesMetaData(
         IdentityStoreInvocationContext invocationContext,
         IdentityObjectType identityType)
   {
      // TODO Auto-generated method stub
      return null;
   }

   public Set<String> getSupportedAttributeNames(
         IdentityStoreInvocationContext invocationContext,
         IdentityObjectType identityType) throws IdentityException
   {
      // TODO Auto-generated method stub
      return null;
   }
  
   public int getIdentityObjectsCount(
         IdentityStoreInvocationContext invocationCtx,
         IdentityObjectType identityType) throws IdentityException
   {
      System.out.println("*** Invoked unimplemented method getIdentityObjectsCount()");
      // TODO Auto-generated method stub
      return 0;
   }

   public Map<String, String> getRelationshipNameProperties(
         IdentityStoreInvocationContext ctx, String name)
         throws IdentityException, OperationNotSupportedException
   {
      throw new OperationNotSupportedException("getRelationshipNameProperties() not supported");
   }
  
   public void setRelationshipNameProperties(
         IdentityStoreInvocationContext ctx, String name,
         Map<String, String> properties) throws IdentityException,
         OperationNotSupportedException
   {
      throw new OperationNotSupportedException("setRelationshipNameProperties() not supported");     
   }

   public void setRelationshipProperties(IdentityStoreInvocationContext ctx,
         IdentityObjectRelationship relationship, Map<String, String> properties)
         throws IdentityException, OperationNotSupportedException
   {
      throw new OperationNotSupportedException("setRelationshipProperties() not supported");
   }
  
   public void removeRelationshipNameProperties(
         IdentityStoreInvocationContext ctx, String name, Set<String> properties)
         throws IdentityException, OperationNotSupportedException
   {
      throw new OperationNotSupportedException("removeRelationshipNameProperties() not supported");
   }

   public void removeRelationshipProperties(IdentityStoreInvocationContext ctx,
         IdentityObjectRelationship relationship, Set<String> properties)
         throws IdentityException, OperationNotSupportedException
   {
      throw new OperationNotSupportedException("removeRelationshipProperties() not supported");
   }  
}
TOP

Related Classes of org.jboss.seam.security.management.picketlink.JpaIdentityStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.