http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/core/provisioning-api/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-api/pom.xml b/core/provisioning-api/pom.xml
index ede504e..c944f19 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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/core/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 11fc9c7..5822e1e 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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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/67ecbea3/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...");
+    }
+}

Reply via email to