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/02/12 10:14:41 UTC

[36/54] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Renaming 'server' after 'core', to provide continuity with older releases (especially for archetype)

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
new file mode 100644
index 0000000..c08e1a2
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -0,0 +1,287 @@
+/*
+ * 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 java.util.Set;
+import javax.annotation.Resource;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+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.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.dao.search.SubjectCond;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
+import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+import org.apache.syncope.core.misc.security.AuthContextUtil;
+import org.apache.syncope.core.misc.security.UnauthorizedRoleException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAttr> implements UserDAO {
+
+    @Autowired
+    private SubjectSearchDAO searchDAO;
+
+    @Autowired
+    private RoleDAO roleDAO;
+
+    @Resource(name = "anonymousUser")
+    private String anonymousUser;
+
+    @Autowired
+    private AttributableUtilFactory attrUtilFactory;
+
+    @Override
+    protected Subject<UPlainAttr, UDerAttr, UVirAttr> findInternal(Long key) {
+        return find(key);
+    }
+
+    @Override
+    public User find(final Long key) {
+        TypedQuery<User> query = entityManager.createQuery(
+                "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE e.id = :id", User.class);
+        query.setParameter("id", key);
+
+        User result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No user found with id {}", key, e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public User find(final String username) {
+        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+                + " e WHERE e.username = :username", User.class);
+        query.setParameter("username", username);
+
+        User result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No user found with username {}", username, e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public User findByWorkflowId(final String workflowId) {
+        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+                + " e WHERE e.workflowId = :workflowId", User.class);
+        query.setParameter("workflowId", workflowId);
+
+        User result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No user found with workflow id {}", workflowId, e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public User findByToken(final String token) {
+        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+                + " e WHERE e.token LIKE :token", User.class);
+        query.setParameter("token", token);
+
+        User result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No user found with token {}", token, e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<User> findBySecurityQuestion(final SecurityQuestion securityQuestion) {
+        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+                + " e WHERE e.securityQuestion = :securityQuestion", User.class);
+        query.setParameter("securityQuestion", securityQuestion);
+
+        return query.getResultList();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<User> findByAttrValue(final String schemaName, final UPlainAttrValue attrValue) {
+        return (List<User>) findByAttrValue(
+                schemaName, attrValue, attrUtilFactory.getInstance(AttributableType.USER));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public User findByAttrUniqueValue(final String schemaName, final UPlainAttrValue attrUniqueValue) {
+        return (User) findByAttrUniqueValue(schemaName, attrUniqueValue,
+                attrUtilFactory.getInstance(AttributableType.USER));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<User> findByDerAttrValue(final String schemaName, final String value) {
+        return (List<User>) findByDerAttrValue(
+                schemaName, value, attrUtilFactory.getInstance(AttributableType.USER));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<User> findByResource(final ExternalResource resource) {
+        return (List<User>) findByResource(resource, attrUtilFactory.getInstance(AttributableType.USER));
+    }
+
+    @Override
+    public final List<User> findAll(final Set<Long> adminRoles, final int page, final int itemsPerPage) {
+        return findAll(adminRoles, page, itemsPerPage, Collections.<OrderByClause>emptyList());
+    }
+
+    private SearchCond getAllMatchingCond() {
+        SubjectCond idCond = new SubjectCond(AttributeCond.Type.ISNOTNULL);
+        idCond.setSchema("id");
+        return SearchCond.getLeafCond(idCond);
+    }
+
+    @Override
+    public List<User> findAll(final Set<Long> adminRoles,
+            final int page, final int itemsPerPage, final List<OrderByClause> orderBy) {
+
+        return searchDAO.search(
+                adminRoles, getAllMatchingCond(), page, itemsPerPage, orderBy, SubjectType.USER);
+    }
+
+    @Override
+    public final int count(final Set<Long> adminRoles) {
+        return searchDAO.count(adminRoles, getAllMatchingCond(), SubjectType.USER);
+    }
+
+    @Override
+    public User save(final User user) {
+        final User merged = entityManager.merge(user);
+        for (VirAttr virAttr : merged.getVirAttrs()) {
+            virAttr.getValues().clear();
+            virAttr.getValues().addAll(user.getVirAttr(virAttr.getSchema().getKey()).getValues());
+        }
+
+        return merged;
+    }
+
+    @Override
+    public void delete(final Long key) {
+        User user = (User) findInternal(key);
+        if (user == null) {
+            return;
+        }
+
+        delete(user);
+    }
+
+    @Override
+    public void delete(final User user) {
+        // Not calling membershipDAO.delete() here because it would try to save this user as well, thus going into
+        // ConcurrentModificationException
+        for (Membership membership : user.getMemberships()) {
+            membership.setUser(null);
+
+            roleDAO.save(membership.getRole());
+            membership.setRole(null);
+
+            entityManager.remove(membership);
+        }
+        user.getMemberships().clear();
+
+        entityManager.remove(user);
+    }
+
+    private void securityChecks(final User user) {
+        // Allows anonymous (during self-registration) and self (during self-update) to read own user,
+        // otherwise goes thorugh security checks to see if needed role entitlements are owned
+        if (!AuthContextUtil.getAuthenticatedUsername().equals(anonymousUser)
+                && !AuthContextUtil.getAuthenticatedUsername().equals(user.getUsername())) {
+
+            Set<Long> roleKeys = user.getRoleKeys();
+            Set<Long> adminRoleKeys = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+            roleKeys.removeAll(adminRoleKeys);
+            if (!roleKeys.isEmpty()) {
+                throw new UnauthorizedRoleException(roleKeys);
+            }
+        }
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public User authFetch(final Long key) {
+        if (key == null) {
+            throw new NotFoundException("Null user id");
+        }
+
+        User user = find(key);
+        if (user == null) {
+            throw new NotFoundException("User " + key);
+        }
+
+        securityChecks(user);
+
+        return user;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public User authFetch(final String username) {
+        if (username == null) {
+            throw new NotFoundException("Null username");
+        }
+
+        User user = find(username);
+        if (user == null) {
+            throw new NotFoundException("User " + username);
+        }
+
+        securityChecks(user);
+
+        return user;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
new file mode 100644
index 0000000..2235295
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
@@ -0,0 +1,86 @@
+/*
+ * 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.VirAttrDAO;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirAttr;
+import org.apache.syncope.core.persistence.api.entity.role.RVirAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractVirAttr;
+import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMVirAttr;
+import org.apache.syncope.core.persistence.jpa.entity.role.JPARVirAttr;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUVirAttr;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAVirAttrDAO extends AbstractDAO<VirAttr, Long> implements VirAttrDAO {
+
+    public <T extends VirAttr> Class<? extends AbstractVirAttr> getJPAEntityReference(
+            final Class<T> reference) {
+
+        return RVirAttr.class.isAssignableFrom(reference)
+                ? JPARVirAttr.class
+                : MVirAttr.class.isAssignableFrom(reference)
+                        ? JPAMVirAttr.class
+                        : UVirAttr.class.isAssignableFrom(reference)
+                                ? JPAUVirAttr.class
+                                : null;
+    }
+
+    @Override
+    public <T extends VirAttr> T find(final Long key, final Class<T> reference) {
+        return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
+    }
+
+    @Override
+    public <T extends VirAttr> 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 VirAttr> T save(final T virAttr) {
+        return entityManager.merge(virAttr);
+    }
+
+    @Override
+    public <T extends VirAttr> void delete(final Long key, final Class<T> reference) {
+        T virAttr = find(key, reference);
+        if (virAttr == null) {
+            return;
+        }
+
+        delete(virAttr);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends VirAttr> void delete(final T virAttr) {
+        if (virAttr.getOwner() != null) {
+            ((Attributable<?, ?, T>) virAttr.getOwner()).removeVirAttr(virAttr);
+        }
+
+        entityManager.remove(virAttr);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
new file mode 100644
index 0000000..f7a1e55
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
@@ -0,0 +1,131 @@
+/*
+ * 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.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.core.persistence.api.dao.AttrTemplateDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.VirAttrDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirSchema;
+import org.apache.syncope.core.persistence.api.entity.role.RVirSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UMappingItem;
+import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UVirSchema;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractVirSchema;
+import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMVirSchema;
+import org.apache.syncope.core.persistence.jpa.entity.role.JPARVirSchema;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUVirSchema;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements VirSchemaDAO {
+
+    @Autowired
+    private VirAttrDAO virAttrDAO;
+
+    @Autowired
+    private AttrTemplateDAO<VirSchema> attrTemplateDAO;
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    private <T extends VirSchema> Class<? extends AbstractVirSchema> getJPAEntityReference(final Class<T> reference) {
+        return RVirSchema.class.isAssignableFrom(reference)
+                ? JPARVirSchema.class
+                : MVirSchema.class.isAssignableFrom(reference)
+                        ? JPAMVirSchema.class
+                        : UVirSchema.class.isAssignableFrom(reference)
+                                ? JPAUVirSchema.class
+                                : null;
+    }
+
+    @Override
+    public <T extends VirSchema> T find(final String key, final Class<T> reference) {
+        return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
+    }
+
+    @Override
+    public <T extends VirSchema> 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 VirAttr> List<T> findAttrs(final VirSchema schema, final Class<T> reference) {
+        final StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+                append(((JPAVirAttrDAO) virAttrDAO).getJPAEntityReference(reference).getSimpleName()).
+                append(" e WHERE e.");
+        if (UVirAttr.class.isAssignableFrom(reference)) {
+            queryString.append("virSchema");
+        } else {
+            queryString.append("template.schema");
+        }
+        queryString.append("=:schema");
+
+        TypedQuery<T> query = entityManager.createQuery(queryString.toString(), reference);
+        query.setParameter("schema", schema);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public <T extends VirSchema> T save(final T virSchema) {
+        return entityManager.merge(virSchema);
+    }
+
+    @Override
+    public void delete(final String name, final AttributableUtil attributableUtil) {
+        final VirSchema schema = find(name, attributableUtil.virSchemaClass());
+        if (schema == null) {
+            return;
+        }
+
+        final Set<Long> attrIds = new HashSet<>();
+        for (VirAttr attr : findAttrs(schema, attributableUtil.virAttrClass())) {
+            attrIds.add(attr.getKey());
+        }
+        for (Long attrId : attrIds) {
+            virAttrDAO.delete(attrId, attributableUtil.virAttrClass());
+        }
+
+        if (attributableUtil.getType() != AttributableType.USER) {
+            for (Iterator<Number> it = attrTemplateDAO.
+                    findBySchemaName(schema.getKey(), attributableUtil.virAttrTemplateClass()).iterator();
+                    it.hasNext();) {
+
+                attrTemplateDAO.delete(it.next().longValue(), attributableUtil.virAttrTemplateClass());
+            }
+        }
+
+        resourceDAO.deleteMapping(name, attributableUtil.virIntMappingType(), UMappingItem.class);
+
+        entityManager.remove(schema);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OrderBySupport.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OrderBySupport.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OrderBySupport.java
new file mode 100644
index 0000000..63f442e
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OrderBySupport.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class OrderBySupport {
+
+    static class Item {
+
+        protected String select;
+
+        protected String where;
+
+        protected String orderBy;
+
+        protected boolean isEmpty() {
+            return (select == null || select.isEmpty())
+                    && (where == null || where.isEmpty())
+                    && (orderBy == null || orderBy.isEmpty());
+        }
+    }
+
+    protected Set<SearchSupport.SearchView> views = new HashSet<>();
+
+    protected List<Item> items = new ArrayList<>();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
new file mode 100644
index 0000000..6922b85
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
@@ -0,0 +1,132 @@
+/*
+ * 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 org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.SubjectType;
+
+class SearchSupport {
+
+    static class SearchView {
+
+        protected String alias;
+
+        protected String name;
+
+        protected SearchView(final String alias, final String name) {
+            this.alias = alias;
+            this.name = name;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return EqualsBuilder.reflectionEquals(this, obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return HashCodeBuilder.reflectionHashCode(this);
+        }
+    }
+
+    private final SubjectType type;
+
+    public SearchSupport(final SubjectType type) {
+        this.type = type;
+    }
+
+    public String fieldName(final AttrSchemaType type) {
+        String result;
+
+        switch (type) {
+            case Boolean:
+                result = "booleanvalue";
+                break;
+
+            case Date:
+                result = "datevalue";
+                break;
+
+            case Double:
+                result = "doublevalue";
+                break;
+
+            case Long:
+                result = "longvalue";
+                break;
+
+            case String:
+            case Enum:
+                result = "stringvalue";
+                break;
+
+            default:
+                result = null;
+        }
+
+        return result;
+    }
+
+    public SearchView field() {
+        String result = "";
+
+        switch (type) {
+            case USER:
+            default:
+                result = "user_search";
+                break;
+
+            case ROLE:
+                result = "role_search";
+                break;
+        }
+
+        return new SearchView("sv", result);
+    }
+
+    public SearchView attr() {
+        return new SearchView("sva", field().name + "_attr");
+    }
+
+    public SearchView membership() {
+        return new SearchView("svm", field().name + "_membership");
+    }
+
+    public SearchView nullAttr() {
+        return new SearchView("svna", field().name + "_null_attr");
+    }
+
+    public SearchView resource() {
+        return new SearchView("svr", field().name + "_resource");
+    }
+
+    public SearchView roleResource() {
+        return new SearchView("svrr", field().name + "_role_resource");
+    }
+
+    public SearchView uniqueAttr() {
+        return new SearchView("svua", field().name + "_unique_attr");
+    }
+
+    public SearchView entitlements() {
+        return new SearchView("sve", field().name + "_entitlements");
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAnnotatedEntity.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAnnotatedEntity.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAnnotatedEntity.java
new file mode 100644
index 0000000..c9bbe77
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAnnotatedEntity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.entity;
+
+import java.util.Date;
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import org.apache.syncope.core.persistence.api.entity.AnnotatedEntity;
+
+/**
+ * Abstract wrapper for common system information.
+ */
+@MappedSuperclass
+@EntityListeners(value = AnnotatedEntityListener.class)
+public abstract class AbstractAnnotatedEntity<KEY> extends AbstractEntity<KEY> implements AnnotatedEntity<KEY> {
+
+    private static final long serialVersionUID = -4801685541488201219L;
+
+    /**
+     * Username of the user that has created this profile.
+     * <br/>
+     * Reference to existing user cannot be used: the creator can either be <tt>admin</tt> or was deleted.
+     */
+    @Column(nullable = false)
+    private String creator;
+
+    /**
+     * Creation date.
+     */
+    @Column(nullable = false)
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date creationDate;
+
+    /**
+     * Username of the user that has performed the last modification to this profile.
+     * <br/>
+     * This field cannot be null: at creation time it needs to be initialized with the creator username.
+     * <br/>
+     * The modifier can be the user itself if the last performed change was a self-modification.
+     * <br/>
+     * Reference to existing user cannot be used: the creator can either be <tt>admin</tt> or was deleted.
+     */
+    @Column(nullable = false)
+    private String lastModifier;
+
+    /**
+     * Last change date.
+     * <br/>
+     * This field cannot be null: at creation time it needs to be initialized with <tt>creationDate</tt> field value.
+     */
+    @Column(nullable = false)
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date lastChangeDate;
+
+    @Override
+    public String getCreator() {
+        return creator;
+    }
+
+    @Override
+    public void setCreator(final String creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public Date getCreationDate() {
+        return creationDate == null ? null : new Date(creationDate.getTime());
+    }
+
+    @Override
+    public void setCreationDate(final Date creationDate) {
+        this.creationDate = creationDate == null ? null : new Date(creationDate.getTime());
+    }
+
+    @Override
+    public String getLastModifier() {
+        return lastModifier;
+    }
+
+    @Override
+    public void setLastModifier(final String lastModifier) {
+        this.lastModifier = lastModifier;
+    }
+
+    @Override
+    public Date getLastChangeDate() {
+        return lastChangeDate == null ? creationDate : lastChangeDate;
+    }
+
+    @Override
+    public void setLastChangeDate(final Date lastChangeDate) {
+        this.lastChangeDate = lastChangeDate == null ? null : new Date(lastChangeDate.getTime());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttrTemplate.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttrTemplate.java
new file mode 100644
index 0000000..f2a32a1
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttrTemplate.java
@@ -0,0 +1,28 @@
+/*
+ * 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.entity;
+
+import org.apache.syncope.core.persistence.api.entity.AttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.Schema;
+
+public abstract class AbstractAttrTemplate<S extends Schema> extends AbstractEntity<Long> implements AttrTemplate<S> {
+
+    private static final long serialVersionUID = 4829112252713766666L;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java
new file mode 100644
index 0000000..d648748
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAttributable.java
@@ -0,0 +1,106 @@
+/*
+ * 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.entity;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+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.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+
+public abstract class AbstractAttributable<P extends PlainAttr, D extends DerAttr, V extends VirAttr>
+        extends AbstractAnnotatedEntity<Long> implements Attributable<P, D, V> {
+
+    private static final long serialVersionUID = -4801685541488201119L;
+
+    @Override
+    public P getPlainAttr(final String plainSchemaName) {
+        P result = null;
+        for (P plainAttr : getPlainAttrs()) {
+            if (plainAttr != null && plainAttr.getSchema() != null
+                    && plainSchemaName.equals(plainAttr.getSchema().getKey())) {
+
+                result = plainAttr;
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public D getDerAttr(final String derSchemaName) {
+        D result = null;
+        for (D derAttr : getDerAttrs()) {
+            if (derAttr != null && derAttr.getSchema() != null
+                    && derSchemaName.equals(derAttr.getSchema().getKey())) {
+
+                result = derAttr;
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public V getVirAttr(final String virSchemaName) {
+        V result = null;
+        for (V virAttr : getVirAttrs()) {
+            if (virAttr != null && virAttr.getSchema() != null
+                    && virSchemaName.equals(virAttr.getSchema().getKey())) {
+
+                result = virAttr;
+            }
+        }
+
+        return result;
+    }
+
+    protected Map<PlainSchema, P> getPlainAttrMap() {
+        final Map<PlainSchema, P> map = new HashMap<>();
+
+        for (P attr : getPlainAttrs()) {
+            map.put(attr.getSchema(), attr);
+        }
+
+        return map;
+    }
+
+    protected Map<DerSchema, D> getDerAttrMap() {
+        final Map<DerSchema, D> map = new HashMap<>();
+
+        for (D attr : getDerAttrs()) {
+            map.put(attr.getSchema(), attr);
+        }
+
+        return map;
+    }
+
+    protected Map<VirSchema, V> getVirAttrMap() {
+        final Map<VirSchema, V> map = new HashMap<>();
+
+        for (V attr : getVirAttrs()) {
+            map.put(attr.getSchema(), attr);
+        }
+
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttr.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttr.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttr.java
new file mode 100644
index 0000000..c43e786
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttr.java
@@ -0,0 +1,52 @@
+/*
+ * 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.entity;
+
+import java.util.Collection;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.core.persistence.api.entity.DerAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.misc.jexl.JexlUtil;
+
+@MappedSuperclass
+public abstract class AbstractDerAttr extends AbstractEntity<Long> implements DerAttr {
+
+    private static final long serialVersionUID = 4740924251090424771L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    protected Long id;
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+    /**
+     * @param attributes the set of attributes against which evaluate this derived attribute
+     * @return the value of this derived attribute
+     */
+    @Override
+    public String getValue(final Collection<? extends PlainAttr> attributes) {
+        return JexlUtil.evaluate(getSchema().getExpression(), getOwner(), attributes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttrTemplate.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttrTemplate.java
new file mode 100644
index 0000000..3846b12
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerAttrTemplate.java
@@ -0,0 +1,41 @@
+/*
+ * 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.entity;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+
+@MappedSuperclass
+public abstract class AbstractDerAttrTemplate<D extends DerSchema> extends AbstractAttrTemplate<D> {
+
+    private static final long serialVersionUID = 8871895736733379865L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    protected Long id;
+
+    @Override
+    public Long getKey() {
+        return id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerSchema.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerSchema.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerSchema.java
new file mode 100644
index 0000000..195f41d
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDerSchema.java
@@ -0,0 +1,85 @@
+/*
+ * 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.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.jpa.validation.entity.SchemaNameCheck;
+
+@MappedSuperclass
+@SchemaNameCheck
+public abstract class AbstractDerSchema extends AbstractEntity<String> implements DerSchema {
+
+    private static final long serialVersionUID = -6173643493348674060L;
+
+    @Id
+    private String name;
+
+    @Column(nullable = false)
+    private String expression;
+
+    @Override
+    public String getKey() {
+        return name;
+    }
+
+    @Override
+    public void setKey(final String key) {
+        this.name = key;
+    }
+
+    @Override
+    public String getExpression() {
+        return expression;
+    }
+
+    @Override
+    public void setExpression(final String expression) {
+        this.expression = expression;
+    }
+
+    @Override
+    public AttrSchemaType getType() {
+        return AttrSchemaType.String;
+    }
+
+    @Override
+    public String getMandatoryCondition() {
+        return Boolean.FALSE.toString().toLowerCase();
+    }
+
+    @Override
+    public boolean isMultivalue() {
+        return Boolean.TRUE;
+    }
+
+    @Override
+    public boolean isUniqueConstraint() {
+        return Boolean.FALSE;
+    }
+
+    @Override
+    public boolean isReadonly() {
+        return Boolean.FALSE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
new file mode 100644
index 0000000..dfd706d
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
@@ -0,0 +1,111 @@
+/*
+ * 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.entity;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+
+public abstract class AbstractEntity<KEY> implements Entity<KEY> {
+
+    private static final long serialVersionUID = -9017214159540857901L;
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractEntity.class);
+
+    protected void checkType(final Object object, final Class<?> clazz) {
+        if (object != null && !clazz.isInstance(object)) {
+            throw new ClassCastException("Expected " + clazz.getName() + ", got " + object.getClass().getName());
+        }
+    }
+
+    /**
+     * @param property the integer representing a boolean value
+     * @return the boolean value corresponding to the property param
+     */
+    public final boolean isBooleanAsInteger(final Integer property) {
+        return property != null && property == 1;
+    }
+
+    /**
+     * @param value the boolean value to be represented as integer
+     * @return the integer corresponding to the property param
+     */
+    public final Integer getBooleanAsInteger(final Boolean value) {
+        return Boolean.TRUE.equals(value)
+                ? 1
+                : 0;
+    }
+
+    /**
+     * @return fields to be excluded when computing equals() or hashcode()
+     */
+    private String[] getExcludeFields() {
+        Set<String> excludeFields = new HashSet<>();
+
+        for (PropertyDescriptor propDesc : BeanUtils.getPropertyDescriptors(getClass())) {
+            if (propDesc.getPropertyType().isInstance(Collections.emptySet())
+                    || propDesc.getPropertyType().isInstance(Collections.emptyList())) {
+
+                excludeFields.add(propDesc.getName());
+            }
+        }
+
+        return excludeFields.toArray(new String[] {});
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EqualsBuilder.reflectionEquals(this, obj, getExcludeFields());
+    }
+
+    @Override
+    public int hashCode() {
+        return HashCodeBuilder.reflectionHashCode(this, getExcludeFields());
+    }
+
+    @Override
+    public String toString() {
+        Method method = BeanUtils.findMethod(getClass(), "getKey");
+
+        StringBuilder result = new StringBuilder().append(getClass().getSimpleName()).append('[');
+        if (method != null) {
+            try {
+                result.append(method.invoke(this));
+            } catch (Exception e) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.error("While serializing to string", e);
+                }
+            }
+        }
+        result.append(']');
+
+        return result.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractExec.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractExec.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractExec.java
new file mode 100644
index 0000000..7e52010
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractExec.java
@@ -0,0 +1,114 @@
+/*
+ * 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.entity;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Lob;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import org.apache.syncope.core.persistence.api.entity.Exec;
+
+@MappedSuperclass
+public abstract class AbstractExec extends AbstractEntity<Long> implements Exec {
+
+    private static final long serialVersionUID = -812344822970166317L;
+
+    @Column(nullable = false)
+    protected String status;
+
+    /**
+     * Any information to be accompanied to this execution's result.
+     */
+    @Lob
+    protected String message;
+
+    /**
+     * Start instant of this execution.
+     */
+    @Temporal(TemporalType.TIMESTAMP)
+    protected Date startDate;
+
+    /**
+     * End instant of this execution.
+     */
+    @Temporal(TemporalType.TIMESTAMP)
+    protected Date endDate;
+
+    @Override
+    public String getStatus() {
+        return status;
+    }
+
+    @Override
+    public void setStatus(final String status) {
+        this.status = status;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Set a message for this execution, taking care of replacing every null character with newline.
+     *
+     * @param message the message to set for this execution
+     */
+    @Override
+    public void setMessage(String message) {
+        if (message != null) {
+            message = message.replace('\0', '\n');
+        }
+        this.message = message;
+    }
+
+    @Override
+    public Date getEndDate() {
+        return endDate == null
+                ? null
+                : new Date(endDate.getTime());
+    }
+
+    @Override
+
+    public void setEndDate(final Date endDate) {
+        this.endDate = endDate == null
+                ? null
+                : new Date(endDate.getTime());
+    }
+
+    @Override
+
+    public Date getStartDate() {
+        return startDate == null
+                ? null
+                : new Date(startDate.getTime());
+    }
+
+    @Override
+
+    public void setStartDate(final Date startDate) {
+        this.startDate = startDate == null
+                ? null
+                : new Date(startDate.getTime());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMapping.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMapping.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMapping.java
new file mode 100644
index 0000000..3a85859
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMapping.java
@@ -0,0 +1,76 @@
+/*
+ * 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.entity;
+
+import javax.persistence.Cacheable;
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.core.persistence.api.entity.Mapping;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+
+@MappedSuperclass
+@Cacheable
+public abstract class AbstractMapping<T extends MappingItem> extends AbstractEntity<Long> implements Mapping<T> {
+
+    private static final long serialVersionUID = 4316047254916259158L;
+
+    /**
+     * A JEXL expression for determining how to find the account id in external resource's space.
+     */
+    private String accountLink;
+
+    @Override
+    public String getAccountLink() {
+        return accountLink;
+    }
+
+    @Override
+    public void setAccountLink(final String accountLink) {
+        this.accountLink = accountLink;
+    }
+
+    @Override
+    public T getAccountIdItem() {
+        T accountIdItem = null;
+        for (T item : getItems()) {
+            if (item.isAccountid()) {
+                accountIdItem = item;
+            }
+        }
+        return accountIdItem;
+    }
+
+    protected boolean addAccountIdItem(final T accountIdItem) {
+        if (IntMappingType.UserVirtualSchema == accountIdItem.getIntMappingType()
+                || IntMappingType.RoleVirtualSchema == accountIdItem.getIntMappingType()
+                || IntMappingType.MembershipVirtualSchema == accountIdItem.getIntMappingType()
+                || IntMappingType.Password == accountIdItem.getIntMappingType()) {
+
+            throw new IllegalArgumentException("Virtual attributes cannot be set as accountId");
+        }
+        if (IntMappingType.Password == accountIdItem.getIntMappingType()) {
+            throw new IllegalArgumentException("Password attributes cannot be set as accountId");
+        }
+
+        accountIdItem.setExtAttrName(accountIdItem.getExtAttrName());
+        accountIdItem.setAccountid(true);
+
+        return this.addItem(accountIdItem);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMappingItem.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMappingItem.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMappingItem.java
new file mode 100644
index 0000000..c183908
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractMappingItem.java
@@ -0,0 +1,190 @@
+/*
+ * 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.entity;
+
+import javax.persistence.Basic;
+import javax.persistence.Cacheable;
+import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.MappedSuperclass;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+
+@MappedSuperclass
+@Cacheable
+public abstract class AbstractMappingItem extends AbstractEntity<Long> implements MappingItem {
+
+    private static final long serialVersionUID = 7383601853619332424L;
+
+    @Column(nullable = true)
+    private String intAttrName;
+
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private IntMappingType intMappingType;
+
+    /**
+     * Target resource's field to be mapped.
+     */
+    @Column(nullable = true)
+    private String extAttrName;
+
+    /**
+     * Specify if the mapped target resource's field is nullable.
+     */
+    @Column(nullable = false)
+    private String mandatoryCondition;
+
+    /**
+     * Specify if the mapped target resource's field is the key.
+     */
+    @Column(nullable = false)
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer accountid;
+
+    /**
+     * Specify if the mapped target resource's field is the password.
+     */
+    @Column(nullable = false)
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer password;
+
+    /**
+     * Mapping purposes: SYNCHRONIZATION, PROPAGATION, BOTH.
+     */
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private MappingPurpose purpose;
+
+    public AbstractMappingItem() {
+        super();
+
+        mandatoryCondition = Boolean.FALSE.toString();
+
+        accountid = getBooleanAsInteger(false);
+        password = getBooleanAsInteger(false);
+    }
+
+    @Override
+    public String getExtAttrName() {
+        return extAttrName;
+    }
+
+    @Override
+    public void setExtAttrName(final String extAttrName) {
+        this.extAttrName = extAttrName;
+    }
+
+    @Override
+    public String getMandatoryCondition() {
+        return mandatoryCondition;
+    }
+
+    @Override
+    public void setMandatoryCondition(final String mandatoryCondition) {
+        this.mandatoryCondition = mandatoryCondition;
+    }
+
+    @Override
+    public String getIntAttrName() {
+        final String name;
+
+        switch (getIntMappingType()) {
+            case UserId:
+            case RoleId:
+            case MembershipId:
+                name = "id";
+                break;
+
+            case Username:
+                name = "username";
+                break;
+
+            case Password:
+                name = "password";
+                break;
+
+            case RoleName:
+                name = "roleName";
+                break;
+
+            case RoleOwnerSchema:
+                name = "roleOwnerSchema";
+                break;
+
+            default:
+                name = intAttrName;
+        }
+
+        return name;
+    }
+
+    @Override
+    public void setIntAttrName(final String intAttrName) {
+        this.intAttrName = intAttrName;
+    }
+
+    @Override
+    public IntMappingType getIntMappingType() {
+        return intMappingType;
+    }
+
+    @Override
+    public void setIntMappingType(final IntMappingType intMappingType) {
+        this.intMappingType = intMappingType;
+    }
+
+    @Override
+    public boolean isAccountid() {
+        return isBooleanAsInteger(accountid);
+    }
+
+    @Override
+    public void setAccountid(final boolean accountid) {
+        this.accountid = getBooleanAsInteger(accountid);
+    }
+
+    @Override
+    public boolean isPassword() {
+        return isBooleanAsInteger(password);
+    }
+
+    @Override
+    public void setPassword(final boolean password) {
+        this.password = getBooleanAsInteger(password);
+    }
+
+    @Override
+    public MappingPurpose getPurpose() {
+        return purpose;
+    }
+
+    @Override
+    public void setPurpose(final MappingPurpose purpose) {
+        this.purpose = purpose;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
new file mode 100644
index 0000000..659b7f1
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java
@@ -0,0 +1,81 @@
+/*
+ * 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.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.MappedSuperclass;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrCheck;
+
+@MappedSuperclass
+@PlainAttrCheck
+public abstract class AbstractPlainAttr extends AbstractEntity<Long> implements PlainAttr {
+
+    private static final long serialVersionUID = -9115431608821806124L;
+
+    protected abstract boolean addValue(PlainAttrValue attrValue);
+
+    @Override
+    public void addValue(final String value, final AttributableUtil attributableUtil)
+            throws InvalidPlainAttrValueException {
+
+        PlainAttrValue attrValue;
+        if (getSchema().isUniqueConstraint()) {
+            attrValue = attributableUtil.newPlainAttrUniqueValue();
+            ((PlainAttrUniqueValue) attrValue).setSchema(getSchema());
+        } else {
+            attrValue = attributableUtil.newPlainAttrValue();
+        }
+
+        attrValue.setAttr(this);
+        getSchema().getValidator().validate(value, attrValue);
+
+        if (getSchema().isUniqueConstraint()) {
+            setUniqueValue((PlainAttrUniqueValue) attrValue);
+        } else {
+            if (!getSchema().isMultivalue()) {
+                getValues().clear();
+            }
+            addValue(attrValue);
+        }
+    }
+
+    @Override
+    public List<String> getValuesAsStrings() {
+        List<String> result;
+        if (getUniqueValue() == null) {
+            result = new ArrayList<>(getValues().size());
+            for (PlainAttrValue attributeValue : getValues()) {
+                result.add(attributeValue.getValueAsString());
+            }
+        } else {
+            result = Collections.singletonList(getUniqueValue().getValueAsString());
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrTemplate.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrTemplate.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrTemplate.java
new file mode 100644
index 0000000..a6777a1
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrTemplate.java
@@ -0,0 +1,27 @@
+/*
+ * 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.entity;
+
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+
+public abstract class AbstractPlainAttrTemplate<P extends PlainSchema> extends AbstractAttrTemplate<P> {
+
+    private static final long serialVersionUID = -943169893494860655L;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
new file mode 100644
index 0000000..4977dad
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
@@ -0,0 +1,282 @@
+/*
+ * 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.entity;
+
+import java.util.Date;
+import javax.persistence.Basic;
+import javax.persistence.Lob;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.misc.DataFormat;
+import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrValueCheck;
+import org.apache.syncope.core.misc.security.Encryptor;
+
+@MappedSuperclass
+@PlainAttrValueCheck
+public abstract class AbstractPlainAttrValue extends AbstractEntity<Long> implements PlainAttrValue {
+
+    private static final long serialVersionUID = -9141923816611244785L;
+
+    private String stringValue;
+
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date dateValue;
+
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer booleanValue;
+
+    private Long longValue;
+
+    private Double doubleValue;
+
+    @Lob
+    private byte[] binaryValue;
+
+    @Override
+    public Boolean getBooleanValue() {
+        return booleanValue == null
+                ? null
+                : isBooleanAsInteger(booleanValue);
+    }
+
+    @Override
+    public void setBooleanValue(final Boolean booleanValue) {
+        this.booleanValue = booleanValue == null
+                ? null
+                : getBooleanAsInteger(booleanValue);
+    }
+
+    @Override
+    public Date getDateValue() {
+        return dateValue == null
+                ? null
+                : new Date(dateValue.getTime());
+    }
+
+    @Override
+    public void setDateValue(final Date dateValue) {
+        this.dateValue = dateValue == null
+                ? null
+                : new Date(dateValue.getTime());
+    }
+
+    @Override
+    public Double getDoubleValue() {
+        return doubleValue;
+    }
+
+    @Override
+    public void setDoubleValue(final Double doubleValue) {
+        this.doubleValue = doubleValue;
+    }
+
+    @Override
+    public Long getLongValue() {
+        return longValue;
+    }
+
+    @Override
+    public void setLongValue(final Long longValue) {
+        this.longValue = longValue;
+    }
+
+    @Override
+    public String getStringValue() {
+        return stringValue;
+    }
+
+    @Override
+    public void setStringValue(final String stringValue) {
+        this.stringValue = stringValue;
+    }
+
+    @Override
+    public byte[] getBinaryValue() {
+        return binaryValue;
+    }
+
+    @Override
+    public void setBinaryValue(final byte[] binaryValue) {
+        this.binaryValue = ArrayUtils.clone(binaryValue);
+    }
+
+    @Override
+    public void parseValue(final PlainSchema schema, final String value) throws ParsingValidationException {
+        Exception exception = null;
+
+        switch (schema.getType()) {
+
+            case Boolean:
+                this.setBooleanValue(Boolean.parseBoolean(value));
+                break;
+
+            case Long:
+                try {
+                    this.setLongValue(schema.getConversionPattern() == null
+                            ? Long.valueOf(value)
+                            : DataFormat.parseNumber(value, schema.getConversionPattern()).longValue());
+                } catch (Exception pe) {
+                    exception = pe;
+                }
+                break;
+
+            case Double:
+                try {
+                    this.setDoubleValue(schema.getConversionPattern() == null
+                            ? Double.valueOf(value)
+                            : DataFormat.parseNumber(value, schema.getConversionPattern()).doubleValue());
+                } catch (Exception pe) {
+                    exception = pe;
+                }
+                break;
+
+            case Date:
+                try {
+                    this.setDateValue(schema.getConversionPattern() == null
+                            ? DataFormat.parseDate(value)
+                            : new Date(DataFormat.parseDate(value, schema.getConversionPattern()).getTime()));
+                } catch (Exception pe) {
+                    exception = pe;
+                }
+                break;
+
+            case Encrypted:
+                try {
+                    this.setStringValue(Encryptor.getInstance(schema.getSecretKey()).
+                            encode(value, schema.getCipherAlgorithm()));
+                } catch (Exception pe) {
+                    exception = pe;
+                }
+                break;
+
+            case Binary:
+                this.setBinaryValue(Base64.decodeBase64(value));
+                break;
+
+            case String:
+            case Enum:
+            default:
+                this.setStringValue(value);
+        }
+
+        if (exception != null) {
+            throw new ParsingValidationException("While trying to parse '" + value + "' as " + schema.getKey(),
+                    exception);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getValue() {
+        return (T) (booleanValue != null
+                ? getBooleanValue()
+                : dateValue != null
+                        ? getDateValue()
+                        : doubleValue != null
+                                ? getDoubleValue()
+                                : longValue != null
+                                        ? getLongValue()
+                                        : binaryValue != null
+                                                ? getBinaryValue()
+                                                : stringValue);
+    }
+
+    @Override
+    public String getValueAsString() {
+        final AttrSchemaType type = getAttr() == null || getAttr().getSchema() == null
+                || getAttr().getSchema().getType() == null
+                        ? AttrSchemaType.String
+                        : getAttr().getSchema().getType();
+
+        return getValueAsString(type);
+    }
+
+    @Override
+    public String getValueAsString(final AttrSchemaType type) {
+        Exception exception = null;
+
+        String result = null;
+
+        switch (type) {
+
+            case Boolean:
+                result = getBooleanValue().toString();
+                break;
+
+            case Long:
+                result = getAttr() == null || getAttr().getSchema() == null
+                        || getAttr().getSchema().getConversionPattern() == null
+                                ? getLongValue().toString()
+                                : DataFormat.format(getLongValue(), getAttr().getSchema().getConversionPattern());
+                break;
+
+            case Double:
+                result = getAttr() == null || getAttr().getSchema() == null
+                        || getAttr().getSchema().getConversionPattern() == null
+                                ? getDoubleValue().toString()
+                                : DataFormat.format(getDoubleValue(), getAttr().getSchema().getConversionPattern());
+                break;
+
+            case Date:
+                result = getAttr() == null || getAttr().getSchema() == null
+                        || getAttr().getSchema().getConversionPattern() == null
+                                ? DataFormat.format(getDateValue())
+                                : DataFormat.format(getDateValue(), false, getAttr().getSchema().
+                                        getConversionPattern());
+                break;
+
+            case Binary:
+                result = new String(Base64.encodeBase64String(getBinaryValue()));
+                break;
+
+            case String:
+            case Enum:
+            case Encrypted:
+            default:
+                result = getStringValue();
+                break;
+        }
+
+        if (exception != null) {
+            throw new InvalidPlainAttrValueException(
+                    "While trying to format '" + getValue() + "' as " + type, exception);
+        }
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainSchema.java
----------------------------------------------------------------------
diff --git a/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainSchema.java b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainSchema.java
new file mode 100644
index 0000000..cf0a3c9
--- /dev/null
+++ b/syncope620/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainSchema.java
@@ -0,0 +1,272 @@
+/*
+ * 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.entity;
+
+import java.lang.reflect.Constructor;
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Transient;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BasicValidator;
+import org.apache.syncope.core.persistence.jpa.validation.entity.PlainSchemaCheck;
+import org.apache.syncope.core.persistence.jpa.validation.entity.SchemaNameCheck;
+
+@MappedSuperclass
+@PlainSchemaCheck
+@SchemaNameCheck
+public abstract class AbstractPlainSchema extends AbstractEntity<String> implements PlainSchema {
+
+    private static final long serialVersionUID = -8621028596062054739L;
+
+    @Id
+    private String name;
+
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private AttrSchemaType type;
+
+    @Column(nullable = false)
+    private String mandatoryCondition;
+
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer multivalue;
+
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer uniqueConstraint;
+
+    @Basic
+    @Min(0)
+    @Max(1)
+    private Integer readonly;
+
+    @Column(nullable = true)
+    private String conversionPattern;
+
+    @Column(nullable = true)
+    private String validatorClass;
+
+    @Column(nullable = true)
+    @Lob
+    private String enumerationValues;
+
+    @Column(nullable = true)
+    @Lob
+    private String enumerationKeys;
+
+    @Column(nullable = true)
+    private String secretKey;
+
+    @Column(nullable = true)
+    @Enumerated(EnumType.STRING)
+    private CipherAlgorithm cipherAlgorithm;
+
+    @Column(nullable = true)
+    private String mimeType;
+
+    @Transient
+    private Validator validator;
+
+    public AbstractPlainSchema() {
+        super();
+
+        type = AttrSchemaType.String;
+        mandatoryCondition = Boolean.FALSE.toString();
+        multivalue = getBooleanAsInteger(false);
+        uniqueConstraint = getBooleanAsInteger(false);
+        readonly = getBooleanAsInteger(false);
+    }
+
+    @Override
+    public String getKey() {
+        return name;
+    }
+
+    @Override
+    public void setKey(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public AttrSchemaType getType() {
+        return type;
+    }
+
+    @Override
+    public void setType(final AttrSchemaType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String getMandatoryCondition() {
+        return mandatoryCondition;
+    }
+
+    @Override
+    public void setMandatoryCondition(final String condition) {
+        this.mandatoryCondition = condition;
+    }
+
+    @Override
+    public boolean isMultivalue() {
+        return isBooleanAsInteger(multivalue);
+    }
+
+    @Override
+    public void setMultivalue(boolean multivalue) {
+        this.multivalue = getBooleanAsInteger(multivalue);
+    }
+
+    @Override
+    public boolean isUniqueConstraint() {
+        return isBooleanAsInteger(uniqueConstraint);
+    }
+
+    @Override
+    public void setUniqueConstraint(final boolean uniquevalue) {
+        this.uniqueConstraint = getBooleanAsInteger(uniquevalue);
+    }
+
+    @Override
+    public boolean isReadonly() {
+        return isBooleanAsInteger(readonly);
+    }
+
+    @Override
+    public void setReadonly(final boolean readonly) {
+        this.readonly = getBooleanAsInteger(readonly);
+    }
+
+    @Override
+    public Validator getValidator() {
+        if (validator != null) {
+            return validator;
+        }
+
+        if (getValidatorClass() != null && getValidatorClass().length() > 0) {
+            try {
+                Constructor<?> validatorConstructor = Class.forName(getValidatorClass()).
+                        getConstructor(new Class<?>[] { PlainSchema.class });
+                validator = (Validator) validatorConstructor.newInstance(this);
+            } catch (Exception e) {
+                LOG.error("Could not instantiate validator of type {}, reverting to {}",
+                        getValidatorClass(), BasicValidator.class.getSimpleName(), e);
+            }
+        }
+
+        if (validator == null) {
+            validator = new BasicValidator(this);
+        }
+
+        return validator;
+    }
+
+    @Override
+    public String getValidatorClass() {
+        return validatorClass;
+    }
+
+    @Override
+    public void setValidatorClass(final String validatorClass) {
+        this.validatorClass = validatorClass;
+    }
+
+    @Override
+    public String getEnumerationValues() {
+        return enumerationValues;
+    }
+
+    @Override
+    public void setEnumerationValues(final String enumerationValues) {
+        this.enumerationValues = enumerationValues;
+    }
+
+    @Override
+    public String getEnumerationKeys() {
+        return enumerationKeys;
+    }
+
+    @Override
+    public void setEnumerationKeys(String enumerationKeys) {
+        this.enumerationKeys = enumerationKeys;
+    }
+
+    @Override
+    public String getConversionPattern() {
+        if (!getType().isConversionPatternNeeded()) {
+            LOG.debug("Conversion pattern is not needed: {}'s type is {}", this, getType());
+        }
+
+        return conversionPattern;
+    }
+
+    @Override
+    public void setConversionPattern(final String conversionPattern) {
+        if (StringUtils.isNotBlank(conversionPattern) && !getType().isConversionPatternNeeded()) {
+            LOG.warn("Conversion pattern will be ignored: this attribute type is {}", getType());
+        }
+
+        this.conversionPattern = conversionPattern;
+    }
+
+    @Override
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    @Override
+    public void setSecretKey(final String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    @Override
+    public CipherAlgorithm getCipherAlgorithm() {
+        return cipherAlgorithm;
+    }
+
+    @Override
+    public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) {
+        this.cipherAlgorithm = cipherAlgorithm;
+    }
+
+    @Override
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    @Override
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
+}