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/06/06 13:06:34 UTC

[08/10] syncope git commit: [SYNCOPE-1103] Option to put Quartz scheduler in standby, from provisioning.properties

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index 1521749..d010e4b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -57,9 +57,9 @@ import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
-import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -210,7 +210,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public AnyObject save(final AnyObject anyObject) {
         AnyObject merged = super.save(anyObject);
-        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged, AuthContextUtils.getDomain()));
 
         groupDAO().refreshDynMemberships(merged);
 
@@ -253,7 +253,8 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
         }
 
         entityManager().remove(anyObject);
-        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.ANY_OBJECT, anyObject.getKey()));
+        publisher.publishEvent(
+                new AnyDeletedEvent(this, AnyTypeKind.ANY_OBJECT, anyObject.getKey(), AuthContextUtils.getDomain()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index cc70cd2..1b7374a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -65,9 +65,9 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
-import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -278,7 +278,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public Group save(final Group group) {
         Group merged = super.save(group);
-        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged, AuthContextUtils.getDomain()));
 
         // refresh dynamic memberships
         if (merged.getUDynMembership() != null) {
@@ -294,7 +294,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                 insert.setParameter(2, merged.getKey());
                 insert.executeUpdate();
 
-                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
             }
         }
         for (ADynGroupMembership memb : merged.getADynMemberships()) {
@@ -311,7 +311,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                 insert.setParameter(3, merged.getKey());
                 insert.executeUpdate();
 
-                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, anyObject));
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, anyObject, AuthContextUtils.getDomain()));
             }
         }
 
@@ -332,7 +332,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             anyObjectDAO().save(leftEnd);
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd, AuthContextUtils.getDomain()));
         }
         for (UMembership membership : findUMemberships(group)) {
             User leftEnd = membership.getLeftEnd();
@@ -346,11 +346,12 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             userDAO().save(leftEnd);
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd, AuthContextUtils.getDomain()));
         }
 
         entityManager().remove(group);
-        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey()));
+        publisher.publishEvent(
+                new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey(), AuthContextUtils.getDomain()));
     }
 
     @Override
@@ -420,7 +421,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                 insert.executeUpdate();
             }
 
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, memb.getGroup()));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, memb.getGroup(), AuthContextUtils.getDomain()));
         }
     }
 
@@ -433,7 +434,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         delete.executeUpdate();
 
         for (Group group : dynGroups) {
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group, AuthContextUtils.getDomain()));
         }
     }
 
@@ -494,7 +495,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
                 insert.executeUpdate();
             }
 
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, memb.getGroup()));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, memb.getGroup(), AuthContextUtils.getDomain()));
         }
     }
 
@@ -507,7 +508,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         delete.executeUpdate();
 
         for (Group group : dynGroups) {
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group, AuthContextUtils.getDomain()));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index b362778..9796f4d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -32,8 +32,9 @@ import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Repository;
@@ -102,7 +103,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
                 insert.setParameter(2, merged.getKey());
                 insert.executeUpdate();
 
-                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
             }
         }
 
@@ -117,7 +118,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
 
         for (User user : query.getResultList()) {
             user.getRoles().remove(role);
-            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
         }
 
         entityManager().remove(role);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 47d6f30..40db0c4 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -72,9 +72,9 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
-import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
@@ -444,7 +444,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             throw e;
         }
 
-        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged, AuthContextUtils.getDomain()));
 
         roleDAO.refreshDynMemberships(merged);
         groupDAO().refreshDynMemberships(merged);
@@ -463,7 +463,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         }
 
         entityManager().remove(user);
-        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.USER, user.getKey()));
+        publisher.publishEvent(
+                new AnyDeletedEvent(this, AnyTypeKind.USER, user.getKey(), AuthContextUtils.getDomain()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/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 113dbba..4521db6 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -655,7 +655,7 @@ under the License.
                 location="${connid.location}"
                 connectorName="net.tirasa.connid.bundles.ldap.LdapConnector"
                 version="${connid.ldap.version}" 
-                jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any LDAP groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"host","displayName":"Host","helpMessage":"The name or IP address of the host where the LDAP server is running.","type":"jav
 a.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["localhost"]},{"schema":{"name":"passwordHashAlgorithm","displayName":"Password Hash Algorithm","helpMessage":"Indicates the algorithm that the Identity system should use to hash the password. Currently supported values are SSHA, SHA, SSHA1, and SHA1. A blank value indicates that the system will not hash passwords. This will cause cleartext passwords to be stored in LDAP unless the LDAP server performs the hash (Netscape Directory Server and iPlanet Directory Server do).","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["SHA"]},{"schema":{"name":"port","displayName":"TCP Port","helpMessage":"TCP/IP port number used to communicate with the LDAP server.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[1389]},{"schema":{"name":"vlvSo
 rtAttribute","displayName":"VLV Sort Attribute","helpMessage":"Specify the sort attribute to use for VLV indexes on the resource.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"statusManagementClass","displayName":"Status management class ","helpMessage":"Class to be used to manage enabled/disabled status. If no class is specified then identity status management wont be possible.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["net.tirasa.connid.bundles.ldap.commons.AttributeStatusManagement"]},{"schema":{"name":"accountObjectClasses","displayName":"Account Object Classes","helpMessage":"The object class or classes that will be used when creating new user objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to separate m
 ultiple object classes. Some object classes may require that you specify all object classes in the class hierarchy.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson"]},{"schema":{"name":"accountUserNameAttributes","displayName":"Account User Name Attributes","helpMessage":"Attribute or attributes which holds the account user name. They will be used when authenticating to find the LDAP entry for the user name to authenticate.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid"]},{"schema":{"name":"baseContextsToSynchronize","displayName":"Base Contexts to Synchronize","helpMessage":"One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. The base contexts attribute will be used to synchronize a change if this property is not set.","type":"[Ljava.lang.S
 tring;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"accountSynchronizationFilter","displayName":"LDAP Filter for Accounts to Synchronize","helpMessage":"An optional LDAP filter for the objects to synchronize. Because the change log is for all objects, this filter updates only objects that match the specified filter. If you specify a filter, an object will be synchronized only if it matches the filter and includes a synchronized object class.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"removeLogEntryObjectClassFromFilter","displayName":"Remove Log Entry Object Class from Filter","helpMessage":"If this property is set (the default), the filter used to fetch change log entries does not contain the \"changeLogEntry\" object class, expecting that there are no entries of oth
 er object types in the change log.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordDecryptionKey","displayName":"Password Decryption Key","helpMessage":"The key to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"readSchema","displayName":"Read Schema","helpMessage":"If true, the connector will read the schema from the server. If false, the connector will provide a default schema based on the object classes in the configuration. This property must be true in order to use extended object classes.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"ssl","displayName":"SSL","helpMessage"
 :"Select the check box to connect to the LDAP server using SSL.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordAttributeToSynchronize","displayName":"Password Attribute to Synchronize","helpMessage":"The name of the password attribute to synchronize when performing password synchronization.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"accountSearchFilter","displayName":"LDAP Filter for Retrieving Accounts","helpMessage":"An optional LDAP filter to control which accounts are returned from the LDAP resource. If no filter is specified, only accounts that include all specified object classes are returned.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=*"]},{"schema":{"name":"passwordDecryptionIn
 itializationVector","displayName":"Password Decryption Initialization Vector","helpMessage":"The initialization vector to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupMemberAttribute","displayName":"Group Member Attribute","helpMessage":"The name of the group attribute that will be updated with the distinguished name of the user when the user is added to the group.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"failover","displayName":"Failover Servers","helpMessage":"List all servers that should be used for failover in case the preferred server fails. If the preferred server fails, JNDI will connect to the next available server in the list. List all servers in the form of 
 \"ldap://ldap.example.com:389/\", which follows the standard LDAP v3 URLs described in RFC 2255. Only the host and port parts of the URL are relevant in this setting.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"modifiersNamesToFilterOut","displayName":"Filter Out Changes By","helpMessage":"The names (DNs) of directory administrators to filter from the changes. Changes with the attribute \"modifiersName\" that match entries in this list will be filtered out. The standard value is the administrator name used by this adapter, to prevent loops. Entries should be of the format \"cn=Directory Manager\".","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupNameAttributes","displayName":"Group Name Attributes","helpMessage":"Attribute or attributes which holds the group name.","type
 ":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":true,"values":["cn"]},{"schema":{"name":"respectResourcePasswordPolicyChangeAfterReset","displayName":"Respect Resource Password Policy Change-After-Reset","helpMessage":"When this resource is specified in a Login Module (i.e., this resource is a pass-through authentication target) and the resource password policy is configured for change-after-reset, a user whose resource account password has been administratively reset will be required to change that password after successfully authenticating.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false
 ,"values":["false"]},{"schema":{"name":"filterWithOrInsteadOfAnd","displayName":"Filter with Or Instead of And","helpMessage":"Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"principal","displayName":"Principal","helpMessage":"The distinguished name with which to authenticate to the LDAP server.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=admin,ou=system"]},{"schema":{"name":"changeLogBlockSize","displayName":"Change Log Block Size","helpMessage":"The number of change log entries to fetch per query.","type":"int","required":true,"order":0,"confidential":false,"defaultValues":null},"overridab
 le":false,"values":[100]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":true,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"passwordAttribute","displayName":"Password Attribute","helpMessage":"The name of the LDAP attribute which holds the password. When changing an user password, the new password is set to this attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["userpassword"]},{"schema":{"name":"changeNumberAttribute","displayName":"Change Number Attribute","helpMessage":"The name of the change number attribute in the cha
 nge log entry.","type":"java.lang.String","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["changeNumber"]},{"schema":{"name":"objectClassesToSynchronize","displayName":"Object Classes to Synchronize","helpMessage":"The object classes to synchronize. The change log is for all objects; this filters updates to just the listed object classes. You should not list the superclasses of an object class unless you intend to synchronize objects with any of the superclass values. For example, if only \"inetOrgPerson\" objects should be synchronized, but the superclasses of \"inetOrgPerson\" (\"person\", \"organizationalperson\" and \"top\") should be filtered out, then list only \"inetOrgPerson\" here. All objects in LDAP are subclassed from \"top\". For this reason, you should never list \"top\", otherwise no object would be filtered.","type":"[Ljava.lang.String;","required":true,"order":0,"confidential":false,"defaultValues":null},"overridabl
 e":false,"values":["inetOrgPerson","groupOfUniqueNames"]},{"schema":{"name":"credentials","displayName":"Password","helpMessage":"Password for the principal.","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["secret"]},{"schema":{"name":"attributesToSynchronize","displayName":"Attributes to Synchronize","helpMessage":"The names of the attributes to synchronize. This ignores updates from the change log if they do not update any of the named attributes. For example, if only \"department\" is listed, then only changes that affect \"department\" will be processed. All other updates are ignored. If blank (the default), then all changes are processed.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"maintainPosixGroupMembership","displayName":"Maintain POSIX Group Membership",
 "helpMessage":"When enabled and a user is renamed or deleted, update any POSIX groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]}]'/>
+                jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any LDAP groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"host","displayName":"Host","helpMessage":"The name or IP address of the host where the LDAP server is running.","type":"jav
 a.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["localhost"]},{"schema":{"name":"passwordHashAlgorithm","displayName":"Password Hash Algorithm","helpMessage":"Indicates the algorithm that the Identity system should use to hash the password. Currently supported values are SSHA, SHA, SSHA1, and SHA1. A blank value indicates that the system will not hash passwords. This will cause cleartext passwords to be stored in LDAP unless the LDAP server performs the hash (Netscape Directory Server and iPlanet Directory Server do).","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["SHA"]},{"schema":{"name":"port","displayName":"TCP Port","helpMessage":"TCP/IP port number used to communicate with the LDAP server.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[1389]},{"schema":{"name":"vlvSo
 rtAttribute","displayName":"VLV Sort Attribute","helpMessage":"Specify the sort attribute to use for VLV indexes on the resource.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"statusManagementClass","displayName":"Status management class ","helpMessage":"Class to be used to manage enabled/disabled status. If no class is specified then identity status management wont be possible.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["net.tirasa.connid.bundles.ldap.commons.AttributeStatusManagement"]},{"schema":{"name":"accountObjectClasses","displayName":"Account Object Classes","helpMessage":"The object class or classes that will be used when creating new user objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to separate m
 ultiple object classes. Some object classes may require that you specify all object classes in the class hierarchy.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson"]},{"schema":{"name":"accountUserNameAttributes","displayName":"Account User Name Attributes","helpMessage":"Attribute or attributes which holds the account user name. They will be used when authenticating to find the LDAP entry for the user name to authenticate.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid"]},{"schema":{"name":"baseContextsToSynchronize","displayName":"Base Contexts to Synchronize","helpMessage":"One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. The base contexts attribute will be used to synchronize a change if this property is not set.","type":"[Ljava.lang.S
 tring;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"accountSynchronizationFilter","displayName":"LDAP Filter for Accounts to Synchronize","helpMessage":"An optional LDAP filter for the objects to synchronize. Because the change log is for all objects, this filter updates only objects that match the specified filter. If you specify a filter, an object will be synchronized only if it matches the filter and includes a synchronized object class.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"removeLogEntryObjectClassFromFilter","displayName":"Remove Log Entry Object Class from Filter","helpMessage":"If this property is set (the default), the filter used to fetch change log entries does not contain the \"changeLogEntry\" object class, expecting that there are no entries of oth
 er object types in the change log.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordDecryptionKey","displayName":"Password Decryption Key","helpMessage":"The key to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"readSchema","displayName":"Read Schema","helpMessage":"If true, the connector will read the schema from the server. If false, the connector will provide a default schema based on the object classes in the configuration. This property must be true in order to use extended object classes.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"ssl","displayName":"SSL","helpMessage":
 "Select the check box to connect to the LDAP server using SSL.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordAttributeToSynchronize","displayName":"Password Attribute to Synchronize","helpMessage":"The name of the password attribute to synchronize when performing password synchronization.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"accountSearchFilter","displayName":"LDAP Filter for Retrieving Accounts","helpMessage":"An optional LDAP filter to control which accounts are returned from the LDAP resource. If no filter is specified, only accounts that include all specified object classes are returned.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=*"]},{"schema":{"name":"passwordDecryptionIni
 tializationVector","displayName":"Password Decryption Initialization Vector","helpMessage":"The initialization vector to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupMemberAttribute","displayName":"Group Member Attribute","helpMessage":"The name of the group attribute that will be updated with the distinguished name of the user when the user is added to the group.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"failover","displayName":"Failover Servers","helpMessage":"List all servers that should be used for failover in case the preferred server fails. If the preferred server fails, JNDI will connect to the next available server in the list. List all servers in the form of \
 "ldap://ldap.example.com:389/\", which follows the standard LDAP v3 URLs described in RFC 2255. Only the host and port parts of the URL are relevant in this setting.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"modifiersNamesToFilterOut","displayName":"Filter Out Changes By","helpMessage":"The names (DNs) of directory administrators to filter from the changes. Changes with the attribute \"modifiersName\" that match entries in this list will be filtered out. The standard value is the administrator name used by this adapter, to prevent loops. Entries should be of the format \"cn=Directory Manager\".","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupNameAttributes","displayName":"Group Name Attributes","helpMessage":"Attribute or attributes which holds the group name.","type"
 :"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":true,"values":["cn"]},{"schema":{"name":"respectResourcePasswordPolicyChangeAfterReset","displayName":"Respect Resource Password Policy Change-After-Reset","helpMessage":"When this resource is specified in a Login Module (i.e., this resource is a pass-through authentication target) and the resource password policy is configured for change-after-reset, a user whose resource account password has been administratively reset will be required to change that password after successfully authenticating.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,
 "values":["false"]},{"schema":{"name":"filterWithOrInsteadOfAnd","displayName":"Filter with Or Instead of And","helpMessage":"Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"principal","displayName":"Principal","helpMessage":"The distinguished name with which to authenticate to the LDAP server.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=admin,ou=system"]},{"schema":{"name":"changeLogBlockSize","displayName":"Change Log Block Size","helpMessage":"The number of change log entries to fetch per query.","type":"int","required":true,"order":0,"confidential":false,"defaultValues":null},"overridabl
 e":false,"values":[100]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":true,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"passwordAttribute","displayName":"Password Attribute","helpMessage":"The name of the LDAP attribute which holds the password. When changing an user password, the new password is set to this attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["userpassword"]},{"schema":{"name":"changeNumberAttribute","displayName":"Change Number Attribute","helpMessage":"The name of the change number attribute in the chan
 ge log entry.","type":"java.lang.String","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["changeNumber"]},{"schema":{"name":"objectClassesToSynchronize","displayName":"Object Classes to Synchronize","helpMessage":"The object classes to synchronize. The change log is for all objects; this filters updates to just the listed object classes. You should not list the superclasses of an object class unless you intend to synchronize objects with any of the superclass values. For example, if only \"inetOrgPerson\" objects should be synchronized, but the superclasses of \"inetOrgPerson\" (\"person\", \"organizationalperson\" and \"top\") should be filtered out, then list only \"inetOrgPerson\" here. All objects in LDAP are subclassed from \"top\". For this reason, you should never list \"top\", otherwise no object would be filtered.","type":"[Ljava.lang.String;","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable
 ":false,"values":["inetOrgPerson","groupOfUniqueNames"]},{"schema":{"name":"credentials","displayName":"Password","helpMessage":"Password for the principal.","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["secret"]},{"schema":{"name":"attributesToSynchronize","displayName":"Attributes to Synchronize","helpMessage":"The names of the attributes to synchronize. This ignores updates from the change log if they do not update any of the named attributes. For example, if only \"department\" is listed, then only changes that affect \"department\" will be processed. All other updates are ignored. If blank (the default), then all changes are processed.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"maintainPosixGroupMembership","displayName":"Maintain POSIX Group Membership","
 helpMessage":"When enabled and a user is renamed or deleted, update any POSIX groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]}]'/>
   <ConnInstance_capabilities connInstance_id="74141a3b-0762-4720-a4aa-fc3e374ef3ef" capability="CREATE"/>
   <ConnInstance_capabilities connInstance_id="74141a3b-0762-4720-a4aa-fc3e374ef3ef" capability="UPDATE"/>
   <ConnInstance_capabilities connInstance_id="74141a3b-0762-4720-a4aa-fc3e374ef3ef" capability="DELETE"/>
@@ -1402,7 +1402,7 @@ $$ }&#10;
     &lt;/html&gt;&#10;
   &lt;/xsl:template&gt;&#10;
 &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.StaticReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.StaticReportlet']&quot;&gt;&#10;
     &lt;h2&gt;Reportlet: &#10;
       &lt;xsl:value-of select=&quot;@name&quot;/&gt;&#10;
     &lt;/h2&gt;&#10;
@@ -1452,7 +1452,7 @@ $$ }&#10;
     &lt;/xsl:if&gt;&#10;
   &lt;/xsl:template&gt;&#10;
   &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.UserReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.UserReportlet']&quot;&gt;&#10;
     &#10;
     &lt;h3&gt;Reportlet: &lt;xsl:value-of select=&quot;@name&quot;/&gt;&lt;/h3&gt;&#10;
     &#10;
@@ -1597,7 +1597,7 @@ $$ }&#10;
        &#10;
   &lt;/xsl:template&gt;&#10;
 &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.GroupReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.GroupReportlet']&quot;&gt;&#10;
     &lt;h2&gt;Reportlet: &lt;xsl:value-of select=&quot;@name&quot;/&gt;&lt;/h2&gt;&#10;
     &lt;xsl:for-each select=&quot;group&quot;&gt;&#10;
       &lt;h3&gt;Group &lt;xsl:value-of select=&quot;@name&quot;/&gt;&lt;/h3&gt;&#10;
@@ -1763,7 +1763,7 @@ $$ }&#10;
 &#10;
   &lt;xsl:variable name=&quot;delimiter&quot; select=&quot;';'&quot;/&gt;&#10;
    &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.StaticReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.StaticReportlet']&quot;&gt;&#10;
     &lt;xsl:call-template name=&quot;header&quot;&gt;&#10;
       &lt;xsl:with-param name=&quot;node&quot; select=&quot;configurations/staticAttributes&quot;/&gt;&#10;
     &lt;/xsl:call-template&gt;&#10;
@@ -1821,7 +1821,7 @@ $$ }&#10;
     &lt;/xsl:for-each&gt;&#10;
   &lt;/xsl:template&gt;&#10;
   &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.UserReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.UserReportlet']&quot;&gt;&#10;
     &#10;
     &lt;xsl:call-template name=&quot;header&quot;&gt;&#10;
       &lt;xsl:with-param name=&quot;node&quot; select=&quot;configurations/userAttributes&quot;/&gt;&#10;
@@ -1902,7 +1902,7 @@ $$ }&#10;
     &lt;/xsl:for-each&gt;&#10;
   &lt;/xsl:template&gt;&#10;
   &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.GroupReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.GroupReportlet']&quot;&gt;&#10;
     &#10;
     &lt;xsl:call-template name=&quot;header&quot;&gt;&#10;
       &lt;xsl:with-param name=&quot;node&quot; select=&quot;configurations/groupAttributes&quot;/&gt;&#10;
@@ -2044,7 +2044,7 @@ $$ }&#10;
     &lt;/fo:root&gt;&#10;
   &lt;/xsl:template&gt;&#10;
 &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.StaticReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.StaticReportlet']&quot;&gt;&#10;
 &#10;
     &lt;fo:block font-size=&quot;14pt&quot; font-weight=&quot;bold&quot; space-after=&quot;0.5cm&quot;&gt;Reportlet: &#10;
       &lt;xsl:value-of select=&quot;@name&quot;/&gt;&#10;
@@ -2103,7 +2103,7 @@ $$ }&#10;
         &#10;
   &lt;/xsl:template&gt;&#10;
   &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.UserReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.UserReportlet']&quot;&gt;&#10;
    &#10;
     &lt;fo:block font-size=&quot;16pt&quot; font-weight=&quot;bold&quot; space-after=&quot;0.5cm&quot; space-before=&quot;5mm&quot;&gt;Reportlet: &lt;xsl:value-of select=&quot;@name&quot;/&gt;&lt;/fo:block&gt;&#10;
         &#10;
@@ -2267,7 +2267,7 @@ $$ }&#10;
     &lt;/xsl:for-each&gt;&#10;
   &lt;/xsl:template&gt;&#10;
   &#10;
-  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.logic.report.GroupReportlet']&quot;&gt;&#10;
+  &lt;xsl:template match=&quot;reportlet[@class='org.apache.syncope.core.provisioning.java.job.report.GroupReportlet']&quot;&gt;&#10;
    &#10;
     &lt;fo:block font-size=&quot;16pt&quot; font-weight=&quot;bold&quot; space-after=&quot;0.5cm&quot; space-before=&quot;5mm&quot;&gt;Reportlet: &lt;xsl:value-of select=&quot;@name&quot;/&gt;&lt;/fo:block&gt;&#10;
         &#10;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-api/pom.xml b/core/provisioning-api/pom.xml
index 4092a09..8078788 100644
--- a/core/provisioning-api/pom.xml
+++ b/core/provisioning-api/pom.xml
@@ -52,6 +52,11 @@ under the License.
     </dependency>
     
     <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+    
+    <dependency>
       <groupId>org.quartz-scheduler</groupId>
       <artifactId>quartz</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
index 64c03e9..f397351 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.api;
 
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.common.lib.types.AuditElements;
 
 public interface AuditManager {
@@ -38,7 +39,14 @@ public interface AuditManager {
             String event);
 
     /**
-     * Create notification tasks for each notification matching provided conditions.
+     * Create audit entries according to the provided event.
+     * 
+     * @param event Spring event raised during Logic processing
+     */
+    void audit(final AfterHandlingEvent event);
+
+    /**
+     * Create audit entries for each audit matching provided conditions.
      *
      * @param type event category type
      * @param category event category

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java
new file mode 100644
index 0000000..e732097
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.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.provisioning.api.event;
+
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.springframework.context.ApplicationEvent;
+
+public class AfterHandlingEvent extends ApplicationEvent {
+
+    private static final long serialVersionUID = 5950986229089263378L;
+
+    private final boolean notificationsAvailable;
+
+    private final boolean auditRequested;
+
+    private final AuditElements.EventCategoryType type;
+
+    private final String category;
+
+    private final String subcategory;
+
+    private final String event;
+
+    private final AuditElements.Result condition;
+
+    private final Object before;
+
+    private final Object output;
+
+    private final Object[] input;
+
+    public AfterHandlingEvent(
+            final Object source,
+            final boolean notificationsAvailable,
+            final boolean auditRequested,
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final String event,
+            final AuditElements.Result condition,
+            final Object before,
+            final Object output,
+            final Object... input) {
+
+        super(source);
+
+        this.notificationsAvailable = notificationsAvailable;
+        this.auditRequested = auditRequested;
+        this.type = type;
+        this.category = category;
+        this.subcategory = subcategory;
+        this.event = event;
+        this.condition = condition;
+        this.before = before;
+        this.output = output;
+        this.input = input;
+    }
+
+    public boolean isNotificationsAvailable() {
+        return notificationsAvailable;
+    }
+
+    public boolean isAuditRequested() {
+        return auditRequested;
+    }
+
+    public AuditElements.EventCategoryType getType() {
+        return type;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public String getSubcategory() {
+        return subcategory;
+    }
+
+    public String getEvent() {
+        return event;
+    }
+
+    public AuditElements.Result getCondition() {
+        return condition;
+    }
+
+    public Object getBefore() {
+        return before;
+    }
+
+    public Object getOutput() {
+        return output;
+    }
+
+    public Object[] getInput() {
+        return input;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyCreatedUpdatedEvent.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyCreatedUpdatedEvent.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyCreatedUpdatedEvent.java
new file mode 100644
index 0000000..4a2f9a1
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyCreatedUpdatedEvent.java
@@ -0,0 +1,46 @@
+/*
+ * 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.provisioning.api.event;
+
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyCreatedUpdatedEvent<A extends Any<?>> extends ApplicationEvent {
+
+    private static final long serialVersionUID = -781747175059834365L;
+
+    private final A any;
+
+    private final String domain;
+
+    public AnyCreatedUpdatedEvent(final Object source, final A any, final String domain) {
+        super(source);
+        this.any = any;
+        this.domain = domain;
+    }
+
+    public A getAny() {
+        return any;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyDeletedEvent.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyDeletedEvent.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyDeletedEvent.java
new file mode 100644
index 0000000..b2c978b
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AnyDeletedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * 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.provisioning.api.event;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyDeletedEvent extends ApplicationEvent {
+
+    private static final long serialVersionUID = 6389886937942135639L;
+
+    private final AnyTypeKind anyTypeKind;
+
+    private final String anyKey;
+
+    private final String domain;
+
+    public AnyDeletedEvent(
+            final Object source,
+            final AnyTypeKind anyTypeKind,
+            final String anyKey,
+            final String domain) {
+
+        super(source);
+        this.anyTypeKind = anyTypeKind;
+        this.anyKey = anyKey;
+        this.domain = domain;
+    }
+
+    public AnyTypeKind getAnyTypeKind() {
+        return anyTypeKind;
+    }
+
+    public String getAnyKey() {
+        return anyKey;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
index a297960..548ed93 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
@@ -22,6 +22,7 @@ import java.util.List;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 
 /**
  * Create notification tasks that will be executed by NotificationJob.
@@ -55,6 +56,13 @@ public interface NotificationManager {
             String event);
 
     /**
+     * Create notification tasks according to the provided event.
+     *
+     * @param event Spring event raised during Logic processing
+     */
+    void createTasks(AfterHandlingEvent event);
+
+    /**
      * Create notification tasks for each notification matching provided conditions.
      *
      * @param type event category type

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 53cd3c5..a9e881e 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -58,7 +58,11 @@ under the License.
       <groupId>org.springframework</groupId>
       <artifactId>spring-context-support</artifactId>
     </dependency>
-    
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-jdbc</artifactId>
+    </dependency>
+
     <dependency>
       <groupId>org.apache.geronimo.javamail</groupId>
       <artifactId>geronimo-javamail_1.4_mail</artifactId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
index 3a1dda4..d553762 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
@@ -27,10 +27,13 @@ import org.apache.syncope.common.lib.types.LoggerType;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 @Transactional(readOnly = true)
@@ -77,6 +80,23 @@ public class AuditManagerImpl implements AuditManager {
         return auditRequested;
     }
 
+    @EventListener
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    @Override
+    public void audit(final AfterHandlingEvent event) {
+        if (event.isAuditRequested()) {
+            audit(
+                    event.getType(),
+                    event.getCategory(),
+                    event.getSubcategory(),
+                    event.getEvent(),
+                    event.getCondition(),
+                    event.getBefore(),
+                    event.getOutput(),
+                    event.getInput());
+        }
+    }
+
     @Override
     public void audit(
             final AuditElements.EventCategoryType type,

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index a8024f2..c763447 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@ -66,6 +66,7 @@ import org.identityconnectors.framework.spi.SearchResultsHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ClassUtils;
 
 public class ConnectorFacadeProxy implements Connector {
@@ -264,6 +265,7 @@ public class ConnectorFacadeProxy implements Connector {
         }
     }
 
+    @Transactional
     @Override
     public void sync(final ObjectClass objectClass, final SyncToken token, final SyncResultsHandler handler,
             final OperationOptions options) {
@@ -330,6 +332,7 @@ public class ConnectorFacadeProxy implements Connector {
         }
     }
 
+    @Transactional
     @Override
     public void fullReconciliation(
             final ObjectClass objectClass,
@@ -339,6 +342,7 @@ public class ConnectorFacadeProxy implements Connector {
         filteredReconciliation(objectClass, null, handler, options);
     }
 
+    @Transactional
     @Override
     public void filteredReconciliation(
             final ObjectClass objectClass,
@@ -348,6 +352,7 @@ public class ConnectorFacadeProxy implements Connector {
 
         search(objectClass, filterBuilder == null ? null : filterBuilder.build(), new ResultsHandler() {
 
+            @Transactional
             @Override
             public boolean handle(final ConnectorObject obj) {
                 return handler.handle(new SyncDeltaBuilder().

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
new file mode 100644
index 0000000..0b2fba0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
@@ -0,0 +1,393 @@
+/*
+ * 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.provisioning.java.job;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.Task;
+import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.transaction.annotation.Transactional;
+import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.identityconnectors.common.IOUtil;
+import org.quartz.impl.jdbcjobstore.Constants;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
+import org.apache.syncope.core.provisioning.java.job.report.ReportJob;
+
+public class JobManagerImpl implements JobManager, SyncopeLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JobManager.class);
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Autowired
+    private SchedulerFactoryBean scheduler;
+
+    @Autowired
+    private TaskDAO taskDAO;
+
+    @Autowired
+    private ReportDAO reportDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    private boolean disableQuartzInstance;
+
+    public void setDisableQuartzInstance(final boolean disableQuartzInstance) {
+        this.disableQuartzInstance = disableQuartzInstance;
+    }
+
+    private boolean isRunningHere(final JobKey jobKey) throws SchedulerException {
+        return IterableUtils.matchesAny(scheduler.getScheduler().getCurrentlyExecutingJobs(),
+                new Predicate<JobExecutionContext>() {
+
+            @Override
+            public boolean evaluate(final JobExecutionContext jec) {
+                return jobKey.equals(jec.getJobDetail().getKey());
+            }
+        });
+    }
+
+    private boolean isRunningElsewhere(final JobKey jobKey) throws SchedulerException {
+        if (!scheduler.getScheduler().getMetaData().isJobStoreClustered()) {
+            return false;
+        }
+
+        Connection conn = DataSourceUtils.getConnection(domainsHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN));
+        PreparedStatement stmt = null;
+        ResultSet resultSet = null;
+        try {
+            stmt = conn.prepareStatement(
+                    "SELECT 1 FROM " + Constants.DEFAULT_TABLE_PREFIX + "FIRED_TRIGGERS "
+                    + "WHERE JOB_NAME = ? AND JOB_GROUP = ?");
+            stmt.setString(1, jobKey.getName());
+            stmt.setString(2, jobKey.getGroup());
+
+            resultSet = stmt.executeQuery();
+            return resultSet.next();
+        } catch (SQLException e) {
+            throw new SchedulerException(e);
+        } finally {
+            IOUtil.quietClose(resultSet);
+            IOUtil.quietClose(stmt);
+            IOUtil.quietClose(conn);
+        }
+    }
+
+    @Override
+    public boolean isRunning(final JobKey jobKey) throws SchedulerException {
+        return isRunningHere(jobKey) || isRunningElsewhere(jobKey);
+    }
+
+    private void registerJob(
+            final String jobName, final Job jobInstance,
+            final String cronExpression, final Date startAt,
+            final Map<String, Object> jobMap)
+            throws SchedulerException {
+
+        if (isRunningHere(new JobKey(jobName, Scheduler.DEFAULT_GROUP))) {
+            LOG.debug("Job {} already running, cancel", jobName);
+            return;
+        }
+
+        // 0. unregister job
+        unregisterJob(jobName);
+
+        // 1. Job bean
+        ApplicationContextProvider.getBeanFactory().registerSingleton(jobName, jobInstance);
+
+        // 2. JobDetail bean
+        JobBuilder jobDetailBuilder = JobBuilder.newJob(jobInstance.getClass()).
+                withIdentity(jobName).
+                usingJobData(new JobDataMap(jobMap));
+
+        // 3. Trigger
+        if (cronExpression == null && startAt == null) {
+            // Jobs added with no trigger must be durable
+            scheduler.getScheduler().addJob(jobDetailBuilder.storeDurably().build(), true);
+        } else {
+            TriggerBuilder<?> triggerBuilder;
+
+            if (cronExpression == null) {
+                triggerBuilder = TriggerBuilder.newTrigger().
+                        withIdentity(JobNamer.getTriggerName(jobName)).
+                        startAt(startAt);
+            } else {
+                triggerBuilder = TriggerBuilder.newTrigger().
+                        withIdentity(JobNamer.getTriggerName(jobName)).
+                        withSchedule(CronScheduleBuilder.cronSchedule(cronExpression));
+
+                if (startAt == null) {
+                    triggerBuilder = triggerBuilder.startNow();
+                } else {
+                    triggerBuilder = triggerBuilder.startAt(startAt);
+                }
+            }
+
+            scheduler.getScheduler().scheduleJob(jobDetailBuilder.build(), triggerBuilder.build());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T createSpringBean(final Class<T> jobClass) {
+        T jobInstance = null;
+        for (int i = 0; i < 5 && jobInstance == null; i++) {
+            LOG.debug("{} attempt to create Spring bean for {}", i, jobClass);
+            try {
+                jobInstance = (T) ApplicationContextProvider.getBeanFactory().
+                        createBean(jobClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                LOG.debug("{} attempt to create Spring bean for {} succeeded", i, jobClass);
+            } catch (BeanCreationException e) {
+                LOG.error("Could not create Spring bean for {}", jobClass, e);
+                try {
+                    Thread.sleep(1000);
+                } catch (final InterruptedException ex) {
+                    // ignore
+                }
+            }
+        }
+        if (jobInstance == null) {
+            throw new NotFoundException("Spring bean for " + jobClass);
+        }
+
+        return jobInstance;
+    }
+
+    @Override
+    public Map<String, Object> register(final SchedTask task, final Date startAt, final long interruptMaxRetries)
+            throws SchedulerException {
+
+        TaskJob job = createSpringBean(TaskJob.class);
+        job.setTaskKey(task.getKey());
+
+        String jobDelegateClassName = task instanceof PullTask
+                ? PullJobDelegate.class.getName()
+                : task instanceof PushTask
+                        ? PushJobDelegate.class.getName()
+                        : task.getJobDelegateClassName();
+
+        Map<String, Object> jobMap = new HashMap<>();
+        jobMap.put(JobManager.DOMAIN_KEY, AuthContextUtils.getDomain());
+        jobMap.put(TaskJob.DELEGATE_CLASS_KEY, jobDelegateClassName);
+        jobMap.put(INTERRUPT_MAX_RETRIES_KEY, interruptMaxRetries);
+
+        registerJob(
+                JobNamer.getJobKey(task).getName(),
+                job,
+                task.getCronExpression(),
+                startAt,
+                jobMap);
+        return jobMap;
+    }
+
+    @Override
+    public void register(final Report report, final Date startAt, final long interruptMaxRetries)
+            throws SchedulerException {
+
+        ReportJob job = createSpringBean(ReportJob.class);
+        job.setReportKey(report.getKey());
+
+        Map<String, Object> jobMap = new HashMap<>();
+        jobMap.put(JobManager.DOMAIN_KEY, AuthContextUtils.getDomain());
+        jobMap.put(INTERRUPT_MAX_RETRIES_KEY, interruptMaxRetries);
+
+        registerJob(JobNamer.getJobKey(report).getName(), job, report.getCronExpression(), startAt, jobMap);
+    }
+
+    private void unregisterJob(final String jobName) {
+        try {
+            scheduler.getScheduler().unscheduleJob(new TriggerKey(jobName, Scheduler.DEFAULT_GROUP));
+            scheduler.getScheduler().deleteJob(new JobKey(jobName, Scheduler.DEFAULT_GROUP));
+        } catch (SchedulerException e) {
+            LOG.error("Could not remove job " + jobName, e);
+        }
+
+        if (ApplicationContextProvider.getBeanFactory().containsSingleton(jobName)) {
+            ApplicationContextProvider.getBeanFactory().destroySingleton(jobName);
+        }
+    }
+
+    @Override
+    public void unregister(final Task task) {
+        unregisterJob(JobNamer.getJobKey(task).getName());
+    }
+
+    @Override
+    public void unregister(final Report report) {
+        unregisterJob(JobNamer.getJobKey(report).getName());
+    }
+
+    @Override
+    public Integer getPriority() {
+        return 200;
+    }
+
+    @Transactional
+    @Override
+    public void load() {
+        if (disableQuartzInstance) {
+            String instanceId = "AUTO";
+            try {
+                instanceId = scheduler.getScheduler().getSchedulerInstanceId();
+                scheduler.getScheduler().standby();
+
+                LOG.info("Successfully put Quartz instance {} in standby", instanceId);
+            } catch (SchedulerException e) {
+                LOG.error("Could not put Quartz instance {} in standby", instanceId, e);
+            }
+        }
+
+        final Pair<String, Long> conf = AuthContextUtils.execWithAuthContext(
+                SyncopeConstants.MASTER_DOMAIN, new AuthContextUtils.Executable<Pair<String, Long>>() {
+
+            @Override
+            public Pair<String, Long> exec() {
+                String notificationJobCronExpression = StringUtils.EMPTY;
+
+                CPlainAttr notificationJobCronExp =
+                        confDAO.find("notificationjob.cronExpression", NotificationJob.DEFAULT_CRON_EXP);
+                if (!notificationJobCronExp.getValuesAsStrings().isEmpty()) {
+                    notificationJobCronExpression = notificationJobCronExp.getValuesAsStrings().get(0);
+                }
+
+                long interruptMaxRetries =
+                        confDAO.find("tasks.interruptMaxRetries", "1").getValues().get(0).getLongValue();
+
+                return ImmutablePair.of(notificationJobCronExpression, interruptMaxRetries);
+            }
+        });
+
+        for (String domain : domainsHolder.getDomains().keySet()) {
+            AuthContextUtils.execWithAuthContext(domain, new AuthContextUtils.Executable<Void>() {
+
+                @Override
+                public Void exec() {
+                    // 1. jobs for SchedTasks
+                    Set<SchedTask> tasks = new HashSet<>(taskDAO.<SchedTask>findAll(TaskType.SCHEDULED));
+                    tasks.addAll(taskDAO.<PullTask>findAll(TaskType.PULL));
+                    tasks.addAll(taskDAO.<PushTask>findAll(TaskType.PUSH));
+                    for (SchedTask task : tasks) {
+                        try {
+                            register(task, task.getStartAt(), conf.getRight());
+                        } catch (Exception e) {
+                            LOG.error("While loading job instance for task " + task.getKey(), e);
+                        }
+                    }
+
+                    // 2. jobs for Reports
+                    for (Report report : reportDAO.findAll()) {
+                        try {
+                            register(report, null, conf.getRight());
+                        } catch (Exception e) {
+                            LOG.error("While loading job instance for report " + report.getName(), e);
+                        }
+                    }
+
+                    return null;
+                }
+            });
+        }
+
+        Map<String, Object> jobMap = new HashMap<>();
+        jobMap.put(JobManager.DOMAIN_KEY, AuthContextUtils.getDomain());
+        jobMap.put(INTERRUPT_MAX_RETRIES_KEY, conf.getRight());
+
+        // 3. NotificationJob
+        if (StringUtils.isBlank(conf.getLeft())) {
+            LOG.debug("Empty value provided for {}'s cron, not registering anything on Quartz",
+                    NotificationJob.class.getSimpleName());
+        } else {
+            LOG.debug("{}'s cron expression: {} - registering Quartz job and trigger",
+                    NotificationJob.class.getSimpleName(), conf.getLeft());
+
+            try {
+                NotificationJob job = createSpringBean(NotificationJob.class);
+                registerJob(
+                        NOTIFICATION_JOB.getName(),
+                        job,
+                        conf.getLeft(),
+                        null,
+                        jobMap);
+            } catch (Exception e) {
+                LOG.error("While loading {} instance", NotificationJob.class.getSimpleName(), e);
+            }
+        }
+
+        // 4. SystemLoadReporterJob (fixed schedule, every minute)
+        LOG.debug("Registering {}", SystemLoadReporterJob.class);
+        try {
+            SystemLoadReporterJob job = createSpringBean(SystemLoadReporterJob.class);
+            registerJob(
+                    "systemLoadReporterJob",
+                    job,
+                    "0 * * * * ?",
+                    null,
+                    jobMap);
+        } catch (Exception e) {
+            LOG.error("While loading {} instance", SystemLoadReporterJob.class.getSimpleName(), e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SystemLoadReporterJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SystemLoadReporterJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SystemLoadReporterJob.java
new file mode 100644
index 0000000..59c195f
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SystemLoadReporterJob.java
@@ -0,0 +1,57 @@
+/*
+ * 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.provisioning.java.job;
+
+import java.lang.management.ManagementFactory;
+import org.apache.syncope.common.lib.info.SystemInfo;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
+
+/**
+ * Reports about system load.
+ */
+@Component
+public class SystemLoadReporterJob extends AbstractInterruptableJob {
+
+    private static final Integer MB = 1024 * 1024;
+
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        super.execute(context);
+
+        SystemInfo.LoadInstant instant = new SystemInfo.LoadInstant();
+
+        instant.setSystemLoadAverage(ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage());
+
+        instant.setUptime(ManagementFactory.getRuntimeMXBean().getUptime());
+
+        Runtime runtime = Runtime.getRuntime();
+        instant.setTotalMemory(runtime.totalMemory() / MB);
+        instant.setMaxMemory(runtime.maxMemory() / MB);
+        instant.setFreeMemory(runtime.freeMemory() / MB);
+
+        publisher.publishEvent(instant);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
new file mode 100644
index 0000000..153a221
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.notification;
+
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Periodically checks for notification to send.
+ *
+ * @see org.apache.syncope.core.persistence.api.entity.task.NotificationTask
+ */
+@Component
+public class NotificationJob extends AbstractInterruptableJob {
+
+    public enum Status {
+
+        SENT,
+        NOT_SENT
+
+    }
+
+    public static final String DEFAULT_CRON_EXP = "0 0/5 * * * ?";
+
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationJob.class);
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Autowired
+    private NotificationJobDelegate delegate;
+
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        super.execute(context);
+
+        LOG.debug("Waking up...");
+
+        for (String domain : domainsHolder.getDomains().keySet()) {
+            try {
+                AuthContextUtils.execWithAuthContext(domain, new AuthContextUtils.Executable<Void>() {
+
+                    @Override
+                    public Void exec() {
+                        try {
+                            delegate.execute();
+                        } catch (Exception e) {
+                            LOG.error("While sending out notifications", e);
+                            throw new RuntimeException(e);
+                        }
+
+                        return null;
+                    }
+                });
+            } catch (RuntimeException e) {
+                LOG.error("While sending out notifications", e);
+                throw new JobExecutionException("While sending out notifications", e);
+            }
+        }
+
+        LOG.debug("Sleeping again...");
+    }
+}