(targetClass);
+ }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java b/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java
new file mode 100644
index 0000000000..5aa38337a8
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java
@@ -0,0 +1,162 @@
+package org.keycloak.models.utils.reflection;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Queries a target class for properties that match certain criteria. A property may either be a private or public
+ * field, declared by the target class or inherited from a superclass, or a public method declared by the target class
+ * or inherited from any of its superclasses. For properties that are exposed via a method, the property must be a
+ * JavaBean style property, i.e. it must provide both an accessor and mutator method according to the JavaBean
+ * specification.
This class is not thread-safe, however the result returned by the getResultList() method
+ * is.
+ *
+ * @see PropertyQueries
+ * @see PropertyCriteria
+ */
+public class PropertyQuery {
+ private final Class> targetClass;
+ private final List criteria;
+
+ PropertyQuery(Class> targetClass) {
+ if (targetClass == null) {
+ throw new IllegalArgumentException("targetClass parameter may not be null");
+ }
+
+ this.targetClass = targetClass;
+ this.criteria = new ArrayList();
+ }
+
+ /**
+ * Add a criteria to query
+ *
+ * @param criteria the criteria to add
+ */
+ public PropertyQuery addCriteria(PropertyCriteria criteria) {
+ this.criteria.add(criteria);
+ return this;
+ }
+
+ /**
+ * Get the first result from the query, causing the query to be run.
+ *
+ * @return the first result, or null if there are no results
+ */
+ public Property getFirstResult() {
+ Map> results = getResultList();
+ return results.isEmpty() ? null : results.values().iterator().next();
+ }
+
+ /**
+ * Get the first result from the query that is not marked as read only, causing the query to be run.
+ *
+ * @return the first writable result, or null if there are no results
+ */
+ public Property getFirstWritableResult() {
+ Map> results = getWritableResultList();
+ return results.isEmpty() ? null : results.values().iterator().next();
+ }
+
+ /**
+ * Get a single result from the query, causing the query to be run. An exception is thrown if the query does not
+ * return exactly one result.
+ *
+ * @return the single result
+ *
+ * @throws RuntimeException if the query does not return exactly one result
+ */
+ public Property getSingleResult() {
+ Map> results = getResultList();
+ if (results.size() == 1) {
+ return results.values().iterator().next();
+ } else if (results.isEmpty()) {
+ throw new RuntimeException(
+ "Expected one property match, but the criteria did not match any properties on " +
+ targetClass.getName());
+ } else {
+ throw new RuntimeException("Expected one property match, but the criteria matched " + results.size() +
+ " properties on " + targetClass.getName());
+ }
+ }
+
+ /**
+ * Get a single result from the query that is not marked as read only, causing the query to be run. An exception is
+ * thrown if the query does not return exactly one result.
+ *
+ * @return the single writable result
+ *
+ * @throws RuntimeException if the query does not return exactly one result
+ */
+ public Property getWritableSingleResult() {
+ Map> results = getWritableResultList();
+ if (results.size() == 1) {
+ return results.values().iterator().next();
+ } else if (results.isEmpty()) {
+ throw new RuntimeException(
+ "Expected one property match, but the criteria did not match any properties on " +
+ targetClass.getName());
+ } else {
+ throw new RuntimeException("Expected one property match, but the criteria matched " +
+ results.size() + " properties on " + targetClass.getName());
+ }
+ }
+
+ /**
+ * Get the result from the query, causing the query to be run.
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ public Map> getResultList() {
+ return getResultList(false);
+ }
+
+ /**
+ * Get the non read only results from the query, causing the query to be run.
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ public Map> getWritableResultList() {
+ return getResultList(true);
+ }
+
+ /**
+ * Get the result from the query, causing the query to be run.
+ *
+ * @param writable if this query should only return properties that are not read only
+ *
+ * @return the results, or an empty list if there are no results
+ */
+ private Map> getResultList(boolean writable) {
+ Map> properties = new HashMap>();
+
+ // First check public accessor methods (we ignore private methods)
+ for (Method method : targetClass.getMethods()) {
+ if (!(method.getName().startsWith("is") || method.getName().startsWith("get"))) {
+ continue;
+ }
+
+ boolean match = true;
+ for (PropertyCriteria c : criteria) {
+ if (!c.methodMatches(method)) {
+ match = false;
+ break;
+ }
+ }
+
+ if (match) {
+ MethodProperty property = Properties.createProperty(method);
+
+ if (!writable || !property.isReadOnly()) {
+ properties.put(property.getName(), property);
+ }
+ }
+ }
+
+ return Collections.unmodifiableMap(properties);
+ }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/reflection/Reflections.java b/model/api/src/main/java/org/keycloak/models/utils/reflection/Reflections.java
new file mode 100644
index 0000000000..e8491a4136
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/reflection/Reflections.java
@@ -0,0 +1,980 @@
+package org.keycloak.models.utils.reflection;
+
+import java.beans.Introspector;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class for working with JDK Reflection and also CDI's {link Annotated} metadata.
+ */
+public class Reflections {
+ /**
+ * An empty array of type {@link java.lang.annotation.Annotation}, useful converting lists to arrays.
+ */
+ public static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
+
+ /**
+ * An empty array of type {@link Object}, useful for converting lists to arrays.
+ */
+ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+ public static final Type[] EMPTY_TYPES = {};
+
+ public static final Class>[] EMPTY_CLASSES = new Class>[0];
+
+ private Reflections() {
+ }
+
+ /**
+ * Perform a runtime cast. Similar to {@link Class#cast(Object)}, but useful when you do not have a {@link
+ * Class} object for type you wish to cast to.
{@link Class#cast(Object)} should be used if possible
+ *
+ *
+ * @param the type to cast to
+ * @param obj the object to perform the cast on
+ *
+ * @return the casted object
+ *
+ * @throws ClassCastException if the type T is not a subtype of the object
+ * @see Class#cast(Object)
+ */
+ @SuppressWarnings("unchecked")
+ public static T cast(Object obj) {
+ return (T) obj;
+ }
+
+ /**
+ * Get all the declared fields on the class hierarchy. This will return overridden fields.
+ *
+ * @param clazz The class to search
+ *
+ * @return the set of all declared fields or an empty set if there are none
+ */
+ public static Set getAllDeclaredFields(Class> clazz) {
+ HashSet fields = new HashSet();
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ for (Field a : c.getDeclaredFields()) {
+ fields.add(a);
+ }
+ }
+ return fields;
+ }
+
+ /**
+ * Search the class hierarchy for a field with the given name. Will return the nearest match, starting with the
+ * class specified and searching up the hierarchy.
+ *
+ * @param clazz The class to search
+ * @param name The name of the field to search for
+ *
+ * @return The field found, or null if no field is found
+ */
+ public static Field findDeclaredField(Class> clazz, String name) {
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ try {
+ return c.getDeclaredField(name);
+ } catch (NoSuchFieldException e) {
+ // No-op, we continue looking up the class hierarchy
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search for annotations with the specified meta annotation type
+ *
+ * @param annotations The annotation set to search
+ * @param metaAnnotationType The type of the meta annotation to search for
+ *
+ * @return The set of annotations with the specified meta annotation, or an empty set if none are found
+ */
+ public static Set getAnnotationsWithMetaAnnotation(
+ Set annotations, Class extends Annotation> metaAnnotationType) {
+ Set set = new HashSet();
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType().isAnnotationPresent(metaAnnotationType)) {
+ set.add(annotation);
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Determine if a method exists in a specified class hierarchy
+ *
+ * @param clazz The class to search
+ * @param name The name of the method
+ *
+ * @return true if a method is found, otherwise false
+ */
+ public static boolean methodExists(Class> clazz, String name) {
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.getName().equals(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get all the declared methods on the class hierarchy. This will return overridden methods.
+ *
+ * @param clazz The class to search
+ *
+ * @return the set of all declared methods or an empty set if there are none
+ */
+ public static Set getAllDeclaredMethods(Class> clazz) {
+ HashSet methods = new HashSet();
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ for (Method a : c.getDeclaredMethods()) {
+ methods.add(a);
+ }
+ }
+ return methods;
+ }
+
+ /**
+ * Search the class hierarchy for a method with the given name and arguments. Will return the nearest match,
+ * starting with the class specified and searching up the hierarchy.
+ *
+ * @param clazz The class to search
+ * @param name The name of the method to search for
+ * @param args The arguments of the method to search for
+ *
+ * @return The method found, or null if no method is found
+ */
+ public static Method findDeclaredMethod(Class> clazz, String name, Class>... args) {
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ try {
+ return c.getDeclaredMethod(name, args);
+ } catch (NoSuchMethodException e) {
+ // No-op, continue the search
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search the class hierarchy for a constructor with the given arguments. Will return the nearest match, starting
+ * with the class specified and searching up the hierarchy.
+ *
+ * @param clazz The class to search
+ * @param args The arguments of the constructor to search for
+ *
+ * @return The constructor found, or null if no constructor is found
+ */
+ public static Constructor> findDeclaredConstructor(Class> clazz, Class>... args) {
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ try {
+ return c.getDeclaredConstructor(args);
+ } catch (NoSuchMethodException e) {
+ // No-op, continue the search
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the declared constructors on the class hierarchy. This will return overridden constructors.
+ *
+ * @param clazz The class to search
+ *
+ * @return the set of all declared constructors or an empty set if there are none
+ */
+ public static Set> getAllDeclaredConstructors(Class> clazz) {
+ HashSet> constructors = new HashSet>();
+ for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
+ for (Constructor> constructor : c.getDeclaredConstructors()) {
+ constructors.add(constructor);
+ }
+ }
+ return constructors;
+ }
+
+ /**
+ * Get the type of the member
+ *
+ * @param member The member
+ *
+ * @return The type of the member
+ *
+ * @throws UnsupportedOperationException if the member is not a field, method, or constructor
+ */
+ public static Class> getMemberType(Member member) {
+ if (member instanceof Field) {
+ return ((Field) member).getType();
+ } else if (member instanceof Method) {
+ return ((Method) member).getReturnType();
+ } else if (member instanceof Constructor>) {
+ return ((Constructor>) member).getDeclaringClass();
+ } else {
+ throw new UnsupportedOperationException("Cannot operate on a member of type " + member.getClass());
+ }
+ }
+
+ /**
+ * Loads and initializes a class for the given name.
If the Thread Context Class Loader is
+ * available, it will be used, otherwise the classloader used to load {@link Reflections} will be used
+ * It is also possible to specify additional classloaders to attempt to load the class with. If the first attempt
+ * fails, then these additional loaders are tried in order.
+ *
+ * @param name the name of the class to load
+ * @param loaders additional classloaders to use to attempt to load the class
+ *
+ * @return the class object
+ *
+ * @throws ClassNotFoundException if the class cannot be found
+ */
+ public static Class classForName(String name, ClassLoader... loaders) throws ClassNotFoundException {
+ try {
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ return (Class) Class.forName(name, true, Thread.currentThread().getContextClassLoader());
+ } else {
+ return (Class) Class.forName(name);
+ }
+ } catch (ClassNotFoundException e) {
+ for (ClassLoader l : loaders) {
+ try {
+ return (Class) Class.forName(name, true, l);
+ } catch (ClassNotFoundException ex) {
+
+ }
+ }
+ }
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ throw new ClassNotFoundException("Could not load class " + name +
+ " with the context class loader " + Thread.currentThread().getContextClassLoader().toString() +
+ " or any of the additional ClassLoaders: " + Arrays.toString(loaders));
+ } else {
+ throw new ClassNotFoundException("Could not load class " + name +
+ " using Class.forName or using any of the additional ClassLoaders: " +
+ Arrays.toString(loaders));
+ }
+ }
+
+ private static String buildInvokeMethodErrorMessage(Method method, Object obj, Object... args) {
+ StringBuilder message = new StringBuilder(
+ String.format("Exception invoking method [%s] on object [%s], using arguments [",
+ method.getName(), obj));
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ message.append((i > 0 ? "," : "") + args[i]);
+ }
+ }
+ message.append("]");
+ return message.toString();
+ }
+
+ /**
+ *