You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/05/25 16:50:56 UTC

[20/29] syncope git commit: [SYNCOPE-666] Initial commit, Travis CI builds disabled

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
new file mode 100644
index 0000000..d91b948
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -0,0 +1,811 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.Query;
+import javax.persistence.TemporalType;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.misc.RealmUtils;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.ReflectionUtils;
+
+@Repository
+public class JPAAnySearchDAO extends AbstractDAO<Any<?, ?, ?>, Long> implements AnySearchDAO {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(AnySearchDAO.class);
+
+    private static final String EMPTY_ATTR_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
+
+    @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private PlainSchemaDAO schemaDAO;
+
+    @Autowired
+    private AnyUtilsFactory anyUtilsFactory;
+
+    private String getAdminRealmsFilter(final Set<String> adminRealms, final SearchSupport svs) {
+        Set<Long> realmKeys = new HashSet<>();
+        for (String realmPath : RealmUtils.normalize(adminRealms)) {
+            Realm realm = realmDAO.find(realmPath);
+            if (realm == null) {
+                LOG.warn("Ignoring invalid realm {}", realmPath);
+            } else {
+                CollectionUtils.collect(realmDAO.findDescendants(realm), new Transformer<Realm, Long>() {
+
+                    @Override
+                    public Long transform(final Realm descendant) {
+                        return descendant.getKey();
+                    }
+                }, realmKeys);
+            }
+        }
+
+        StringBuilder adminRealmFilter = new StringBuilder().
+                append("SELECT any_id FROM ").append(svs.field().name).
+                append(" WHERE realm_id IN (SELECT id AS realm_id FROM Realm");
+
+        boolean firstRealm = true;
+        for (Long realmKey : realmKeys) {
+            if (firstRealm) {
+                adminRealmFilter.append(" WHERE");
+                firstRealm = false;
+            } else {
+                adminRealmFilter.append(" OR");
+            }
+            adminRealmFilter.append(" id = ").append(realmKey);
+        }
+
+        adminRealmFilter.append(')');
+
+        return adminRealmFilter.toString();
+    }
+
+    @Override
+    public int count(final Set<String> adminRealms, final SearchCond searchCondition, final AnyTypeKind typeKind) {
+        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
+
+        // 1. get the query string from the search condition
+        SearchSupport svs = new SearchSupport(typeKind);
+        StringBuilder queryString = getQuery(searchCondition, parameters, typeKind, svs);
+
+        // 2. take into account administrative realms
+        queryString.insert(0, "SELECT u.any_id FROM (");
+        queryString.append(") u WHERE any_id IN (");
+        queryString.append(getAdminRealmsFilter(adminRealms, svs)).append(')');
+
+        // 3. prepare the COUNT query
+        queryString.insert(0, "SELECT COUNT(any_id) FROM (");
+        queryString.append(") count_any_id");
+
+        Query countQuery = entityManager.createNativeQuery(queryString.toString());
+        fillWithParameters(countQuery, parameters);
+
+        return ((Number) countQuery.getSingleResult()).intValue();
+    }
+
+    @Override
+    public <T extends Any<?, ?, ?>> List<T> search(
+            final Set<String> adminRealms, final SearchCond searchCondition, final AnyTypeKind typeKind) {
+
+        return search(adminRealms, searchCondition, Collections.<OrderByClause>emptyList(), typeKind);
+    }
+
+    @Override
+    public <T extends Any<?, ?, ?>> List<T> search(
+            final Set<String> adminRealms, final SearchCond searchCondition, final List<OrderByClause> orderBy,
+            final AnyTypeKind typeKind) {
+
+        return search(adminRealms, searchCondition, -1, -1, orderBy, typeKind);
+    }
+
+    @Override
+    public <T extends Any<?, ?, ?>> List<T> search(
+            final Set<String> adminRealms, final SearchCond searchCondition, final int page, final int itemsPerPage,
+            final List<OrderByClause> orderBy, final AnyTypeKind typeKind) {
+
+        List<T> result = Collections.<T>emptyList();
+
+        if (adminRealms != null && !adminRealms.isEmpty()) {
+            LOG.debug("Search condition:\n{}", searchCondition);
+
+            if (searchCondition != null && searchCondition.isValid()) {
+                try {
+                    result = doSearch(adminRealms, searchCondition, page, itemsPerPage, orderBy, typeKind);
+                } catch (Exception e) {
+                    LOG.error("While searching for {}", typeKind, e);
+                }
+            } else {
+                LOG.error("Invalid search condition:\n{}", searchCondition);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public <T extends Any<?, ?, ?>> boolean matches(
+            final T subject, final SearchCond searchCondition, final AnyTypeKind typeKind) {
+
+        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
+
+        // 1. get the query string from the search condition
+        SearchSupport svs = new SearchSupport(typeKind);
+        StringBuilder queryString = getQuery(searchCondition, parameters, typeKind, svs);
+
+        boolean matches;
+        if (queryString.length() == 0) {
+            // Could be empty: got into a group search with a single membership condition ...
+            matches = false;
+        } else {
+            // 2. take into account the passed user
+            queryString.insert(0, "SELECT u.any_id FROM (");
+            queryString.append(") u WHERE any_id=?").append(setParameter(parameters, subject.getKey()));
+
+            // 3. prepare the search query
+            Query query = entityManager.createNativeQuery(queryString.toString());
+
+            // 4. populate the search query with parameter values
+            fillWithParameters(query, parameters);
+
+            // 5. executes query
+            matches = !query.getResultList().isEmpty();
+        }
+
+        return matches;
+    }
+
+    private int setParameter(final List<Object> parameters, final Object parameter) {
+        int key;
+        synchronized (parameters) {
+            parameters.add(parameter);
+            key = parameters.size();
+        }
+
+        return key;
+    }
+
+    private void fillWithParameters(final Query query, final List<Object> parameters) {
+        for (int i = 0; i < parameters.size(); i++) {
+            if (parameters.get(i) instanceof Date) {
+                query.setParameter(i + 1, (Date) parameters.get(i), TemporalType.TIMESTAMP);
+            } else if (parameters.get(i) instanceof Boolean) {
+                query.setParameter(i + 1, ((Boolean) parameters.get(i))
+                        ? 1
+                        : 0);
+            } else {
+                query.setParameter(i + 1, parameters.get(i));
+            }
+        }
+    }
+
+    private StringBuilder buildSelect(final OrderBySupport orderBySupport) {
+        final StringBuilder select = new StringBuilder("SELECT u.any_id");
+
+        for (OrderBySupport.Item obs : orderBySupport.items) {
+            select.append(',').append(obs.select);
+        }
+        select.append(" FROM ");
+
+        return select;
+    }
+
+    private StringBuilder buildWhere(final OrderBySupport orderBySupport) {
+        final StringBuilder where = new StringBuilder(" u");
+        for (SearchSupport.SearchView searchView : orderBySupport.views) {
+            where.append(',').append(searchView.name).append(' ').append(searchView.alias);
+        }
+        where.append(" WHERE ");
+        for (SearchSupport.SearchView searchView : orderBySupport.views) {
+            where.append("u.any_id=").append(searchView.alias).append(".any_id AND ");
+        }
+
+        for (OrderBySupport.Item obs : orderBySupport.items) {
+            if (StringUtils.isNotBlank(obs.where)) {
+                where.append(obs.where).append(" AND ");
+            }
+        }
+        where.append("u.any_id IN (");
+
+        return where;
+    }
+
+    private StringBuilder buildOrderBy(final OrderBySupport orderBySupport) {
+        final StringBuilder orderBy = new StringBuilder();
+
+        for (OrderBySupport.Item obs : orderBySupport.items) {
+            orderBy.append(obs.orderBy).append(',');
+        }
+        if (!orderBySupport.items.isEmpty()) {
+            orderBy.insert(0, " ORDER BY ");
+            orderBy.deleteCharAt(orderBy.length() - 1);
+        }
+
+        return orderBy;
+    }
+
+    private OrderBySupport parseOrderBy(final AnyTypeKind type, final SearchSupport svs,
+            final List<OrderByClause> orderByClauses) {
+
+        final AnyUtils attrUtils = anyUtilsFactory.getInstance(type);
+
+        OrderBySupport orderBySupport = new OrderBySupport();
+
+        for (OrderByClause clause : orderByClauses) {
+            OrderBySupport.Item obs = new OrderBySupport.Item();
+
+            // Manage difference among external key attribute and internal JPA @Id
+            String fieldName = "key".equals(clause.getField()) ? "id" : clause.getField();
+
+            Field subjectField = ReflectionUtils.findField(attrUtils.anyClass(), fieldName);
+            if (subjectField == null) {
+                PlainSchema schema = schemaDAO.find(fieldName);
+                if (schema != null) {
+                    if (schema.isUniqueConstraint()) {
+                        orderBySupport.views.add(svs.uniqueAttr());
+
+                        obs.select = new StringBuilder().
+                                append(svs.uniqueAttr().alias).append('.').append(svs.fieldName(schema.getType())).
+                                append(" AS ").append(fieldName).toString();
+                        obs.where = new StringBuilder().
+                                append(svs.uniqueAttr().alias).
+                                append(".schema_name='").append(fieldName).append("'").toString();
+                        obs.orderBy = fieldName + " " + clause.getDirection().name();
+                    } else {
+                        orderBySupport.views.add(svs.attr());
+
+                        obs.select = new StringBuilder().
+                                append(svs.attr().alias).append('.').append(svs.fieldName(schema.getType())).
+                                append(" AS ").append(fieldName).toString();
+                        obs.where = new StringBuilder().
+                                append(svs.attr().alias).
+                                append(".schema_name='").append(fieldName).append("'").toString();
+                        obs.orderBy = fieldName + " " + clause.getDirection().name();
+                    }
+                }
+            } else {
+                orderBySupport.views.add(svs.field());
+
+                obs.select = svs.field().alias + "." + fieldName;
+                obs.where = StringUtils.EMPTY;
+                obs.orderBy = svs.field().alias + "." + fieldName + " " + clause.getDirection().name();
+            }
+
+            if (obs.isEmpty()) {
+                LOG.warn("Cannot build any valid clause from {}", clause);
+            } else {
+                orderBySupport.items.add(obs);
+            }
+        }
+
+        return orderBySupport;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Any<?, ?, ?>> List<T> doSearch(final Set<String> adminRealms,
+            final SearchCond nodeCond, final int page, final int itemsPerPage, final List<OrderByClause> orderBy,
+            final AnyTypeKind typeKind) {
+
+        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
+
+        // 1. get the query string from the search condition
+        SearchSupport svs = new SearchSupport(typeKind);
+        StringBuilder queryString = getQuery(nodeCond, parameters, typeKind, svs);
+
+        // 2. take into account administrative groups and ordering
+        OrderBySupport orderBySupport = parseOrderBy(typeKind, svs, orderBy);
+        if (queryString.charAt(0) == '(') {
+            queryString.insert(0, buildSelect(orderBySupport));
+            queryString.append(buildWhere(orderBySupport));
+        } else {
+            queryString.insert(0, buildSelect(orderBySupport).append('('));
+            queryString.append(')').append(buildWhere(orderBySupport));
+        }
+        queryString.
+                append(getAdminRealmsFilter(adminRealms, svs)).append(')').
+                append(buildOrderBy(orderBySupport));
+
+        // 3. prepare the search query
+        Query query = entityManager.createNativeQuery(queryString.toString());
+
+        // 4. page starts from 1, while setFirtResult() starts from 0
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+
+        if (itemsPerPage >= 0) {
+            query.setMaxResults(itemsPerPage);
+        }
+
+        // 5. populate the search query with parameter values
+        fillWithParameters(query, parameters);
+
+        // 6. Prepare the result (avoiding duplicates)
+        List<T> result = new ArrayList<>();
+
+        for (Object subjectKey : query.getResultList()) {
+            long actualKey;
+            if (subjectKey instanceof Object[]) {
+                actualKey = ((Number) ((Object[]) subjectKey)[0]).longValue();
+            } else {
+                actualKey = ((Number) subjectKey).longValue();
+            }
+
+            T subject = typeKind == AnyTypeKind.USER
+                    ? (T) userDAO.find(actualKey)
+                    : typeKind == AnyTypeKind.GROUP
+                            ? (T) groupDAO.find(actualKey)
+                            : (T) anyObjectDAO.find(actualKey);
+            if (subject == null) {
+                LOG.error("Could not find {} with id {}, even though returned by the native query",
+                        typeKind, actualKey);
+            } else {
+                if (!result.contains(subject)) {
+                    result.add(subject);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private StringBuilder getQuery(final SearchCond nodeCond, final List<Object> parameters,
+            final AnyTypeKind type, final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder();
+
+        switch (nodeCond.getType()) {
+
+            case LEAF:
+            case NOT_LEAF:
+                if (nodeCond.getRelationshipCond() != null
+                        && (AnyTypeKind.USER == type || AnyTypeKind.ANY_OBJECT == type)) {
+
+                    query.append(getQuery(nodeCond.getRelationshipCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                }
+                if (nodeCond.getMembershipCond() != null
+                        && (AnyTypeKind.USER == type || AnyTypeKind.ANY_OBJECT == type)) {
+
+                    query.append(getQuery(nodeCond.getMembershipCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                }
+                if (nodeCond.getRoleCond() != null && AnyTypeKind.USER == type) {
+                    query.append(getQuery(nodeCond.getRoleCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+                }
+                if (nodeCond.getResourceCond() != null) {
+                    query.append(getQuery(nodeCond.getResourceCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
+                }
+                if (nodeCond.getAttributeCond() != null) {
+                    query.append(getQuery(nodeCond.getAttributeCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
+                }
+                if (nodeCond.getAnyCond() != null) {
+                    query.append(getQuery(nodeCond.getAnyCond(),
+                            nodeCond.getType() == SearchCond.Type.NOT_LEAF, parameters, type, svs));
+                }
+                break;
+
+            case AND:
+                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, type, svs)).
+                        append(" AND any_id IN ( ").
+                        append(getQuery(nodeCond.getRightNodeCond(), parameters, type, svs)).
+                        append(")");
+                break;
+
+            case OR:
+                query.append(getQuery(nodeCond.getLeftNodeCond(), parameters, type, svs)).
+                        append(" OR any_id IN ( ").
+                        append(getQuery(nodeCond.getRightNodeCond(), parameters, type, svs)).
+                        append(")");
+                break;
+
+            default:
+        }
+
+        return query;
+    }
+
+    private String getQuery(final RelationshipCond cond, final boolean not, final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id ").append("FROM ").
+                append(svs.relationship().name).append(" WHERE ").
+                append("right_anyObject_id=?").append(setParameter(parameters, cond.getAnyObjectKey())).
+                append(')');
+
+        return query.toString();
+    }
+
+    private String getQuery(final MembershipCond cond, final boolean not, final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id ").append("FROM ").
+                append(svs.membership().name).append(" WHERE ").
+                append("group_id=?").append(setParameter(parameters, cond.getGroupKey())).
+                append(')');
+
+        if (not) {
+            query.append("AND any_id NOT IN (");
+        } else {
+            query.append("OR any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id ").append("FROM ").
+                append(svs.dyngroupmembership().name).append(" WHERE ").
+                append("group_id=?").append(setParameter(parameters, cond.getGroupKey())).
+                append(')');
+
+        return query.toString();
+    }
+
+    private String getQuery(final RoleCond cond, final boolean not, final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id ").append("FROM ").
+                append(svs.role().name).append(" WHERE ").
+                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append(')');
+
+        if (not) {
+            query.append("AND any_id NOT IN (");
+        } else {
+            query.append("OR any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id ").append("FROM ").
+                append(svs.dynrolemembership().name).append(" WHERE ").
+                append("role_id=?").append(setParameter(parameters, cond.getRoleKey())).
+                append(')');
+
+        return query.toString();
+    }
+
+    private String getQuery(final ResourceCond cond, final boolean not, final List<Object> parameters,
+            final AnyTypeKind typeKind, final SearchSupport svs) {
+
+        final StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id FROM ").
+                append(svs.resource().name).
+                append(" WHERE resource_name=?").
+                append(setParameter(parameters, cond.getResourceName()));
+
+        if (typeKind == AnyTypeKind.USER) {
+            query.append(" UNION SELECT DISTINCT any_id FROM ").
+                    append(svs.groupResource().name).
+                    append(" WHERE resource_name=?").
+                    append(setParameter(parameters, cond.getResourceName()));
+        }
+
+        query.append(')');
+
+        return query.toString();
+    }
+
+    private void fillAttributeQuery(final StringBuilder query, final PlainAttrValue attrValue,
+            final PlainSchema schema, final AttributeCond cond, final boolean not,
+            final List<Object> parameters, final SearchSupport svs) {
+
+        String column = (cond instanceof AnyCond)
+                ? cond.getSchema()
+                : "' AND " + svs.fieldName(schema.getType());
+
+        switch (cond.getType()) {
+
+            case ISNULL:
+                query.append(column).append(not
+                        ? " IS NOT NULL"
+                        : " IS NULL");
+                break;
+
+            case ISNOTNULL:
+                query.append(column).append(not
+                        ? " IS NULL"
+                        : " IS NOT NULL");
+                break;
+
+            case LIKE:
+                if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) {
+                    query.append(column);
+                    if (not) {
+                        query.append(" NOT ");
+                    }
+                    query.append(" LIKE ?").append(setParameter(parameters, cond.getExpression()));
+                } else {
+                    if (!(cond instanceof AnyCond)) {
+                        query.append("' AND");
+                    }
+                    query.append(" 1=2");
+                    LOG.error("LIKE is only compatible with string or enum schemas");
+                }
+                break;
+
+            case EQ:
+                query.append(column);
+                if (not) {
+                    query.append("<>");
+                } else {
+                    query.append('=');
+                }
+                query.append('?').append(setParameter(parameters, attrValue.getValue()));
+                break;
+
+            case GE:
+                query.append(column);
+                if (not) {
+                    query.append('<');
+                } else {
+                    query.append(">=");
+                }
+                query.append('?').append(setParameter(parameters, attrValue.getValue()));
+                break;
+
+            case GT:
+                query.append(column);
+                if (not) {
+                    query.append("<=");
+                } else {
+                    query.append('>');
+                }
+                query.append('?').append(setParameter(parameters, attrValue.getValue()));
+                break;
+
+            case LE:
+                query.append(column);
+                if (not) {
+                    query.append('>');
+                } else {
+                    query.append("<=");
+                }
+                query.append('?').append(setParameter(parameters, attrValue.getValue()));
+                break;
+
+            case LT:
+                query.append(column);
+                if (not) {
+                    query.append(">=");
+                } else {
+                    query.append('<');
+                }
+                query.append('?').append(setParameter(parameters, attrValue.getValue()));
+                break;
+
+            default:
+        }
+    }
+
+    private String getQuery(final AttributeCond cond, final boolean not, final List<Object> parameters,
+            final AnyTypeKind typeKind, final SearchSupport svs) {
+
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(typeKind);
+
+        PlainSchema schema = schemaDAO.find(cond.getSchema());
+        if (schema == null) {
+            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
+            return EMPTY_ATTR_QUERY;
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        try {
+            if (cond.getType() != AttributeCond.Type.LIKE && cond.getType() != AttributeCond.Type.ISNULL
+                    && cond.getType() != AttributeCond.Type.ISNOTNULL) {
+
+                schema.getValidator().validate(cond.getExpression(), attrValue);
+            }
+        } catch (ValidationException e) {
+            LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+            return EMPTY_ATTR_QUERY;
+        }
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ");
+        if (cond.getType() == AttributeCond.Type.ISNOTNULL) {
+            query.append(svs.field().name).
+                    append(" WHERE any_id NOT IN (SELECT any_id FROM ").
+                    append(svs.nullAttr().name).
+                    append(" WHERE schema_name='").append(schema.getKey()).append("')");
+        } else {
+            if (cond.getType() == AttributeCond.Type.ISNULL) {
+                query.append(svs.nullAttr().name).
+                        append(" WHERE schema_name='").append(schema.getKey()).append("'");
+            } else {
+                if (schema.isUniqueConstraint()) {
+                    query.append(svs.uniqueAttr().name);
+                } else {
+                    query.append(svs.attr().name);
+                }
+                query.append(" WHERE schema_name='").append(schema.getKey());
+
+                fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
+            }
+        }
+
+        return query.toString();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private String getQuery(final AnyCond cond, final boolean not, final List<Object> parameters,
+            final AnyTypeKind typeKind, final SearchSupport svs) {
+
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(typeKind);
+
+        // Keeps track of difference between entity's getKey() and JPA @Id fields
+        if ("key".equals(cond.getSchema())) {
+            cond.setSchema("id");
+        }
+
+        Field subjectField = ReflectionUtils.findField(attrUtils.anyClass(), cond.getSchema());
+        if (subjectField == null) {
+            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
+            return EMPTY_ATTR_QUERY;
+        }
+
+        PlainSchema schema = new JPAPlainSchema();
+        schema.setKey(subjectField.getName());
+        for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
+            if (subjectField.getType().isAssignableFrom(attrSchemaType.getType())) {
+                schema.setType(attrSchemaType);
+            }
+        }
+
+        // Deal with subject Integer fields logically mapping to boolean values
+        // (JPAGroup.inheritPlainAttrs, for example)
+        boolean foundBooleanMin = false;
+        boolean foundBooleanMax = false;
+        if (Integer.class.equals(subjectField.getType())) {
+            for (Annotation annotation : subjectField.getAnnotations()) {
+                if (Min.class.equals(annotation.annotationType())) {
+                    foundBooleanMin = ((Min) annotation).value() == 0;
+                } else if (Max.class.equals(annotation.annotationType())) {
+                    foundBooleanMax = ((Max) annotation).value() == 1;
+                }
+            }
+        }
+        if (foundBooleanMin && foundBooleanMax) {
+            schema.setType(AttrSchemaType.Boolean);
+        }
+
+        // Deal with subject fields representing relationships to other entities
+        if (subjectField.getType().getAnnotation(Entity.class) != null) {
+            Method relMethod = null;
+            try {
+                relMethod = ClassUtils.getPublicMethod(subjectField.getType(), "getKey", new Class[0]);
+            } catch (Exception e) {
+                LOG.error("Could not find {}#getKey", subjectField.getType(), e);
+            }
+
+            if (relMethod != null) {
+                if (Long.class.isAssignableFrom(relMethod.getReturnType())) {
+                    cond.setSchema(cond.getSchema() + "_id");
+                    schema.setType(AttrSchemaType.Long);
+                }
+                if (String.class.isAssignableFrom(relMethod.getReturnType())) {
+                    cond.setSchema(cond.getSchema() + "_name");
+                    schema.setType(AttrSchemaType.String);
+                }
+            }
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        if (cond.getType() != AttributeCond.Type.LIKE
+                && cond.getType() != AttributeCond.Type.ISNULL
+                && cond.getType() != AttributeCond.Type.ISNOTNULL) {
+
+            try {
+                schema.getValidator().validate(cond.getExpression(), attrValue);
+            } catch (ValidationException e) {
+                LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+                return EMPTY_ATTR_QUERY;
+            }
+        }
+
+        final StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
+
+        return query.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeClassDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeClassDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeClassDAO.java
new file mode 100644
index 0000000..4d82d31
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeClassDAO.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAAnyTypeClassDAO extends AbstractDAO<AnyTypeClass, String> implements AnyTypeClassDAO {
+
+    @Override
+    public AnyTypeClass find(final String key) {
+        return entityManager.find(JPAAnyTypeClass.class, key);
+    }
+
+    @Override
+    public List<AnyTypeClass> findAll() {
+        TypedQuery<AnyTypeClass> query = entityManager.createQuery(
+                "SELECT e FROM " + JPAAnyTypeClass.class.getSimpleName() + " e ", AnyTypeClass.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public AnyTypeClass save(final AnyTypeClass anyTypeClass) {
+        return entityManager.merge(anyTypeClass);
+    }
+
+    @Override
+    public void delete(final String key) {
+        AnyTypeClass anyTypeClass = find(key);
+        if (anyTypeClass == null) {
+            return;
+        }
+
+        entityManager.remove(anyTypeClass);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeDAO.java
new file mode 100644
index 0000000..bbbf859
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyTypeDAO.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAAnyTypeDAO extends AbstractDAO<AnyType, String> implements AnyTypeDAO {
+
+    @Override
+    public AnyType find(final String key) {
+        return entityManager.find(JPAAnyType.class, key);
+    }
+
+    private AnyType find(final AnyTypeKind typeKind) {
+        AnyType anyType = find(typeKind.name());
+        if (anyType == null) {
+            anyType = new JPAAnyType();
+            anyType.setKey(typeKind.name());
+            anyType.setKind(typeKind);
+            anyType = save(anyType);
+        }
+        return anyType;
+    }
+
+    @Transactional(readOnly = false)
+    @Override
+    public AnyType findUser() {
+        return find(AnyTypeKind.USER);
+    }
+
+    @Transactional(readOnly = false)
+    @Override
+    public AnyType findGroup() {
+        return find(AnyTypeKind.GROUP);
+    }
+
+    @Override
+    public List<AnyType> findAll() {
+        TypedQuery<AnyType> query = entityManager.createQuery(
+                "SELECT e FROM " + JPAAnyType.class.getSimpleName() + " e ", AnyType.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public AnyType save(final AnyType anyType) {
+        return entityManager.merge(anyType);
+    }
+
+    @Override
+    public void delete(final String key) {
+        AnyType anyType = find(key);
+        if (anyType == null) {
+            return;
+        }
+
+        entityManager.remove(anyType);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrTemplateDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrTemplateDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrTemplateDAO.java
deleted file mode 100644
index 2b33178..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrTemplateDAO.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.dao;
-
-import java.util.Collections;
-import java.util.List;
-import javax.persistence.Query;
-import org.apache.syncope.core.persistence.api.dao.AttrTemplateDAO;
-import org.apache.syncope.core.persistence.api.entity.AttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.Schema;
-import org.apache.syncope.core.persistence.api.entity.membership.MDerAttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.membership.MVirAttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.group.GDerAttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrTemplate;
-import org.apache.syncope.core.persistence.api.entity.group.GVirAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMDerAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMPlainAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMVirAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.group.JPAGDerAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrTemplate;
-import org.apache.syncope.core.persistence.jpa.entity.group.JPAGVirAttrTemplate;
-import org.springframework.stereotype.Repository;
-import org.springframework.util.ReflectionUtils;
-
-@Repository
-public class JPAAttrTemplateDAO<S extends Schema>
-        extends AbstractDAO<AttrTemplate<S>, Long> implements AttrTemplateDAO<S> {
-
-    private <T extends AttrTemplate<S>> Class<? extends AbstractAttrTemplate<? extends Schema>> getJPAEntityReference(
-            final Class<T> reference) {
-
-        return MPlainAttrTemplate.class.isAssignableFrom(reference)
-                ? JPAMPlainAttrTemplate.class
-                : MDerAttrTemplate.class.isAssignableFrom(reference)
-                        ? JPAMDerAttrTemplate.class
-                        : MVirAttrTemplate.class.isAssignableFrom(reference)
-                                ? JPAMVirAttrTemplate.class
-                                : GPlainAttrTemplate.class.isAssignableFrom(reference)
-                                        ? JPAGPlainAttrTemplate.class
-                                        : GDerAttrTemplate.class.isAssignableFrom(reference)
-                                                ? JPAGDerAttrTemplate.class
-                                                : GVirAttrTemplate.class.isAssignableFrom(reference)
-                                                        ? JPAGVirAttrTemplate.class
-                                                        : null;
-    }
-
-    @Override
-    public <T extends AttrTemplate<S>> T find(final Long key, final Class<T> reference) {
-        return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T extends AttrTemplate<S>> List<Number> findBySchemaName(
-            final String schemaName, final Class<T> reference) {
-
-        Query query = null;
-        try {
-            query = entityManager.createNativeQuery("SELECT id FROM "
-                    + ReflectionUtils.findField(getJPAEntityReference(reference), "TABLE").get(null).toString()
-                    + " WHERE schema_name=?1");
-            query.setParameter(1, schemaName);
-        } catch (Exception e) {
-            LOG.error("Unexpected exception", e);
-        }
-
-        return query == null ? Collections.<Number>emptyList() : query.getResultList();
-    }
-
-    @Override
-    public <T extends AttrTemplate<S>> void delete(final Long key, final Class<T> reference) {
-        T attrTemplate = find(key, reference);
-        if (attrTemplate == null) {
-            return;
-        }
-
-        delete(attrTemplate);
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T extends AttrTemplate<S>> void delete(final T attrTemplate) {
-        if (attrTemplate.getOwner() != null) {
-            attrTemplate.getOwner().getAttrTemplates(attrTemplate.getClass()).remove(attrTemplate);
-        }
-
-        entityManager.remove(attrTemplate);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
index 48e9f5e..7045a1a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfDAO.java
@@ -18,15 +18,14 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import org.apache.syncope.common.lib.types.AttributableType;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.entity.AttributableUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
-import org.apache.syncope.core.persistence.api.entity.conf.CPlainSchema;
 import org.apache.syncope.core.persistence.api.entity.conf.Conf;
 import org.apache.syncope.core.persistence.jpa.entity.conf.JPACPlainAttr;
+import org.apache.syncope.core.persistence.jpa.entity.conf.JPACPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.conf.JPAConf;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
@@ -41,9 +40,6 @@ public class JPAConfDAO extends AbstractDAO<Conf, Long> implements ConfDAO {
     @Autowired
     private PlainAttrDAO attrDAO;
 
-    @Autowired
-    private AttributableUtilsFactory attrUtilsFactory;
-
     @Override
     public Conf get() {
         Conf instance = entityManager.find(JPAConf.class, 1L);
@@ -68,10 +64,19 @@ public class JPAConfDAO extends AbstractDAO<Conf, Long> implements ConfDAO {
     public CPlainAttr find(final String key, final String defaultValue) {
         CPlainAttr result = find(key);
         if (result == null) {
-            result = new JPACPlainAttr();
-            result.setSchema(schemaDAO.find(key, CPlainSchema.class));
-
-            result.addValue(defaultValue, attrUtilsFactory.getInstance(AttributableType.CONFIGURATION));
+            JPACPlainAttr newAttr = new JPACPlainAttr();
+            newAttr.setSchema(schemaDAO.find(key));
+
+            JPACPlainAttrValue attrValue;
+            if (newAttr.getSchema().isUniqueConstraint()) {
+                attrValue = new JPACPlainAttrValue();
+                ((PlainAttrUniqueValue) attrValue).setSchema(newAttr.getSchema());
+            } else {
+                attrValue = new JPACPlainAttrValue();
+            }
+            newAttr.add(defaultValue, attrValue);
+
+            result = newAttr;
         }
 
         return result;
@@ -85,11 +90,11 @@ public class JPAConfDAO extends AbstractDAO<Conf, Long> implements ConfDAO {
         if (old != null && (!attr.getSchema().isUniqueConstraint()
                 || (!attr.getUniqueValue().getStringValue().equals(old.getUniqueValue().getStringValue())))) {
 
-            instance.removePlainAttr(old);
+            instance.remove(old);
             attrDAO.delete(old.getKey(), CPlainAttr.class);
         }
 
-        instance.addPlainAttr(attr);
+        instance.add(attr);
         attr.setOwner(instance);
 
         return entityManager.merge(instance);
@@ -100,7 +105,7 @@ public class JPAConfDAO extends AbstractDAO<Conf, Long> implements ConfDAO {
         Conf instance = get();
         CPlainAttr attr = instance.getPlainAttr(key);
         if (attr != null) {
-            instance.removePlainAttr(attr);
+            instance.remove(attr);
             instance = entityManager.merge(instance);
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceDAO.java
index e148b04..453c363 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceDAO.java
@@ -27,7 +27,7 @@ import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
-import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance;
 import org.apache.syncope.core.provisioning.api.ConnectorRegistry;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerAttrDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerAttrDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerAttrDAO.java
index ff32fb8..2162898 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerAttrDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerAttrDAO.java
@@ -21,51 +21,51 @@ package org.apache.syncope.core.persistence.jpa.dao;
 import java.util.List;
 import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.api.dao.DerAttrDAO;
-import org.apache.syncope.core.persistence.api.entity.Attributable;
+import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.DerAttr;
-import org.apache.syncope.core.persistence.api.entity.membership.MDerAttr;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ADerAttr;
 import org.apache.syncope.core.persistence.api.entity.group.GDerAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractDerAttr;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMDerAttr;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADerAttr;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGDerAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDerAttr;
 import org.springframework.stereotype.Repository;
 
 @Repository
-public class JPADerAttrDAO extends AbstractDAO<DerAttr, Long> implements DerAttrDAO {
+public class JPADerAttrDAO extends AbstractDAO<DerAttr<?>, Long> implements DerAttrDAO {
 
-    public <T extends DerAttr> Class<? extends AbstractDerAttr> getJPAEntityReference(
+    public <T extends DerAttr<?>> Class<? extends AbstractDerAttr<?>> getJPAEntityReference(
             final Class<T> reference) {
 
         return GDerAttr.class.isAssignableFrom(reference)
                 ? JPAGDerAttr.class
-                : MDerAttr.class.isAssignableFrom(reference)
-                        ? JPAMDerAttr.class
+                : ADerAttr.class.isAssignableFrom(reference)
+                        ? JPAADerAttr.class
                         : UDerAttr.class.isAssignableFrom(reference)
                                 ? JPAUDerAttr.class
                                 : null;
     }
 
     @Override
-    public <T extends DerAttr> T find(final Long key, final Class<T> reference) {
+    public <T extends DerAttr<?>> T find(final Long key, final Class<T> reference) {
         return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
     }
 
     @Override
-    public <T extends DerAttr> List<T> findAll(final Class<T> reference) {
+    public <T extends DerAttr<?>> List<T> findAll(final Class<T> reference) {
         TypedQuery<T> query = entityManager.createQuery(
                 "SELECT e FROM " + getJPAEntityReference(reference).getSimpleName() + " e", reference);
         return query.getResultList();
     }
 
     @Override
-    public <T extends DerAttr> T save(final T derAttr) {
+    public <T extends DerAttr<?>> T save(final T derAttr) {
         return entityManager.merge(derAttr);
     }
 
     @Override
-    public <T extends DerAttr> void delete(final Long key, final Class<T> reference) {
+    public <T extends DerAttr<?>> void delete(final Long key, final Class<T> reference) {
         T derAttr = find(key, reference);
         if (derAttr == null) {
             return;
@@ -76,9 +76,9 @@ public class JPADerAttrDAO extends AbstractDAO<DerAttr, Long> implements DerAttr
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T extends DerAttr> void delete(final T derAttr) {
+    public <T extends DerAttr<?>> void delete(final T derAttr) {
         if (derAttr.getOwner() != null) {
-            ((Attributable<?, T, ?>) derAttr.getOwner()).removeDerAttr(derAttr);
+            ((Any<?, T, ?>) derAttr.getOwner()).remove(derAttr);
         }
 
         entityManager.remove(derAttr);

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerSchemaDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerSchemaDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerSchemaDAO.java
index be0ec94..c3c10bf 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerSchemaDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADerSchemaDAO.java
@@ -20,25 +20,16 @@ package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
-import org.apache.commons.collections4.Closure;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.core.persistence.api.dao.AttrTemplateDAO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.dao.DerAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
-import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.DerAttr;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
-import org.apache.syncope.core.persistence.api.entity.membership.MDerSchema;
-import org.apache.syncope.core.persistence.api.entity.group.GDerSchema;
-import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
-import org.apache.syncope.core.persistence.api.entity.user.UDerSchema;
-import org.apache.syncope.core.persistence.api.entity.user.UMappingItem;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractDerSchema;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMDerSchema;
-import org.apache.syncope.core.persistence.jpa.entity.group.JPAGDerSchema;
-import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDerSchema;
+import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import org.apache.syncope.core.persistence.jpa.entity.JPADerSchema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 
@@ -49,44 +40,25 @@ public class JPADerSchemaDAO extends AbstractDAO<DerSchema, String> implements D
     private DerAttrDAO derAttrDAO;
 
     @Autowired
-    private AttrTemplateDAO<DerSchema> attrTemplateDAO;
-
-    @Autowired
     private ExternalResourceDAO resourceDAO;
 
-    private <T extends DerSchema> Class<? extends AbstractDerSchema> getJPAEntityReference(final Class<T> reference) {
-        return GDerSchema.class.isAssignableFrom(reference)
-                ? JPAGDerSchema.class
-                : MDerSchema.class.isAssignableFrom(reference)
-                        ? JPAMDerSchema.class
-                        : UDerSchema.class.isAssignableFrom(reference)
-                                ? JPAUDerSchema.class
-                                : null;
-    }
-
     @Override
-    public <T extends DerSchema> T find(final String key, final Class<T> reference) {
-        return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
+    public DerSchema find(final String key) {
+        return entityManager.find(JPADerSchema.class, key);
     }
 
     @Override
-    public <T extends DerSchema> List<T> findAll(final Class<T> reference) {
-        TypedQuery<T> query = entityManager.createQuery(
-                "SELECT e FROM " + getJPAEntityReference(reference).getSimpleName() + " e", reference);
+    public List<DerSchema> findAll() {
+        TypedQuery<DerSchema> query = entityManager.createQuery(
+                "SELECT e FROM " + JPADerSchema.class.getSimpleName() + " e", DerSchema.class);
         return query.getResultList();
     }
 
     @Override
-    public <T extends DerAttr> List<T> findAttrs(final DerSchema schema, final Class<T> reference) {
+    public <T extends DerAttr<?>> List<T> findAttrs(final DerSchema schema, final Class<T> reference) {
         final StringBuilder queryString = new StringBuilder("SELECT e FROM ").
                 append(((JPADerAttrDAO) derAttrDAO).getJPAEntityReference(reference).getSimpleName()).
-                append(" e WHERE e.");
-        if (UDerAttr.class.isAssignableFrom(reference)) {
-            queryString.append("derSchema");
-        } else {
-            queryString.append("template.schema");
-        }
-        queryString.append("=:schema");
+                append(" e WHERE e.schema=:schema");
 
         TypedQuery<T> query = entityManager.createQuery(queryString.toString(), reference);
         query.setParameter("schema", schema);
@@ -95,42 +67,28 @@ public class JPADerSchemaDAO extends AbstractDAO<DerSchema, String> implements D
     }
 
     @Override
-    public <T extends DerSchema> T save(final T derSchema) {
+    public DerSchema save(final DerSchema derSchema) {
         return entityManager.merge(derSchema);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public void delete(final String key, final AttributableUtils attributableUtil) {
-        final DerSchema schema = find(key, attributableUtil.derSchemaClass());
+    public void delete(final String key) {
+        final DerSchema schema = find(key);
         if (schema == null) {
             return;
         }
 
-        CollectionUtils.forAllDo(findAttrs(schema, attributableUtil.derAttrClass()), new Closure<DerAttr>() {
+        AnyUtilsFactory anyUtilsFactory = new JPAAnyUtilsFactory();
+        for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
+            AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
 
-            @Override
-            public void execute(final DerAttr input) {
-                derAttrDAO.delete(input.getKey(), attributableUtil.derAttrClass());
+            for (DerAttr<?> attr : findAttrs(schema, anyUtils.derAttrClass())) {
+                derAttrDAO.delete(attr.getKey(), anyUtils.derAttrClass());
             }
 
-        });
-
-        if (attributableUtil.getType() != AttributableType.USER) {
-            CollectionUtils.forAllDo(attrTemplateDAO.
-                    findBySchemaName(schema.getKey(), attributableUtil.derAttrTemplateClass()).iterator(),
-                    new Closure<Number>() {
-
-                        @Override
-                        public void execute(final Number input) {
-                            attrTemplateDAO.delete(input.longValue(), attributableUtil.derAttrTemplateClass());
-                        }
-
-                    });
+            resourceDAO.deleteMapping(key, anyUtils.derIntMappingType());
         }
 
-        resourceDAO.deleteMapping(key, attributableUtil.derIntMappingType(), UMappingItem.class);
-
         entityManager.remove(schema);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
index 70f00f7..b13a8bd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
@@ -25,6 +25,7 @@ import javax.persistence.TypedQuery;
 import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
@@ -32,17 +33,17 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
-import org.apache.syncope.core.persistence.api.entity.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.Mapping;
-import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.Policy;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.entity.user.UMappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractMappingItem;
-import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
-import org.apache.syncope.core.persistence.jpa.entity.group.JPAGMappingItem;
-import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMappingItem;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAMappingItem;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAMapping;
 import org.apache.syncope.core.provisioning.api.ConnectorRegistry;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
@@ -55,6 +56,9 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
     private TaskDAO taskDAO;
 
     @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    @Autowired
     private UserDAO userDAO;
 
     @Autowired
@@ -66,6 +70,9 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
     @Autowired
     private ConnectorRegistry connRegistry;
 
+    @Autowired
+    private AnyUtilsFactory anyUtilsFactory;
+
     @Override
     public ExternalResource find(final String name) {
         return entityManager.find(JPAExternalResource.class, name);
@@ -146,33 +153,25 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T extends MappingItem> void deleteMapping(
-            final String intAttrName, final IntMappingType intMappingType, final Class<T> reference) {
-
+    public void deleteMapping(final String intAttrName, final IntMappingType intMappingType) {
         if (IntMappingType.getEmbedded().contains(intMappingType)) {
             return;
         }
 
-        Class<? extends AbstractMappingItem> jpaRef = reference.equals(UMappingItem.class)
-                ? JPAUMappingItem.class
-                : JPAGMappingItem.class;
-
-        TypedQuery<T> query = entityManager.createQuery("SELECT m FROM " + jpaRef.getSimpleName()
-                + " m WHERE m.intAttrName=:intAttrName AND m.intMappingType=:intMappingType", reference);
+        TypedQuery<MappingItem> query = entityManager.createQuery(
+                "SELECT m FROM " + JPAMappingItem.class.getSimpleName()
+                + " m WHERE m.intAttrName=:intAttrName AND m.intMappingType=:intMappingType", MappingItem.class);
         query.setParameter("intAttrName", intAttrName);
         query.setParameter("intMappingType", intMappingType);
 
-        Set<Long> itemIds = new HashSet<>();
-        for (T item : query.getResultList()) {
-            itemIds.add(item.getKey());
+        Set<Long> itemKeys = new HashSet<>();
+        for (MappingItem item : query.getResultList()) {
+            itemKeys.add(item.getKey());
         }
-        Class<?> mappingRef = null;
-        for (Long itemId : itemIds) {
-            T item = (T) entityManager.find(jpaRef, itemId);
+        for (Long itemKey : itemKeys) {
+            MappingItem item = entityManager.find(JPAMappingItem.class, itemKey);
             if (item != null) {
-                mappingRef = item.getMapping().getClass();
-
-                ((Mapping<T>) item.getMapping()).removeItem(item);
+                item.getMapping().remove(item);
                 item.setMapping(null);
 
                 entityManager.remove(item);
@@ -180,10 +179,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
         }
 
         // Make empty query cache for *MappingItem and related *Mapping
-        entityManager.getEntityManagerFactory().getCache().evict(jpaRef);
-        if (mappingRef != null) {
-            entityManager.getEntityManagerFactory().getCache().evict(mappingRef);
-        }
+        entityManager.getEntityManagerFactory().getCache().evict(JPAMappingItem.class);
+        entityManager.getEntityManagerFactory().getCache().evict(JPAMapping.class);
     }
 
     @Override
@@ -197,11 +194,14 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
         taskDAO.deleteAll(resource, TaskType.SYNCHRONIZATION);
         taskDAO.deleteAll(resource, TaskType.PUSH);
 
+        for (AnyObject anyObject : anyObjectDAO.findByResource(resource)) {
+            anyObject.remove(resource);
+        }
         for (User user : userDAO.findByResource(resource)) {
-            user.removeResource(resource);
+            user.remove(resource);
         }
         for (Group group : groupDAO.findByResource(resource)) {
-            group.removeResource(resource);
+            group.remove(resource);
         }
         for (AccountPolicy policy : policyDAO.findByResource(resource)) {
             policy.removeResource(resource);
@@ -214,21 +214,13 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource, String
         }
         resource.setConnector(null);
 
-        if (resource.getUmapping() != null) {
-            for (MappingItem item : resource.getUmapping().getItems()) {
-                item.setMapping(null);
-            }
-            resource.getUmapping().getItems().clear();
-            resource.getUmapping().setResource(null);
-            resource.setUmapping(null);
-        }
-        if (resource.getGmapping() != null) {
-            for (MappingItem item : resource.getGmapping().getItems()) {
+        for (Provision provision : resource.getProvisions()) {
+            for (MappingItem item : provision.getMapping().getItems()) {
                 item.setMapping(null);
             }
-            resource.getGmapping().getItems().clear();
-            resource.getGmapping().setResource(null);
-            resource.setGmapping(null);
+            provision.getMapping().getItems().clear();
+            provision.setMapping(null);
+            provision.setResource(null);
         }
 
         entityManager.remove(resource);