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 2017/07/06 14:56:16 UTC

[1/4] syncope git commit: [SYNCOPE-1145] Core and Common implementation completed, Console still TODO

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 22acb57e1 -> ca99a469a
  refs/heads/master d4cd44842 -> 7451efb6d


http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
index 63cb084..ba4d62d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
@@ -22,6 +22,8 @@ import java.net.URI;
 import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
+import java.util.List;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.tuple.Pair;
@@ -31,8 +33,12 @@ import org.apache.syncope.common.lib.to.ConnPoolConfTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.ConnConfPropSchema;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+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.ConnInstanceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
 import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils;
@@ -40,6 +46,7 @@ import org.identityconnectors.framework.api.ConfigurationProperties;
 import org.identityconnectors.framework.api.ConfigurationProperty;
 import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl;
 import org.apache.syncope.core.spring.BeanUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.api.ConnectorInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -56,6 +63,12 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
     private ConnInstanceDAO connInstanceDAO;
 
     @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Override
@@ -102,14 +115,31 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
     }
 
     @Override
-    public ConnInstance update(final String key, final ConnInstanceTO connInstanceTO) {
-        SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
-
-        if (key == null) {
-            sce.getElements().add("connector key");
+    public ConnInstance update(final ConnInstanceTO connInstanceTO) {
+        ConnInstance connInstance = connInstanceDAO.find(connInstanceTO.getKey());
+        if (connInstance == null) {
+            throw new NotFoundException("Connector '" + connInstanceTO.getKey() + "'");
+        }
+
+        // 1. save the current configuration, before update
+        ConnInstanceHistoryConf connInstanceHistoryConf = entityFactory.newEntity(ConnInstanceHistoryConf.class);
+        connInstanceHistoryConf.setCreator(AuthContextUtils.getUsername());
+        connInstanceHistoryConf.setCreation(new Date());
+        connInstanceHistoryConf.setEntity(connInstance);
+        connInstanceHistoryConf.setConf(getConnInstanceTO(connInstance));
+        connInstanceHistoryConfDAO.save(connInstanceHistoryConf);
+
+        // 2. ensure the maximum history size is not exceeded
+        List<ConnInstanceHistoryConf> history = connInstanceHistoryConfDAO.findByEntity(connInstance);
+        long maxHistorySize = confDAO.find("connector.conf.history.size", "10").getValues().get(0).getLongValue();
+        if (maxHistorySize < history.size()) {
+            // always remove the last item since history was obtained  by a query with ORDER BY creation DESC
+            for (int i = 0; i < history.size() - maxHistorySize; i++) {
+                connInstanceHistoryConfDAO.delete(history.get(history.size() - 1).getKey());
+            }
         }
 
-        ConnInstance connInstance = connInstanceDAO.find(key);
+        // 3. actual update
         connInstance.getCapabilities().clear();
         connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities());
 
@@ -148,8 +178,12 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
                     ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf()));
         }
 
-        if (!sce.isEmpty()) {
-            throw sce;
+        try {
+            connInstance = connInstanceDAO.save(connInstance);
+        } catch (Exception e) {
+            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
+            ex.getElements().add(e.getMessage());
+            throw ex;
         }
 
         return connInstance;
@@ -188,8 +222,7 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
         BeanUtils.copyProperties(connInstance, connInstanceTO, IGNORE_PROPERTIES);
         connInstanceTO.setLocation(info.getLeft().toASCIIString());
         // refresh stored properties in the given connInstance with direct information from underlying connector
-        ConfigurationProperties properties =
-                connIdBundleManager.getConfigurationProperties(info.getRight());
+        ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(info.getRight());
         for (final String propName : properties.getPropertyNames()) {
             ConnConfPropSchema schema = build(properties.getProperty(propName));
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index 8e730b7..e6293cf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -18,8 +18,10 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
@@ -46,6 +48,8 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
 import org.apache.syncope.core.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
@@ -53,12 +57,14 @@ import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
 import org.apache.syncope.core.provisioning.api.IntAttrName;
 import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -88,6 +94,12 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
     private AnyTypeClassDAO anyTypeClassDAO;
 
     @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Autowired
@@ -100,8 +112,25 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
     @Override
     public ExternalResource update(final ExternalResource resource, final ResourceTO resourceTO) {
-        if (resourceTO == null) {
-            return null;
+        if (resource.getKey() != null) {
+            // 1. save the current configuration, before update
+            ExternalResourceHistoryConf resourceHistoryConf =
+                    entityFactory.newEntity(ExternalResourceHistoryConf.class);
+            resourceHistoryConf.setCreator(AuthContextUtils.getUsername());
+            resourceHistoryConf.setCreation(new Date());
+            resourceHistoryConf.setEntity(resource);
+            resourceHistoryConf.setConf(getResourceTO(resource));
+            resourceHistoryConfDAO.save(resourceHistoryConf);
+
+            // 2. ensure the maximum history size is not exceeded
+            List<ExternalResourceHistoryConf> history = resourceHistoryConfDAO.findByEntity(resource);
+            long maxHistorySize = confDAO.find("resource.conf.history.size", "10").getValues().get(0).getLongValue();
+            if (maxHistorySize < history.size()) {
+            // always remove the last item since history was obtained  by a query with ORDER BY creation DESC
+                for (int i = 0; i < history.size() - maxHistorySize; i++) {
+                    resourceHistoryConfDAO.delete(history.get(history.size() - 1).getKey());
+                }
+            }
         }
 
         resource.setKey(resourceTO.getKey());

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index f43e662..2635413 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -153,7 +153,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected TaskDataBinder taskDataBinder;
 
     @Autowired
-    private TaskUtilsFactory taskUtilsFactory;
+    protected TaskUtilsFactory taskUtilsFactory;
 
     @Autowired
     protected EntityFactory entityFactory;
@@ -365,6 +365,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected TaskExec execute(final PropagationTask task, final PropagationReporter reporter) {
         List<PropagationActions> actions = getPropagationActions(task.getResource());
 
+        String resource = task.getResource().getKey();
+
         Date start = new Date();
 
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
@@ -384,7 +386,6 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
         Uid uid = null;
         Connector connector = null;
         Result result;
-        String resource = task.getResource().getKey();
         try {
             provision = task.getResource().getProvision(new ObjectClass(task.getObjectClassName()));
             orgUnit = task.getResource().getOrgUnit();
@@ -507,10 +508,9 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
         for (PropagationActions action : actions) {
             action.after(task, execution, afterObj);
         }
-        
+
         String anyTypeKind = task.getAnyTypeKind() == null ? "realm" : task.getAnyTypeKind().name().toLowerCase();
         String operation = task.getOperation().name().toLowerCase();
-        // SYNCOPE-1139, check if notification or audit are requested and use TOs instead of persistence objects
         boolean notificationsAvailable = notificationManager.notificationsAvailable(
                 AuditElements.EventCategoryType.PROPAGATION, anyTypeKind, resource, operation);
         boolean auditRequested = auditManager.auditRequested(AuditElements.EventCategoryType.PROPAGATION, anyTypeKind,

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
new file mode 100644
index 0000000..3424c5e
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rest.cxf.service;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+import org.apache.syncope.core.logic.ConnectorHistoryLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ConnectorHistoryServiceImpl extends AbstractServiceImpl implements ConnectorHistoryService {
+
+    @Autowired
+    private ConnectorHistoryLogic logic;
+
+    @Override
+    public List<ConnInstanceHistoryConfTO> list(final String connectorKey) {
+        return logic.list(connectorKey);
+    }
+
+    @Override
+    public void restore(final String key) {
+        logic.restore(key);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
new file mode 100644
index 0000000..b69b5be
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rest.cxf.service;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
+import org.apache.syncope.core.logic.ResourceHistoryLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ResourceHistoryServiceImpl extends AbstractServiceImpl implements ResourceHistoryService {
+
+    @Autowired
+    private ResourceHistoryLogic logic;
+
+    @Override
+    public List<ResourceHistoryConfTO> list(final String resourceKey) {
+        return logic.list(resourceKey);
+    }
+
+    @Override
+    public void restore(final String key) {
+        logic.restore(key);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index b584802..880ed66 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.fail;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
 import javax.naming.Context;
@@ -61,7 +60,6 @@ import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.types.TraceLevel;
@@ -105,6 +103,9 @@ import org.junit.runners.MethodSorters;
 import org.springframework.jdbc.core.JdbcTemplate;
 
 import com.google.common.net.HttpHeaders;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
 
 @FixMethodOrder(MethodSorters.JVM)
 public abstract class AbstractITCase {
@@ -209,10 +210,14 @@ public abstract class AbstractITCase {
 
     protected static ResourceService resourceService;
 
+    protected static ResourceHistoryService resourceHistoryService;
+
     protected static ConfigurationService configurationService;
 
     protected static ConnectorService connectorService;
 
+    protected static ConnectorHistoryService connectorHistoryService;
+
     protected static LoggerService loggerService;
 
     protected static ReportTemplateService reportTemplateService;
@@ -289,8 +294,10 @@ public abstract class AbstractITCase {
         userWorkflowService = adminClient.getService(UserWorkflowService.class);
         groupService = adminClient.getService(GroupService.class);
         resourceService = adminClient.getService(ResourceService.class);
+        resourceHistoryService = adminClient.getService(ResourceHistoryService.class);
         configurationService = adminClient.getService(ConfigurationService.class);
         connectorService = adminClient.getService(ConnectorService.class);
+        connectorHistoryService = adminClient.getService(ConnectorHistoryService.class);
         loggerService = adminClient.getService(LoggerService.class);
         reportTemplateService = adminClient.getService(ReportTemplateService.class);
         reportService = adminClient.getService(ReportService.class);
@@ -507,18 +514,17 @@ public abstract class AbstractITCase {
     protected InitialDirContext getLdapResourceDirContext(final String bindDn, final String bindPwd)
             throws NamingException {
         ResourceTO ldapRes = resourceService.read(RESOURCE_NAME_LDAP);
-        final Map<String, ConnConfProperty> ldapConnConf =
-                connectorService.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage()).getConfMap();
+        ConnInstanceTO ldapConn = connectorService.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage());
 
         Properties env = new Properties();
         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
-        env.put(Context.PROVIDER_URL, "ldap://" + ldapConnConf.get("host").getValues().get(0)
-                + ":" + ldapConnConf.get("port").getValues().get(0) + "/");
+        env.put(Context.PROVIDER_URL, "ldap://" + ldapConn.getConf("host").getValues().get(0)
+                + ":" + ldapConn.getConf("port").getValues().get(0) + "/");
         env.put(Context.SECURITY_AUTHENTICATION, "simple");
         env.put(Context.SECURITY_PRINCIPAL,
-                bindDn == null ? ldapConnConf.get("principal").getValues().get(0) : bindDn);
+                bindDn == null ? ldapConn.getConf("principal").getValues().get(0) : bindDn);
         env.put(Context.SECURITY_CREDENTIALS,
-                bindPwd == null ? ldapConnConf.get("credentials").getValues().get(0) : bindPwd);
+                bindPwd == null ? ldapConn.getConf("credentials").getValues().get(0) : bindPwd);
 
         return new InitialDirContext(env);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index 4cea200..c5190b3 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -27,24 +28,25 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.UUID;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ConnBundleTO;
 import org.apache.syncope.common.lib.to.ConnIdObjectClassTO;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.ConnPoolConfTO;
 import org.apache.syncope.common.lib.to.MappingItemTO;
@@ -268,97 +270,9 @@ public class ConnectorITCase extends AbstractITCase {
         assertEquals(Integer.valueOf(20), actual.getConnRequestTimeout());
     }
 
-    private List<ResourceTO> filter(final List<ResourceTO> input, final String connectorKey) {
-        List<ResourceTO> result = new ArrayList<>();
-
-        for (ResourceTO resource : input) {
-            if (connectorKey.equals(resource.getConnector())) {
-                result.add(resource);
-            }
-        }
-
-        return result;
-    }
-
     @Test
-    public void issueSYNCOPE10() {
-        // ----------------------------------
-        // Copy resource and connector in order to create new objects.
-        // ----------------------------------
-        // Retrieve a connector instance template.
-        ConnInstanceTO connInstanceTO = connectorService.read(
-                "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());
-        assertNotNull(connInstanceTO);
-
-        // check for resource
-        List<ResourceTO> resources =
-                filter(resourceService.list(), "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b");
-        assertEquals(4, resources.size());
-
-        // Retrieve a resource TO template.
-        ResourceTO resourceTO = resources.get(0);
-
-        // Make it new.
-        resourceTO.setKey("newAbout103" + getUUIDString());
-
-        // Make it new.
-        connInstanceTO.setKey(null);
-        connInstanceTO.setDisplayName("newDisplayName" + getUUIDString());
-        // ----------------------------------
-
-        // ----------------------------------
-        // Create a new connector instance.
-        // ----------------------------------
-        Response response = connectorService.create(connInstanceTO);
-        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
-            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
-        }
-
-        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
-        assertNotNull(connInstanceTO);
-        assertFalse(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-
-        String connKey = connInstanceTO.getKey();
-
-        // Link resourceTO to the new connector instance.
-        resourceTO.setConnector(connKey);
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for connector instance update after resource creation.
-        // ----------------------------------
-        response = resourceService.create(resourceTO);
-        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
-
-        assertNotNull(resourceTO);
-
-        resources = filter(resourceService.list(), connKey);
-        assertEquals(1, resources.size());
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for spring bean.
-        // ----------------------------------
-        ConnInstanceTO connInstanceBean = connectorService.readByResource(
-                resourceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertNotNull(connInstanceBean);
-        assertFalse(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for spring bean update after connector instance update.
-        // ----------------------------------
-        connInstanceTO.getCapabilities().add(ConnectorCapability.AUTHENTICATE);
-
-        connectorService.update(connInstanceTO);
-        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertNotNull(actual);
-        assertTrue(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-
-        // check for spring bean update
-        connInstanceBean = connectorService.readByResource(resourceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertTrue(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-        // ----------------------------------
+    public void reload() {
+        connectorService.reload();
     }
 
     @Test
@@ -425,11 +339,9 @@ public class ConnectorITCase extends AbstractITCase {
         // 1. Check Italian
         List<ConnInstanceTO> connectorInstanceTOs = connectorService.list("it");
 
-        Map<String, ConnConfProperty> instanceConfMap;
         for (ConnInstanceTO instance : connectorInstanceTOs) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
-                instanceConfMap = instance.getConfMap();
-                assertEquals("Utente", instanceConfMap.get("user").getSchema().getDisplayName());
+                assertEquals("Utente", instance.getConf("user").getSchema().getDisplayName());
             }
         }
 
@@ -438,8 +350,7 @@ public class ConnectorITCase extends AbstractITCase {
 
         for (ConnInstanceTO instance : connectorInstanceTOs) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
-                instanceConfMap = instance.getConfMap();
-                assertEquals("User", instanceConfMap.get("user").getSchema().getDisplayName());
+                assertEquals("User", instance.getConf("user").getSchema().getDisplayName());
             }
         }
     }
@@ -574,6 +485,132 @@ public class ConnectorITCase extends AbstractITCase {
     }
 
     @Test
+    public void history() {
+        List<ConnInstanceHistoryConfTO> history = connectorHistoryService.list("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertNotNull(history);
+        int pre = history.size();
+
+        ConnInstanceTO ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        String originalDisplayName = ldapConn.getDisplayName();
+        Set<ConnectorCapability> originalCapabilities = new HashSet<>(ldapConn.getCapabilities());
+        ConnConfProperty originalConfProp = SerializationUtils.clone(ldapConn.getConf("maintainPosixGroupMembership"));
+        assertEquals(1, originalConfProp.getValues().size());
+        assertEquals("false", originalConfProp.getValues().get(0));
+
+        ldapConn.setDisplayName(originalDisplayName + " modified");
+        ldapConn.getCapabilities().clear();
+        ldapConn.getConf("maintainPosixGroupMembership").getValues().set(0, "true");
+        connectorService.update(ldapConn);
+
+        ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        assertNotEquals(originalDisplayName, ldapConn.getDisplayName());
+        assertNotEquals(originalCapabilities, ldapConn.getCapabilities());
+        assertNotEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership"));
+
+        history = connectorHistoryService.list("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertEquals(pre + 1, history.size());
+
+        connectorHistoryService.restore(history.get(0).getKey());
+
+        ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        assertEquals(originalDisplayName, ldapConn.getDisplayName());
+        assertEquals(originalCapabilities, ldapConn.getCapabilities());
+        assertEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership"));
+    }
+
+    @Test
+    public void issueSYNCOPE10() {
+        // ----------------------------------
+        // Copy resource and connector in order to create new objects.
+        // ----------------------------------
+        // Retrieve a connector instance template.
+        ConnInstanceTO connInstanceTO = connectorService.read(
+                "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());
+        assertNotNull(connInstanceTO);
+
+        // check for resource
+        Collection<ResourceTO> resources = CollectionUtils.select(resourceService.list(), new Predicate<ResourceTO>() {
+
+            @Override
+            public boolean evaluate(final ResourceTO object) {
+                return "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b".equals(object.getConnector());
+            }
+        });
+        assertEquals(4, resources.size());
+
+        // Retrieve a resource TO template.
+        ResourceTO resourceTO = resources.iterator().next();
+
+        // Make it new.
+        resourceTO.setKey("newAbout103" + getUUIDString());
+
+        // Make it new.
+        connInstanceTO.setKey(null);
+        connInstanceTO.setDisplayName("newDisplayName" + getUUIDString());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Create a new connector instance.
+        // ----------------------------------
+        Response response = connectorService.create(connInstanceTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+        }
+
+        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+        assertNotNull(connInstanceTO);
+        assertFalse(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+
+        final String connKey = connInstanceTO.getKey();
+
+        // Link resourceTO to the new connector instance.
+        resourceTO.setConnector(connKey);
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for connector instance update after resource creation.
+        // ----------------------------------
+        response = resourceService.create(resourceTO);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+        assertNotNull(resourceTO);
+
+        resources = CollectionUtils.select(resourceService.list(), new Predicate<ResourceTO>() {
+
+            @Override
+            public boolean evaluate(final ResourceTO object) {
+                return connKey.equals(object.getConnector());
+            }
+        });
+        assertEquals(1, resources.size());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean.
+        // ----------------------------------
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(
+                resourceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertNotNull(connInstanceBean);
+        assertFalse(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean update after connector instance update.
+        // ----------------------------------
+        connInstanceTO.getCapabilities().add(ConnectorCapability.AUTHENTICATE);
+
+        connectorService.update(connInstanceTO);
+        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertNotNull(actual);
+        assertTrue(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+
+        // check for spring bean update
+        connInstanceBean = connectorService.readByResource(resourceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertTrue(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+        // ----------------------------------
+    }
+
+    @Test
     public void issueSYNCOPE112() {
         // ----------------------------------------
         // Create a new connector
@@ -686,11 +723,6 @@ public class ConnectorITCase extends AbstractITCase {
     }
 
     @Test
-    public void reload() {
-        connectorService.reload();
-    }
-
-    @Test
     public void issueSYNCOPE605() {
         ConnInstanceTO connectorInstanceTO = connectorService.read(
                 "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index 59e290e..d1d85f8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -563,7 +563,7 @@ public class PullTaskITCase extends AbstractTaskITCase {
         try {
             connForTest.getCapabilities().add(ConnectorCapability.SYNC);
 
-            ConnConfProperty changeLogColumn = connForTest.getConfMap().get("changeLogColumn");
+            ConnConfProperty changeLogColumn = connForTest.getConf("changeLogColumn");
             assertNotNull(changeLogColumn);
             assertTrue(changeLogColumn.getValues().isEmpty());
             changeLogColumn.getValues().add("lastModification");
@@ -953,7 +953,7 @@ public class PullTaskITCase extends AbstractTaskITCase {
             ResourceTO ldapResource = resourceService.read(RESOURCE_NAME_LDAP);
             resourceConnector = connectorService.read(
                     ldapResource.getConnector(), Locale.ENGLISH.getLanguage());
-            property = resourceConnector.getConfMap().get("retrievePasswordsWithSearch");
+            property = resourceConnector.getConf("retrievePasswordsWithSearch");
             property.getValues().clear();
             property.getValues().add(Boolean.TRUE);
             connectorService.update(resourceConnector);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
index ebfb113..751ce0e 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -35,6 +36,7 @@ import java.util.Set;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.client.console.commons.ConnIdSpecialName;
 import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -46,6 +48,7 @@ import org.apache.syncope.common.lib.to.MappingTO;
 import org.apache.syncope.common.lib.to.OrgUnitTO;
 import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
 import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
@@ -53,6 +56,7 @@ import org.apache.syncope.common.lib.types.ConnConfPropSchema;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
 import org.apache.syncope.common.rest.api.service.ResourceService;
 import org.identityconnectors.framework.common.objects.ObjectClass;
@@ -498,29 +502,6 @@ public class ResourceITCase extends AbstractITCase {
     }
 
     @Test
-    public void issueSYNCOPE323() {
-        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
-        assertNotNull(actual);
-
-        try {
-            createResource(actual);
-            fail();
-        } catch (SyncopeClientException e) {
-            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
-            assertEquals(ClientExceptionType.EntityExists, e.getType());
-        }
-
-        actual.setKey(null);
-        try {
-            createResource(actual);
-            fail();
-        } catch (SyncopeClientException e) {
-            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
-            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
-        }
-    }
-
-    @Test
     public void anonymous() {
         ResourceService unauthenticated = clientFactory.create().getService(ResourceService.class);
         try {
@@ -593,6 +574,65 @@ public class ResourceITCase extends AbstractITCase {
     }
 
     @Test
+    public void history() {
+        List<ResourceHistoryConfTO> history = resourceHistoryService.list(RESOURCE_NAME_LDAP);
+        assertNotNull(history);
+        int pre = history.size();
+
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        TraceLevel originalTraceLevel = SerializationUtils.clone(ldap.getUpdateTraceLevel());
+        assertEquals(TraceLevel.ALL, originalTraceLevel);
+        ProvisionTO originalProvision = SerializationUtils.clone(ldap.getProvision(AnyTypeKind.USER.name()));
+        assertEquals(ObjectClass.ACCOUNT_NAME, originalProvision.getObjectClass());
+        boolean originalFlag = ldap.isRandomPwdIfNotProvided();
+        assertTrue(originalFlag);
+
+        ldap.setUpdateTraceLevel(TraceLevel.FAILURES);
+        ldap.getProvision(AnyTypeKind.USER.name()).setObjectClass("ANOTHER");
+        ldap.setRandomPwdIfNotProvided(false);
+        resourceService.update(ldap);
+
+        ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertNotEquals(originalTraceLevel, ldap.getUpdateTraceLevel());
+        assertNotEquals(
+                originalProvision.getObjectClass(), ldap.getProvision(AnyTypeKind.USER.name()).getObjectClass());
+        assertNotEquals(originalFlag, ldap.isRandomPwdIfNotProvided());
+
+        history = resourceHistoryService.list(RESOURCE_NAME_LDAP);
+        assertEquals(pre + 1, history.size());
+
+        resourceHistoryService.restore(history.get(0).getKey());
+
+        ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertEquals(originalTraceLevel, ldap.getUpdateTraceLevel());
+        assertEquals(originalProvision.getObjectClass(), ldap.getProvision(AnyTypeKind.USER.name()).getObjectClass());
+        assertEquals(originalFlag, ldap.isRandomPwdIfNotProvided());
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
+        assertNotNull(actual);
+
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
     public void issueSYNCOPE360() {
         final String name = "SYNCOPE360-" + getUUIDString();
         resourceService.create(buildResourceTO(name));


[3/4] syncope git commit: [SYNCOPE-1145] Core and Common implementation completed, Console still TODO

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
index 63cb084..ba4d62d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
@@ -22,6 +22,8 @@ import java.net.URI;
 import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
+import java.util.List;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.tuple.Pair;
@@ -31,8 +33,12 @@ import org.apache.syncope.common.lib.to.ConnPoolConfTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.ConnConfPropSchema;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+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.ConnInstanceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
 import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils;
@@ -40,6 +46,7 @@ import org.identityconnectors.framework.api.ConfigurationProperties;
 import org.identityconnectors.framework.api.ConfigurationProperty;
 import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl;
 import org.apache.syncope.core.spring.BeanUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.api.ConnectorInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -56,6 +63,12 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
     private ConnInstanceDAO connInstanceDAO;
 
     @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Override
@@ -102,14 +115,31 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
     }
 
     @Override
-    public ConnInstance update(final String key, final ConnInstanceTO connInstanceTO) {
-        SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
-
-        if (key == null) {
-            sce.getElements().add("connector key");
+    public ConnInstance update(final ConnInstanceTO connInstanceTO) {
+        ConnInstance connInstance = connInstanceDAO.find(connInstanceTO.getKey());
+        if (connInstance == null) {
+            throw new NotFoundException("Connector '" + connInstanceTO.getKey() + "'");
+        }
+
+        // 1. save the current configuration, before update
+        ConnInstanceHistoryConf connInstanceHistoryConf = entityFactory.newEntity(ConnInstanceHistoryConf.class);
+        connInstanceHistoryConf.setCreator(AuthContextUtils.getUsername());
+        connInstanceHistoryConf.setCreation(new Date());
+        connInstanceHistoryConf.setEntity(connInstance);
+        connInstanceHistoryConf.setConf(getConnInstanceTO(connInstance));
+        connInstanceHistoryConfDAO.save(connInstanceHistoryConf);
+
+        // 2. ensure the maximum history size is not exceeded
+        List<ConnInstanceHistoryConf> history = connInstanceHistoryConfDAO.findByEntity(connInstance);
+        long maxHistorySize = confDAO.find("connector.conf.history.size", "10").getValues().get(0).getLongValue();
+        if (maxHistorySize < history.size()) {
+            // always remove the last item since history was obtained  by a query with ORDER BY creation DESC
+            for (int i = 0; i < history.size() - maxHistorySize; i++) {
+                connInstanceHistoryConfDAO.delete(history.get(history.size() - 1).getKey());
+            }
         }
 
-        ConnInstance connInstance = connInstanceDAO.find(key);
+        // 3. actual update
         connInstance.getCapabilities().clear();
         connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities());
 
@@ -148,8 +178,12 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
                     ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf()));
         }
 
-        if (!sce.isEmpty()) {
-            throw sce;
+        try {
+            connInstance = connInstanceDAO.save(connInstance);
+        } catch (Exception e) {
+            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
+            ex.getElements().add(e.getMessage());
+            throw ex;
         }
 
         return connInstance;
@@ -188,8 +222,7 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
         BeanUtils.copyProperties(connInstance, connInstanceTO, IGNORE_PROPERTIES);
         connInstanceTO.setLocation(info.getLeft().toASCIIString());
         // refresh stored properties in the given connInstance with direct information from underlying connector
-        ConfigurationProperties properties =
-                connIdBundleManager.getConfigurationProperties(info.getRight());
+        ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(info.getRight());
         for (final String propName : properties.getPropertyNames()) {
             ConnConfPropSchema schema = build(properties.getProperty(propName));
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index 8e730b7..e6293cf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -18,8 +18,10 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
@@ -46,6 +48,8 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
 import org.apache.syncope.core.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
@@ -53,12 +57,14 @@ import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
 import org.apache.syncope.core.provisioning.api.IntAttrName;
 import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -88,6 +94,12 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
     private AnyTypeClassDAO anyTypeClassDAO;
 
     @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Autowired
@@ -100,8 +112,25 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
     @Override
     public ExternalResource update(final ExternalResource resource, final ResourceTO resourceTO) {
-        if (resourceTO == null) {
-            return null;
+        if (resource.getKey() != null) {
+            // 1. save the current configuration, before update
+            ExternalResourceHistoryConf resourceHistoryConf =
+                    entityFactory.newEntity(ExternalResourceHistoryConf.class);
+            resourceHistoryConf.setCreator(AuthContextUtils.getUsername());
+            resourceHistoryConf.setCreation(new Date());
+            resourceHistoryConf.setEntity(resource);
+            resourceHistoryConf.setConf(getResourceTO(resource));
+            resourceHistoryConfDAO.save(resourceHistoryConf);
+
+            // 2. ensure the maximum history size is not exceeded
+            List<ExternalResourceHistoryConf> history = resourceHistoryConfDAO.findByEntity(resource);
+            long maxHistorySize = confDAO.find("resource.conf.history.size", "10").getValues().get(0).getLongValue();
+            if (maxHistorySize < history.size()) {
+            // always remove the last item since history was obtained  by a query with ORDER BY creation DESC
+                for (int i = 0; i < history.size() - maxHistorySize; i++) {
+                    resourceHistoryConfDAO.delete(history.get(history.size() - 1).getKey());
+                }
+            }
         }
 
         resource.setKey(resourceTO.getKey());

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index f43e662..2635413 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -153,7 +153,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected TaskDataBinder taskDataBinder;
 
     @Autowired
-    private TaskUtilsFactory taskUtilsFactory;
+    protected TaskUtilsFactory taskUtilsFactory;
 
     @Autowired
     protected EntityFactory entityFactory;
@@ -365,6 +365,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected TaskExec execute(final PropagationTask task, final PropagationReporter reporter) {
         List<PropagationActions> actions = getPropagationActions(task.getResource());
 
+        String resource = task.getResource().getKey();
+
         Date start = new Date();
 
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
@@ -384,7 +386,6 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
         Uid uid = null;
         Connector connector = null;
         Result result;
-        String resource = task.getResource().getKey();
         try {
             provision = task.getResource().getProvision(new ObjectClass(task.getObjectClassName()));
             orgUnit = task.getResource().getOrgUnit();
@@ -507,10 +508,9 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
         for (PropagationActions action : actions) {
             action.after(task, execution, afterObj);
         }
-        
+
         String anyTypeKind = task.getAnyTypeKind() == null ? "realm" : task.getAnyTypeKind().name().toLowerCase();
         String operation = task.getOperation().name().toLowerCase();
-        // SYNCOPE-1139, check if notification or audit are requested and use TOs instead of persistence objects
         boolean notificationsAvailable = notificationManager.notificationsAvailable(
                 AuditElements.EventCategoryType.PROPAGATION, anyTypeKind, resource, operation);
         boolean auditRequested = auditManager.auditRequested(AuditElements.EventCategoryType.PROPAGATION, anyTypeKind,

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
new file mode 100644
index 0000000..3424c5e
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorHistoryServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rest.cxf.service;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+import org.apache.syncope.core.logic.ConnectorHistoryLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ConnectorHistoryServiceImpl extends AbstractServiceImpl implements ConnectorHistoryService {
+
+    @Autowired
+    private ConnectorHistoryLogic logic;
+
+    @Override
+    public List<ConnInstanceHistoryConfTO> list(final String connectorKey) {
+        return logic.list(connectorKey);
+    }
+
+    @Override
+    public void restore(final String key) {
+        logic.restore(key);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
new file mode 100644
index 0000000..b69b5be
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceHistoryServiceImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rest.cxf.service;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
+import org.apache.syncope.core.logic.ResourceHistoryLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ResourceHistoryServiceImpl extends AbstractServiceImpl implements ResourceHistoryService {
+
+    @Autowired
+    private ResourceHistoryLogic logic;
+
+    @Override
+    public List<ResourceHistoryConfTO> list(final String resourceKey) {
+        return logic.list(resourceKey);
+    }
+
+    @Override
+    public void restore(final String key) {
+        logic.restore(key);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index b584802..880ed66 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.fail;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
 import javax.naming.Context;
@@ -61,7 +60,6 @@ import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.types.TraceLevel;
@@ -105,6 +103,9 @@ import org.junit.runners.MethodSorters;
 import org.springframework.jdbc.core.JdbcTemplate;
 
 import com.google.common.net.HttpHeaders;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
+import org.apache.syncope.common.rest.api.service.ResourceHistoryService;
 
 @FixMethodOrder(MethodSorters.JVM)
 public abstract class AbstractITCase {
@@ -209,10 +210,14 @@ public abstract class AbstractITCase {
 
     protected static ResourceService resourceService;
 
+    protected static ResourceHistoryService resourceHistoryService;
+
     protected static ConfigurationService configurationService;
 
     protected static ConnectorService connectorService;
 
+    protected static ConnectorHistoryService connectorHistoryService;
+
     protected static LoggerService loggerService;
 
     protected static ReportTemplateService reportTemplateService;
@@ -289,8 +294,10 @@ public abstract class AbstractITCase {
         userWorkflowService = adminClient.getService(UserWorkflowService.class);
         groupService = adminClient.getService(GroupService.class);
         resourceService = adminClient.getService(ResourceService.class);
+        resourceHistoryService = adminClient.getService(ResourceHistoryService.class);
         configurationService = adminClient.getService(ConfigurationService.class);
         connectorService = adminClient.getService(ConnectorService.class);
+        connectorHistoryService = adminClient.getService(ConnectorHistoryService.class);
         loggerService = adminClient.getService(LoggerService.class);
         reportTemplateService = adminClient.getService(ReportTemplateService.class);
         reportService = adminClient.getService(ReportService.class);
@@ -507,18 +514,17 @@ public abstract class AbstractITCase {
     protected InitialDirContext getLdapResourceDirContext(final String bindDn, final String bindPwd)
             throws NamingException {
         ResourceTO ldapRes = resourceService.read(RESOURCE_NAME_LDAP);
-        final Map<String, ConnConfProperty> ldapConnConf =
-                connectorService.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage()).getConfMap();
+        ConnInstanceTO ldapConn = connectorService.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage());
 
         Properties env = new Properties();
         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
-        env.put(Context.PROVIDER_URL, "ldap://" + ldapConnConf.get("host").getValues().get(0)
-                + ":" + ldapConnConf.get("port").getValues().get(0) + "/");
+        env.put(Context.PROVIDER_URL, "ldap://" + ldapConn.getConf("host").getValues().get(0)
+                + ":" + ldapConn.getConf("port").getValues().get(0) + "/");
         env.put(Context.SECURITY_AUTHENTICATION, "simple");
         env.put(Context.SECURITY_PRINCIPAL,
-                bindDn == null ? ldapConnConf.get("principal").getValues().get(0) : bindDn);
+                bindDn == null ? ldapConn.getConf("principal").getValues().get(0) : bindDn);
         env.put(Context.SECURITY_CREDENTIALS,
-                bindPwd == null ? ldapConnConf.get("credentials").getValues().get(0) : bindPwd);
+                bindPwd == null ? ldapConn.getConf("credentials").getValues().get(0) : bindPwd);
 
         return new InitialDirContext(env);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index 4cea200..c5190b3 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -27,24 +28,25 @@ import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.UUID;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ConnBundleTO;
 import org.apache.syncope.common.lib.to.ConnIdObjectClassTO;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.ConnPoolConfTO;
 import org.apache.syncope.common.lib.to.MappingItemTO;
@@ -268,97 +270,9 @@ public class ConnectorITCase extends AbstractITCase {
         assertEquals(Integer.valueOf(20), actual.getConnRequestTimeout());
     }
 
-    private List<ResourceTO> filter(final List<ResourceTO> input, final String connectorKey) {
-        List<ResourceTO> result = new ArrayList<>();
-
-        for (ResourceTO resource : input) {
-            if (connectorKey.equals(resource.getConnector())) {
-                result.add(resource);
-            }
-        }
-
-        return result;
-    }
-
     @Test
-    public void issueSYNCOPE10() {
-        // ----------------------------------
-        // Copy resource and connector in order to create new objects.
-        // ----------------------------------
-        // Retrieve a connector instance template.
-        ConnInstanceTO connInstanceTO = connectorService.read(
-                "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());
-        assertNotNull(connInstanceTO);
-
-        // check for resource
-        List<ResourceTO> resources =
-                filter(resourceService.list(), "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b");
-        assertEquals(4, resources.size());
-
-        // Retrieve a resource TO template.
-        ResourceTO resourceTO = resources.get(0);
-
-        // Make it new.
-        resourceTO.setKey("newAbout103" + getUUIDString());
-
-        // Make it new.
-        connInstanceTO.setKey(null);
-        connInstanceTO.setDisplayName("newDisplayName" + getUUIDString());
-        // ----------------------------------
-
-        // ----------------------------------
-        // Create a new connector instance.
-        // ----------------------------------
-        Response response = connectorService.create(connInstanceTO);
-        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
-            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
-        }
-
-        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
-        assertNotNull(connInstanceTO);
-        assertFalse(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-
-        String connKey = connInstanceTO.getKey();
-
-        // Link resourceTO to the new connector instance.
-        resourceTO.setConnector(connKey);
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for connector instance update after resource creation.
-        // ----------------------------------
-        response = resourceService.create(resourceTO);
-        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
-
-        assertNotNull(resourceTO);
-
-        resources = filter(resourceService.list(), connKey);
-        assertEquals(1, resources.size());
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for spring bean.
-        // ----------------------------------
-        ConnInstanceTO connInstanceBean = connectorService.readByResource(
-                resourceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertNotNull(connInstanceBean);
-        assertFalse(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-        // ----------------------------------
-
-        // ----------------------------------
-        // Check for spring bean update after connector instance update.
-        // ----------------------------------
-        connInstanceTO.getCapabilities().add(ConnectorCapability.AUTHENTICATE);
-
-        connectorService.update(connInstanceTO);
-        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertNotNull(actual);
-        assertTrue(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-
-        // check for spring bean update
-        connInstanceBean = connectorService.readByResource(resourceTO.getKey(), Locale.ENGLISH.getLanguage());
-        assertTrue(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
-        // ----------------------------------
+    public void reload() {
+        connectorService.reload();
     }
 
     @Test
@@ -425,11 +339,9 @@ public class ConnectorITCase extends AbstractITCase {
         // 1. Check Italian
         List<ConnInstanceTO> connectorInstanceTOs = connectorService.list("it");
 
-        Map<String, ConnConfProperty> instanceConfMap;
         for (ConnInstanceTO instance : connectorInstanceTOs) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
-                instanceConfMap = instance.getConfMap();
-                assertEquals("Utente", instanceConfMap.get("user").getSchema().getDisplayName());
+                assertEquals("Utente", instance.getConf("user").getSchema().getDisplayName());
             }
         }
 
@@ -438,8 +350,7 @@ public class ConnectorITCase extends AbstractITCase {
 
         for (ConnInstanceTO instance : connectorInstanceTOs) {
             if ("net.tirasa.connid.bundles.db.table".equals(instance.getBundleName())) {
-                instanceConfMap = instance.getConfMap();
-                assertEquals("User", instanceConfMap.get("user").getSchema().getDisplayName());
+                assertEquals("User", instance.getConf("user").getSchema().getDisplayName());
             }
         }
     }
@@ -574,6 +485,132 @@ public class ConnectorITCase extends AbstractITCase {
     }
 
     @Test
+    public void history() {
+        List<ConnInstanceHistoryConfTO> history = connectorHistoryService.list("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertNotNull(history);
+        int pre = history.size();
+
+        ConnInstanceTO ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        String originalDisplayName = ldapConn.getDisplayName();
+        Set<ConnectorCapability> originalCapabilities = new HashSet<>(ldapConn.getCapabilities());
+        ConnConfProperty originalConfProp = SerializationUtils.clone(ldapConn.getConf("maintainPosixGroupMembership"));
+        assertEquals(1, originalConfProp.getValues().size());
+        assertEquals("false", originalConfProp.getValues().get(0));
+
+        ldapConn.setDisplayName(originalDisplayName + " modified");
+        ldapConn.getCapabilities().clear();
+        ldapConn.getConf("maintainPosixGroupMembership").getValues().set(0, "true");
+        connectorService.update(ldapConn);
+
+        ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        assertNotEquals(originalDisplayName, ldapConn.getDisplayName());
+        assertNotEquals(originalCapabilities, ldapConn.getCapabilities());
+        assertNotEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership"));
+
+        history = connectorHistoryService.list("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertEquals(pre + 1, history.size());
+
+        connectorHistoryService.restore(history.get(0).getKey());
+
+        ldapConn = connectorService.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null);
+        assertEquals(originalDisplayName, ldapConn.getDisplayName());
+        assertEquals(originalCapabilities, ldapConn.getCapabilities());
+        assertEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership"));
+    }
+
+    @Test
+    public void issueSYNCOPE10() {
+        // ----------------------------------
+        // Copy resource and connector in order to create new objects.
+        // ----------------------------------
+        // Retrieve a connector instance template.
+        ConnInstanceTO connInstanceTO = connectorService.read(
+                "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());
+        assertNotNull(connInstanceTO);
+
+        // check for resource
+        Collection<ResourceTO> resources = CollectionUtils.select(resourceService.list(), new Predicate<ResourceTO>() {
+
+            @Override
+            public boolean evaluate(final ResourceTO object) {
+                return "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b".equals(object.getConnector());
+            }
+        });
+        assertEquals(4, resources.size());
+
+        // Retrieve a resource TO template.
+        ResourceTO resourceTO = resources.iterator().next();
+
+        // Make it new.
+        resourceTO.setKey("newAbout103" + getUUIDString());
+
+        // Make it new.
+        connInstanceTO.setKey(null);
+        connInstanceTO.setDisplayName("newDisplayName" + getUUIDString());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Create a new connector instance.
+        // ----------------------------------
+        Response response = connectorService.create(connInstanceTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
+        }
+
+        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+        assertNotNull(connInstanceTO);
+        assertFalse(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+
+        final String connKey = connInstanceTO.getKey();
+
+        // Link resourceTO to the new connector instance.
+        resourceTO.setConnector(connKey);
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for connector instance update after resource creation.
+        // ----------------------------------
+        response = resourceService.create(resourceTO);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+        assertNotNull(resourceTO);
+
+        resources = CollectionUtils.select(resourceService.list(), new Predicate<ResourceTO>() {
+
+            @Override
+            public boolean evaluate(final ResourceTO object) {
+                return connKey.equals(object.getConnector());
+            }
+        });
+        assertEquals(1, resources.size());
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean.
+        // ----------------------------------
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(
+                resourceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertNotNull(connInstanceBean);
+        assertFalse(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+        // ----------------------------------
+
+        // ----------------------------------
+        // Check for spring bean update after connector instance update.
+        // ----------------------------------
+        connInstanceTO.getCapabilities().add(ConnectorCapability.AUTHENTICATE);
+
+        connectorService.update(connInstanceTO);
+        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertNotNull(actual);
+        assertTrue(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+
+        // check for spring bean update
+        connInstanceBean = connectorService.readByResource(resourceTO.getKey(), Locale.ENGLISH.getLanguage());
+        assertTrue(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
+        // ----------------------------------
+    }
+
+    @Test
     public void issueSYNCOPE112() {
         // ----------------------------------------
         // Create a new connector
@@ -686,11 +723,6 @@ public class ConnectorITCase extends AbstractITCase {
     }
 
     @Test
-    public void reload() {
-        connectorService.reload();
-    }
-
-    @Test
     public void issueSYNCOPE605() {
         ConnInstanceTO connectorInstanceTO = connectorService.read(
                 "fcf9f2b0-f7d6-42c9-84a6-61b28255a42b", Locale.ENGLISH.getLanguage());

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index 59e290e..d1d85f8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -563,7 +563,7 @@ public class PullTaskITCase extends AbstractTaskITCase {
         try {
             connForTest.getCapabilities().add(ConnectorCapability.SYNC);
 
-            ConnConfProperty changeLogColumn = connForTest.getConfMap().get("changeLogColumn");
+            ConnConfProperty changeLogColumn = connForTest.getConf("changeLogColumn");
             assertNotNull(changeLogColumn);
             assertTrue(changeLogColumn.getValues().isEmpty());
             changeLogColumn.getValues().add("lastModification");
@@ -953,7 +953,7 @@ public class PullTaskITCase extends AbstractTaskITCase {
             ResourceTO ldapResource = resourceService.read(RESOURCE_NAME_LDAP);
             resourceConnector = connectorService.read(
                     ldapResource.getConnector(), Locale.ENGLISH.getLanguage());
-            property = resourceConnector.getConfMap().get("retrievePasswordsWithSearch");
+            property = resourceConnector.getConf("retrievePasswordsWithSearch");
             property.getValues().clear();
             property.getValues().add(Boolean.TRUE);
             connectorService.update(resourceConnector);

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
index ebfb113..751ce0e 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -35,6 +36,7 @@ import java.util.Set;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.client.console.commons.ConnIdSpecialName;
 import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -46,6 +48,7 @@ import org.apache.syncope.common.lib.to.MappingTO;
 import org.apache.syncope.common.lib.to.OrgUnitTO;
 import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
 import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
@@ -53,6 +56,7 @@ import org.apache.syncope.common.lib.types.ConnConfPropSchema;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
 import org.apache.syncope.common.rest.api.service.ResourceService;
 import org.identityconnectors.framework.common.objects.ObjectClass;
@@ -498,29 +502,6 @@ public class ResourceITCase extends AbstractITCase {
     }
 
     @Test
-    public void issueSYNCOPE323() {
-        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
-        assertNotNull(actual);
-
-        try {
-            createResource(actual);
-            fail();
-        } catch (SyncopeClientException e) {
-            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
-            assertEquals(ClientExceptionType.EntityExists, e.getType());
-        }
-
-        actual.setKey(null);
-        try {
-            createResource(actual);
-            fail();
-        } catch (SyncopeClientException e) {
-            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
-            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
-        }
-    }
-
-    @Test
     public void anonymous() {
         ResourceService unauthenticated = clientFactory.create().getService(ResourceService.class);
         try {
@@ -593,6 +574,65 @@ public class ResourceITCase extends AbstractITCase {
     }
 
     @Test
+    public void history() {
+        List<ResourceHistoryConfTO> history = resourceHistoryService.list(RESOURCE_NAME_LDAP);
+        assertNotNull(history);
+        int pre = history.size();
+
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        TraceLevel originalTraceLevel = SerializationUtils.clone(ldap.getUpdateTraceLevel());
+        assertEquals(TraceLevel.ALL, originalTraceLevel);
+        ProvisionTO originalProvision = SerializationUtils.clone(ldap.getProvision(AnyTypeKind.USER.name()));
+        assertEquals(ObjectClass.ACCOUNT_NAME, originalProvision.getObjectClass());
+        boolean originalFlag = ldap.isRandomPwdIfNotProvided();
+        assertTrue(originalFlag);
+
+        ldap.setUpdateTraceLevel(TraceLevel.FAILURES);
+        ldap.getProvision(AnyTypeKind.USER.name()).setObjectClass("ANOTHER");
+        ldap.setRandomPwdIfNotProvided(false);
+        resourceService.update(ldap);
+
+        ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertNotEquals(originalTraceLevel, ldap.getUpdateTraceLevel());
+        assertNotEquals(
+                originalProvision.getObjectClass(), ldap.getProvision(AnyTypeKind.USER.name()).getObjectClass());
+        assertNotEquals(originalFlag, ldap.isRandomPwdIfNotProvided());
+
+        history = resourceHistoryService.list(RESOURCE_NAME_LDAP);
+        assertEquals(pre + 1, history.size());
+
+        resourceHistoryService.restore(history.get(0).getKey());
+
+        ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertEquals(originalTraceLevel, ldap.getUpdateTraceLevel());
+        assertEquals(originalProvision.getObjectClass(), ldap.getProvision(AnyTypeKind.USER.name()).getObjectClass());
+        assertEquals(originalFlag, ldap.isRandomPwdIfNotProvided());
+    }
+
+    @Test
+    public void issueSYNCOPE323() {
+        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
+        assertNotNull(actual);
+
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.EntityExists, e.getType());
+        }
+
+        actual.setKey(null);
+        try {
+            createResource(actual);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
+            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
+        }
+    }
+
+    @Test
     public void issueSYNCOPE360() {
         final String name = "SYNCOPE360-" + getUUIDString();
         resourceService.create(buildResourceTO(name));


[2/4] syncope git commit: [SYNCOPE-1145] Core and Common implementation completed, Console still TODO

Posted by il...@apache.org.
[SYNCOPE-1145] Core and Common implementation completed, Console still TODO


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/ca99a469
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/ca99a469
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/ca99a469

Branch: refs/heads/2_0_X
Commit: ca99a469af61d5cf948eaf10a86e1096349c5e0c
Parents: 22acb57
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Jul 6 16:55:56 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jul 6 16:55:56 2017 +0200

----------------------------------------------------------------------
 .../wizards/resources/ConnectorConfPanel.java   |   8 +-
 .../common/lib/to/AbstractHistoryConf.java      |  72 ++++++
 .../lib/to/ConnInstanceHistoryConfTO.java       |  40 ++++
 .../syncope/common/lib/to/ConnInstanceTO.java   |  22 +-
 .../common/lib/to/ResourceHistoryConfTO.java    |  40 ++++
 .../common/lib/types/StandardEntitlement.java   |  12 +
 .../api/service/ConnectorHistoryService.java    |  66 ++++++
 .../api/service/ResourceHistoryService.java     |  66 ++++++
 .../core/logic/ConnectorHistoryLogic.java       | 115 +++++++++
 .../syncope/core/logic/ConnectorLogic.java      |  26 +-
 .../core/logic/ResourceHistoryLogic.java        | 114 +++++++++
 .../syncope/core/logic/ResourceLogic.java       |  40 +---
 .../apache/syncope/core/logic/SchemaLogic.java  |  28 +--
 .../api/dao/ConnInstanceHistoryConfDAO.java     |  36 +++
 .../api/dao/ExternalResourceHistoryConfDAO.java |  36 +++
 .../api/entity/ConnInstanceHistoryConf.java     |  24 ++
 .../persistence/api/entity/HistoryConf.java     |  41 ++++
 .../resource/ExternalResourceHistoryConf.java   |  25 ++
 .../persistence/jpa/dao/JPAConnInstanceDAO.java |   8 +-
 .../jpa/dao/JPAConnInstanceHistoryConfDAO.java  |  71 ++++++
 .../jpa/dao/JPAExternalResourceDAO.java         |  15 ++
 .../dao/JPAExternalResourceHistoryConfDAO.java  |  71 ++++++
 .../jpa/entity/AbstractHistoryConf.java         |  76 ++++++
 .../jpa/entity/JPAConnInstanceHistoryConf.java  |  59 +++++
 .../jpa/entity/JPAEntityFactory.java            |   7 +
 .../JPAExternalResourceHistoryConf.java         |  60 +++++
 .../main/resources/domains/MasterContent.xml    |  17 ++
 .../persistence/jpa/inner/PlainSchemaTest.java  |   2 +-
 .../jpa/outer/ConnInstanceHistoryConfTest.java  |  74 ++++++
 .../jpa/outer/ResourceHistoryConfTest.java      |  74 ++++++
 .../test/resources/domains/MasterContent.xml    |  19 +-
 .../api/data/ConnInstanceDataBinder.java        |   2 +-
 .../java/data/ConnInstanceDataBinderImpl.java   |  53 ++++-
 .../java/data/ResourceDataBinderImpl.java       |  33 ++-
 .../AbstractPropagationTaskExecutor.java        |   8 +-
 .../service/ConnectorHistoryServiceImpl.java    |  49 ++++
 .../cxf/service/ResourceHistoryServiceImpl.java |  49 ++++
 .../org/apache/syncope/fit/AbstractITCase.java  |  22 +-
 .../syncope/fit/core/ConnectorITCase.java       | 236 +++++++++++--------
 .../apache/syncope/fit/core/PullTaskITCase.java |   4 +-
 .../apache/syncope/fit/core/ResourceITCase.java |  86 +++++--
 41 files changed, 1665 insertions(+), 241 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
index 21d494d..580e2c8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
@@ -87,11 +87,9 @@ public abstract class ConnectorConfPanel extends AbstractConnConfPanel<ConnInsta
                 final ConnConfProperty property = new ConnConfProperty();
                 property.setSchema(key);
 
-                if (instance.getConfMap().containsKey(key.getName())
-                        && instance.getConfMap().get(key.getName()).getValues() != null) {
-
-                    property.getValues().addAll(instance.getConfMap().get(key.getName()).getValues());
-                    property.setOverridable(instance.getConfMap().get(key.getName()).isOverridable());
+                if (instance.getConf(key.getName()) != null && instance.getConf(key.getName()).getValues() != null) {
+                    property.getValues().addAll(instance.getConf(key.getName()).getValues());
+                    property.setOverridable(instance.getConf(key.getName()).isOverridable());
                 }
 
                 if (property.getValues().isEmpty() && !key.getDefaultValues().isEmpty()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
new file mode 100644
index 0000000..a410a73
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
@@ -0,0 +1,72 @@
+/*
+ * 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.common.lib.to;
+
+import java.util.Date;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "abstractHistoryConf")
+@XmlType
+@XmlSeeAlso({ ConnInstanceHistoryConfTO.class, ResourceHistoryConfTO.class })
+public abstract class AbstractHistoryConf extends AbstractBaseBean implements EntityTO {
+
+    private static final long serialVersionUID = -8001640160293506651L;
+
+    private String key;
+
+    private String creator;
+
+    private Date creation;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getCreator() {
+        return creator;
+    }
+
+    public void setCreator(final String creator) {
+        this.creator = creator;
+    }
+
+    public Date getCreation() {
+        return creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    public void setCreation(final Date creation) {
+        this.creation = creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
new file mode 100644
index 0000000..6583c54
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "connInstanceHistoryConf")
+@XmlType
+public class ConnInstanceHistoryConfTO extends AbstractHistoryConf {
+
+    private static final long serialVersionUID = 8067539341757100479L;
+
+    private ConnInstanceTO connInstanceTO;
+
+    public ConnInstanceTO getConnInstanceTO() {
+        return connInstanceTO;
+    }
+
+    public void setConnInstanceTO(final ConnInstanceTO connInstanceTO) {
+        this.connInstanceTO = connInstanceTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
index 4f91355..d7c8be7 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
@@ -20,17 +20,16 @@ package org.apache.syncope.common.lib.to;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 import javax.ws.rs.PathParam;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.ConnectorCapability;
@@ -112,15 +111,14 @@ public class ConnInstanceTO extends AbstractBaseBean implements EntityTO {
     }
 
     @JsonIgnore
-    public Map<String, ConnConfProperty> getConfMap() {
-        Map<String, ConnConfProperty> result = new HashMap<>();
-
-        for (ConnConfProperty prop : getConf()) {
-            result.put(prop.getSchema().getName(), prop);
-        }
-        result = Collections.unmodifiableMap(result);
-
-        return Collections.unmodifiableMap(result);
+    public ConnConfProperty getConf(final String schemaName) {
+        return IterableUtils.find(conf, new Predicate<ConnConfProperty>() {
+
+            @Override
+            public boolean evaluate(final ConnConfProperty object) {
+                return object.getSchema().getName().equals(schemaName);
+            }
+        });
     }
 
     @XmlElementWrapper(name = "capabilities")

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
new file mode 100644
index 0000000..388b63c
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "resourceHistoryConf")
+@XmlType
+public class ResourceHistoryConfTO extends AbstractHistoryConf {
+
+    private static final long serialVersionUID = -7308499697925335157L;
+
+    private ResourceTO resourceTO;
+
+    public ResourceTO getResourceTO() {
+        return resourceTO;
+    }
+
+    public void setResourceTO(final ResourceTO resourceTO) {
+        this.resourceTO = resourceTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 81709f6..9bf8de3 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -128,6 +128,12 @@ public final class StandardEntitlement {
 
     public static final String RESOURCE_LIST_CONNOBJECT = "RESOURCE_LIST_CONNOBJECT";
 
+    public static final String RESOURCE_HISTORY_LIST = "RESOURCE_HISTORY_LIST";
+
+    public static final String RESOURCE_HISTORY_RESTORE = "RESOURCE_HISTORY_RESTORE";
+
+    public static final String RESOURCE_HISTORY_DELETE = "RESOURCE_HISTORY_DELETE";
+
     public static final String CONNECTOR_LIST = "CONNECTOR_LIST";
 
     public static final String CONNECTOR_CREATE = "CONNECTOR_CREATE";
@@ -140,6 +146,12 @@ public final class StandardEntitlement {
 
     public static final String CONNECTOR_RELOAD = "CONNECTOR_RELOAD";
 
+    public static final String CONNECTOR_HISTORY_LIST = "CONNECTOR_HISTORY_LIST";
+
+    public static final String CONNECTOR_HISTORY_RESTORE = "CONNECTOR_HISTORY_RESTORE";
+
+    public static final String CONNECTOR_HISTORY_DELETE = "CONNECTOR_HISTORY_DELETE";
+
     public static final String CONFIGURATION_EXPORT = "CONFIGURATION_EXPORT";
 
     public static final String CONFIGURATION_LIST = "CONFIGURATION_LIST";

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
new file mode 100644
index 0000000..28a0b3c
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+
+/**
+ * REST operations for connector instance configuration versioning.
+ */
+@Path("connectorsHistory")
+public interface ConnectorHistoryService extends JAXRSService {
+
+    /**
+     * Returns a list of all connector configuration history instances for the given connector instance key.
+     *
+     * @param connectorKey connector instance key
+     * @return list of all connector configuration history instances for the given connector instance key
+     */
+    @GET
+    @Path("{connectorKey}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<ConnInstanceHistoryConfTO> list(@NotNull @PathParam("connectorKey") String connectorKey);
+
+    /**
+     * Restores the connector configuration history instance matching the provided key.
+     * 
+     * @param key connector configuration history instance key to be restored
+     */
+    @POST
+    @Path("{key}")
+    void restore(@NotNull @PathParam("key") String key);
+
+    /**
+     * Deletes the connector configuration history instance matching the provided key.
+     *
+     * @param key connector configuration history instance key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
new file mode 100644
index 0000000..fbf748a
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+
+/**
+ * REST operations for resource configuration versioning.
+ */
+@Path("resourcesHistory")
+public interface ResourceHistoryService extends JAXRSService {
+
+    /**
+     * Returns a list of all resource configuration history for the given resource key.
+     *
+     * @param resourceKey resource key
+     * @return list of all resource configuration history for the given resource key
+     */
+    @GET
+    @Path("{resourceKey}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<ResourceHistoryConfTO> list(@NotNull @PathParam("resourceKey") String resourceKey);
+
+    /**
+     * Restores the resource configuration history matching the provided key.
+     *
+     * @param key resource configuration history key to be restored
+     */
+    @POST
+    @Path("{key}")
+    void restore(@NotNull @PathParam("key") String key);
+
+    /**
+     * Deletes the resource configuration history matching the provided key.
+     *
+     * @param key resource configuration history key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
new file mode 100644
index 0000000..2d0afa7
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
@@ -0,0 +1,115 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+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.ConnInstanceHistoryConf;
+import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConnectorHistoryLogic extends AbstractTransactionalLogic<ConnInstanceHistoryConfTO> {
+
+    @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
+    private ConnInstanceDAO connInstanceDAO;
+
+    @Autowired
+    private ConnInstanceDataBinder binder;
+
+    private ConnInstanceHistoryConfTO getConnInstanceHistoryConfTO(final ConnInstanceHistoryConf history) {
+        ConnInstanceHistoryConfTO historyTO = new ConnInstanceHistoryConfTO();
+        historyTO.setKey(history.getKey());
+        historyTO.setCreator(history.getCreator());
+        historyTO.setCreation(history.getCreation());
+        historyTO.setConnInstanceTO(history.getConf());
+
+        return historyTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_LIST + "')")
+    public List<ConnInstanceHistoryConfTO> list(final String key) {
+        ConnInstance connInstance = connInstanceDAO.find(key);
+        if (connInstance == null) {
+            throw new NotFoundException("Connector '" + key + "'");
+        }
+
+        return CollectionUtils.collect(connInstanceHistoryConfDAO.findByEntity(connInstance),
+                new Transformer<ConnInstanceHistoryConf, ConnInstanceHistoryConfTO>() {
+
+            @Override
+            public ConnInstanceHistoryConfTO transform(final ConnInstanceHistoryConf input) {
+                return getConnInstanceHistoryConfTO(input);
+            }
+
+        }, new ArrayList<ConnInstanceHistoryConfTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_RESTORE + "')")
+    public void restore(final String key) {
+        ConnInstanceHistoryConf connInstanceHistoryConf = connInstanceHistoryConfDAO.find(key);
+        if (connInstanceHistoryConf == null) {
+            throw new NotFoundException("Connector History Conf '" + key + "'");
+        }
+
+        binder.update(connInstanceHistoryConf.getConf());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_DELETE + "')")
+    public void delete(final String key) {
+        ConnInstanceHistoryConf connInstanceHistoryConf = connInstanceHistoryConfDAO.find(key);
+        if (connInstanceHistoryConf == null) {
+            throw new NotFoundException("Connector History Conf '" + key + "'");
+        }
+
+        connInstanceHistoryConfDAO.delete(key);
+    }
+
+    @Override
+    protected ConnInstanceHistoryConfTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        if (!"list".equals(method.getName())) {
+            try {
+                String key = (String) args[0];
+                return getConnInstanceHistoryConfTO(connInstanceHistoryConfDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
index 65d02d3..e84dfe5 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
@@ -77,34 +77,12 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_CREATE + "')")
     public ConnInstanceTO create(final ConnInstanceTO connInstanceTO) {
-        ConnInstance connInstance = binder.getConnInstance(connInstanceTO);
-        try {
-            connInstance = connInstanceDAO.save(connInstance);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getConnInstanceTO(connInstance);
+        return binder.getConnInstanceTO(connInstanceDAO.save(binder.getConnInstance(connInstanceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_UPDATE + "')")
     public ConnInstanceTO update(final ConnInstanceTO connInstanceTO) {
-        ConnInstance connInstance = binder.update(connInstanceTO.getKey(), connInstanceTO);
-        try {
-            connInstance = connInstanceDAO.save(connInstance);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getConnInstanceTO(connInstance);
+        return binder.getConnInstanceTO(binder.update(connInstanceTO));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_DELETE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java
new file mode 100644
index 0000000..fc95f81
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ResourceHistoryLogic extends AbstractTransactionalLogic<ResourceHistoryConfTO> {
+
+    @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private ResourceDataBinder binder;
+
+    private ResourceHistoryConfTO getResourceHistoryConfTO(final ExternalResourceHistoryConf history) {
+        ResourceHistoryConfTO historyTO = new ResourceHistoryConfTO();
+        historyTO.setKey(history.getKey());
+        historyTO.setCreator(history.getCreator());
+        historyTO.setCreation(history.getCreation());
+        historyTO.setResourceTO(history.getConf());
+
+        return historyTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_LIST + "')")
+    public List<ResourceHistoryConfTO> list(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + key + "'");
+        }
+
+        return CollectionUtils.collect(resourceHistoryConfDAO.findByEntity(resource),
+                new Transformer<ExternalResourceHistoryConf, ResourceHistoryConfTO>() {
+
+            @Override
+            public ResourceHistoryConfTO transform(final ExternalResourceHistoryConf input) {
+                return getResourceHistoryConfTO(input);
+            }
+        }, new ArrayList<ResourceHistoryConfTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_RESTORE + "')")
+    public void restore(final String key) {
+        ExternalResourceHistoryConf resourceHistoryConf = resourceHistoryConfDAO.find(key);
+        if (resourceHistoryConf == null) {
+            throw new NotFoundException("Resource History Conf '" + key + "'");
+        }
+
+        binder.update(resourceHistoryConf.getEntity(), resourceHistoryConf.getConf());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_DELETE + "')")
+    public void delete(final String key) {
+        ExternalResourceHistoryConf resourceHistoryConf = resourceHistoryConfDAO.find(key);
+        if (resourceHistoryConf == null) {
+            throw new NotFoundException("Resource History Conf '" + key + "'");
+        }
+
+        resourceHistoryConfDAO.delete(key);
+    }
+
+    @Override
+    protected ResourceHistoryConfTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        if (!"list".equals(method.getName())) {
+            try {
+                String key = (String) args[0];
+                return getResourceHistoryConfTO(resourceHistoryConfDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index b07a92b..5269c1e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -125,18 +125,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
             throw new DuplicateException(resourceTO.getKey());
         }
 
-        ExternalResource resource = null;
-        try {
-            resource = resourceDAO.save(binder.create(resourceTO));
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getResourceTO(resource);
+        return binder.getResourceTO(resourceDAO.save(binder.create(resourceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_UPDATE + "')")
@@ -146,18 +135,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
             throw new NotFoundException("Resource '" + resourceTO.getKey() + "'");
         }
 
-        resource = binder.update(resource, resourceTO);
-        try {
-            resource = resourceDAO.save(resource);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getResourceTO(resource);
+        return binder.getResourceTO(resourceDAO.save(binder.update(resource, resourceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_UPDATE + "')")
@@ -208,25 +186,25 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_DELETE + "')")
-    public ResourceTO delete(final String resourceName) {
-        ExternalResource resource = resourceDAO.find(resourceName);
+    public ResourceTO delete(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
         if (resource == null) {
-            throw new NotFoundException("Resource '" + resourceName + "'");
+            throw new NotFoundException("Resource '" + key + "'");
         }
 
         ResourceTO resourceToDelete = binder.getResourceTO(resource);
 
-        resourceDAO.delete(resourceName);
+        resourceDAO.delete(key);
 
         return resourceToDelete;
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_READ + "')")
     @Transactional(readOnly = true)
-    public ResourceTO read(final String resourceName) {
-        ExternalResource resource = resourceDAO.find(resourceName);
+    public ResourceTO read(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
         if (resource == null) {
-            throw new NotFoundException("Resource '" + resourceName + "'");
+            throw new NotFoundException("Resource '" + key + "'");
         }
 
         return binder.getResourceTO(resource);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
index 2eb14c5..97da0f6 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
@@ -95,7 +95,7 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
     public <T extends AbstractSchemaTO> T create(final SchemaType schemaType, final T schemaTO) {
         if (StringUtils.isBlank(schemaTO.getKey())) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
-            sce.getElements().add("Schema name");
+            sce.getElements().add("Schema key");
             throw sce;
         }
 
@@ -124,23 +124,23 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.SCHEMA_DELETE + "')")
-    public void delete(final SchemaType schemaType, final String schemaName) {
-        if (!doesSchemaExist(schemaType, schemaName)) {
-            throw new NotFoundException(schemaType + "/" + schemaName);
+    public void delete(final SchemaType schemaType, final String schemaKey) {
+        if (!doesSchemaExist(schemaType, schemaKey)) {
+            throw new NotFoundException(schemaType + "/" + schemaKey);
         }
 
         switch (schemaType) {
             case VIRTUAL:
-                virSchemaDAO.delete(schemaName);
+                virSchemaDAO.delete(schemaKey);
                 break;
 
             case DERIVED:
-                derSchemaDAO.delete(schemaName);
+                derSchemaDAO.delete(schemaKey);
                 break;
 
             case PLAIN:
             default:
-                plainSchemaDAO.delete(schemaName);
+                plainSchemaDAO.delete(schemaKey);
         }
     }
 
@@ -207,22 +207,22 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
 
     @PreAuthorize("isAuthenticated()")
     @SuppressWarnings("unchecked")
-    public <T extends AbstractSchemaTO> T read(final SchemaType schemaType, final String schemaName) {
+    public <T extends AbstractSchemaTO> T read(final SchemaType schemaType, final String schemaKey) {
         T read;
         switch (schemaType) {
             case VIRTUAL:
-                VirSchema virSchema = virSchemaDAO.find(schemaName);
+                VirSchema virSchema = virSchemaDAO.find(schemaKey);
                 if (virSchema == null) {
-                    throw new NotFoundException("Virtual Schema '" + schemaName + "'");
+                    throw new NotFoundException("Virtual Schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getVirSchemaTO(virSchema);
                 break;
 
             case DERIVED:
-                DerSchema derSchema = derSchemaDAO.find(schemaName);
+                DerSchema derSchema = derSchemaDAO.find(schemaKey);
                 if (derSchema == null) {
-                    throw new NotFoundException("Derived schema '" + schemaName + "'");
+                    throw new NotFoundException("Derived schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getDerSchemaTO(derSchema);
@@ -230,9 +230,9 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
 
             case PLAIN:
             default:
-                PlainSchema schema = plainSchemaDAO.find(schemaName);
+                PlainSchema schema = plainSchemaDAO.find(schemaKey);
                 if (schema == null) {
-                    throw new NotFoundException("Schema '" + schemaName + "'");
+                    throw new NotFoundException("Schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getPlainSchemaTO(schema);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
new file mode 100644
index 0000000..1caad69
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+
+public interface ConnInstanceHistoryConfDAO extends DAO<ConnInstanceHistoryConf> {
+
+    ConnInstanceHistoryConf find(String key);
+
+    List<ConnInstanceHistoryConf> findByEntity(ConnInstance entity);
+
+    ConnInstanceHistoryConf save(ConnInstanceHistoryConf conf);
+
+    void delete(String key);
+
+    void deleteByEntity(ConnInstance entity);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
new file mode 100644
index 0000000..1c4db5a
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+
+public interface ExternalResourceHistoryConfDAO extends DAO<ExternalResourceHistoryConf> {
+
+    ExternalResourceHistoryConf find(String key);
+
+    List<ExternalResourceHistoryConf> findByEntity(ExternalResource entity);
+
+    ExternalResourceHistoryConf save(ExternalResourceHistoryConf conf);
+
+    void delete(String key);
+
+    void deleteByEntity(ExternalResource entity);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
new file mode 100644
index 0000000..88a4481
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
@@ -0,0 +1,24 @@
+/*
+ * 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.api.entity;
+
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+
+public interface ConnInstanceHistoryConf extends HistoryConf<ConnInstance, ConnInstanceTO> {
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java
new file mode 100644
index 0000000..5da50c1
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.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.api.entity;
+
+import java.util.Date;
+import org.apache.syncope.common.lib.to.EntityTO;
+
+public interface HistoryConf<E extends Entity, T extends EntityTO> extends Entity {
+
+    String getCreator();
+
+    void setCreator(String creator);
+
+    Date getCreation();
+
+    void setCreation(Date creation);
+
+    E getEntity();
+
+    void setEntity(E entity);
+
+    T getConf();
+
+    void setConf(T conf);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
new file mode 100644
index 0000000..0fd8a25
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
@@ -0,0 +1,25 @@
+/*
+ * 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.api.entity.resource;
+
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.entity.HistoryConf;
+
+public interface ExternalResourceHistoryConf extends HistoryConf<ExternalResource, ResourceTO> {
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/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 91cf1dd..8972b8b 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
@@ -24,6 +24,7 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.Closure;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
 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;
@@ -37,6 +38,9 @@ import org.springframework.stereotype.Repository;
 public class JPAConnInstanceDAO extends AbstractDAO<ConnInstance> implements ConnInstanceDAO {
 
     @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
     private ExternalResourceDAO resourceDAO;
 
     @Autowired
@@ -85,8 +89,10 @@ public class JPAConnInstanceDAO extends AbstractDAO<ConnInstance> implements Con
 
         });
 
+        connInstanceHistoryConfDAO.deleteByEntity(connInstance);
+
         entityManager().remove(connInstance);
 
-        connRegistry.unregisterConnector(key.toString());
+        connRegistry.unregisterConnector(key);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
new file mode 100644
index 0000000..001d7fb
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstanceHistoryConf;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAConnInstanceHistoryConfDAO
+        extends AbstractDAO<ConnInstanceHistoryConf> implements ConnInstanceHistoryConfDAO {
+
+    @Override
+    public ConnInstanceHistoryConf find(final String key) {
+        return entityManager().find(JPAConnInstanceHistoryConf.class, key);
+    }
+
+    @Override
+    public List<ConnInstanceHistoryConf> findByEntity(final ConnInstance entity) {
+        TypedQuery<ConnInstanceHistoryConf> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAConnInstanceHistoryConf.class.getSimpleName()
+                + " e WHERE e.entity=:entity ORDER BY e.creation DESC", ConnInstanceHistoryConf.class);
+        query.setParameter("entity", entity);
+        return query.getResultList();
+    }
+
+    @Override
+    public ConnInstanceHistoryConf save(final ConnInstanceHistoryConf conf) {
+        return entityManager().merge(conf);
+    }
+
+    @Override
+    public void delete(final String key) {
+        ConnInstanceHistoryConf conf = find(key);
+        if (conf == null) {
+            return;
+        }
+
+        entityManager().remove(conf);
+    }
+
+    @Override
+    public void deleteByEntity(final ConnInstance entity) {
+        Query query = entityManager().createQuery(
+                "DELETE FROM " + JPAConnInstanceHistoryConf.class.getSimpleName() + " e WHERE e.entity=:entity");
+        query.setParameter("entity", entity);
+        query.executeUpdate();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/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 03a0fe5..c49bd5a 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
@@ -26,6 +26,7 @@ import javax.persistence.TypedQuery;
 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.ExternalResourceHistoryConfDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -76,6 +77,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
 
     private RealmDAO realmDAO;
 
+    private ExternalResourceHistoryConfDAO externalResourceHistoryConfDAO;
+
     private TaskDAO taskDAO() {
         synchronized (this) {
             if (taskDAO == null) {
@@ -139,6 +142,16 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
         return realmDAO;
     }
 
+    private ExternalResourceHistoryConfDAO externalResourceHistoryConfDAO() {
+        synchronized (this) {
+            if (externalResourceHistoryConfDAO == null) {
+                externalResourceHistoryConfDAO = ApplicationContextProvider.getApplicationContext().
+                        getBean(ExternalResourceHistoryConfDAO.class);
+            }
+        }
+        return externalResourceHistoryConfDAO;
+    }
+
     @Override
     public int count() {
         Query query = entityManager().createQuery(
@@ -286,6 +299,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
             }
         }
 
+        externalResourceHistoryConfDAO().deleteByEntity(resource);
+
         if (resource.getConnector() != null && resource.getConnector().getResources() != null
                 && !resource.getConnector().getResources().isEmpty()) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
new file mode 100644
index 0000000..ba43355
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAExternalResourceHistoryConfDAO
+        extends AbstractDAO<ExternalResourceHistoryConf> implements ExternalResourceHistoryConfDAO {
+
+    @Override
+    public ExternalResourceHistoryConf find(final String key) {
+        return entityManager().find(JPAExternalResourceHistoryConf.class, key);
+    }
+
+    @Override
+    public List<ExternalResourceHistoryConf> findByEntity(final ExternalResource entity) {
+        TypedQuery<ExternalResourceHistoryConf> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAExternalResourceHistoryConf.class.getSimpleName()
+                + " e WHERE e.entity=:entity ORDER BY e.creation DESC", ExternalResourceHistoryConf.class);
+        query.setParameter("entity", entity);
+        return query.getResultList();
+    }
+
+    @Override
+    public ExternalResourceHistoryConf save(final ExternalResourceHistoryConf conf) {
+        return entityManager().merge(conf);
+    }
+
+    @Override
+    public void delete(final String key) {
+        ExternalResourceHistoryConf conf = find(key);
+        if (conf == null) {
+            return;
+        }
+
+        entityManager().remove(conf);
+    }
+
+    @Override
+    public void deleteByEntity(final ExternalResource entity) {
+        Query query = entityManager().createQuery(
+                "DELETE FROM " + JPAExternalResourceHistoryConf.class.getSimpleName() + " e WHERE e.entity=:entity");
+        query.setParameter("entity", entity);
+        query.executeUpdate();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java
new file mode 100644
index 0000000..9ee2c65
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.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 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.common.lib.to.EntityTO;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.HistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@MappedSuperclass
+public abstract class AbstractHistoryConf<E extends Entity, T extends EntityTO>
+        extends AbstractGeneratedKeyEntity implements HistoryConf<E, T> {
+
+    private static final long serialVersionUID = -7210303753586235180L;
+
+    @Column(nullable = false)
+    private String creator;
+
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date creation;
+
+    @Lob
+    protected String conf;
+
+    @Override
+    public String getCreator() {
+        return creator;
+    }
+
+    @Override
+    public void setCreator(final String creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public Date getCreation() {
+        return creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    @Override
+    public void setCreation(final Date creation) {
+        this.creation = creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    @Override
+    public void setConf(final T conf) {
+        this.conf = POJOHelper.serialize(conf);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
new file mode 100644
index 0000000..9001676
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
@@ -0,0 +1,59 @@
+/*
+ * 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.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAConnInstanceHistoryConf.TABLE)
+public class JPAConnInstanceHistoryConf
+        extends AbstractHistoryConf<ConnInstance, ConnInstanceTO> implements ConnInstanceHistoryConf {
+
+    private static final long serialVersionUID = -4152915369607435186L;
+
+    public static final String TABLE = "ConnInstanceHistoryConf";
+
+    @OneToOne
+    private JPAConnInstance entity;
+
+    @Override
+    public ConnInstance getEntity() {
+        return entity;
+    }
+
+    @Override
+    public void setEntity(final ConnInstance entity) {
+        checkType(entity, JPAConnInstance.class);
+        this.entity = (JPAConnInstance) entity;
+    }
+
+    @Override
+    public ConnInstanceTO getConf() {
+        return StringUtils.isBlank(conf)
+                ? null
+                : POJOHelper.deserialize(conf, ConnInstanceTO.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index daea67a..88f4e5a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -31,6 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.ConnPoolConf;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.Domain;
@@ -125,6 +126,8 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
 import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
 
 @Component
 public class JPAEntityFactory implements EntityFactory {
@@ -184,12 +187,16 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPANotification();
         } else if (reference.equals(ExternalResource.class)) {
             result = (E) new JPAExternalResource();
+        } else if (reference.equals(ExternalResourceHistoryConf.class)) {
+            result = (E) new JPAExternalResourceHistoryConf();
         } else if (reference.equals(Provision.class)) {
             result = (E) new JPAProvision();
         } else if (reference.equals(OrgUnit.class)) {
             result = (E) new JPAOrgUnit();
         } else if (reference.equals(ConnInstance.class)) {
             result = (E) new JPAConnInstance();
+        } else if (reference.equals(ConnInstanceHistoryConf.class)) {
+            result = (E) new JPAConnInstanceHistoryConf();
         } else if (reference.equals(PlainSchema.class)) {
             result = (E) new JPAPlainSchema();
         } else if (reference.equals(APlainAttr.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
new file mode 100644
index 0000000..31fa726
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
@@ -0,0 +1,60 @@
+/*
+ * 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.resource;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractHistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAExternalResourceHistoryConf.TABLE)
+public class JPAExternalResourceHistoryConf
+        extends AbstractHistoryConf<ExternalResource, ResourceTO> implements ExternalResourceHistoryConf {
+
+    public static final String TABLE = "ExternalResourceHistoryConf";
+
+    private static final long serialVersionUID = 4994551965604592529L;
+
+    @OneToOne
+    private JPAExternalResource entity;
+
+    @Override
+    public ExternalResource getEntity() {
+        return entity;
+    }
+
+    @Override
+    public void setEntity(final ExternalResource entity) {
+        checkType(entity, JPAExternalResource.class);
+        this.entity = (JPAExternalResource) entity;
+    }
+
+    @Override
+    public ResourceTO getConf() {
+        return StringUtils.isBlank(conf)
+                ? null
+                : POJOHelper.deserialize(conf, ResourceTO.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index 33d8a70..9d42535 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -152,6 +152,23 @@ under the License.
   <CPlainAttrValue id="447e2456-3ff5-41bc-8ff1-cbb0567546cb"
                    attribute_id="cfec3140-562d-459c-ac6a-e3e10758661d" longValue="120"/>
   
+  <!--  Connector and Resource configuration history -->                   
+  <SyncopeSchema id="connector.conf.history.size"/>
+  <PlainSchema id="connector.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="connector.conf.history.size"/>
+  <CPlainAttrValue id="4989ce5d-7b42-4a5c-b725-54286888df10"
+                   attribute_id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c" longValue="10"/>
+  
+  <SyncopeSchema id="resource.conf.history.size"/>
+  <PlainSchema id="resource.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="resource.conf.history.size"/>
+  <CPlainAttrValue id="059ac3e1-687f-4dd9-a28b-a23f8b436f31"
+                   attribute_id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60" longValue="10"/>
+
   <AnyType id="USER" kind="USER"/>
   <AnyTypeClass id="BaseUser"/>
   <AnyType_AnyTypeClass anyType_id="USER" anyTypeClass_id="BaseUser"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
index f1d0f95..ffe1edc 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
@@ -47,7 +47,7 @@ public class PlainSchemaTest extends AbstractTest {
     @Test
     public void findAll() {
         List<PlainSchema> schemas = plainSchemaDAO.findAll();
-        assertEquals(41, schemas.size());
+        assertEquals(43, schemas.size());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
new file mode 100644
index 0000000..43c0b65
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.outer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ConnInstanceHistoryConfTest extends AbstractTest {
+
+    @Autowired
+    private ConnInstanceDAO connInstanceDAO;
+
+    @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Test
+    public void createDelete() {
+        ConnInstance ldapConnector = connInstanceDAO.find("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertNotNull(ldapConnector);
+
+        ConnInstanceHistoryConf ldapHistory = entityFactory.newEntity(ConnInstanceHistoryConf.class);
+        ldapHistory.setCreation(new Date());
+        ldapHistory.setCreator("me");
+        ldapHistory.setEntity(ldapConnector);
+        ldapHistory.setConf(new ConnInstanceTO());
+
+        ldapHistory = connInstanceHistoryConfDAO.save(ldapHistory);
+        assertNotNull(ldapHistory.getKey());
+
+        connInstanceHistoryConfDAO.flush();
+
+        List<ConnInstanceHistoryConf> history = connInstanceHistoryConfDAO.findByEntity(ldapConnector);
+        assertEquals(1, history.size());
+        assertEquals(ldapHistory, history.get(0));
+
+        connInstanceHistoryConfDAO.delete(ldapHistory.getKey());
+
+        connInstanceHistoryConfDAO.flush();
+
+        assertNull(connInstanceHistoryConfDAO.find(ldapHistory.getKey()));
+        assertTrue(connInstanceHistoryConfDAO.findByEntity(ldapConnector).isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
new file mode 100644
index 0000000..2bc36a6
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.outer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ResourceHistoryConfTest extends AbstractTest {
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Test
+    public void createDelete() {
+        ExternalResource ldapResource = resourceDAO.find("resource-ldap");
+        assertNotNull(ldapResource);
+
+        ExternalResourceHistoryConf ldapHistory = entityFactory.newEntity(ExternalResourceHistoryConf.class);
+        ldapHistory.setCreation(new Date());
+        ldapHistory.setCreator("me");
+        ldapHistory.setEntity(ldapResource);
+        ldapHistory.setConf(new ResourceTO());
+
+        ldapHistory = resourceHistoryConfDAO.save(ldapHistory);
+        assertNotNull(ldapHistory.getKey());
+
+        resourceHistoryConfDAO.flush();
+
+        List<ExternalResourceHistoryConf> history = resourceHistoryConfDAO.findByEntity(ldapResource);
+        assertEquals(1, history.size());
+        assertEquals(ldapHistory, history.get(0));
+
+        resourceHistoryConfDAO.delete(ldapHistory.getKey());
+
+        resourceHistoryConfDAO.flush();
+
+        assertNull(resourceHistoryConfDAO.find(ldapHistory.getKey()));
+        assertTrue(resourceHistoryConfDAO.findByEntity(ldapResource).isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 5e9c46e..83bacf0 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -151,7 +151,24 @@ under the License.
               owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="jwt.lifetime.minutes"/>
   <CPlainAttrValue id="447e2456-3ff5-41bc-8ff1-cbb0567546cb"
                    attribute_id="cfec3140-562d-459c-ac6a-e3e10758661d" longValue="120"/>
-
+    
+  <!--  Connector and Resource configuration history -->                   
+  <SyncopeSchema id="connector.conf.history.size"/>
+  <PlainSchema id="connector.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="connector.conf.history.size"/>
+  <CPlainAttrValue id="4989ce5d-7b42-4a5c-b725-54286888df10"
+                   attribute_id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c" longValue="10"/>
+  
+  <SyncopeSchema id="resource.conf.history.size"/>
+  <PlainSchema id="resource.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="resource.conf.history.size"/>
+  <CPlainAttrValue id="059ac3e1-687f-4dd9-a28b-a23f8b436f31"
+                   attribute_id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60" longValue="10"/>
+  
   <!-- sample policies -->
   <PullPolicy id="66691e96-285f-4464-bc19-e68384ea4c85" description="a pull policy"
               specification='{"conflictResolutionAction":"IGNORE"}'/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ca99a469/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
index 698fd25..ae7074f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
@@ -31,6 +31,6 @@ public interface ConnInstanceDataBinder {
 
     ConnInstanceTO getConnInstanceTO(ConnInstance connInstance);
 
-    ConnInstance update(String key, ConnInstanceTO connInstanceTO);
+    ConnInstance update(ConnInstanceTO connInstanceTO);
 
 }


[4/4] syncope git commit: [SYNCOPE-1145] Core and Common implementation completed, Console still TODO

Posted by il...@apache.org.
[SYNCOPE-1145] Core and Common implementation completed, Console still TODO


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/7451efb6
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/7451efb6
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/7451efb6

Branch: refs/heads/master
Commit: 7451efb6d274cc446da952d7b607357b0422f30f
Parents: d4cd448
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Jul 6 16:55:56 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jul 6 16:56:07 2017 +0200

----------------------------------------------------------------------
 .../wizards/resources/ConnectorConfPanel.java   |   8 +-
 .../common/lib/to/AbstractHistoryConf.java      |  72 ++++++
 .../lib/to/ConnInstanceHistoryConfTO.java       |  40 ++++
 .../syncope/common/lib/to/ConnInstanceTO.java   |  22 +-
 .../common/lib/to/ResourceHistoryConfTO.java    |  40 ++++
 .../common/lib/types/StandardEntitlement.java   |  12 +
 .../api/service/ConnectorHistoryService.java    |  66 ++++++
 .../api/service/ResourceHistoryService.java     |  66 ++++++
 .../core/logic/ConnectorHistoryLogic.java       | 115 +++++++++
 .../syncope/core/logic/ConnectorLogic.java      |  26 +-
 .../core/logic/ResourceHistoryLogic.java        | 114 +++++++++
 .../syncope/core/logic/ResourceLogic.java       |  40 +---
 .../apache/syncope/core/logic/SchemaLogic.java  |  28 +--
 .../api/dao/ConnInstanceHistoryConfDAO.java     |  36 +++
 .../api/dao/ExternalResourceHistoryConfDAO.java |  36 +++
 .../api/entity/ConnInstanceHistoryConf.java     |  24 ++
 .../persistence/api/entity/HistoryConf.java     |  41 ++++
 .../resource/ExternalResourceHistoryConf.java   |  25 ++
 .../persistence/jpa/dao/JPAConnInstanceDAO.java |   8 +-
 .../jpa/dao/JPAConnInstanceHistoryConfDAO.java  |  71 ++++++
 .../jpa/dao/JPAExternalResourceDAO.java         |  15 ++
 .../dao/JPAExternalResourceHistoryConfDAO.java  |  71 ++++++
 .../jpa/entity/AbstractHistoryConf.java         |  76 ++++++
 .../jpa/entity/JPAConnInstanceHistoryConf.java  |  59 +++++
 .../jpa/entity/JPAEntityFactory.java            |   7 +
 .../JPAExternalResourceHistoryConf.java         |  60 +++++
 .../main/resources/domains/MasterContent.xml    |  17 ++
 .../persistence/jpa/inner/PlainSchemaTest.java  |   2 +-
 .../jpa/outer/ConnInstanceHistoryConfTest.java  |  74 ++++++
 .../jpa/outer/ResourceHistoryConfTest.java      |  74 ++++++
 .../test/resources/domains/MasterContent.xml    |  19 +-
 .../api/data/ConnInstanceDataBinder.java        |   2 +-
 .../java/data/ConnInstanceDataBinderImpl.java   |  53 ++++-
 .../java/data/ResourceDataBinderImpl.java       |  33 ++-
 .../AbstractPropagationTaskExecutor.java        |   8 +-
 .../service/ConnectorHistoryServiceImpl.java    |  49 ++++
 .../cxf/service/ResourceHistoryServiceImpl.java |  49 ++++
 .../org/apache/syncope/fit/AbstractITCase.java  |  22 +-
 .../syncope/fit/core/ConnectorITCase.java       | 236 +++++++++++--------
 .../apache/syncope/fit/core/PullTaskITCase.java |   4 +-
 .../apache/syncope/fit/core/ResourceITCase.java |  86 +++++--
 41 files changed, 1665 insertions(+), 241 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
index 21d494d..580e2c8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java
@@ -87,11 +87,9 @@ public abstract class ConnectorConfPanel extends AbstractConnConfPanel<ConnInsta
                 final ConnConfProperty property = new ConnConfProperty();
                 property.setSchema(key);
 
-                if (instance.getConfMap().containsKey(key.getName())
-                        && instance.getConfMap().get(key.getName()).getValues() != null) {
-
-                    property.getValues().addAll(instance.getConfMap().get(key.getName()).getValues());
-                    property.setOverridable(instance.getConfMap().get(key.getName()).isOverridable());
+                if (instance.getConf(key.getName()) != null && instance.getConf(key.getName()).getValues() != null) {
+                    property.getValues().addAll(instance.getConf(key.getName()).getValues());
+                    property.setOverridable(instance.getConf(key.getName()).isOverridable());
                 }
 
                 if (property.getValues().isEmpty() && !key.getDefaultValues().isEmpty()) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
new file mode 100644
index 0000000..a410a73
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractHistoryConf.java
@@ -0,0 +1,72 @@
+/*
+ * 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.common.lib.to;
+
+import java.util.Date;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "abstractHistoryConf")
+@XmlType
+@XmlSeeAlso({ ConnInstanceHistoryConfTO.class, ResourceHistoryConfTO.class })
+public abstract class AbstractHistoryConf extends AbstractBaseBean implements EntityTO {
+
+    private static final long serialVersionUID = -8001640160293506651L;
+
+    private String key;
+
+    private String creator;
+
+    private Date creation;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getCreator() {
+        return creator;
+    }
+
+    public void setCreator(final String creator) {
+        this.creator = creator;
+    }
+
+    public Date getCreation() {
+        return creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    public void setCreation(final Date creation) {
+        this.creation = creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
new file mode 100644
index 0000000..6583c54
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceHistoryConfTO.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "connInstanceHistoryConf")
+@XmlType
+public class ConnInstanceHistoryConfTO extends AbstractHistoryConf {
+
+    private static final long serialVersionUID = 8067539341757100479L;
+
+    private ConnInstanceTO connInstanceTO;
+
+    public ConnInstanceTO getConnInstanceTO() {
+        return connInstanceTO;
+    }
+
+    public void setConnInstanceTO(final ConnInstanceTO connInstanceTO) {
+        this.connInstanceTO = connInstanceTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
index 4f91355..d7c8be7 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java
@@ -20,17 +20,16 @@ package org.apache.syncope.common.lib.to;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 import javax.ws.rs.PathParam;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.ConnectorCapability;
@@ -112,15 +111,14 @@ public class ConnInstanceTO extends AbstractBaseBean implements EntityTO {
     }
 
     @JsonIgnore
-    public Map<String, ConnConfProperty> getConfMap() {
-        Map<String, ConnConfProperty> result = new HashMap<>();
-
-        for (ConnConfProperty prop : getConf()) {
-            result.put(prop.getSchema().getName(), prop);
-        }
-        result = Collections.unmodifiableMap(result);
-
-        return Collections.unmodifiableMap(result);
+    public ConnConfProperty getConf(final String schemaName) {
+        return IterableUtils.find(conf, new Predicate<ConnConfProperty>() {
+
+            @Override
+            public boolean evaluate(final ConnConfProperty object) {
+                return object.getSchema().getName().equals(schemaName);
+            }
+        });
     }
 
     @XmlElementWrapper(name = "capabilities")

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
new file mode 100644
index 0000000..388b63c
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceHistoryConfTO.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "resourceHistoryConf")
+@XmlType
+public class ResourceHistoryConfTO extends AbstractHistoryConf {
+
+    private static final long serialVersionUID = -7308499697925335157L;
+
+    private ResourceTO resourceTO;
+
+    public ResourceTO getResourceTO() {
+        return resourceTO;
+    }
+
+    public void setResourceTO(final ResourceTO resourceTO) {
+        this.resourceTO = resourceTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 81709f6..9bf8de3 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -128,6 +128,12 @@ public final class StandardEntitlement {
 
     public static final String RESOURCE_LIST_CONNOBJECT = "RESOURCE_LIST_CONNOBJECT";
 
+    public static final String RESOURCE_HISTORY_LIST = "RESOURCE_HISTORY_LIST";
+
+    public static final String RESOURCE_HISTORY_RESTORE = "RESOURCE_HISTORY_RESTORE";
+
+    public static final String RESOURCE_HISTORY_DELETE = "RESOURCE_HISTORY_DELETE";
+
     public static final String CONNECTOR_LIST = "CONNECTOR_LIST";
 
     public static final String CONNECTOR_CREATE = "CONNECTOR_CREATE";
@@ -140,6 +146,12 @@ public final class StandardEntitlement {
 
     public static final String CONNECTOR_RELOAD = "CONNECTOR_RELOAD";
 
+    public static final String CONNECTOR_HISTORY_LIST = "CONNECTOR_HISTORY_LIST";
+
+    public static final String CONNECTOR_HISTORY_RESTORE = "CONNECTOR_HISTORY_RESTORE";
+
+    public static final String CONNECTOR_HISTORY_DELETE = "CONNECTOR_HISTORY_DELETE";
+
     public static final String CONFIGURATION_EXPORT = "CONFIGURATION_EXPORT";
 
     public static final String CONFIGURATION_LIST = "CONFIGURATION_LIST";

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
new file mode 100644
index 0000000..28a0b3c
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorHistoryService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+
+/**
+ * REST operations for connector instance configuration versioning.
+ */
+@Path("connectorsHistory")
+public interface ConnectorHistoryService extends JAXRSService {
+
+    /**
+     * Returns a list of all connector configuration history instances for the given connector instance key.
+     *
+     * @param connectorKey connector instance key
+     * @return list of all connector configuration history instances for the given connector instance key
+     */
+    @GET
+    @Path("{connectorKey}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<ConnInstanceHistoryConfTO> list(@NotNull @PathParam("connectorKey") String connectorKey);
+
+    /**
+     * Restores the connector configuration history instance matching the provided key.
+     * 
+     * @param key connector configuration history instance key to be restored
+     */
+    @POST
+    @Path("{key}")
+    void restore(@NotNull @PathParam("key") String key);
+
+    /**
+     * Deletes the connector configuration history instance matching the provided key.
+     *
+     * @param key connector configuration history instance key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
new file mode 100644
index 0000000..fbf748a
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceHistoryService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+
+/**
+ * REST operations for resource configuration versioning.
+ */
+@Path("resourcesHistory")
+public interface ResourceHistoryService extends JAXRSService {
+
+    /**
+     * Returns a list of all resource configuration history for the given resource key.
+     *
+     * @param resourceKey resource key
+     * @return list of all resource configuration history for the given resource key
+     */
+    @GET
+    @Path("{resourceKey}")
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    List<ResourceHistoryConfTO> list(@NotNull @PathParam("resourceKey") String resourceKey);
+
+    /**
+     * Restores the resource configuration history matching the provided key.
+     *
+     * @param key resource configuration history key to be restored
+     */
+    @POST
+    @Path("{key}")
+    void restore(@NotNull @PathParam("key") String key);
+
+    /**
+     * Deletes the resource configuration history matching the provided key.
+     *
+     * @param key resource configuration history key to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
new file mode 100644
index 0000000..2d0afa7
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorHistoryLogic.java
@@ -0,0 +1,115 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+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.ConnInstanceHistoryConf;
+import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConnectorHistoryLogic extends AbstractTransactionalLogic<ConnInstanceHistoryConfTO> {
+
+    @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
+    private ConnInstanceDAO connInstanceDAO;
+
+    @Autowired
+    private ConnInstanceDataBinder binder;
+
+    private ConnInstanceHistoryConfTO getConnInstanceHistoryConfTO(final ConnInstanceHistoryConf history) {
+        ConnInstanceHistoryConfTO historyTO = new ConnInstanceHistoryConfTO();
+        historyTO.setKey(history.getKey());
+        historyTO.setCreator(history.getCreator());
+        historyTO.setCreation(history.getCreation());
+        historyTO.setConnInstanceTO(history.getConf());
+
+        return historyTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_LIST + "')")
+    public List<ConnInstanceHistoryConfTO> list(final String key) {
+        ConnInstance connInstance = connInstanceDAO.find(key);
+        if (connInstance == null) {
+            throw new NotFoundException("Connector '" + key + "'");
+        }
+
+        return CollectionUtils.collect(connInstanceHistoryConfDAO.findByEntity(connInstance),
+                new Transformer<ConnInstanceHistoryConf, ConnInstanceHistoryConfTO>() {
+
+            @Override
+            public ConnInstanceHistoryConfTO transform(final ConnInstanceHistoryConf input) {
+                return getConnInstanceHistoryConfTO(input);
+            }
+
+        }, new ArrayList<ConnInstanceHistoryConfTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_RESTORE + "')")
+    public void restore(final String key) {
+        ConnInstanceHistoryConf connInstanceHistoryConf = connInstanceHistoryConfDAO.find(key);
+        if (connInstanceHistoryConf == null) {
+            throw new NotFoundException("Connector History Conf '" + key + "'");
+        }
+
+        binder.update(connInstanceHistoryConf.getConf());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_HISTORY_DELETE + "')")
+    public void delete(final String key) {
+        ConnInstanceHistoryConf connInstanceHistoryConf = connInstanceHistoryConfDAO.find(key);
+        if (connInstanceHistoryConf == null) {
+            throw new NotFoundException("Connector History Conf '" + key + "'");
+        }
+
+        connInstanceHistoryConfDAO.delete(key);
+    }
+
+    @Override
+    protected ConnInstanceHistoryConfTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        if (!"list".equals(method.getName())) {
+            try {
+                String key = (String) args[0];
+                return getConnInstanceHistoryConfTO(connInstanceHistoryConfDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
index 65d02d3..e84dfe5 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
@@ -77,34 +77,12 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_CREATE + "')")
     public ConnInstanceTO create(final ConnInstanceTO connInstanceTO) {
-        ConnInstance connInstance = binder.getConnInstance(connInstanceTO);
-        try {
-            connInstance = connInstanceDAO.save(connInstance);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getConnInstanceTO(connInstance);
+        return binder.getConnInstanceTO(connInstanceDAO.save(binder.getConnInstance(connInstanceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_UPDATE + "')")
     public ConnInstanceTO update(final ConnInstanceTO connInstanceTO) {
-        ConnInstance connInstance = binder.update(connInstanceTO.getKey(), connInstanceTO);
-        try {
-            connInstance = connInstanceDAO.save(connInstance);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidConnInstance);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getConnInstanceTO(connInstance);
+        return binder.getConnInstanceTO(binder.update(connInstanceTO));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.CONNECTOR_DELETE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.java
new file mode 100644
index 0000000..fc95f81
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceHistoryLogic.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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ResourceHistoryLogic extends AbstractTransactionalLogic<ResourceHistoryConfTO> {
+
+    @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private ResourceDataBinder binder;
+
+    private ResourceHistoryConfTO getResourceHistoryConfTO(final ExternalResourceHistoryConf history) {
+        ResourceHistoryConfTO historyTO = new ResourceHistoryConfTO();
+        historyTO.setKey(history.getKey());
+        historyTO.setCreator(history.getCreator());
+        historyTO.setCreation(history.getCreation());
+        historyTO.setResourceTO(history.getConf());
+
+        return historyTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_LIST + "')")
+    public List<ResourceHistoryConfTO> list(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + key + "'");
+        }
+
+        return CollectionUtils.collect(resourceHistoryConfDAO.findByEntity(resource),
+                new Transformer<ExternalResourceHistoryConf, ResourceHistoryConfTO>() {
+
+            @Override
+            public ResourceHistoryConfTO transform(final ExternalResourceHistoryConf input) {
+                return getResourceHistoryConfTO(input);
+            }
+        }, new ArrayList<ResourceHistoryConfTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_RESTORE + "')")
+    public void restore(final String key) {
+        ExternalResourceHistoryConf resourceHistoryConf = resourceHistoryConfDAO.find(key);
+        if (resourceHistoryConf == null) {
+            throw new NotFoundException("Resource History Conf '" + key + "'");
+        }
+
+        binder.update(resourceHistoryConf.getEntity(), resourceHistoryConf.getConf());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_HISTORY_DELETE + "')")
+    public void delete(final String key) {
+        ExternalResourceHistoryConf resourceHistoryConf = resourceHistoryConfDAO.find(key);
+        if (resourceHistoryConf == null) {
+            throw new NotFoundException("Resource History Conf '" + key + "'");
+        }
+
+        resourceHistoryConfDAO.delete(key);
+    }
+
+    @Override
+    protected ResourceHistoryConfTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        if (!"list".equals(method.getName())) {
+            try {
+                String key = (String) args[0];
+                return getResourceHistoryConfTO(resourceHistoryConfDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index b07a92b..5269c1e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -125,18 +125,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
             throw new DuplicateException(resourceTO.getKey());
         }
 
-        ExternalResource resource = null;
-        try {
-            resource = resourceDAO.save(binder.create(resourceTO));
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getResourceTO(resource);
+        return binder.getResourceTO(resourceDAO.save(binder.create(resourceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_UPDATE + "')")
@@ -146,18 +135,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
             throw new NotFoundException("Resource '" + resourceTO.getKey() + "'");
         }
 
-        resource = binder.update(resource, resourceTO);
-        try {
-            resource = resourceDAO.save(resource);
-        } catch (SyncopeClientException e) {
-            throw e;
-        } catch (Exception e) {
-            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
-            ex.getElements().add(e.getMessage());
-            throw ex;
-        }
-
-        return binder.getResourceTO(resource);
+        return binder.getResourceTO(resourceDAO.save(binder.update(resource, resourceTO)));
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_UPDATE + "')")
@@ -208,25 +186,25 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_DELETE + "')")
-    public ResourceTO delete(final String resourceName) {
-        ExternalResource resource = resourceDAO.find(resourceName);
+    public ResourceTO delete(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
         if (resource == null) {
-            throw new NotFoundException("Resource '" + resourceName + "'");
+            throw new NotFoundException("Resource '" + key + "'");
         }
 
         ResourceTO resourceToDelete = binder.getResourceTO(resource);
 
-        resourceDAO.delete(resourceName);
+        resourceDAO.delete(key);
 
         return resourceToDelete;
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_READ + "')")
     @Transactional(readOnly = true)
-    public ResourceTO read(final String resourceName) {
-        ExternalResource resource = resourceDAO.find(resourceName);
+    public ResourceTO read(final String key) {
+        ExternalResource resource = resourceDAO.find(key);
         if (resource == null) {
-            throw new NotFoundException("Resource '" + resourceName + "'");
+            throw new NotFoundException("Resource '" + key + "'");
         }
 
         return binder.getResourceTO(resource);

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
index 2eb14c5..97da0f6 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
@@ -95,7 +95,7 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
     public <T extends AbstractSchemaTO> T create(final SchemaType schemaType, final T schemaTO) {
         if (StringUtils.isBlank(schemaTO.getKey())) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
-            sce.getElements().add("Schema name");
+            sce.getElements().add("Schema key");
             throw sce;
         }
 
@@ -124,23 +124,23 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
     }
 
     @PreAuthorize("hasRole('" + StandardEntitlement.SCHEMA_DELETE + "')")
-    public void delete(final SchemaType schemaType, final String schemaName) {
-        if (!doesSchemaExist(schemaType, schemaName)) {
-            throw new NotFoundException(schemaType + "/" + schemaName);
+    public void delete(final SchemaType schemaType, final String schemaKey) {
+        if (!doesSchemaExist(schemaType, schemaKey)) {
+            throw new NotFoundException(schemaType + "/" + schemaKey);
         }
 
         switch (schemaType) {
             case VIRTUAL:
-                virSchemaDAO.delete(schemaName);
+                virSchemaDAO.delete(schemaKey);
                 break;
 
             case DERIVED:
-                derSchemaDAO.delete(schemaName);
+                derSchemaDAO.delete(schemaKey);
                 break;
 
             case PLAIN:
             default:
-                plainSchemaDAO.delete(schemaName);
+                plainSchemaDAO.delete(schemaKey);
         }
     }
 
@@ -207,22 +207,22 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
 
     @PreAuthorize("isAuthenticated()")
     @SuppressWarnings("unchecked")
-    public <T extends AbstractSchemaTO> T read(final SchemaType schemaType, final String schemaName) {
+    public <T extends AbstractSchemaTO> T read(final SchemaType schemaType, final String schemaKey) {
         T read;
         switch (schemaType) {
             case VIRTUAL:
-                VirSchema virSchema = virSchemaDAO.find(schemaName);
+                VirSchema virSchema = virSchemaDAO.find(schemaKey);
                 if (virSchema == null) {
-                    throw new NotFoundException("Virtual Schema '" + schemaName + "'");
+                    throw new NotFoundException("Virtual Schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getVirSchemaTO(virSchema);
                 break;
 
             case DERIVED:
-                DerSchema derSchema = derSchemaDAO.find(schemaName);
+                DerSchema derSchema = derSchemaDAO.find(schemaKey);
                 if (derSchema == null) {
-                    throw new NotFoundException("Derived schema '" + schemaName + "'");
+                    throw new NotFoundException("Derived schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getDerSchemaTO(derSchema);
@@ -230,9 +230,9 @@ public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
 
             case PLAIN:
             default:
-                PlainSchema schema = plainSchemaDAO.find(schemaName);
+                PlainSchema schema = plainSchemaDAO.find(schemaKey);
                 if (schema == null) {
-                    throw new NotFoundException("Schema '" + schemaName + "'");
+                    throw new NotFoundException("Schema '" + schemaKey + "'");
                 }
 
                 read = (T) binder.getPlainSchemaTO(schema);

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
new file mode 100644
index 0000000..1caad69
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConnInstanceHistoryConfDAO.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+
+public interface ConnInstanceHistoryConfDAO extends DAO<ConnInstanceHistoryConf> {
+
+    ConnInstanceHistoryConf find(String key);
+
+    List<ConnInstanceHistoryConf> findByEntity(ConnInstance entity);
+
+    ConnInstanceHistoryConf save(ConnInstanceHistoryConf conf);
+
+    void delete(String key);
+
+    void deleteByEntity(ConnInstance entity);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
new file mode 100644
index 0000000..1c4db5a
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ExternalResourceHistoryConfDAO.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+
+public interface ExternalResourceHistoryConfDAO extends DAO<ExternalResourceHistoryConf> {
+
+    ExternalResourceHistoryConf find(String key);
+
+    List<ExternalResourceHistoryConf> findByEntity(ExternalResource entity);
+
+    ExternalResourceHistoryConf save(ExternalResourceHistoryConf conf);
+
+    void delete(String key);
+
+    void deleteByEntity(ExternalResource entity);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
new file mode 100644
index 0000000..88a4481
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstanceHistoryConf.java
@@ -0,0 +1,24 @@
+/*
+ * 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.api.entity;
+
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+
+public interface ConnInstanceHistoryConf extends HistoryConf<ConnInstance, ConnInstanceTO> {
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.java
new file mode 100644
index 0000000..5da50c1
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/HistoryConf.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.api.entity;
+
+import java.util.Date;
+import org.apache.syncope.common.lib.to.EntityTO;
+
+public interface HistoryConf<E extends Entity, T extends EntityTO> extends Entity {
+
+    String getCreator();
+
+    void setCreator(String creator);
+
+    Date getCreation();
+
+    void setCreation(Date creation);
+
+    E getEntity();
+
+    void setEntity(E entity);
+
+    T getConf();
+
+    void setConf(T conf);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
new file mode 100644
index 0000000..0fd8a25
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResourceHistoryConf.java
@@ -0,0 +1,25 @@
+/*
+ * 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.api.entity.resource;
+
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.entity.HistoryConf;
+
+public interface ExternalResourceHistoryConf extends HistoryConf<ExternalResource, ResourceTO> {
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/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 91cf1dd..8972b8b 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
@@ -24,6 +24,7 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.Closure;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
 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;
@@ -37,6 +38,9 @@ import org.springframework.stereotype.Repository;
 public class JPAConnInstanceDAO extends AbstractDAO<ConnInstance> implements ConnInstanceDAO {
 
     @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Autowired
     private ExternalResourceDAO resourceDAO;
 
     @Autowired
@@ -85,8 +89,10 @@ public class JPAConnInstanceDAO extends AbstractDAO<ConnInstance> implements Con
 
         });
 
+        connInstanceHistoryConfDAO.deleteByEntity(connInstance);
+
         entityManager().remove(connInstance);
 
-        connRegistry.unregisterConnector(key.toString());
+        connRegistry.unregisterConnector(key);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
new file mode 100644
index 0000000..001d7fb
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConnInstanceHistoryConfDAO.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstanceHistoryConf;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAConnInstanceHistoryConfDAO
+        extends AbstractDAO<ConnInstanceHistoryConf> implements ConnInstanceHistoryConfDAO {
+
+    @Override
+    public ConnInstanceHistoryConf find(final String key) {
+        return entityManager().find(JPAConnInstanceHistoryConf.class, key);
+    }
+
+    @Override
+    public List<ConnInstanceHistoryConf> findByEntity(final ConnInstance entity) {
+        TypedQuery<ConnInstanceHistoryConf> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAConnInstanceHistoryConf.class.getSimpleName()
+                + " e WHERE e.entity=:entity ORDER BY e.creation DESC", ConnInstanceHistoryConf.class);
+        query.setParameter("entity", entity);
+        return query.getResultList();
+    }
+
+    @Override
+    public ConnInstanceHistoryConf save(final ConnInstanceHistoryConf conf) {
+        return entityManager().merge(conf);
+    }
+
+    @Override
+    public void delete(final String key) {
+        ConnInstanceHistoryConf conf = find(key);
+        if (conf == null) {
+            return;
+        }
+
+        entityManager().remove(conf);
+    }
+
+    @Override
+    public void deleteByEntity(final ConnInstance entity) {
+        Query query = entityManager().createQuery(
+                "DELETE FROM " + JPAConnInstanceHistoryConf.class.getSimpleName() + " e WHERE e.entity=:entity");
+        query.setParameter("entity", entity);
+        query.executeUpdate();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/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 03a0fe5..c49bd5a 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
@@ -26,6 +26,7 @@ import javax.persistence.TypedQuery;
 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.ExternalResourceHistoryConfDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -76,6 +77,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
 
     private RealmDAO realmDAO;
 
+    private ExternalResourceHistoryConfDAO externalResourceHistoryConfDAO;
+
     private TaskDAO taskDAO() {
         synchronized (this) {
             if (taskDAO == null) {
@@ -139,6 +142,16 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
         return realmDAO;
     }
 
+    private ExternalResourceHistoryConfDAO externalResourceHistoryConfDAO() {
+        synchronized (this) {
+            if (externalResourceHistoryConfDAO == null) {
+                externalResourceHistoryConfDAO = ApplicationContextProvider.getApplicationContext().
+                        getBean(ExternalResourceHistoryConfDAO.class);
+            }
+        }
+        return externalResourceHistoryConfDAO;
+    }
+
     @Override
     public int count() {
         Query query = entityManager().createQuery(
@@ -286,6 +299,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
             }
         }
 
+        externalResourceHistoryConfDAO().deleteByEntity(resource);
+
         if (resource.getConnector() != null && resource.getConnector().getResources() != null
                 && !resource.getConnector().getResources().isEmpty()) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
new file mode 100644
index 0000000..ba43355
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceHistoryConfDAO.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Query;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAExternalResourceHistoryConfDAO
+        extends AbstractDAO<ExternalResourceHistoryConf> implements ExternalResourceHistoryConfDAO {
+
+    @Override
+    public ExternalResourceHistoryConf find(final String key) {
+        return entityManager().find(JPAExternalResourceHistoryConf.class, key);
+    }
+
+    @Override
+    public List<ExternalResourceHistoryConf> findByEntity(final ExternalResource entity) {
+        TypedQuery<ExternalResourceHistoryConf> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAExternalResourceHistoryConf.class.getSimpleName()
+                + " e WHERE e.entity=:entity ORDER BY e.creation DESC", ExternalResourceHistoryConf.class);
+        query.setParameter("entity", entity);
+        return query.getResultList();
+    }
+
+    @Override
+    public ExternalResourceHistoryConf save(final ExternalResourceHistoryConf conf) {
+        return entityManager().merge(conf);
+    }
+
+    @Override
+    public void delete(final String key) {
+        ExternalResourceHistoryConf conf = find(key);
+        if (conf == null) {
+            return;
+        }
+
+        entityManager().remove(conf);
+    }
+
+    @Override
+    public void deleteByEntity(final ExternalResource entity) {
+        Query query = entityManager().createQuery(
+                "DELETE FROM " + JPAExternalResourceHistoryConf.class.getSimpleName() + " e WHERE e.entity=:entity");
+        query.setParameter("entity", entity);
+        query.executeUpdate();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.java
new file mode 100644
index 0000000..9ee2c65
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractHistoryConf.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 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.common.lib.to.EntityTO;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.HistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@MappedSuperclass
+public abstract class AbstractHistoryConf<E extends Entity, T extends EntityTO>
+        extends AbstractGeneratedKeyEntity implements HistoryConf<E, T> {
+
+    private static final long serialVersionUID = -7210303753586235180L;
+
+    @Column(nullable = false)
+    private String creator;
+
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date creation;
+
+    @Lob
+    protected String conf;
+
+    @Override
+    public String getCreator() {
+        return creator;
+    }
+
+    @Override
+    public void setCreator(final String creator) {
+        this.creator = creator;
+    }
+
+    @Override
+    public Date getCreation() {
+        return creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    @Override
+    public void setCreation(final Date creation) {
+        this.creation = creation == null
+                ? null
+                : new Date(creation.getTime());
+    }
+
+    @Override
+    public void setConf(final T conf) {
+        this.conf = POJOHelper.serialize(conf);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
new file mode 100644
index 0000000..9001676
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstanceHistoryConf.java
@@ -0,0 +1,59 @@
+/*
+ * 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.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAConnInstanceHistoryConf.TABLE)
+public class JPAConnInstanceHistoryConf
+        extends AbstractHistoryConf<ConnInstance, ConnInstanceTO> implements ConnInstanceHistoryConf {
+
+    private static final long serialVersionUID = -4152915369607435186L;
+
+    public static final String TABLE = "ConnInstanceHistoryConf";
+
+    @OneToOne
+    private JPAConnInstance entity;
+
+    @Override
+    public ConnInstance getEntity() {
+        return entity;
+    }
+
+    @Override
+    public void setEntity(final ConnInstance entity) {
+        checkType(entity, JPAConnInstance.class);
+        this.entity = (JPAConnInstance) entity;
+    }
+
+    @Override
+    public ConnInstanceTO getConf() {
+        return StringUtils.isBlank(conf)
+                ? null
+                : POJOHelper.deserialize(conf, ConnInstanceTO.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index daea67a..88f4e5a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -31,6 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.ConnPoolConf;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.Domain;
@@ -125,6 +126,8 @@ import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
 import org.apache.syncope.core.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
 
 @Component
 public class JPAEntityFactory implements EntityFactory {
@@ -184,12 +187,16 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPANotification();
         } else if (reference.equals(ExternalResource.class)) {
             result = (E) new JPAExternalResource();
+        } else if (reference.equals(ExternalResourceHistoryConf.class)) {
+            result = (E) new JPAExternalResourceHistoryConf();
         } else if (reference.equals(Provision.class)) {
             result = (E) new JPAProvision();
         } else if (reference.equals(OrgUnit.class)) {
             result = (E) new JPAOrgUnit();
         } else if (reference.equals(ConnInstance.class)) {
             result = (E) new JPAConnInstance();
+        } else if (reference.equals(ConnInstanceHistoryConf.class)) {
+            result = (E) new JPAConnInstanceHistoryConf();
         } else if (reference.equals(PlainSchema.class)) {
             result = (E) new JPAPlainSchema();
         } else if (reference.equals(APlainAttr.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
new file mode 100644
index 0000000..31fa726
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResourceHistoryConf.java
@@ -0,0 +1,60 @@
+/*
+ * 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.resource;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractHistoryConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAExternalResourceHistoryConf.TABLE)
+public class JPAExternalResourceHistoryConf
+        extends AbstractHistoryConf<ExternalResource, ResourceTO> implements ExternalResourceHistoryConf {
+
+    public static final String TABLE = "ExternalResourceHistoryConf";
+
+    private static final long serialVersionUID = 4994551965604592529L;
+
+    @OneToOne
+    private JPAExternalResource entity;
+
+    @Override
+    public ExternalResource getEntity() {
+        return entity;
+    }
+
+    @Override
+    public void setEntity(final ExternalResource entity) {
+        checkType(entity, JPAExternalResource.class);
+        this.entity = (JPAExternalResource) entity;
+    }
+
+    @Override
+    public ResourceTO getConf() {
+        return StringUtils.isBlank(conf)
+                ? null
+                : POJOHelper.deserialize(conf, ResourceTO.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index 33d8a70..9d42535 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -152,6 +152,23 @@ under the License.
   <CPlainAttrValue id="447e2456-3ff5-41bc-8ff1-cbb0567546cb"
                    attribute_id="cfec3140-562d-459c-ac6a-e3e10758661d" longValue="120"/>
   
+  <!--  Connector and Resource configuration history -->                   
+  <SyncopeSchema id="connector.conf.history.size"/>
+  <PlainSchema id="connector.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="connector.conf.history.size"/>
+  <CPlainAttrValue id="4989ce5d-7b42-4a5c-b725-54286888df10"
+                   attribute_id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c" longValue="10"/>
+  
+  <SyncopeSchema id="resource.conf.history.size"/>
+  <PlainSchema id="resource.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="resource.conf.history.size"/>
+  <CPlainAttrValue id="059ac3e1-687f-4dd9-a28b-a23f8b436f31"
+                   attribute_id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60" longValue="10"/>
+
   <AnyType id="USER" kind="USER"/>
   <AnyTypeClass id="BaseUser"/>
   <AnyType_AnyTypeClass anyType_id="USER" anyTypeClass_id="BaseUser"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
index f1d0f95..ffe1edc 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java
@@ -47,7 +47,7 @@ public class PlainSchemaTest extends AbstractTest {
     @Test
     public void findAll() {
         List<PlainSchema> schemas = plainSchemaDAO.findAll();
-        assertEquals(41, schemas.size());
+        assertEquals(43, schemas.size());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
new file mode 100644
index 0000000..43c0b65
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceHistoryConfTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.outer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ConnInstanceHistoryConfTest extends AbstractTest {
+
+    @Autowired
+    private ConnInstanceDAO connInstanceDAO;
+
+    @Autowired
+    private ConnInstanceHistoryConfDAO connInstanceHistoryConfDAO;
+
+    @Test
+    public void createDelete() {
+        ConnInstance ldapConnector = connInstanceDAO.find("74141a3b-0762-4720-a4aa-fc3e374ef3ef");
+        assertNotNull(ldapConnector);
+
+        ConnInstanceHistoryConf ldapHistory = entityFactory.newEntity(ConnInstanceHistoryConf.class);
+        ldapHistory.setCreation(new Date());
+        ldapHistory.setCreator("me");
+        ldapHistory.setEntity(ldapConnector);
+        ldapHistory.setConf(new ConnInstanceTO());
+
+        ldapHistory = connInstanceHistoryConfDAO.save(ldapHistory);
+        assertNotNull(ldapHistory.getKey());
+
+        connInstanceHistoryConfDAO.flush();
+
+        List<ConnInstanceHistoryConf> history = connInstanceHistoryConfDAO.findByEntity(ldapConnector);
+        assertEquals(1, history.size());
+        assertEquals(ldapHistory, history.get(0));
+
+        connInstanceHistoryConfDAO.delete(ldapHistory.getKey());
+
+        connInstanceHistoryConfDAO.flush();
+
+        assertNull(connInstanceHistoryConfDAO.find(ldapHistory.getKey()));
+        assertTrue(connInstanceHistoryConfDAO.findByEntity(ldapConnector).isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
new file mode 100644
index 0000000..2bc36a6
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceHistoryConfTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.outer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ResourceHistoryConfTest extends AbstractTest {
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private ExternalResourceHistoryConfDAO resourceHistoryConfDAO;
+
+    @Test
+    public void createDelete() {
+        ExternalResource ldapResource = resourceDAO.find("resource-ldap");
+        assertNotNull(ldapResource);
+
+        ExternalResourceHistoryConf ldapHistory = entityFactory.newEntity(ExternalResourceHistoryConf.class);
+        ldapHistory.setCreation(new Date());
+        ldapHistory.setCreator("me");
+        ldapHistory.setEntity(ldapResource);
+        ldapHistory.setConf(new ResourceTO());
+
+        ldapHistory = resourceHistoryConfDAO.save(ldapHistory);
+        assertNotNull(ldapHistory.getKey());
+
+        resourceHistoryConfDAO.flush();
+
+        List<ExternalResourceHistoryConf> history = resourceHistoryConfDAO.findByEntity(ldapResource);
+        assertEquals(1, history.size());
+        assertEquals(ldapHistory, history.get(0));
+
+        resourceHistoryConfDAO.delete(ldapHistory.getKey());
+
+        resourceHistoryConfDAO.flush();
+
+        assertNull(resourceHistoryConfDAO.find(ldapHistory.getKey()));
+        assertTrue(resourceHistoryConfDAO.findByEntity(ldapResource).isEmpty());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 5e9c46e..83bacf0 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -151,7 +151,24 @@ under the License.
               owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="jwt.lifetime.minutes"/>
   <CPlainAttrValue id="447e2456-3ff5-41bc-8ff1-cbb0567546cb"
                    attribute_id="cfec3140-562d-459c-ac6a-e3e10758661d" longValue="120"/>
-
+    
+  <!--  Connector and Resource configuration history -->                   
+  <SyncopeSchema id="connector.conf.history.size"/>
+  <PlainSchema id="connector.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="connector.conf.history.size"/>
+  <CPlainAttrValue id="4989ce5d-7b42-4a5c-b725-54286888df10"
+                   attribute_id="ae36c6ab-0f39-40d7-b6e6-3b8394547c9c" longValue="10"/>
+  
+  <SyncopeSchema id="resource.conf.history.size"/>
+  <PlainSchema id="resource.conf.history.size" type="Long"
+               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
+  <CPlainAttr id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60"
+              owner_id="cd64d66f-6fff-4008-b966-a06b1cc1436d" schema_id="resource.conf.history.size"/>
+  <CPlainAttrValue id="059ac3e1-687f-4dd9-a28b-a23f8b436f31"
+                   attribute_id="ae7ae53a-74cf-48b0-9a57-6d2c94928e60" longValue="10"/>
+  
   <!-- sample policies -->
   <PullPolicy id="66691e96-285f-4464-bc19-e68384ea4c85" description="a pull policy"
               specification='{"conflictResolutionAction":"IGNORE"}'/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/7451efb6/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
index 698fd25..ae7074f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java
@@ -31,6 +31,6 @@ public interface ConnInstanceDataBinder {
 
     ConnInstanceTO getConnInstanceTO(ConnInstance connInstance);
 
-    ConnInstance update(String key, ConnInstanceTO connInstanceTO);
+    ConnInstance update(ConnInstanceTO connInstanceTO);
 
 }