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:27 UTC

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

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 69a0fe453 -> 67ecbea39
  refs/heads/master 95c933ac8 -> bd695da79


http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
new file mode 100644
index 0000000..200f64a
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
@@ -0,0 +1,383 @@
+/*
+ * 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.report;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(UserReportletConf.class)
+public class UserReportlet extends AbstractReportlet {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private UserDataBinder userDataBinder;
+
+    @Autowired
+    private GroupDataBinder groupDataBinder;
+
+    @Autowired
+    private AnyObjectDataBinder anyObjectDataBinder;
+
+    private UserReportletConf conf;
+
+    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
+            throws SAXException {
+
+        if (anyTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : anyTO.getResources()) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
+                handler.startElement("", "", "resource", atts);
+                handler.endElement("", "", "resource");
+            }
+
+            handler.endElement("", "", "resources");
+        }
+    }
+
+    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
+            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+        if (!attrs.isEmpty()) {
+            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
+
+            handler.startElement("", "", "attributes", null);
+            for (String attrName : attrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "attribute", atts);
+
+                if (attrMap.containsKey(attrName)) {
+                    for (String value : attrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
+
+            handler.startElement("", "", "derivedAttributes", null);
+            for (String attrName : derAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "derivedAttribute", atts);
+
+                if (derAttrMap.containsKey(attrName)) {
+                    for (String value : derAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
+
+            handler.startElement("", "", "virtualAttributes", null);
+            for (String attrName : virAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "virtualAttribute", atts);
+
+                if (virAttrMap.containsKey(attrName)) {
+                    for (String value : virAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        for (User user : users) {
+            atts.clear();
+
+            for (Feature feature : conf.getFeatures()) {
+                String type = null;
+                String value = null;
+                switch (feature) {
+                    case key:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getKey();
+                        break;
+
+                    case username:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getUsername();
+                        break;
+
+                    case workflowId:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getWorkflowId();
+                        break;
+
+                    case status:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getStatus();
+                        break;
+
+                    case creationDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getCreationDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getCreationDate());
+                        break;
+
+                    case lastLoginDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getLastLoginDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getLastLoginDate());
+                        break;
+
+                    case changePwdDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getChangePwdDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getChangePwdDate());
+                        break;
+
+                    case passwordHistorySize:
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(user.getPasswordHistory().size());
+                        break;
+
+                    case failedLoginCount:
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(user.getFailedLogins());
+                        break;
+
+                    default:
+                }
+
+                if (type != null && value != null) {
+                    atts.addAttribute("", "", feature.name(), type, value);
+                }
+            }
+
+            handler.startElement("", "", "user", atts);
+
+            // Using UserTO for attribute values, since the conversion logic of
+            // values to String is already encapsulated there
+            UserTO userTO = userDataBinder.getUserTO(user, true);
+
+            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+
+            if (conf.getFeatures().contains(Feature.relationships)) {
+                handler.startElement("", "", "relationships", null);
+
+                for (RelationshipTO rel : userTO.getRelationships()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "anyObjectKey",
+                            ReportXMLConst.XSD_STRING, rel.getRightKey());
+                    handler.startElement("", "", "relationship", atts);
+
+                    if (conf.getFeatures().contains(Feature.resources)) {
+                        for (URelationship actualRel : user.getRelationships(rel.getRightKey())) {
+                            doExtractResources(
+                                    handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true));
+                        }
+                    }
+
+                    handler.endElement("", "", "relationship");
+                }
+
+                handler.endElement("", "", "relationships");
+            }
+            if (conf.getFeatures().contains(Feature.memberships)) {
+                handler.startElement("", "", "memberships", null);
+
+                for (MembershipTO memb : userTO.getMemberships()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "groupKey",
+                            ReportXMLConst.XSD_STRING, memb.getRightKey());
+                    atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, memb.getGroupName());
+                    handler.startElement("", "", "membership", atts);
+
+                    if (conf.getFeatures().contains(Feature.resources)) {
+                        UMembership actualMemb = user.getMembership(memb.getRightKey());
+                        if (actualMemb == null) {
+                            LOG.warn("Unexpected: cannot find membership for group {} for user {}",
+                                    memb.getRightKey(), user);
+                        } else {
+                            doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true));
+                        }
+                    }
+
+                    handler.endElement("", "", "membership");
+                }
+
+                handler.endElement("", "", "memberships");
+            }
+
+            if (conf.getFeatures().contains(Feature.resources)) {
+                doExtractResources(handler, userTO);
+            }
+
+            handler.endElement("", "", "user");
+        }
+    }
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "userAttributes", atts);
+
+        for (Feature feature : conf.getFeatures()) {
+            atts.clear();
+            handler.startElement("", "", "feature", atts);
+            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
+            handler.endElement("", "", "feature");
+        }
+
+        for (String attr : conf.getPlainAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "attribute", atts);
+            handler.characters(attr.toCharArray(), 0, attr.length());
+            handler.endElement("", "", "attribute");
+        }
+
+        for (String derAttr : conf.getDerAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "derAttribute", atts);
+            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
+            handler.endElement("", "", "derAttribute");
+        }
+
+        for (String virAttr : conf.getVirAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "virAttribute", atts);
+            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
+            handler.endElement("", "", "virAttribute");
+        }
+
+        handler.endElement("", "", "userAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    private int count() {
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? userDAO.count()
+                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER);
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof UserReportletConf) {
+            this.conf = UserReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<User> users;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                users = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(this.conf.getMatchingCond()),
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
+            }
+
+            doExtract(handler, users);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index 06df8d6..aecfe69 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
@@ -76,11 +76,14 @@ import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.notification.NotificationRecipientsProvider;
+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.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 @Component
@@ -276,6 +279,23 @@ public class NotificationManagerImpl implements NotificationManager {
         });
     }
 
+    @EventListener
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    @Override
+    public void createTasks(final AfterHandlingEvent event) {
+        if (event.isNotificationsAvailable()) {
+            createTasks(
+                    event.getType(),
+                    event.getCategory(),
+                    event.getSubcategory(),
+                    event.getEvent(),
+                    event.getCondition(),
+                    event.getBefore(),
+                    event.getOutput(),
+                    event.getInput());
+        }
+    }
+
     @Override
     public List<NotificationTask> createTasks(
             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/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index e487986..3491736 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -42,14 +42,17 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.identityconnectors.framework.common.objects.SyncDeltaType;
@@ -65,14 +68,23 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
     protected PullUtils pullUtils;
 
     @Autowired
-    protected VirSchemaDAO virSchemaDAO;
+    private NotificationManager notificationManager;
 
     @Autowired
-    protected VirAttrCache virAttrCache;
+    private AuditManager auditManager;
 
-    protected SyncopePullExecutor executor;
+    @Autowired
+    private ConnObjectUtils connObjectUtils;
+
+    @Autowired
+    private VirSchemaDAO virSchemaDAO;
+
+    @Autowired
+    private VirAttrCache virAttrCache;
+
+    private SyncopePullExecutor executor;
 
-    protected Result latestResult;
+    private Result latestResult;
 
     protected abstract String getName(AnyTO anyTO);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 9d4ab3e..a17c789 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -46,6 +46,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
@@ -55,6 +56,7 @@ import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -64,6 +66,9 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
     @Autowired
     protected MappingManager mappingManager;
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     protected abstract String getName(Any<?> any);
 
     protected void deprovision(final Any<?> any) {
@@ -357,22 +362,17 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
 
                 throw new JobExecutionException(e);
             } finally {
-                notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
-                        any.getType().getKind().name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        any);
-                auditManager.audit(AuditElements.EventCategoryType.PUSH,
+                publisher.publishEvent(new AfterHandlingEvent(this,
+                        true,
+                        true,
+                        AuditElements.EventCategoryType.PUSH,
                         any.getType().getKind().name().toLowerCase(),
                         profile.getTask().getResource().getKey(),
                         operation,
                         resultStatus,
                         beforeObj,
                         output,
-                        any);
+                        any));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
index 0df7f49..e914ce3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
@@ -38,7 +38,7 @@ public abstract class AbstractRealmResultHandler<T extends ProvisioningTask, A e
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
 
     protected static final String REALM_TYPE = "REALM";
-    
+
     @Autowired
     protected RealmDAO realmDAO;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
index 8f3224c..61e37e1 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
@@ -23,24 +23,18 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
-import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler;
-import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
-import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
@@ -64,24 +58,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
     protected GroupDAO groupDAO;
 
     /**
-     * ConnectorObject utils.
-     */
-    @Autowired
-    protected ConnObjectUtils connObjectUtils;
-
-    /**
-     * Notification Manager.
-     */
-    @Autowired
-    protected NotificationManager notificationManager;
-
-    /**
-     * Audit Manager.
-     */
-    @Autowired
-    protected AuditManager auditManager;
-
-    /**
      * Propagation manager.
      */
     @Autowired
@@ -117,15 +93,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
     protected GroupDataBinder groupDataBinder;
 
     @Autowired
-    protected AnyObjectProvisioningManager anyObjectProvisioningManager;
-
-    @Autowired
-    protected UserProvisioningManager userProvisioningManager;
-
-    @Autowired
-    protected GroupProvisioningManager groupProvisioningManager;
-
-    @Autowired
     protected AnyUtilsFactory anyUtilsFactory;
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
index 025b7e9..a328e56 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
@@ -29,14 +29,19 @@ import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler implements AnyObjectPullResultHandler {
 
+    @Autowired
+    private AnyObjectProvisioningManager anyObjectProvisioningManager;
+
     @Override
     protected AnyUtils getAnyUtils() {
         return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
index dc1613f..01af49f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
@@ -32,15 +32,20 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implements GroupPullResultHandler {
 
-    protected final Map<String, String> groupOwnerMap = new HashMap<>();
+    @Autowired
+    private GroupProvisioningManager groupProvisioningManager;
+
+    private final Map<String, String> groupOwnerMap = new HashMap<>();
 
     @Override
     public Map<String, String> getGroupOwnerMap() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
index 34e7530..4d7ad3c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
@@ -34,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
 import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
@@ -44,6 +45,8 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.ResultsHandler;
 import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
 import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -51,6 +54,9 @@ public class RealmPushResultHandlerImpl
         extends AbstractRealmResultHandler<PushTask, PushActions>
         implements SyncopePushResultHandler {
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public boolean handle(final String realmKey) {
@@ -313,22 +319,17 @@ public class RealmPushResultHandlerImpl
 
                 throw new JobExecutionException(e);
             } finally {
-                notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
-                        REALM_TYPE.toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        realm);
-                auditManager.audit(AuditElements.EventCategoryType.PUSH,
+                publisher.publishEvent(new AfterHandlingEvent(this,
+                        true,
+                        true,
+                        AuditElements.EventCategoryType.PUSH,
                         REALM_TYPE.toLowerCase(),
                         profile.getTask().getResource().getKey(),
                         operation,
                         resultStatus,
                         beforeObj,
                         output,
-                        realm);
+                        realm));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
index 72855bb..91575e2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
@@ -31,13 +31,18 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class UserPullResultHandlerImpl extends AbstractPullResultHandler implements UserPullResultHandler {
 
+    @Autowired
+    private UserProvisioningManager userProvisioningManager;
+
     @Override
     protected AnyUtils getAnyUtils() {
         return anyUtilsFactory.getInstance(AnyTypeKind.USER);

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioning.properties b/core/provisioning-java/src/main/resources/provisioning.properties
index b7e405d..29aca0d 100644
--- a/core/provisioning-java/src/main/resources/provisioning.properties
+++ b/core/provisioning-java/src/main/resources/provisioning.properties
@@ -28,3 +28,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml
index 37c8181..fab7099 100644
--- a/core/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -93,6 +93,9 @@ under the License.
     </property>
   </bean>
   <bean class="org.apache.syncope.core.provisioning.java.job.SchedulerShutdown"/>
+  <bean class="org.apache.syncope.core.provisioning.java.job.JobManagerImpl">
+    <property name="disableQuartzInstance" value="${quartz.disableInstance:false}"/>
+  </bean>
   
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
     <property name="defaultEncoding" value="${smtpEncoding}"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
deleted file mode 100644
index f71e59c..0000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.event;
-
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-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) {
-        super(source);
-        this.any = any;
-        this.domain = AuthContextUtils.getDomain();
-    }
-
-    public A getAny() {
-        return any;
-    }
-
-    public String getDomain() {
-        return domain;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
deleted file mode 100644
index 8d33609..0000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.event;
-
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-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) {
-        super(source);
-        this.anyTypeKind = anyTypeKind;
-        this.anyKey = anyKey;
-        this.domain = AuthContextUtils.getDomain();
-    }
-
-    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/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties b/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
index 41f21ad..5af0e91 100644
--- a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
+++ b/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
@@ -30,3 +30,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
index 1352f9e..1e34f6a 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -20,8 +20,8 @@ package org.apache.syncope.ext.elasticsearch.client;
 
 import java.io.IOException;
 import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
-import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.elasticsearch.action.delete.DeleteResponse;
 import org.elasticsearch.action.get.GetResponse;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index 5951117..ea86696 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -35,11 +35,11 @@ import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
 import org.apache.syncope.core.logic.TaskLogic;
-import org.apache.syncope.core.logic.report.AuditReportlet;
-import org.apache.syncope.core.logic.report.GroupReportlet;
-import org.apache.syncope.core.logic.report.ReconciliationReportlet;
-import org.apache.syncope.core.logic.report.StaticReportlet;
-import org.apache.syncope.core.logic.report.UserReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.AuditReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.GroupReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.ReconciliationReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.StaticReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.UserReportlet;
 import org.apache.syncope.core.migration.MigrationPullActions;
 import org.apache.syncope.core.persistence.api.DomainsHolder;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/all/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/all/provisioning.properties b/fit/core-reference/src/main/resources/all/provisioning.properties
index 8147828..b66ef97 100644
--- a/fit/core-reference/src/main/resources/all/provisioning.properties
+++ b/fit/core-reference/src/main/resources/all/provisioning.properties
@@ -31,3 +31,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_h2.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/mariadb/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/mariadb/provisioning.properties b/fit/core-reference/src/main/resources/mariadb/provisioning.properties
index 5b6539d..cc042e7 100644
--- a/fit/core-reference/src/main/resources/mariadb/provisioning.properties
+++ b/fit/core-reference/src/main/resources/mariadb/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_mariadb.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/mysql/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/mysql/provisioning.properties
index acb8a8a..48f9ef6 100644
--- a/fit/core-reference/src/main/resources/mysql/provisioning.properties
+++ b/fit/core-reference/src/main/resources/mysql/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_mysql.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/oracle/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/oracle/provisioning.properties b/fit/core-reference/src/main/resources/oracle/provisioning.properties
index a71d41e..67bc013 100644
--- a/fit/core-reference/src/main/resources/oracle/provisioning.properties
+++ b/fit/core-reference/src/main/resources/oracle/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
 quartz.sql=tables_oracle.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/postgres/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/postgres/provisioning.properties b/fit/core-reference/src/main/resources/postgres/provisioning.properties
index 4b578ba..378e75e 100644
--- a/fit/core-reference/src/main/resources/postgres/provisioning.properties
+++ b/fit/core-reference/src/main/resources/postgres/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/provisioning.properties b/fit/core-reference/src/main/resources/provisioning.properties
index dae8ca8..b2a8cb6 100644
--- a/fit/core-reference/src/main/resources/provisioning.properties
+++ b/fit/core-reference/src/main/resources/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_h2.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties b/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
index 510eae0..1a21fef 100644
--- a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
+++ b/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.MSSQLDelegate
 quartz.sql=tables_sqlServer.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
index d3b0192..1674621 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
@@ -42,7 +42,7 @@ import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.TaskService;
-import org.apache.syncope.core.logic.notification.NotificationJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.apache.syncope.fit.AbstractITCase;
 
 public abstract class AbstractTaskITCase extends AbstractITCase {

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index 65f37f4..4cea200 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -559,7 +559,6 @@ public class ConnectorITCase extends AbstractITCase {
 
         objectClassInfo = connectorService.buildObjectClassInfo(ldap, true);
         assertNotNull(objectClassInfo);
-        assertEquals(2, objectClassInfo.size());
 
         Collection<String> objectClasses = CollectionUtils.collect(objectClassInfo,
                 new Transformer<ConnIdObjectClassTO, String>() {
@@ -570,7 +569,6 @@ public class ConnectorITCase extends AbstractITCase {
             }
 
         });
-        assertEquals(2, objectClasses.size());
         assertTrue(objectClasses.contains(ObjectClass.ACCOUNT_NAME));
         assertTrue(objectClasses.contains(ObjectClass.GROUP_NAME));
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
index 8f3a1cb..f4cd5d7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
@@ -51,7 +51,7 @@ import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.NotificationService;
-import org.apache.syncope.core.logic.notification.NotificationJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.apache.syncope.fit.core.reference.TestNotificationRecipientsProvider;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
index b7d53e7..e5171e7 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
@@ -18,6 +18,9 @@
 //
 ==== High-Availability
 
+[discrete]
+===== OpenJPA
+
 When deploying multiple Syncope <<core>> instances with a single database or database cluster, it is of
 fundamental importance that the contained OpenJPA instances are correctly configured for
 http://openjpa.apache.org/builds/2.4.2/apache-openjpa/docs/ref_guide_event.html[remote event notification^]. +
@@ -93,3 +96,26 @@ becomes:
 <entry key="openjpa.RemoteCommitProvider" value="tcp(Addresses=10.0.1.10;10.0.1.11)"/>
 ....
 ====
+
+[discrete]
+===== Quartz
+
+The http://www.quartz-scheduler.org[Quartz^] scheduler is largely used within Syncope <<core>> to schedule the execution
+of jobs, including <<tasks-pull,pull>>, <<tasks-push,push>>, <<tasks-notification,notification>> and 
+<<tasks-custom,custom>> tasks, and <<reportlets,reportlets>>.
+
+By default, Quartz is configured for
+http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/ConfigJDBCJobStoreClustering.html[clustering^],
+where each node is automatically handled via the underlying JDBC store, and all cluster nodes are equally selectable
+for processing jobs.
+
+There are deployment scenarios which might have different requirements: for example, there could be three Core nodes
+configured with OpenJPA remote commit provider (see above), where two of them are dedicated to serve REST requests,
+leaving the third for running Quartz jobs.
+
+In such cases, it is possible to prevent Quartz from running on a given node by setting the following parameter in
+`core/src/main/resources/provisioning.properties`:
+
+....
+quartz.disableInstance=true
+....


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

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
deleted file mode 100644
index 0aeaf0a..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.GroupReportletConf;
-import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(GroupReportletConf.class)
-public class GroupReportlet extends AbstractReportlet {
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private GroupDataBinder groupDataBinder;
-
-    private GroupReportletConf conf;
-
-    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
-            throws SAXException {
-
-        if (anyTO.getResources().isEmpty()) {
-            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
-        } else {
-            AttributesImpl atts = new AttributesImpl();
-            handler.startElement("", "", "resources", null);
-
-            for (String resourceName : anyTO.getResources()) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
-                handler.startElement("", "", "resource", atts);
-                handler.endElement("", "", "resource");
-            }
-
-            handler.endElement("", "", "resources");
-        }
-    }
-
-    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
-            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-        if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
-
-            handler.startElement("", "", "attributes", null);
-            for (String attrName : attrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "attribute", atts);
-
-                if (attrMap.containsKey(attrName)) {
-                    for (String value : attrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "attribute");
-            }
-            handler.endElement("", "", "attributes");
-        }
-
-        if (!derAttrs.isEmpty()) {
-            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
-
-            handler.startElement("", "", "derivedAttributes", null);
-            for (String attrName : derAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "derivedAttribute", atts);
-
-                if (derAttrMap.containsKey(attrName)) {
-                    for (String value : derAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "derivedAttribute");
-            }
-            handler.endElement("", "", "derivedAttributes");
-        }
-
-        if (!virAttrs.isEmpty()) {
-            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
-
-            handler.startElement("", "", "virtualAttributes", null);
-            for (String attrName : virAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "virtualAttribute", atts);
-
-                if (virAttrMap.containsKey(attrName)) {
-                    for (String value : virAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "virtualAttribute");
-            }
-            handler.endElement("", "", "virtualAttributes");
-        }
-    }
-
-    private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        for (Group group : groups) {
-            atts.clear();
-
-            for (Feature feature : conf.getFeatures()) {
-                String type = null;
-                String value = null;
-                switch (feature) {
-                    case key:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getKey();
-                        break;
-
-                    case name:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = String.valueOf(group.getName());
-                        break;
-
-                    case groupOwner:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getGroupOwner().getKey();
-                        break;
-
-                    case userOwner:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getUserOwner().getKey();
-                        break;
-
-                    default:
-                }
-
-                if (type != null && value != null) {
-                    atts.addAttribute("", "", feature.name(), type, value);
-                }
-            }
-
-            handler.startElement("", "", "group", atts);
-
-            // Using GroupTO for attribute values, since the conversion logic of
-            // values to String is already encapsulated there
-            GroupTO groupTO = groupDataBinder.getGroupTO(group, true);
-
-            doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
-
-            // to get resources associated to a group
-            if (conf.getFeatures().contains(Feature.resources)) {
-                doExtractResources(handler, groupTO);
-            }
-            //to get users asscoiated to a group is preferred GroupDAO to GroupTO
-            if (conf.getFeatures().contains(Feature.users)) {
-                handler.startElement("", "", "users", null);
-
-                for (UMembership memb : groupDAO.findUMemberships(group)) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_STRING,
-                            memb.getLeftEnd().getKey());
-                    atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING,
-                            String.valueOf(memb.getLeftEnd().getUsername()));
-
-                    handler.startElement("", "", "user", atts);
-                    handler.endElement("", "", "user");
-                }
-
-                handler.endElement("", "", "users");
-            }
-
-            handler.endElement("", "", "group");
-        }
-    }
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        if (conf == null) {
-            LOG.debug("Report configuration is not present");
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "groupAttributes", atts);
-
-        if (conf != null) {
-            for (Feature feature : conf.getFeatures()) {
-                atts.clear();
-                handler.startElement("", "", "feature", atts);
-                handler.characters(feature.name().toCharArray(), 0, feature.name().length());
-                handler.endElement("", "", "feature");
-            }
-
-            for (String attr : conf.getPlainAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "attribute", atts);
-                handler.characters(attr.toCharArray(), 0, attr.length());
-                handler.endElement("", "", "attribute");
-            }
-
-            for (String derAttr : conf.getDerAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "derAttribute", atts);
-                handler.characters(derAttr.toCharArray(), 0, derAttr.length());
-                handler.endElement("", "", "derAttribute");
-            }
-
-            for (String virAttr : conf.getVirAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "virAttribute", atts);
-                handler.characters(virAttr.toCharArray(), 0, virAttr.length());
-                handler.endElement("", "", "virAttribute");
-            }
-        }
-
-        handler.endElement("", "", "groupAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    private int count() {
-        return StringUtils.isBlank(conf.getMatchingCond())
-                ? groupDAO.count()
-                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP);
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof GroupReportletConf) {
-            this.conf = GroupReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-            List<Group> groups;
-            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
-            } else {
-                groups = searchDAO.search(
-                        SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page,
-                        AnyDAO.DEFAULT_PAGE_SIZE,
-                        Collections.<OrderByClause>emptyList(),
-                        AnyTypeKind.USER);
-            }
-
-            doExtract(handler, groups);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
deleted file mode 100644
index 4922015..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import org.apache.commons.collections4.Closure;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.IterableUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
-import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.AnyType;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
-import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.api.Connector;
-import org.apache.syncope.core.provisioning.api.ConnectorFactory;
-import org.apache.syncope.core.provisioning.api.MappingManager;
-import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.identityconnectors.common.Base64;
-import org.identityconnectors.framework.common.objects.Attribute;
-import org.identityconnectors.framework.common.objects.AttributeBuilder;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.OperationalAttributes;
-import org.identityconnectors.framework.common.objects.Uid;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * Reportlet for extracting information for the current reconciliation status, e.g. the coherence between Syncope
- * information and mapped entities on external resources.
- */
-@ReportletConfClass(ReconciliationReportletConf.class)
-public class ReconciliationReportlet extends AbstractReportlet {
-
-    private static final int PAGE_SIZE = 10;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private AnyTypeDAO anyTypeDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private MappingManager mappingManager;
-
-    @Autowired
-    private ConnectorFactory connFactory;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
-    private ReconciliationReportletConf conf;
-
-    private String getAnyElementName(final AnyTypeKind anyTypeKind) {
-        String elementName;
-
-        switch (anyTypeKind) {
-            case USER:
-                elementName = "user";
-                break;
-
-            case GROUP:
-                elementName = "group";
-                break;
-
-            case ANY_OBJECT:
-            default:
-                elementName = "anyObject";
-        }
-
-        return elementName;
-    }
-
-    private void doExtract(
-            final ContentHandler handler,
-            final Any<?> any,
-            final Set<Missing> missing,
-            final Set<Misaligned> misaligned)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-
-        for (Feature feature : conf.getFeatures()) {
-            String type = null;
-            String value = null;
-            switch (feature) {
-                case key:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getKey();
-                    break;
-
-                case username:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_STRING;
-                        value = User.class.cast(any).getUsername();
-                    }
-                    break;
-
-                case groupName:
-                    if (any instanceof Group) {
-                        type = ReportXMLConst.XSD_STRING;
-                        value = Group.class.cast(any).getName();
-                    }
-                    break;
-
-                case workflowId:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getWorkflowId();
-                    break;
-
-                case status:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getStatus();
-                    break;
-
-                case creationDate:
-                    type = ReportXMLConst.XSD_DATETIME;
-                    value = any.getCreationDate() == null
-                            ? StringUtils.EMPTY
-                            : FormatUtils.format(any.getCreationDate());
-                    break;
-
-                case lastLoginDate:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = User.class.cast(any).getLastLoginDate() == null
-                                ? StringUtils.EMPTY
-                                : FormatUtils.format(User.class.cast(any).getLastLoginDate());
-                    }
-                    break;
-
-                case changePwdDate:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = User.class.cast(any).getChangePwdDate() == null
-                                ? StringUtils.EMPTY
-                                : FormatUtils.format(User.class.cast(any).getChangePwdDate());
-                    }
-                    break;
-
-                case passwordHistorySize:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(User.class.cast(any).getPasswordHistory().size());
-                    }
-                    break;
-
-                case failedLoginCount:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(User.class.cast(any).getFailedLogins());
-                    }
-                    break;
-
-                default:
-            }
-
-            if (type != null && value != null) {
-                atts.addAttribute("", "", feature.name(), type, value);
-            }
-        }
-
-        handler.startElement("", "", getAnyElementName(any.getType().getKind()), atts);
-
-        for (Missing item : missing) {
-            atts.clear();
-            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
-            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
-
-            handler.startElement("", "", "missing", atts);
-            handler.endElement("", "", "missing");
-        }
-        for (Misaligned item : misaligned) {
-            atts.clear();
-            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
-            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
-            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, item.getName());
-
-            handler.startElement("", "", "misaligned", atts);
-
-            handler.startElement("", "", "onSyncope", null);
-            if (item.getOnSyncope() != null) {
-                for (Object value : item.getOnSyncope()) {
-                    char[] asChars = value.toString().toCharArray();
-
-                    handler.startElement("", "", "value", null);
-                    handler.characters(asChars, 0, asChars.length);
-                    handler.endElement("", "", "value");
-                }
-            }
-            handler.endElement("", "", "onSyncope");
-
-            handler.startElement("", "", "onResource", null);
-            if (item.getOnResource() != null) {
-                for (Object value : item.getOnResource()) {
-                    char[] asChars = value.toString().toCharArray();
-
-                    handler.startElement("", "", "value", null);
-                    handler.characters(asChars, 0, asChars.length);
-                    handler.endElement("", "", "value");
-                }
-            }
-            handler.endElement("", "", "onResource");
-
-            handler.endElement("", "", "misaligned");
-        }
-
-        handler.endElement("", "", getAnyElementName(any.getType().getKind()));
-    }
-
-    private Set<Object> getValues(final Attribute attr) {
-        Set<Object> values;
-        if (attr.getValue() == null || attr.getValue().isEmpty()) {
-            values = Collections.emptySet();
-        } else if (attr.getValue().get(0) instanceof byte[]) {
-            values = new HashSet<>(attr.getValue().size());
-            for (Object single : attr.getValue()) {
-                values.add(Base64.encode((byte[]) single));
-            }
-        } else {
-            values = new HashSet<>(attr.getValue());
-        }
-
-        return values;
-    }
-
-    private void doExtract(final ContentHandler handler, final List<? extends Any<?>> anys)
-            throws SAXException, ReportException {
-
-        final Set<Missing> missing = new HashSet<>();
-        final Set<Misaligned> misaligned = new HashSet<>();
-
-        for (Any<?> any : anys) {
-            missing.clear();
-            misaligned.clear();
-
-            AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
-            for (final ExternalResource resource : anyUtils.getAllResources(any)) {
-                Provision provision = resource.getProvision(any.getType());
-                MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
-                final String connObjectKeyValue = connObjectKeyItem == null
-                        ? StringUtils.EMPTY
-                        : mappingManager.getConnObjectKeyValue(any, provision);
-                if (provision != null && connObjectKeyItem != null && StringUtils.isNotBlank(connObjectKeyValue)) {
-                    // 1. read from the underlying connector
-                    Connector connector = connFactory.getConnector(resource);
-                    ConnectorObject connectorObject = connector.getObject(provision.getObjectClass(),
-                            new Uid(connObjectKeyValue),
-                            MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
-
-                    if (connectorObject == null) {
-                        // 2. not found on resource?
-                        LOG.error("Object {} with class {} not found on resource {}",
-                                connObjectKeyValue, provision.getObjectClass(), resource);
-
-                        missing.add(new Missing(resource.getKey(), connObjectKeyValue));
-                    } else {
-                        // 3. found but misaligned?
-                        Pair<String, Set<Attribute>> preparedAttrs =
-                                mappingManager.prepareAttrs(any, null, false, null, provision);
-                        preparedAttrs.getRight().add(AttributeBuilder.build(
-                                Uid.NAME, preparedAttrs.getLeft()));
-                        preparedAttrs.getRight().add(AttributeBuilder.build(
-                                connObjectKeyItem.getExtAttrName(), preparedAttrs.getLeft()));
-
-                        final Map<String, Set<Object>> syncopeAttrs = new HashMap<>();
-                        for (Attribute attr : preparedAttrs.getRight()) {
-                            syncopeAttrs.put(attr.getName(), getValues(attr));
-                        }
-
-                        final Map<String, Set<Object>> resourceAttrs = new HashMap<>();
-                        for (Attribute attr : connectorObject.getAttributes()) {
-                            if (!OperationalAttributes.PASSWORD_NAME.equals(attr.getName())
-                                    && !OperationalAttributes.ENABLE_NAME.equals(attr.getName())) {
-
-                                resourceAttrs.put(attr.getName(), getValues(attr));
-                            }
-                        }
-
-                        IterableUtils.forEach(CollectionUtils.subtract(syncopeAttrs.keySet(), resourceAttrs.keySet()),
-                                new Closure<String>() {
-
-                            @Override
-                            public void execute(final String name) {
-                                misaligned.add(new Misaligned(
-                                        resource.getKey(),
-                                        connObjectKeyValue,
-                                        name,
-                                        syncopeAttrs.get(name),
-                                        Collections.emptySet()));
-                            }
-                        });
-
-                        for (Map.Entry<String, Set<Object>> entry : resourceAttrs.entrySet()) {
-                            if (syncopeAttrs.containsKey(entry.getKey())) {
-                                if (!Objects.equals(syncopeAttrs.get(entry.getKey()), entry.getValue())) {
-                                    misaligned.add(new Misaligned(
-                                            resource.getKey(),
-                                            connObjectKeyValue,
-                                            entry.getKey(),
-                                            syncopeAttrs.get(entry.getKey()),
-                                            entry.getValue()));
-                                }
-                            } else {
-                                misaligned.add(new Misaligned(
-                                        resource.getKey(),
-                                        connObjectKeyValue,
-                                        entry.getKey(),
-                                        Collections.emptySet(),
-                                        entry.getValue()));
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (!missing.isEmpty() || !misaligned.isEmpty()) {
-                doExtract(handler, any, missing, misaligned);
-            }
-        }
-    }
-
-    private void doExtract(
-            final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
-            throws SAXException {
-
-        for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
-            List<AnyObject> anys = searchDAO.search(
-                    SyncopeConstants.FULL_ADMIN_REALMS,
-                    cond,
-                    page,
-                    PAGE_SIZE,
-                    Collections.<OrderByClause>emptyList(),
-                    anyTypeKind);
-
-            doExtract(handler, anys);
-        }
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof ReconciliationReportletConf) {
-            this.conf = ReconciliationReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-
-        if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
-
-            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
-            }
-        } else {
-            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
-
-            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
-
-            doExtract(handler, count, cond, AnyTypeKind.USER);
-        }
-        handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
-
-        atts.clear();
-        if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
-
-            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
-            }
-        } else {
-            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
-
-            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
-
-            doExtract(handler, count, cond, AnyTypeKind.GROUP);
-        }
-        handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
-
-        for (AnyType anyType : anyTypeDAO.findAll()) {
-            if (!anyType.equals(anyTypeDAO.findUser()) && !anyType.equals(anyTypeDAO.findGroup())) {
-                AnyTypeCond anyTypeCond = new AnyTypeCond();
-                anyTypeCond.setAnyTypeKey(anyType.getKey());
-                SearchCond cond = StringUtils.isBlank(this.conf.getAnyObjectMatchingCond())
-                        ? SearchCond.getLeafCond(anyTypeCond)
-                        : SearchCond.getAndCond(
-                                SearchCond.getLeafCond(anyTypeCond),
-                                SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
-
-                int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
-
-                atts.clear();
-                atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
-                atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-                handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
-
-                doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
-
-                handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
-            }
-        }
-    }
-
-    private static class Missing {
-
-        private final String resource;
-
-        private final String connObjectKeyValue;
-
-        Missing(final String resource, final String connObjectKeyValue) {
-            this.resource = resource;
-            this.connObjectKeyValue = connObjectKeyValue;
-        }
-
-        public String getResource() {
-            return resource;
-        }
-
-        public String getConnObjectKeyValue() {
-            return connObjectKeyValue;
-        }
-
-    }
-
-    private static class Misaligned extends Missing {
-
-        private final String name;
-
-        private final Set<Object> onSyncope;
-
-        private final Set<Object> onResource;
-
-        Misaligned(
-                final String resource,
-                final String connObjectKeyValue,
-                final String name,
-                final Set<Object> onSyncope,
-                final Set<Object> onResource) {
-
-            super(resource, connObjectKeyValue);
-
-            this.name = name;
-            this.onSyncope = onSyncope;
-            this.onResource = onResource;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public Set<Object> getOnSyncope() {
-            return onSyncope;
-        }
-
-        public Set<Object> getOnResource() {
-            return onResource;
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
deleted file mode 100644
index f4495ad..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-public class ReportException extends RuntimeException {
-
-    private static final long serialVersionUID = 6719507778589395283L;
-
-    public ReportException(final Throwable cause) {
-        super(cause);
-    }
-
-    public ReportException(final String message, final Throwable cause) {
-        super(message, cause);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
deleted file mode 100644
index 8b14024..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.apache.syncope.core.provisioning.api.job.JobManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Quartz job for executing a given report.
- */
-public class ReportJob extends AbstractInterruptableJob {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
-
-    /**
-     * Key, set by the caller, for identifying the report to be executed.
-     */
-    private String reportKey;
-
-    @Autowired
-    private ReportJobDelegate delegate;
-
-    /**
-     * Report id setter.
-     *
-     * @param reportKey to be set
-     */
-    public void setReportKey(final String reportKey) {
-        this.reportKey = reportKey;
-    }
-
-    @Override
-    public void execute(final JobExecutionContext context) throws JobExecutionException {
-        super.execute(context);
-
-        try {
-            AuthContextUtils.execWithAuthContext(context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY),
-                    new AuthContextUtils.Executable<Void>() {
-
-                @Override
-                public Void exec() {
-                    try {
-                        delegate.execute(reportKey);
-                    } catch (Exception e) {
-                        LOG.error("While executing report {}", reportKey, e);
-                        throw new RuntimeException(e);
-                    }
-
-                    return null;
-                }
-            });
-        } catch (RuntimeException e) {
-            LOG.error("While executing report {}", reportKey, e);
-            throw new JobExecutionException("While executing report " + reportKey, e);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
deleted file mode 100644
index 6d41686..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.zip.Deflater;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import org.apache.commons.io.IOUtils;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.persistence.api.ImplementationLookup;
-import org.apache.syncope.core.persistence.api.dao.ReportDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.Report;
-import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.helpers.AttributesImpl;
-
-@Component
-public class ReportJobDelegate {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
-
-    /**
-     * Report DAO.
-     */
-    @Autowired
-    private ReportDAO reportDAO;
-
-    /**
-     * Report execution DAO.
-     */
-    @Autowired
-    private ReportExecDAO reportExecDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private ImplementationLookup implementationLookup;
-
-    @Transactional
-    public void execute(final String reportKey) throws JobExecutionException {
-        Report report = reportDAO.find(reportKey);
-        if (report == null) {
-            throw new JobExecutionException("Report " + reportKey + " not found");
-        }
-
-        if (!report.isActive()) {
-            LOG.info("Report {} not active, aborting...", reportKey);
-            return;
-        }
-
-        // 1. create execution
-        ReportExec execution = entityFactory.newEntity(ReportExec.class);
-        execution.setStatus(ReportExecStatus.STARTED);
-        execution.setStart(new Date());
-        execution.setReport(report);
-        execution = reportExecDAO.save(execution);
-
-        report.add(execution);
-        report = reportDAO.save(report);
-
-        // 2. define a SAX handler for generating result as XML
-        TransformerHandler handler;
-
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ZipOutputStream zos = new ZipOutputStream(baos);
-        zos.setLevel(Deflater.BEST_COMPRESSION);
-        try {
-            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
-            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
-            handler = tFactory.newTransformerHandler();
-            Transformer serializer = handler.getTransformer();
-            serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
-            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
-
-            // a single ZipEntry in the ZipOutputStream
-            zos.putNextEntry(new ZipEntry(report.getName()));
-
-            // streaming SAX handler in a compressed byte array stream
-            handler.setResult(new StreamResult(zos));
-        } catch (Exception e) {
-            throw new JobExecutionException("While configuring for SAX generation", e, true);
-        }
-
-        execution.setStatus(ReportExecStatus.RUNNING);
-        execution = reportExecDAO.save(execution);
-
-        // 3. actual report execution
-        StringBuilder reportExecutionMessage = new StringBuilder();
-        try {
-            // report header
-            handler.startDocument();
-            AttributesImpl atts = new AttributesImpl();
-            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
-            handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
-
-            // iterate over reportlet instances defined for this report
-            for (ReportletConf reportletConf : report.getReportletConfs()) {
-                Class<? extends Reportlet> reportletClass =
-                        implementationLookup.getReportletClass(reportletConf.getClass());
-                if (reportletClass == null) {
-                    LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
-                } else {
-                    // fetch (or create) reportlet
-                    Reportlet reportlet;
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(reportletClass.getName());
-                    } else {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(reportletClass.getName(), reportlet);
-                    }
-
-                    // invoke reportlet
-                    try {
-                        reportlet.extract(reportletConf, handler);
-                    } catch (Throwable t) {
-                        LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
-
-                        execution.setStatus(ReportExecStatus.FAILURE);
-
-                        Throwable effective = t instanceof ReportException
-                                ? t.getCause()
-                                : t;
-                        reportExecutionMessage.
-                                append(ExceptionUtils2.getFullStackTrace(effective)).
-                                append("\n==================\n");
-                    }
-                }
-            }
-
-            // report footer
-            handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
-            handler.endDocument();
-
-            if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
-                execution.setStatus(ReportExecStatus.SUCCESS);
-            }
-        } catch (Exception e) {
-            execution.setStatus(ReportExecStatus.FAILURE);
-            reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
-
-            throw new JobExecutionException(e, true);
-        } finally {
-            try {
-                zos.closeEntry();
-                IOUtils.closeQuietly(zos);
-                IOUtils.closeQuietly(baos);
-            } catch (IOException e) {
-                LOG.error("While closing StreamResult's backend", e);
-            }
-
-            execution.setExecResult(baos.toByteArray());
-            execution.setMessage(reportExecutionMessage.toString());
-            execution.setEnd(new Date());
-            reportExecDAO.save(execution);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
deleted file mode 100644
index 1bbae73..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-public final class ReportXMLConst {
-
-    public static final String XSD_STRING = "xsd:string";
-
-    public static final String XSD_INT = "xsd:integer";
-
-    public static final String XSD_LONG = "xsd:long";
-
-    public static final String XSD_BOOLEAN = "xsd:boolean";
-
-    public static final String XSD_DATETIME = "xsd:dateTime";
-
-    public static final String ELEMENT_REPORT = "report";
-
-    public static final String ATTR_NAME = "name";
-
-    public static final String ATTR_CLASS = "class";
-
-    public static final String ELEMENT_REPORTLET = "reportlet";
-
-    private ReportXMLConst() {
-        // empty private constructor for static utility class
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
deleted file mode 100644
index 404d086..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.report.StaticReportletConf;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.springframework.util.StringUtils;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(StaticReportletConf.class)
-public class StaticReportlet extends AbstractReportlet {
-
-    private StaticReportletConf conf;
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "staticAttributes", atts);
-
-        handler.startElement("", "", "string", atts);
-        handler.characters("string".toCharArray(), 0, "string".length());
-        handler.endElement("", "", "string");
-
-        handler.startElement("", "", "long", atts);
-        handler.characters("long".toCharArray(), 0, "long".length());
-        handler.endElement("", "", "long");
-
-        handler.startElement("", "", "double", atts);
-        handler.characters("double".toCharArray(), 0, "double".length());
-        handler.endElement("", "", "double");
-
-        handler.startElement("", "", "date", atts);
-        handler.characters("date".toCharArray(), 0, "date".length());
-        handler.endElement("", "", "date");
-
-        handler.startElement("", "", "double", atts);
-        handler.characters("double".toCharArray(), 0, "double".length());
-        handler.endElement("", "", "double");
-
-        handler.startElement("", "", "enum", atts);
-        handler.characters("enum".toCharArray(), 0, "enum".length());
-        handler.endElement("", "", "enum");
-
-        handler.startElement("", "", "list", atts);
-        handler.characters("list".toCharArray(), 0, "list".length());
-        handler.endElement("", "", "list");
-
-        handler.endElement("", "", "staticAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof StaticReportletConf) {
-            this.conf = StaticReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        if (StringUtils.hasText(this.conf.getStringField())) {
-            handler.startElement("", "", "string", null);
-            handler.characters(this.conf.getStringField().toCharArray(), 0, this.conf.getStringField().length());
-            handler.endElement("", "", "string");
-        }
-
-        if (this.conf.getLongField() != null) {
-            handler.startElement("", "", "long", null);
-            String printed = String.valueOf(this.conf.getLongField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "long");
-        }
-
-        if (this.conf.getDoubleField() != null) {
-            handler.startElement("", "", "double", null);
-            String printed = String.valueOf(this.conf.getDoubleField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "double");
-        }
-
-        if (this.conf.getDateField() != null) {
-            handler.startElement("", "", "date", null);
-            String printed = FormatUtils.format(this.conf.getDateField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "date");
-        }
-
-        if (this.conf.getTraceLevel() != null) {
-            handler.startElement("", "", "enum", null);
-            String printed = this.conf.getTraceLevel().name();
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "enum");
-        }
-
-        if (this.conf.getListField() != null && !this.conf.getListField().isEmpty()) {
-            handler.startElement("", "", "list", null);
-            for (String item : this.conf.getListField()) {
-                if (StringUtils.hasText(item)) {
-                    handler.startElement("", "", "string", null);
-                    handler.characters(item.toCharArray(), 0, item.length());
-                    handler.endElement("", "", "string");
-                }
-            }
-            handler.endElement("", "", "list");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
deleted file mode 100644
index f1f3a9e..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.cocoon.sax.component.XMLSerializer;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-/**
- * Converts XML into plain text. It omits all XML tags and writes only character events to the output. Input document
- * must have at least one element - root element - which should wrap all the text inside it.
- *
- */
-public class TextSerializer extends XMLSerializer {
-
-    private static final String UTF_8 = "UTF-8";
-
-    private static final String TXT = "text";
-
-    public TextSerializer() {
-        super();
-        super.setOmitXmlDeclaration(true);
-    }
-
-    @Override
-    public void setDocumentLocator(final Locator locator) {
-        // nothing
-    }
-
-    @Override
-    public void processingInstruction(final String target, final String data)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void startDTD(final String name, final String publicId, final String systemId)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void endDTD() throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void startElement(final String uri, final String loc, final String raw, final Attributes atts)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void endElement(final String uri, final String name, final String raw)
-            throws SAXException {
-        // nothing
-    }
-
-    public static TextSerializer createPlainSerializer() {
-        final TextSerializer serializer = new TextSerializer();
-        serializer.setContentType("text/plain; charset=" + UTF_8);
-        serializer.setEncoding(UTF_8);
-        serializer.setMethod(TXT);
-        return serializer;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
deleted file mode 100644
index bb1048b..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.report.UserReportletConf;
-import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.MembershipTO;
-import org.apache.syncope.common.lib.to.RelationshipTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.persistence.api.entity.user.URelationship;
-import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
-import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(UserReportletConf.class)
-public class UserReportlet extends AbstractReportlet {
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private UserDataBinder userDataBinder;
-
-    @Autowired
-    private GroupDataBinder groupDataBinder;
-
-    @Autowired
-    private AnyObjectDataBinder anyObjectDataBinder;
-
-    private UserReportletConf conf;
-
-    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
-            throws SAXException {
-
-        if (anyTO.getResources().isEmpty()) {
-            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
-        } else {
-            AttributesImpl atts = new AttributesImpl();
-            handler.startElement("", "", "resources", null);
-
-            for (String resourceName : anyTO.getResources()) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
-                handler.startElement("", "", "resource", atts);
-                handler.endElement("", "", "resource");
-            }
-
-            handler.endElement("", "", "resources");
-        }
-    }
-
-    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
-            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-        if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
-
-            handler.startElement("", "", "attributes", null);
-            for (String attrName : attrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "attribute", atts);
-
-                if (attrMap.containsKey(attrName)) {
-                    for (String value : attrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "attribute");
-            }
-            handler.endElement("", "", "attributes");
-        }
-
-        if (!derAttrs.isEmpty()) {
-            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
-
-            handler.startElement("", "", "derivedAttributes", null);
-            for (String attrName : derAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "derivedAttribute", atts);
-
-                if (derAttrMap.containsKey(attrName)) {
-                    for (String value : derAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "derivedAttribute");
-            }
-            handler.endElement("", "", "derivedAttributes");
-        }
-
-        if (!virAttrs.isEmpty()) {
-            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
-
-            handler.startElement("", "", "virtualAttributes", null);
-            for (String attrName : virAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "virtualAttribute", atts);
-
-                if (virAttrMap.containsKey(attrName)) {
-                    for (String value : virAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "virtualAttribute");
-            }
-            handler.endElement("", "", "virtualAttributes");
-        }
-    }
-
-    private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        for (User user : users) {
-            atts.clear();
-
-            for (Feature feature : conf.getFeatures()) {
-                String type = null;
-                String value = null;
-                switch (feature) {
-                    case key:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getKey();
-                        break;
-
-                    case username:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getUsername();
-                        break;
-
-                    case workflowId:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getWorkflowId();
-                        break;
-
-                    case status:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getStatus();
-                        break;
-
-                    case creationDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getCreationDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getCreationDate());
-                        break;
-
-                    case lastLoginDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getLastLoginDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getLastLoginDate());
-                        break;
-
-                    case changePwdDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getChangePwdDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getChangePwdDate());
-                        break;
-
-                    case passwordHistorySize:
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(user.getPasswordHistory().size());
-                        break;
-
-                    case failedLoginCount:
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(user.getFailedLogins());
-                        break;
-
-                    default:
-                }
-
-                if (type != null && value != null) {
-                    atts.addAttribute("", "", feature.name(), type, value);
-                }
-            }
-
-            handler.startElement("", "", "user", atts);
-
-            // Using UserTO for attribute values, since the conversion logic of
-            // values to String is already encapsulated there
-            UserTO userTO = userDataBinder.getUserTO(user, true);
-
-            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
-
-            if (conf.getFeatures().contains(Feature.relationships)) {
-                handler.startElement("", "", "relationships", null);
-
-                for (RelationshipTO rel : userTO.getRelationships()) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "anyObjectKey",
-                            ReportXMLConst.XSD_STRING, rel.getRightKey());
-                    handler.startElement("", "", "relationship", atts);
-
-                    if (conf.getFeatures().contains(Feature.resources)) {
-                        for (URelationship actualRel : user.getRelationships(rel.getRightKey())) {
-                            doExtractResources(
-                                    handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true));
-                        }
-                    }
-
-                    handler.endElement("", "", "relationship");
-                }
-
-                handler.endElement("", "", "relationships");
-            }
-            if (conf.getFeatures().contains(Feature.memberships)) {
-                handler.startElement("", "", "memberships", null);
-
-                for (MembershipTO memb : userTO.getMemberships()) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "groupKey",
-                            ReportXMLConst.XSD_STRING, memb.getRightKey());
-                    atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, memb.getGroupName());
-                    handler.startElement("", "", "membership", atts);
-
-                    if (conf.getFeatures().contains(Feature.resources)) {
-                        UMembership actualMemb = user.getMembership(memb.getRightKey());
-                        if (actualMemb == null) {
-                            LOG.warn("Unexpected: cannot find membership for group {} for user {}",
-                                    memb.getRightKey(), user);
-                        } else {
-                            doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true));
-                        }
-                    }
-
-                    handler.endElement("", "", "membership");
-                }
-
-                handler.endElement("", "", "memberships");
-            }
-
-            if (conf.getFeatures().contains(Feature.resources)) {
-                doExtractResources(handler, userTO);
-            }
-
-            handler.endElement("", "", "user");
-        }
-    }
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "userAttributes", atts);
-
-        for (Feature feature : conf.getFeatures()) {
-            atts.clear();
-            handler.startElement("", "", "feature", atts);
-            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
-            handler.endElement("", "", "feature");
-        }
-
-        for (String attr : conf.getPlainAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "attribute", atts);
-            handler.characters(attr.toCharArray(), 0, attr.length());
-            handler.endElement("", "", "attribute");
-        }
-
-        for (String derAttr : conf.getDerAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "derAttribute", atts);
-            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
-            handler.endElement("", "", "derAttribute");
-        }
-
-        for (String virAttr : conf.getVirAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "virAttribute", atts);
-            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
-            handler.endElement("", "", "virAttribute");
-        }
-
-        handler.endElement("", "", "userAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    private int count() {
-        return StringUtils.isBlank(conf.getMatchingCond())
-                ? userDAO.count()
-                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER);
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof UserReportletConf) {
-            this.conf = UserReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-            List<User> users;
-            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
-            } else {
-                users = searchDAO.search(
-                        SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page,
-                        AnyDAO.DEFAULT_PAGE_SIZE,
-                        Collections.<OrderByClause>emptyList(),
-                        AnyTypeKind.USER);
-            }
-
-            doExtract(handler, users);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
deleted file mode 100644
index 3dd6d78..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-import javax.xml.transform.Source;
-import javax.xml.transform.Templates;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import org.apache.cocoon.pipeline.SetupException;
-import org.apache.cocoon.pipeline.caching.CacheKey;
-import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
-import org.apache.cocoon.pipeline.util.StringRepresentation;
-import org.apache.cocoon.sax.AbstractSAXTransformer;
-import org.apache.cocoon.sax.SAXConsumer;
-import org.apache.cocoon.sax.util.SAXConsumerAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
-
-    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
-
-    /**
-     * A generic transformer factory to parse XSLTs.
-     */
-    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
-
-    /**
-     * The XSLT parameters name pattern.
-     */
-    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
-
-    /**
-     * The XSLT parameters reference.
-     */
-    private Map<String, Object> parameters;
-
-    /**
-     * The XSLT Template reference.
-     */
-    private Templates templates;
-
-    private Source source;
-
-    public XSLTTransformer(final Source source) {
-        super();
-        this.load(source, null);
-    }
-
-    /**
-     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
-     *
-     * This constructor is useful when users want to perform XSLT transformation using <a
-     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
-     *
-     * @param source the XSLT source
-     * @param attributes the Transformer Factory attributes
-     */
-    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
-        super();
-        this.load(source, attributes);
-    }
-
-    /**
-     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
-     * Factory attributes.
-     *
-     * This method is useful when users want to perform XSLT transformation using <a
-     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
-     *
-     * @param source the XSLT source
-     * @param attributes the Transformer Factory attributes
-     */
-    private void load(final Source source, final Map<String, Object> attributes) {
-        if (source == null) {
-            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
-        }
-
-        this.source = source;
-
-        this.load(this.source, this.source.toString(), attributes);
-    }
-
-    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
-        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
-
-        // XSLT has to be parsed
-        final SAXTransformerFactory transformerFactory;
-        if (attributes == null || attributes.isEmpty()) {
-            transformerFactory = TRAX_FACTORY;
-        } else {
-            transformerFactory = createNewSAXTransformerFactory();
-            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
-                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
-            }
-        }
-
-        try {
-            this.templates = transformerFactory.newTemplates(source);
-        } catch (TransformerConfigurationException e) {
-            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
-        }
-    }
-
-    /**
-     * Sets the XSLT parameters to be applied to XSLT stylesheet.
-     *
-     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
-     */
-    public void setParameters(final Map<String, ? extends Object> parameters) {
-        if (parameters == null) {
-            this.parameters = null;
-        } else {
-            this.parameters = new HashMap<String, Object>(parameters);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setSAXConsumer(final SAXConsumer consumer) {
-        TransformerHandler transformerHandler;
-        try {
-            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
-        } catch (Exception e) {
-            throw new SetupException("Could not initialize transformer handler.", e);
-        }
-
-        if (this.parameters != null) {
-            final Transformer transformer = transformerHandler.getTransformer();
-
-            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
-                final String name = entry.getKey();
-
-                // is valid XSLT parameter name
-                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
-                    transformer.setParameter(name, entry.getValue());
-                }
-            }
-        }
-
-        final SAXResult result = new SAXResult();
-        result.setHandler(consumer);
-        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
-        result.setLexicalHandler(consumer);
-        transformerHandler.setResult(result);
-
-        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
-        saxConsumerAdapter.setContentHandler(transformerHandler);
-        super.setSAXConsumer(saxConsumerAdapter);
-    }
-
-    @Override
-    public CacheKey constructCacheKey() {
-        return null;
-    }
-
-    /**
-     * Utility method to create a new transformer factory.
-     *
-     * @return a new transformer factory
-     */
-    private static SAXTransformerFactory createNewSAXTransformerFactory() {
-        return (SAXTransformerFactory) TransformerFactory.newInstance();
-    }
-
-    @Override
-    public String toString() {
-        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
-    }
-}


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

Posted by il...@apache.org.
[SYNCOPE-1103] Option to put Quartz scheduler in standby, from provisioning.properties


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

Branch: refs/heads/master
Commit: bd695da79888cab82c8cf4803cc01cecba804e41
Parents: 95c933a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Jun 6 15:01:54 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Jun 6 15:02:37 2017 +0200

----------------------------------------------------------------------
 core/logic/pom.xml                              |   4 -
 .../syncope/core/logic/AnyObjectLogic.java      |   1 +
 .../apache/syncope/core/logic/GroupLogic.java   |   4 +
 .../core/logic/LogicInvocationHandler.java      |  37 +-
 .../syncope/core/logic/NotificationLogic.java   |   2 +-
 .../apache/syncope/core/logic/ReportLogic.java  |  20 +-
 .../apache/syncope/core/logic/SyncopeLogic.java |   7 +-
 .../core/logic/SystemLoadReporterJob.java       |  57 --
 .../apache/syncope/core/logic/TaskLogic.java    |   2 +-
 .../apache/syncope/core/logic/UserLogic.java    |   2 +
 .../core/logic/cocoon/FopSerializer.java        |  82 +++
 .../core/logic/cocoon/TextSerializer.java       |  83 +++
 .../core/logic/cocoon/XSLTTransformer.java      | 193 +++++++
 .../syncope/core/logic/init/JobManagerImpl.java | 379 --------------
 .../logic/notification/NotificationJob.java     |  86 ---
 .../notification/NotificationJobDelegate.java   | 259 ---------
 .../core/logic/report/AbstractReportlet.java    |  52 --
 .../core/logic/report/AuditReportlet.java       | 141 -----
 .../core/logic/report/FopSerializer.java        |  82 ---
 .../core/logic/report/GroupReportlet.java       | 316 -----------
 .../logic/report/ReconciliationReportlet.java   | 519 -------------------
 .../core/logic/report/ReportException.java      |  32 --
 .../syncope/core/logic/report/ReportJob.java    |  80 ---
 .../core/logic/report/ReportJobDelegate.java    | 198 -------
 .../core/logic/report/ReportXMLConst.java       |  44 --
 .../core/logic/report/StaticReportlet.java      | 128 -----
 .../core/logic/report/TextSerializer.java       |  83 ---
 .../core/logic/report/UserReportlet.java        | 383 --------------
 .../core/logic/report/XSLTTransformer.java      | 193 -------
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |   9 +-
 .../core/persistence/jpa/dao/JPAGroupDAO.java   |  25 +-
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   7 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    |   9 +-
 .../test/resources/domains/MasterContent.xml    |  20 +-
 core/provisioning-api/pom.xml                   |   5 +
 .../core/provisioning/api/AuditManager.java     |  10 +-
 .../api/event/AfterHandlingEvent.java           | 115 ++++
 .../api/event/AnyCreatedUpdatedEvent.java       |  46 ++
 .../provisioning/api/event/AnyDeletedEvent.java |  57 ++
 .../api/notification/NotificationManager.java   |   8 +
 core/provisioning-java/pom.xml                  |   6 +-
 .../provisioning/java/AuditManagerImpl.java     |  20 +
 .../provisioning/java/ConnectorFacadeProxy.java |   5 +
 .../provisioning/java/job/JobManagerImpl.java   | 393 ++++++++++++++
 .../java/job/SystemLoadReporterJob.java         |  57 ++
 .../java/job/notification/NotificationJob.java  |  86 +++
 .../notification/NotificationJobDelegate.java   | 268 ++++++++++
 .../java/job/report/AbstractReportlet.java      |  52 ++
 .../java/job/report/AuditReportlet.java         | 141 +++++
 .../java/job/report/GroupReportlet.java         | 316 +++++++++++
 .../job/report/ReconciliationReportlet.java     | 519 +++++++++++++++++++
 .../java/job/report/ReportException.java        |  32 ++
 .../provisioning/java/job/report/ReportJob.java |  80 +++
 .../java/job/report/ReportJobDelegate.java      | 198 +++++++
 .../java/job/report/ReportXMLConst.java         |  44 ++
 .../java/job/report/StaticReportlet.java        | 128 +++++
 .../java/job/report/UserReportlet.java          | 383 ++++++++++++++
 .../notification/NotificationManagerImpl.java   |  20 +
 .../pushpull/AbstractPullResultHandler.java     |  20 +-
 .../pushpull/AbstractPushResultHandler.java     |  20 +-
 .../pushpull/AbstractRealmResultHandler.java    |   2 +-
 .../pushpull/AbstractSyncopeResultHandler.java  |  33 --
 .../AnyObjectPullResultHandlerImpl.java         |   5 +
 .../pushpull/GroupPullResultHandlerImpl.java    |   7 +-
 .../pushpull/RealmPushResultHandlerImpl.java    |  21 +-
 .../pushpull/UserPullResultHandlerImpl.java     |   5 +
 .../src/main/resources/provisioning.properties  |   1 +
 .../src/main/resources/provisioningContext.xml  |   3 +
 .../spring/event/AnyCreatedUpdatedEvent.java    |  47 --
 .../core/spring/event/AnyDeletedEvent.java      |  53 --
 .../src/main/resources/provisioning.properties  |   1 +
 .../client/ElasticsearchIndexManager.java       |   4 +-
 .../core/reference/ITImplementationLookup.java  |  10 +-
 .../main/resources/all/provisioning.properties  |   1 +
 .../resources/mariadb/provisioning.properties   |   1 +
 .../resources/mysql/provisioning.properties     |   1 +
 .../resources/oracle/provisioning.properties    |   1 +
 .../resources/postgres/provisioning.properties  |   1 +
 .../src/main/resources/provisioning.properties  |   1 +
 .../resources/sqlserver/provisioning.properties |   1 +
 .../syncope/fit/core/AbstractTaskITCase.java    |   2 +-
 .../syncope/fit/core/ConnectorITCase.java       |   2 -
 .../fit/core/NotificationTaskITCase.java        |   2 +-
 .../systemadministration/highavailability.adoc  |  26 +
 84 files changed, 3523 insertions(+), 3276 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/pom.xml
----------------------------------------------------------------------
diff --git a/core/logic/pom.xml b/core/logic/pom.xml
index 9630c08..640e1fa 100644
--- a/core/logic/pom.xml
+++ b/core/logic/pom.xml
@@ -51,10 +51,6 @@ 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.aspectj</groupId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index c4ee237..b4779df 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -136,6 +136,7 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
                 effectiveRealms, searchCond, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
         return CollectionUtils.collect(matchingAnyObjects, new Transformer<AnyObject, AnyObjectTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public AnyObjectTO transform(final AnyObject input) {
                 return binder.getAnyObjectTO(input, details);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index c59907c..b93142d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -155,6 +155,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 userDAO.findAllGroups(userDAO.findByUsername(AuthContextUtils.getUsername())),
                 new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, true);
@@ -193,6 +194,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 page, size, orderBy),
                 new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, details);
@@ -220,6 +222,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 searchCondition, page, size, orderBy, AnyTypeKind.GROUP);
         return CollectionUtils.collect(matchingGroups, new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, details);
@@ -281,6 +284,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
             sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
 
+                @Transactional(readOnly = true)
                 @Override
                 public String transform(final Group group) {
                     return group.getKey() + " " + group.getName();

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java b/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
index 87eda20..2d12682 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
@@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -31,6 +32,7 @@ import org.aspectj.lang.reflect.MethodSignature;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 
 @Aspect
 public class LogicInvocationHandler {
@@ -43,6 +45,9 @@ public class LogicInvocationHandler {
     @Autowired
     private AuditManager auditManager;
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     @Around("execution(* org.apache.syncope.core.logic.AbstractLogic+.*(..))")
     public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
         Class<?> clazz = joinPoint.getTarget().getClass();
@@ -89,27 +94,17 @@ public class LogicInvocationHandler {
             LOG.debug("After throwing {}.{}", clazz.getSimpleName(), event);
             throw t;
         } finally {
-            if (notificationsAvailable) {
-                notificationManager.createTasks(AuditElements.EventCategoryType.LOGIC,
-                        category,
-                        null,
-                        event,
-                        condition,
-                        before,
-                        output,
-                        input);
-            }
-
-            if (auditRequested) {
-                auditManager.audit(AuditElements.EventCategoryType.LOGIC,
-                        category,
-                        null,
-                        event,
-                        condition,
-                        before,
-                        output,
-                        input);
-            }
+            publisher.publishEvent(new AfterHandlingEvent(this,
+                    notificationsAvailable,
+                    auditRequested,
+                    AuditElements.EventCategoryType.LOGIC,
+                    category,
+                    null,
+                    event,
+                    condition,
+                    before,
+                    output,
+                    input));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
index aa54c0d..3b867b4 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
@@ -30,12 +30,12 @@ import org.apache.syncope.common.lib.to.NotificationTO;
 import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
-import org.apache.syncope.core.logic.notification.NotificationJob;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 5a27343..1f232ad 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -40,29 +40,29 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.logic.cocoon.FopSerializer;
+import org.apache.syncope.core.logic.cocoon.TextSerializer;
+import org.apache.syncope.core.logic.cocoon.XSLTTransformer;
+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.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobNamer;
-import org.apache.syncope.core.logic.report.TextSerializer;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.JobTO;
-import org.apache.syncope.common.lib.types.JobAction;
-import org.apache.syncope.common.lib.types.JobType;
-import org.apache.syncope.common.lib.types.StandardEntitlement;
-import org.apache.syncope.core.logic.report.FopSerializer;
-import org.apache.syncope.core.logic.report.XSLTTransformer;
-import org.apache.syncope.core.persistence.api.dao.ConfDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.xmlgraphics.util.MimeConstants;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 2c01e66..82ca4b9 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -62,6 +62,8 @@ import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.PayloadApplicationEvent;
+import org.springframework.context.event.EventListener;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -234,10 +236,11 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
         }
     }
 
-    public void addLoadInstant(final SystemInfo.LoadInstant instant) {
+    @EventListener
+    public void addLoadInstant(final PayloadApplicationEvent<SystemInfo.LoadInstant> event) {
         synchronized (MONITOR) {
             initSystemInfo();
-            SYSTEM_INFO.getLoad().add(instant);
+            SYSTEM_INFO.getLoad().add(event.getPayload());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
deleted file mode 100644
index bb419bc..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic;
-
-import java.lang.management.ManagementFactory;
-import org.apache.syncope.common.lib.info.SystemInfo;
-import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * Reports about system load.
- */
-@Component
-public class SystemLoadReporterJob extends AbstractInterruptableJob {
-
-    private static final Integer MB = 1024 * 1024;
-
-    @Autowired
-    private SyncopeLogic logic;
-
-    @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);
-
-        logic.addLoadInstant(instant);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 21cb4ee..a310e47 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -53,11 +53,11 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobNamer;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
-import org.apache.syncope.core.logic.notification.NotificationJobDelegate;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJobDelegate;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 1672cfd..7cc5414 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -118,6 +118,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
                 page, size, orderBy),
                 new Transformer<User, UserTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public UserTO transform(final User input) {
                 return binder.returnUserTO(binder.getUserTO(input, details));
@@ -160,6 +161,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
                 searchCondition, page, size, orderBy, AnyTypeKind.USER);
         return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public UserTO transform(final User input) {
                 return binder.returnUserTO(binder.getUserTO(input, details));

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
new file mode 100644
index 0000000..bacec1f
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import java.io.File;
+import java.io.OutputStream;
+
+import org.apache.cocoon.pipeline.ProcessingException;
+import org.apache.cocoon.pipeline.caching.CacheKey;
+import org.apache.cocoon.pipeline.caching.SimpleCacheKey;
+import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
+import org.apache.cocoon.pipeline.util.StringRepresentation;
+import org.apache.cocoon.sax.AbstractSAXSerializer;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FopFactoryBuilder;
+import org.apache.xmlgraphics.util.MimeConstants;
+import org.xml.sax.ContentHandler;
+
+public class FopSerializer extends AbstractSAXSerializer implements CachingPipelineComponent {
+
+    private static final FopFactory FOP_FACTORY = new FopFactoryBuilder(new File(".").toURI()).build();
+
+    private String outputFormat;
+
+    /**
+     * Create a new FOP serializer that produces a PDF in output
+     */
+    public FopSerializer() {
+        this(MimeConstants.MIME_PDF);
+    }
+
+    /**
+     * Create a new FOP serializer that produces the specified mime
+     *
+     * @param outputFormat the output's mime type
+     */
+    public FopSerializer(final String outputFormat) {
+        if (outputFormat == null) {
+            throw new IllegalArgumentException("The parameter 'outputFormat' mustn't be null.");
+        }
+
+        this.outputFormat = outputFormat;
+    }
+
+    @Override
+    public CacheKey constructCacheKey() {
+        return new SimpleCacheKey();
+    }
+
+    @Override
+    public void setOutputStream(final OutputStream outputStream) {
+        try {
+            Fop fop = FOP_FACTORY.newFop(this.outputFormat, outputStream);
+            ContentHandler fopContentHandler = fop.getDefaultHandler();
+
+            this.setContentHandler(fopContentHandler);
+        } catch (FOPException e) {
+            throw new ProcessingException("Impossible to initialize FOPSerializer", e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return StringRepresentation.buildString(this, "outputFormat=" + this.outputFormat);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
new file mode 100644
index 0000000..ce4ffb4
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import org.apache.cocoon.sax.component.XMLSerializer;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * Converts XML into plain text. It omits all XML tags and writes only character events to the output. Input document
+ * must have at least one element - root element - which should wrap all the text inside it.
+ *
+ */
+public class TextSerializer extends XMLSerializer {
+
+    private static final String UTF_8 = "UTF-8";
+
+    private static final String TXT = "text";
+
+    public TextSerializer() {
+        super();
+        super.setOmitXmlDeclaration(true);
+    }
+
+    @Override
+    public void setDocumentLocator(final Locator locator) {
+        // nothing
+    }
+
+    @Override
+    public void processingInstruction(final String target, final String data)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void startDTD(final String name, final String publicId, final String systemId)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void endDTD() throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void startElement(final String uri, final String loc, final String raw, final Attributes atts)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void endElement(final String uri, final String name, final String raw)
+            throws SAXException {
+        // nothing
+    }
+
+    public static TextSerializer createPlainSerializer() {
+        final TextSerializer serializer = new TextSerializer();
+        serializer.setContentType("text/plain; charset=" + UTF_8);
+        serializer.setEncoding(UTF_8);
+        serializer.setMethod(TXT);
+        return serializer;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
new file mode 100644
index 0000000..1d8c6e2
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import org.apache.cocoon.pipeline.SetupException;
+import org.apache.cocoon.pipeline.caching.CacheKey;
+import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
+import org.apache.cocoon.pipeline.util.StringRepresentation;
+import org.apache.cocoon.sax.AbstractSAXTransformer;
+import org.apache.cocoon.sax.SAXConsumer;
+import org.apache.cocoon.sax.util.SAXConsumerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
+
+    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
+
+    /**
+     * A generic transformer factory to parse XSLTs.
+     */
+    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
+
+    /**
+     * The XSLT parameters name pattern.
+     */
+    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
+
+    /**
+     * The XSLT parameters reference.
+     */
+    private Map<String, Object> parameters;
+
+    /**
+     * The XSLT Template reference.
+     */
+    private Templates templates;
+
+    private Source source;
+
+    public XSLTTransformer(final Source source) {
+        super();
+        this.load(source, null);
+    }
+
+    /**
+     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
+     *
+     * This constructor is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param attributes the Transformer Factory attributes
+     */
+    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
+        super();
+        this.load(source, attributes);
+    }
+
+    /**
+     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
+     * Factory attributes.
+     *
+     * This method is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param attributes the Transformer Factory attributes
+     */
+    private void load(final Source source, final Map<String, Object> attributes) {
+        if (source == null) {
+            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
+        }
+
+        this.source = source;
+
+        this.load(this.source, this.source.toString(), attributes);
+    }
+
+    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
+        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
+
+        // XSLT has to be parsed
+        final SAXTransformerFactory transformerFactory;
+        if (attributes == null || attributes.isEmpty()) {
+            transformerFactory = TRAX_FACTORY;
+        } else {
+            transformerFactory = createNewSAXTransformerFactory();
+            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
+                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
+            }
+        }
+
+        try {
+            this.templates = transformerFactory.newTemplates(source);
+        } catch (TransformerConfigurationException e) {
+            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
+        }
+    }
+
+    /**
+     * Sets the XSLT parameters to be applied to XSLT stylesheet.
+     *
+     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
+     */
+    public void setParameters(final Map<String, ? extends Object> parameters) {
+        if (parameters == null) {
+            this.parameters = null;
+        } else {
+            this.parameters = new HashMap<String, Object>(parameters);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setSAXConsumer(final SAXConsumer consumer) {
+        TransformerHandler transformerHandler;
+        try {
+            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
+        } catch (Exception e) {
+            throw new SetupException("Could not initialize transformer handler.", e);
+        }
+
+        if (this.parameters != null) {
+            final Transformer transformer = transformerHandler.getTransformer();
+
+            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
+                final String name = entry.getKey();
+
+                // is valid XSLT parameter name
+                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
+                    transformer.setParameter(name, entry.getValue());
+                }
+            }
+        }
+
+        final SAXResult result = new SAXResult();
+        result.setHandler(consumer);
+        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
+        result.setLexicalHandler(consumer);
+        transformerHandler.setResult(result);
+
+        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
+        saxConsumerAdapter.setContentHandler(transformerHandler);
+        super.setSAXConsumer(saxConsumerAdapter);
+    }
+
+    @Override
+    public CacheKey constructCacheKey() {
+        return null;
+    }
+
+    /**
+     * Utility method to create a new transformer factory.
+     *
+     * @return a new transformer factory
+     */
+    private static SAXTransformerFactory createNewSAXTransformerFactory() {
+        return (SAXTransformerFactory) TransformerFactory.newInstance();
+    }
+
+    @Override
+    public String toString() {
+        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
deleted file mode 100644
index d8b08f1..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.init;
-
-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.logic.SystemLoadReporterJob;
-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.logic.notification.NotificationJob;
-import org.apache.syncope.core.logic.report.ReportJob;
-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.job.TaskJob;
-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.stereotype.Component;
-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;
-
-@Component
-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 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() {
-        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/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
deleted file mode 100644
index 7a0270f..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.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...");
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
deleted file mode 100644
index ea9b1da..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.notification;
-
-import java.util.Date;
-import java.util.Properties;
-import javax.mail.internet.MimeMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-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.AuditManager;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@Component
-public class NotificationJobDelegate {
-
-    private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
-
-    /**
-     * Task DAO.
-     */
-    @Autowired
-    private TaskDAO taskDAO;
-
-    @Autowired
-    private JavaMailSender mailSender;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AuditManager auditManager;
-
-    @Autowired
-    private NotificationManager notificationManager;
-
-    private long maxRetries;
-
-    private void init() {
-        maxRetries = notificationManager.getMaxRetries();
-
-        if (mailSender instanceof JavaMailSenderImpl
-                && StringUtils.isNotBlank(((JavaMailSenderImpl) mailSender).getUsername())) {
-
-            Properties javaMailProperties = ((JavaMailSenderImpl) mailSender).getJavaMailProperties();
-            javaMailProperties.setProperty("mail.smtp.auth", "true");
-            ((JavaMailSenderImpl) mailSender).setJavaMailProperties(javaMailProperties);
-        }
-    }
-
-    @Transactional
-    public TaskExec executeSingle(final NotificationTask task) {
-        init();
-
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
-        execution.setTask(task);
-        execution.setStart(new Date());
-
-        boolean retryPossible = true;
-
-        if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
-                || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
-
-            String message = "Could not fetch all required information for sending e-mails:\n"
-                    + task.getRecipients() + "\n"
-                    + task.getSender() + "\n"
-                    + task.getSubject() + "\n"
-                    + task.getHtmlBody() + "\n"
-                    + task.getTextBody();
-            LOG.error(message);
-
-            execution.setStatus(NotificationJob.Status.NOT_SENT.name());
-            retryPossible = false;
-
-            if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
-                execution.setMessage(message);
-            }
-        } else {
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("About to send e-mails:\n"
-                        + task.getRecipients() + "\n"
-                        + task.getSender() + "\n"
-                        + task.getSubject() + "\n"
-                        + task.getHtmlBody() + "\n"
-                        + task.getTextBody() + "\n");
-            }
-
-            for (String to : task.getRecipients()) {
-                try {
-                    MimeMessage message = mailSender.createMimeMessage();
-                    MimeMessageHelper helper = new MimeMessageHelper(message, true);
-                    helper.setTo(to);
-                    helper.setFrom(task.getSender());
-                    helper.setSubject(task.getSubject());
-                    helper.setText(task.getTextBody(), task.getHtmlBody());
-
-                    mailSender.send(message);
-
-                    execution.setStatus(NotificationJob.Status.SENT.name());
-
-                    StringBuilder report = new StringBuilder();
-                    switch (task.getTraceLevel()) {
-                        case ALL:
-                            report.append("FROM: ").append(task.getSender()).append('\n').
-                                    append("TO: ").append(to).append('\n').
-                                    append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
-                                    append(task.getTextBody()).append('\n').append('\n').
-                                    append(task.getHtmlBody()).append('\n');
-                            break;
-
-                        case SUMMARY:
-                            report.append("E-mail sent to ").append(to).append('\n');
-                            break;
-
-                        case FAILURES:
-                        case NONE:
-                        default:
-                    }
-                    if (report.length() > 0) {
-                        execution.setMessage(report.toString());
-                    }
-
-                    auditManager.audit(
-                            AuditElements.EventCategoryType.TASK,
-                            "notification",
-                            null,
-                            "send",
-                            AuditElements.Result.SUCCESS,
-                            null,
-                            null,
-                            task,
-                            "Successfully sent notification to " + to);
-                } catch (Exception e) {
-                    LOG.error("Could not send e-mail", e);
-
-                    execution.setStatus(NotificationJob.Status.NOT_SENT.name());
-                    if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
-                        execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
-                    }
-
-                    auditManager.audit(
-                            AuditElements.EventCategoryType.TASK,
-                            "notification",
-                            null,
-                            "send",
-                            AuditElements.Result.FAILURE,
-                            null,
-                            null,
-                            task,
-                            "Could not send notification to " + to, e);
-                }
-
-                execution.setEnd(new Date());
-            }
-        }
-
-        if (hasToBeRegistered(execution)) {
-            execution = notificationManager.storeExec(execution);
-            if (retryPossible
-                    && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
-
-                handleRetries(execution);
-            }
-        } else {
-            notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
-        }
-
-        return execution;
-    }
-
-    @Transactional
-    public void execute() throws JobExecutionException {
-        for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
-            LOG.debug("Found notification task {} to be executed: starting...", task);
-            executeSingle(task);
-            LOG.debug("Notification task {} executed", task);
-        }
-    }
-
-    private boolean hasToBeRegistered(final TaskExec execution) {
-        NotificationTask task = (NotificationTask) execution.getTask();
-
-        // True if either failed and failures have to be registered, or if ALL
-        // has to be registered.
-        return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
-                && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
-                || task.getTraceLevel() == TraceLevel.ALL;
-    }
-
-    private void handleRetries(final TaskExec execution) {
-        if (maxRetries <= 0) {
-            return;
-        }
-
-        long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
-                execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
-
-        if (failedExecutionsCount <= maxRetries) {
-            LOG.debug("Execution of notification task {} will be retried [{}/{}]",
-                    execution.getTask(), failedExecutionsCount, maxRetries);
-            notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
-
-            auditManager.audit(
-                    AuditElements.EventCategoryType.TASK,
-                    "notification",
-                    null,
-                    "retry",
-                    AuditElements.Result.SUCCESS,
-                    null,
-                    null,
-                    execution,
-                    "Notification task " + execution.getTask().getKey() + " will be retried");
-        } else {
-            LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
-
-            auditManager.audit(
-                    AuditElements.EventCategoryType.TASK,
-                    "notification",
-                    null,
-                    "retry",
-                    AuditElements.Result.FAILURE,
-                    null,
-                    null,
-                    execution,
-                    "Giving up retries on notification task " + execution.getTask().getKey());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
deleted file mode 100644
index 69e6b4f..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-public abstract class AbstractReportlet implements Reportlet {
-
-    protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
-
-    protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
-
-    @Override
-    @Transactional(readOnly = true)
-    public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf == null) {
-            throw new ReportException(new IllegalArgumentException("No configuration provided"));
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-        atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, conf.getName());
-        atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
-        handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
-
-        doExtract(conf, handler);
-
-        handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
deleted file mode 100644
index ffd94a4..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.List;
-import java.util.Map;
-import javax.sql.DataSource;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.syncope.common.lib.report.AuditReportletConf;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.provisioning.java.AuditEntry;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.DomainsHolder;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(AuditReportletConf.class)
-public class AuditReportlet extends AbstractReportlet {
-
-    @Autowired
-    private DomainsHolder domainsHolder;
-
-    private AuditReportletConf conf;
-
-    private DataSource datasource;
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
-        jdbcTemplate.setMaxRows(conf.getSize());
-        List<Map<String, Object>> rows = jdbcTemplate.
-                queryForList("SELECT * FROM SYNCOPEAUDIT ORDER BY EVENT_DATE DESC");
-
-        handler.startElement("", "", "events", null);
-        AttributesImpl atts = new AttributesImpl();
-        for (Map<String, Object> row : rows) {
-            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
-
-            atts.clear();
-            if (StringUtils.isNotBlank(auditEntry.getWho())) {
-                atts.addAttribute("", "", "who", ReportXMLConst.XSD_STRING, auditEntry.getWho());
-            }
-            handler.startElement("", "", "event", atts);
-
-            atts.clear();
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getCategory())) {
-                atts.addAttribute("", "", "category",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getCategory());
-            }
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getSubcategory())) {
-                atts.addAttribute("", "", "subcategory",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getSubcategory());
-            }
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getEvent())) {
-                atts.addAttribute("", "", "event",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getEvent());
-            }
-            if (auditEntry.getLogger().getResult() != null) {
-                atts.addAttribute("", "", "result",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getResult().name());
-            }
-            handler.startElement("", "", "logger", atts);
-            handler.endElement("", "", "logger");
-
-            if (auditEntry.getBefore() != null) {
-                char[] before = ToStringBuilder.reflectionToString(
-                        auditEntry.getBefore(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                handler.startElement("", "", "before", null);
-                handler.characters(before, 0, before.length);
-                handler.endElement("", "", "before");
-            }
-
-            if (auditEntry.getInput() != null) {
-                handler.startElement("", "", "inputs", null);
-                for (Object inputObj : auditEntry.getInput()) {
-                    char[] input = ToStringBuilder.reflectionToString(
-                            inputObj, ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                    handler.startElement("", "", "input", null);
-                    handler.characters(input, 0, input.length);
-                    handler.endElement("", "", "input");
-                }
-                handler.endElement("", "", "inputs");
-            }
-
-            if (auditEntry.getOutput() != null) {
-                char[] output = ToStringBuilder.reflectionToString(
-                        auditEntry.getOutput(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                handler.startElement("", "", "output", null);
-                handler.characters(output, 0, output.length);
-                handler.endElement("", "", "output");
-            }
-
-            handler.startElement("", "", "throwable", null);
-            char[] throwable = row.get("THROWABLE").toString().toCharArray();
-            handler.characters(throwable, 0, throwable.length);
-            handler.endElement("", "", "throwable");
-
-            handler.endElement("", "", "event");
-        }
-        handler.endElement("", "", "events");
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof AuditReportletConf) {
-            this.conf = AuditReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        datasource = domainsHolder.getDomains().get(AuthContextUtils.getDomain());
-        if (datasource == null) {
-            throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
-        }
-
-        doExtractConf(handler);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
deleted file mode 100644
index d90a619..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.io.File;
-import java.io.OutputStream;
-
-import org.apache.cocoon.pipeline.ProcessingException;
-import org.apache.cocoon.pipeline.caching.CacheKey;
-import org.apache.cocoon.pipeline.caching.SimpleCacheKey;
-import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
-import org.apache.cocoon.pipeline.util.StringRepresentation;
-import org.apache.cocoon.sax.AbstractSAXSerializer;
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.apps.Fop;
-import org.apache.fop.apps.FopFactory;
-import org.apache.fop.apps.FopFactoryBuilder;
-import org.apache.xmlgraphics.util.MimeConstants;
-import org.xml.sax.ContentHandler;
-
-public class FopSerializer extends AbstractSAXSerializer implements CachingPipelineComponent {
-
-    private static final FopFactory FOP_FACTORY = new FopFactoryBuilder(new File(".").toURI()).build();
-
-    private String outputFormat;
-
-    /**
-     * Create a new FOP serializer that produces a PDF in output
-     */
-    public FopSerializer() {
-        this(MimeConstants.MIME_PDF);
-    }
-
-    /**
-     * Create a new FOP serializer that produces the specified mime
-     *
-     * @param outputFormat the output's mime type
-     */
-    public FopSerializer(final String outputFormat) {
-        if (outputFormat == null) {
-            throw new IllegalArgumentException("The parameter 'outputFormat' mustn't be null.");
-        }
-
-        this.outputFormat = outputFormat;
-    }
-
-    @Override
-    public CacheKey constructCacheKey() {
-        return new SimpleCacheKey();
-    }
-
-    @Override
-    public void setOutputStream(final OutputStream outputStream) {
-        try {
-            Fop fop = FOP_FACTORY.newFop(this.outputFormat, outputStream);
-            ContentHandler fopContentHandler = fop.getDefaultHandler();
-
-            this.setContentHandler(fopContentHandler);
-        } catch (FOPException e) {
-            throw new ProcessingException("Impossible to initialize FOPSerializer", e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return StringRepresentation.buildString(this, "outputFormat=" + this.outputFormat);
-    }
-}


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

Posted by il...@apache.org.
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/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
new file mode 100644
index 0000000..0d37d86
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
@@ -0,0 +1,268 @@
+/*
+ * 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 java.util.Date;
+import java.util.Properties;
+import javax.mail.internet.MimeMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+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.AuditManager;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class NotificationJobDelegate {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
+
+    /**
+     * Task DAO.
+     */
+    @Autowired
+    private TaskDAO taskDAO;
+
+    @Autowired
+    private JavaMailSender mailSender;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private AuditManager auditManager;
+
+    @Autowired
+    private NotificationManager notificationManager;
+
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
+    private long maxRetries;
+
+    private void init() {
+        maxRetries = notificationManager.getMaxRetries();
+
+        if (mailSender instanceof JavaMailSenderImpl
+                && StringUtils.isNotBlank(((JavaMailSenderImpl) mailSender).getUsername())) {
+
+            Properties javaMailProperties = ((JavaMailSenderImpl) mailSender).getJavaMailProperties();
+            javaMailProperties.setProperty("mail.smtp.auth", "true");
+            ((JavaMailSenderImpl) mailSender).setJavaMailProperties(javaMailProperties);
+        }
+    }
+
+    @Transactional
+    public TaskExec executeSingle(final NotificationTask task) {
+        init();
+
+        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        execution.setTask(task);
+        execution.setStart(new Date());
+
+        boolean retryPossible = true;
+
+        if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
+                || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
+
+            String message = "Could not fetch all required information for sending e-mails:\n"
+                    + task.getRecipients() + "\n"
+                    + task.getSender() + "\n"
+                    + task.getSubject() + "\n"
+                    + task.getHtmlBody() + "\n"
+                    + task.getTextBody();
+            LOG.error(message);
+
+            execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+            retryPossible = false;
+
+            if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+                execution.setMessage(message);
+            }
+        } else {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("About to send e-mails:\n"
+                        + task.getRecipients() + "\n"
+                        + task.getSender() + "\n"
+                        + task.getSubject() + "\n"
+                        + task.getHtmlBody() + "\n"
+                        + task.getTextBody() + "\n");
+            }
+
+            for (String to : task.getRecipients()) {
+                try {
+                    MimeMessage message = mailSender.createMimeMessage();
+                    MimeMessageHelper helper = new MimeMessageHelper(message, true);
+                    helper.setTo(to);
+                    helper.setFrom(task.getSender());
+                    helper.setSubject(task.getSubject());
+                    helper.setText(task.getTextBody(), task.getHtmlBody());
+
+                    mailSender.send(message);
+
+                    execution.setStatus(NotificationJob.Status.SENT.name());
+
+                    StringBuilder report = new StringBuilder();
+                    switch (task.getTraceLevel()) {
+                        case ALL:
+                            report.append("FROM: ").append(task.getSender()).append('\n').
+                                    append("TO: ").append(to).append('\n').
+                                    append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
+                                    append(task.getTextBody()).append('\n').append('\n').
+                                    append(task.getHtmlBody()).append('\n');
+                            break;
+
+                        case SUMMARY:
+                            report.append("E-mail sent to ").append(to).append('\n');
+                            break;
+
+                        case FAILURES:
+                        case NONE:
+                        default:
+                    }
+                    if (report.length() > 0) {
+                        execution.setMessage(report.toString());
+                    }
+
+                    publisher.publishEvent(new AfterHandlingEvent(this,
+                            true,
+                            true,
+                            AuditElements.EventCategoryType.TASK,
+                            "notification",
+                            null,
+                            "send",
+                            AuditElements.Result.SUCCESS,
+                            null,
+                            null,
+                            task,
+                            "Successfully sent notification to " + to));
+                } catch (Exception e) {
+                    LOG.error("Could not send e-mail", e);
+
+                    execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+                    if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+                        execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
+                    }
+
+                    publisher.publishEvent(new AfterHandlingEvent(this,
+                            true,
+                            true,
+                            AuditElements.EventCategoryType.TASK,
+                            "notification",
+                            null,
+                            "send",
+                            AuditElements.Result.FAILURE,
+                            null,
+                            null,
+                            task,
+                            "Could not send notification to " + to, e));
+                }
+
+                execution.setEnd(new Date());
+            }
+        }
+
+        if (hasToBeRegistered(execution)) {
+            execution = notificationManager.storeExec(execution);
+            if (retryPossible
+                    && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
+
+                handleRetries(execution);
+            }
+        } else {
+            notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
+        }
+
+        return execution;
+    }
+
+    @Transactional
+    public void execute() throws JobExecutionException {
+        for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
+            LOG.debug("Found notification task {} to be executed: starting...", task);
+            executeSingle(task);
+            LOG.debug("Notification task {} executed", task);
+        }
+    }
+
+    private boolean hasToBeRegistered(final TaskExec execution) {
+        NotificationTask task = (NotificationTask) execution.getTask();
+
+        // True if either failed and failures have to be registered, or if ALL
+        // has to be registered.
+        return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
+                && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+                || task.getTraceLevel() == TraceLevel.ALL;
+    }
+
+    private void handleRetries(final TaskExec execution) {
+        if (maxRetries <= 0) {
+            return;
+        }
+
+        long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
+                execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
+
+        if (failedExecutionsCount <= maxRetries) {
+            LOG.debug("Execution of notification task {} will be retried [{}/{}]",
+                    execution.getTask(), failedExecutionsCount, maxRetries);
+            notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.TASK,
+                    "notification",
+                    null,
+                    "retry",
+                    AuditElements.Result.SUCCESS,
+                    null,
+                    null,
+                    execution,
+                    "Notification task " + execution.getTask().getKey() + " will be retried");
+        } else {
+            LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.TASK,
+                    "notification",
+                    null,
+                    "retry",
+                    AuditElements.Result.FAILURE,
+                    null,
+                    null,
+                    execution,
+                    "Giving up retries on notification task " + execution.getTask().getKey());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
new file mode 100644
index 0000000..2c85b20
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.report;
+
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+public abstract class AbstractReportlet implements Reportlet {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
+
+    protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
+
+    @Override
+    @Transactional(readOnly = true)
+    public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf == null) {
+            throw new ReportException(new IllegalArgumentException("No configuration provided"));
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+        atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, conf.getName());
+        atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
+        handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
+
+        doExtract(conf, handler);
+
+        handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
new file mode 100644
index 0000000..96039f0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -0,0 +1,141 @@
+/*
+ * 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.report;
+
+import java.util.List;
+import java.util.Map;
+import javax.sql.DataSource;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.syncope.common.lib.report.AuditReportletConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.provisioning.java.AuditEntry;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(AuditReportletConf.class)
+public class AuditReportlet extends AbstractReportlet {
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    private AuditReportletConf conf;
+
+    private DataSource datasource;
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
+        jdbcTemplate.setMaxRows(conf.getSize());
+        List<Map<String, Object>> rows = jdbcTemplate.
+                queryForList("SELECT * FROM SYNCOPEAUDIT ORDER BY EVENT_DATE DESC");
+
+        handler.startElement("", "", "events", null);
+        AttributesImpl atts = new AttributesImpl();
+        for (Map<String, Object> row : rows) {
+            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
+
+            atts.clear();
+            if (StringUtils.isNotBlank(auditEntry.getWho())) {
+                atts.addAttribute("", "", "who", ReportXMLConst.XSD_STRING, auditEntry.getWho());
+            }
+            handler.startElement("", "", "event", atts);
+
+            atts.clear();
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getCategory())) {
+                atts.addAttribute("", "", "category",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getCategory());
+            }
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getSubcategory())) {
+                atts.addAttribute("", "", "subcategory",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getSubcategory());
+            }
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getEvent())) {
+                atts.addAttribute("", "", "event",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getEvent());
+            }
+            if (auditEntry.getLogger().getResult() != null) {
+                atts.addAttribute("", "", "result",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getResult().name());
+            }
+            handler.startElement("", "", "logger", atts);
+            handler.endElement("", "", "logger");
+
+            if (auditEntry.getBefore() != null) {
+                char[] before = ToStringBuilder.reflectionToString(
+                        auditEntry.getBefore(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                handler.startElement("", "", "before", null);
+                handler.characters(before, 0, before.length);
+                handler.endElement("", "", "before");
+            }
+
+            if (auditEntry.getInput() != null) {
+                handler.startElement("", "", "inputs", null);
+                for (Object inputObj : auditEntry.getInput()) {
+                    char[] input = ToStringBuilder.reflectionToString(
+                            inputObj, ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                    handler.startElement("", "", "input", null);
+                    handler.characters(input, 0, input.length);
+                    handler.endElement("", "", "input");
+                }
+                handler.endElement("", "", "inputs");
+            }
+
+            if (auditEntry.getOutput() != null) {
+                char[] output = ToStringBuilder.reflectionToString(
+                        auditEntry.getOutput(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                handler.startElement("", "", "output", null);
+                handler.characters(output, 0, output.length);
+                handler.endElement("", "", "output");
+            }
+
+            handler.startElement("", "", "throwable", null);
+            char[] throwable = row.get("THROWABLE").toString().toCharArray();
+            handler.characters(throwable, 0, throwable.length);
+            handler.endElement("", "", "throwable");
+
+            handler.endElement("", "", "event");
+        }
+        handler.endElement("", "", "events");
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof AuditReportletConf) {
+            this.conf = AuditReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        datasource = domainsHolder.getDomains().get(AuthContextUtils.getDomain());
+        if (datasource == null) {
+            throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
+        }
+
+        doExtractConf(handler);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
new file mode 100644
index 0000000..fa558b6
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
@@ -0,0 +1,316 @@
+/*
+ * 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.report;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.GroupReportletConf;
+import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(GroupReportletConf.class)
+public class GroupReportlet extends AbstractReportlet {
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private GroupDataBinder groupDataBinder;
+
+    private GroupReportletConf conf;
+
+    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
+            throws SAXException {
+
+        if (anyTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : anyTO.getResources()) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
+                handler.startElement("", "", "resource", atts);
+                handler.endElement("", "", "resource");
+            }
+
+            handler.endElement("", "", "resources");
+        }
+    }
+
+    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
+            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+        if (!attrs.isEmpty()) {
+            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
+
+            handler.startElement("", "", "attributes", null);
+            for (String attrName : attrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "attribute", atts);
+
+                if (attrMap.containsKey(attrName)) {
+                    for (String value : attrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
+
+            handler.startElement("", "", "derivedAttributes", null);
+            for (String attrName : derAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "derivedAttribute", atts);
+
+                if (derAttrMap.containsKey(attrName)) {
+                    for (String value : derAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
+
+            handler.startElement("", "", "virtualAttributes", null);
+            for (String attrName : virAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "virtualAttribute", atts);
+
+                if (virAttrMap.containsKey(attrName)) {
+                    for (String value : virAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        for (Group group : groups) {
+            atts.clear();
+
+            for (Feature feature : conf.getFeatures()) {
+                String type = null;
+                String value = null;
+                switch (feature) {
+                    case key:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getKey();
+                        break;
+
+                    case name:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = String.valueOf(group.getName());
+                        break;
+
+                    case groupOwner:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getGroupOwner().getKey();
+                        break;
+
+                    case userOwner:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getUserOwner().getKey();
+                        break;
+
+                    default:
+                }
+
+                if (type != null && value != null) {
+                    atts.addAttribute("", "", feature.name(), type, value);
+                }
+            }
+
+            handler.startElement("", "", "group", atts);
+
+            // Using GroupTO for attribute values, since the conversion logic of
+            // values to String is already encapsulated there
+            GroupTO groupTO = groupDataBinder.getGroupTO(group, true);
+
+            doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+
+            // to get resources associated to a group
+            if (conf.getFeatures().contains(Feature.resources)) {
+                doExtractResources(handler, groupTO);
+            }
+            //to get users asscoiated to a group is preferred GroupDAO to GroupTO
+            if (conf.getFeatures().contains(Feature.users)) {
+                handler.startElement("", "", "users", null);
+
+                for (UMembership memb : groupDAO.findUMemberships(group)) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_STRING,
+                            memb.getLeftEnd().getKey());
+                    atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING,
+                            String.valueOf(memb.getLeftEnd().getUsername()));
+
+                    handler.startElement("", "", "user", atts);
+                    handler.endElement("", "", "user");
+                }
+
+                handler.endElement("", "", "users");
+            }
+
+            handler.endElement("", "", "group");
+        }
+    }
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        if (conf == null) {
+            LOG.debug("Report configuration is not present");
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "groupAttributes", atts);
+
+        if (conf != null) {
+            for (Feature feature : conf.getFeatures()) {
+                atts.clear();
+                handler.startElement("", "", "feature", atts);
+                handler.characters(feature.name().toCharArray(), 0, feature.name().length());
+                handler.endElement("", "", "feature");
+            }
+
+            for (String attr : conf.getPlainAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "attribute", atts);
+                handler.characters(attr.toCharArray(), 0, attr.length());
+                handler.endElement("", "", "attribute");
+            }
+
+            for (String derAttr : conf.getDerAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "derAttribute", atts);
+                handler.characters(derAttr.toCharArray(), 0, derAttr.length());
+                handler.endElement("", "", "derAttribute");
+            }
+
+            for (String virAttr : conf.getVirAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "virAttribute", atts);
+                handler.characters(virAttr.toCharArray(), 0, virAttr.length());
+                handler.endElement("", "", "virAttribute");
+            }
+        }
+
+        handler.endElement("", "", "groupAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    private int count() {
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? groupDAO.count()
+                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP);
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof GroupReportletConf) {
+            this.conf = GroupReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<Group> groups;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                groups = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(this.conf.getMatchingCond()),
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
+            }
+
+            doExtract(handler, groups);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
new file mode 100644
index 0000000..f39ee1c
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
@@ -0,0 +1,519 @@
+/*
+ * 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.report;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.commons.collections4.Closure;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
+import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Reportlet for extracting information for the current reconciliation status, e.g. the coherence between Syncope
+ * information and mapped entities on external resources.
+ */
+@ReportletConfClass(ReconciliationReportletConf.class)
+public class ReconciliationReportlet extends AbstractReportlet {
+
+    private static final int PAGE_SIZE = 10;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private MappingManager mappingManager;
+
+    @Autowired
+    private ConnectorFactory connFactory;
+
+    @Autowired
+    private AnyUtilsFactory anyUtilsFactory;
+
+    private ReconciliationReportletConf conf;
+
+    private String getAnyElementName(final AnyTypeKind anyTypeKind) {
+        String elementName;
+
+        switch (anyTypeKind) {
+            case USER:
+                elementName = "user";
+                break;
+
+            case GROUP:
+                elementName = "group";
+                break;
+
+            case ANY_OBJECT:
+            default:
+                elementName = "anyObject";
+        }
+
+        return elementName;
+    }
+
+    private void doExtract(
+            final ContentHandler handler,
+            final Any<?> any,
+            final Set<Missing> missing,
+            final Set<Misaligned> misaligned)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+
+        for (Feature feature : conf.getFeatures()) {
+            String type = null;
+            String value = null;
+            switch (feature) {
+                case key:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getKey();
+                    break;
+
+                case username:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_STRING;
+                        value = User.class.cast(any).getUsername();
+                    }
+                    break;
+
+                case groupName:
+                    if (any instanceof Group) {
+                        type = ReportXMLConst.XSD_STRING;
+                        value = Group.class.cast(any).getName();
+                    }
+                    break;
+
+                case workflowId:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getWorkflowId();
+                    break;
+
+                case status:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getStatus();
+                    break;
+
+                case creationDate:
+                    type = ReportXMLConst.XSD_DATETIME;
+                    value = any.getCreationDate() == null
+                            ? StringUtils.EMPTY
+                            : FormatUtils.format(any.getCreationDate());
+                    break;
+
+                case lastLoginDate:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = User.class.cast(any).getLastLoginDate() == null
+                                ? StringUtils.EMPTY
+                                : FormatUtils.format(User.class.cast(any).getLastLoginDate());
+                    }
+                    break;
+
+                case changePwdDate:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = User.class.cast(any).getChangePwdDate() == null
+                                ? StringUtils.EMPTY
+                                : FormatUtils.format(User.class.cast(any).getChangePwdDate());
+                    }
+                    break;
+
+                case passwordHistorySize:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(User.class.cast(any).getPasswordHistory().size());
+                    }
+                    break;
+
+                case failedLoginCount:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(User.class.cast(any).getFailedLogins());
+                    }
+                    break;
+
+                default:
+            }
+
+            if (type != null && value != null) {
+                atts.addAttribute("", "", feature.name(), type, value);
+            }
+        }
+
+        handler.startElement("", "", getAnyElementName(any.getType().getKind()), atts);
+
+        for (Missing item : missing) {
+            atts.clear();
+            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
+            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
+
+            handler.startElement("", "", "missing", atts);
+            handler.endElement("", "", "missing");
+        }
+        for (Misaligned item : misaligned) {
+            atts.clear();
+            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
+            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
+            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, item.getName());
+
+            handler.startElement("", "", "misaligned", atts);
+
+            handler.startElement("", "", "onSyncope", null);
+            if (item.getOnSyncope() != null) {
+                for (Object value : item.getOnSyncope()) {
+                    char[] asChars = value.toString().toCharArray();
+
+                    handler.startElement("", "", "value", null);
+                    handler.characters(asChars, 0, asChars.length);
+                    handler.endElement("", "", "value");
+                }
+            }
+            handler.endElement("", "", "onSyncope");
+
+            handler.startElement("", "", "onResource", null);
+            if (item.getOnResource() != null) {
+                for (Object value : item.getOnResource()) {
+                    char[] asChars = value.toString().toCharArray();
+
+                    handler.startElement("", "", "value", null);
+                    handler.characters(asChars, 0, asChars.length);
+                    handler.endElement("", "", "value");
+                }
+            }
+            handler.endElement("", "", "onResource");
+
+            handler.endElement("", "", "misaligned");
+        }
+
+        handler.endElement("", "", getAnyElementName(any.getType().getKind()));
+    }
+
+    private Set<Object> getValues(final Attribute attr) {
+        Set<Object> values;
+        if (attr.getValue() == null || attr.getValue().isEmpty()) {
+            values = Collections.emptySet();
+        } else if (attr.getValue().get(0) instanceof byte[]) {
+            values = new HashSet<>(attr.getValue().size());
+            for (Object single : attr.getValue()) {
+                values.add(Base64.encode((byte[]) single));
+            }
+        } else {
+            values = new HashSet<>(attr.getValue());
+        }
+
+        return values;
+    }
+
+    private void doExtract(final ContentHandler handler, final List<? extends Any<?>> anys)
+            throws SAXException, ReportException {
+
+        final Set<Missing> missing = new HashSet<>();
+        final Set<Misaligned> misaligned = new HashSet<>();
+
+        for (Any<?> any : anys) {
+            missing.clear();
+            misaligned.clear();
+
+            AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+            for (final ExternalResource resource : anyUtils.getAllResources(any)) {
+                Provision provision = resource.getProvision(any.getType());
+                MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
+                final String connObjectKeyValue = connObjectKeyItem == null
+                        ? StringUtils.EMPTY
+                        : mappingManager.getConnObjectKeyValue(any, provision);
+                if (provision != null && connObjectKeyItem != null && StringUtils.isNotBlank(connObjectKeyValue)) {
+                    // 1. read from the underlying connector
+                    Connector connector = connFactory.getConnector(resource);
+                    ConnectorObject connectorObject = connector.getObject(provision.getObjectClass(),
+                            new Uid(connObjectKeyValue),
+                            MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
+
+                    if (connectorObject == null) {
+                        // 2. not found on resource?
+                        LOG.error("Object {} with class {} not found on resource {}",
+                                connObjectKeyValue, provision.getObjectClass(), resource);
+
+                        missing.add(new Missing(resource.getKey(), connObjectKeyValue));
+                    } else {
+                        // 3. found but misaligned?
+                        Pair<String, Set<Attribute>> preparedAttrs =
+                                mappingManager.prepareAttrs(any, null, false, null, provision);
+                        preparedAttrs.getRight().add(AttributeBuilder.build(
+                                Uid.NAME, preparedAttrs.getLeft()));
+                        preparedAttrs.getRight().add(AttributeBuilder.build(
+                                connObjectKeyItem.getExtAttrName(), preparedAttrs.getLeft()));
+
+                        final Map<String, Set<Object>> syncopeAttrs = new HashMap<>();
+                        for (Attribute attr : preparedAttrs.getRight()) {
+                            syncopeAttrs.put(attr.getName(), getValues(attr));
+                        }
+
+                        final Map<String, Set<Object>> resourceAttrs = new HashMap<>();
+                        for (Attribute attr : connectorObject.getAttributes()) {
+                            if (!OperationalAttributes.PASSWORD_NAME.equals(attr.getName())
+                                    && !OperationalAttributes.ENABLE_NAME.equals(attr.getName())) {
+
+                                resourceAttrs.put(attr.getName(), getValues(attr));
+                            }
+                        }
+
+                        IterableUtils.forEach(CollectionUtils.subtract(syncopeAttrs.keySet(), resourceAttrs.keySet()),
+                                new Closure<String>() {
+
+                            @Override
+                            public void execute(final String name) {
+                                misaligned.add(new Misaligned(
+                                        resource.getKey(),
+                                        connObjectKeyValue,
+                                        name,
+                                        syncopeAttrs.get(name),
+                                        Collections.emptySet()));
+                            }
+                        });
+
+                        for (Map.Entry<String, Set<Object>> entry : resourceAttrs.entrySet()) {
+                            if (syncopeAttrs.containsKey(entry.getKey())) {
+                                if (!Objects.equals(syncopeAttrs.get(entry.getKey()), entry.getValue())) {
+                                    misaligned.add(new Misaligned(
+                                            resource.getKey(),
+                                            connObjectKeyValue,
+                                            entry.getKey(),
+                                            syncopeAttrs.get(entry.getKey()),
+                                            entry.getValue()));
+                                }
+                            } else {
+                                misaligned.add(new Misaligned(
+                                        resource.getKey(),
+                                        connObjectKeyValue,
+                                        entry.getKey(),
+                                        Collections.emptySet(),
+                                        entry.getValue()));
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!missing.isEmpty() || !misaligned.isEmpty()) {
+                doExtract(handler, any, missing, misaligned);
+            }
+        }
+    }
+
+    private void doExtract(
+            final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
+            throws SAXException {
+
+        for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
+            List<AnyObject> anys = searchDAO.search(
+                    SyncopeConstants.FULL_ADMIN_REALMS,
+                    cond,
+                    page,
+                    PAGE_SIZE,
+                    Collections.<OrderByClause>emptyList(),
+                    anyTypeKind);
+
+            doExtract(handler, anys);
+        }
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof ReconciliationReportletConf) {
+            this.conf = ReconciliationReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+
+        if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
+
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
+        } else {
+            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
+
+            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
+
+            doExtract(handler, count, cond, AnyTypeKind.USER);
+        }
+        handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
+
+        atts.clear();
+        if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
+
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
+        } else {
+            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
+
+            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
+
+            doExtract(handler, count, cond, AnyTypeKind.GROUP);
+        }
+        handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
+
+        for (AnyType anyType : anyTypeDAO.findAll()) {
+            if (!anyType.equals(anyTypeDAO.findUser()) && !anyType.equals(anyTypeDAO.findGroup())) {
+                AnyTypeCond anyTypeCond = new AnyTypeCond();
+                anyTypeCond.setAnyTypeKey(anyType.getKey());
+                SearchCond cond = StringUtils.isBlank(this.conf.getAnyObjectMatchingCond())
+                        ? SearchCond.getLeafCond(anyTypeCond)
+                        : SearchCond.getAndCond(
+                                SearchCond.getLeafCond(anyTypeCond),
+                                SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
+
+                int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+
+                atts.clear();
+                atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
+                atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+                handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
+
+                doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
+
+                handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
+            }
+        }
+    }
+
+    private static class Missing {
+
+        private final String resource;
+
+        private final String connObjectKeyValue;
+
+        Missing(final String resource, final String connObjectKeyValue) {
+            this.resource = resource;
+            this.connObjectKeyValue = connObjectKeyValue;
+        }
+
+        public String getResource() {
+            return resource;
+        }
+
+        public String getConnObjectKeyValue() {
+            return connObjectKeyValue;
+        }
+
+    }
+
+    private static class Misaligned extends Missing {
+
+        private final String name;
+
+        private final Set<Object> onSyncope;
+
+        private final Set<Object> onResource;
+
+        Misaligned(
+                final String resource,
+                final String connObjectKeyValue,
+                final String name,
+                final Set<Object> onSyncope,
+                final Set<Object> onResource) {
+
+            super(resource, connObjectKeyValue);
+
+            this.name = name;
+            this.onSyncope = onSyncope;
+            this.onResource = onResource;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Set<Object> getOnSyncope() {
+            return onSyncope;
+        }
+
+        public Set<Object> getOnResource() {
+            return onResource;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
new file mode 100644
index 0000000..db42152
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.report;
+
+public class ReportException extends RuntimeException {
+
+    private static final long serialVersionUID = 6719507778589395283L;
+
+    public ReportException(final Throwable cause) {
+        super(cause);
+    }
+
+    public ReportException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
new file mode 100644
index 0000000..78183d5
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -0,0 +1,80 @@
+/*
+ * 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.report;
+
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Quartz job for executing a given report.
+ */
+public class ReportJob extends AbstractInterruptableJob {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
+
+    /**
+     * Key, set by the caller, for identifying the report to be executed.
+     */
+    private String reportKey;
+
+    @Autowired
+    private ReportJobDelegate delegate;
+
+    /**
+     * Report id setter.
+     *
+     * @param reportKey to be set
+     */
+    public void setReportKey(final String reportKey) {
+        this.reportKey = reportKey;
+    }
+
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        super.execute(context);
+
+        try {
+            AuthContextUtils.execWithAuthContext(context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY),
+                    new AuthContextUtils.Executable<Void>() {
+
+                @Override
+                public Void exec() {
+                    try {
+                        delegate.execute(reportKey);
+                    } catch (Exception e) {
+                        LOG.error("While executing report {}", reportKey, e);
+                        throw new RuntimeException(e);
+                    }
+
+                    return null;
+                }
+            });
+        } catch (RuntimeException e) {
+            LOG.error("While executing report {}", reportKey, e);
+            throw new JobExecutionException("While executing report " + reportKey, 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/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
new file mode 100644
index 0000000..28f4894
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
@@ -0,0 +1,198 @@
+/*
+ * 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.report;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.helpers.AttributesImpl;
+
+@Component
+public class ReportJobDelegate {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
+
+    /**
+     * Report DAO.
+     */
+    @Autowired
+    private ReportDAO reportDAO;
+
+    /**
+     * Report execution DAO.
+     */
+    @Autowired
+    private ReportExecDAO reportExecDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
+    @Transactional
+    public void execute(final String reportKey) throws JobExecutionException {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new JobExecutionException("Report " + reportKey + " not found");
+        }
+
+        if (!report.isActive()) {
+            LOG.info("Report {} not active, aborting...", reportKey);
+            return;
+        }
+
+        // 1. create execution
+        ReportExec execution = entityFactory.newEntity(ReportExec.class);
+        execution.setStatus(ReportExecStatus.STARTED);
+        execution.setStart(new Date());
+        execution.setReport(report);
+        execution = reportExecDAO.save(execution);
+
+        report.add(execution);
+        report = reportDAO.save(report);
+
+        // 2. define a SAX handler for generating result as XML
+        TransformerHandler handler;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ZipOutputStream zos = new ZipOutputStream(baos);
+        zos.setLevel(Deflater.BEST_COMPRESSION);
+        try {
+            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            handler = tFactory.newTransformerHandler();
+            Transformer serializer = handler.getTransformer();
+            serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
+            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            // a single ZipEntry in the ZipOutputStream
+            zos.putNextEntry(new ZipEntry(report.getName()));
+
+            // streaming SAX handler in a compressed byte array stream
+            handler.setResult(new StreamResult(zos));
+        } catch (Exception e) {
+            throw new JobExecutionException("While configuring for SAX generation", e, true);
+        }
+
+        execution.setStatus(ReportExecStatus.RUNNING);
+        execution = reportExecDAO.save(execution);
+
+        // 3. actual report execution
+        StringBuilder reportExecutionMessage = new StringBuilder();
+        try {
+            // report header
+            handler.startDocument();
+            AttributesImpl atts = new AttributesImpl();
+            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
+            handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
+
+            // iterate over reportlet instances defined for this report
+            for (ReportletConf reportletConf : report.getReportletConfs()) {
+                Class<? extends Reportlet> reportletClass =
+                        implementationLookup.getReportletClass(reportletConf.getClass());
+                if (reportletClass == null) {
+                    LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
+                } else {
+                    // fetch (or create) reportlet
+                    Reportlet reportlet;
+                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                getSingleton(reportletClass.getName());
+                    } else {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                        ApplicationContextProvider.getBeanFactory().
+                                registerSingleton(reportletClass.getName(), reportlet);
+                    }
+
+                    // invoke reportlet
+                    try {
+                        reportlet.extract(reportletConf, handler);
+                    } catch (Throwable t) {
+                        LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
+
+                        execution.setStatus(ReportExecStatus.FAILURE);
+
+                        Throwable effective = t instanceof ReportException
+                                ? t.getCause()
+                                : t;
+                        reportExecutionMessage.
+                                append(ExceptionUtils2.getFullStackTrace(effective)).
+                                append("\n==================\n");
+                    }
+                }
+            }
+
+            // report footer
+            handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
+            handler.endDocument();
+
+            if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
+                execution.setStatus(ReportExecStatus.SUCCESS);
+            }
+        } catch (Exception e) {
+            execution.setStatus(ReportExecStatus.FAILURE);
+            reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
+
+            throw new JobExecutionException(e, true);
+        } finally {
+            try {
+                zos.closeEntry();
+                IOUtils.closeQuietly(zos);
+                IOUtils.closeQuietly(baos);
+            } catch (IOException e) {
+                LOG.error("While closing StreamResult's backend", e);
+            }
+
+            execution.setExecResult(baos.toByteArray());
+            execution.setMessage(reportExecutionMessage.toString());
+            execution.setEnd(new Date());
+            reportExecDAO.save(execution);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
new file mode 100644
index 0000000..6b21695
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
@@ -0,0 +1,44 @@
+/*
+ * 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.report;
+
+public final class ReportXMLConst {
+
+    public static final String XSD_STRING = "xsd:string";
+
+    public static final String XSD_INT = "xsd:integer";
+
+    public static final String XSD_LONG = "xsd:long";
+
+    public static final String XSD_BOOLEAN = "xsd:boolean";
+
+    public static final String XSD_DATETIME = "xsd:dateTime";
+
+    public static final String ELEMENT_REPORT = "report";
+
+    public static final String ATTR_NAME = "name";
+
+    public static final String ATTR_CLASS = "class";
+
+    public static final String ELEMENT_REPORTLET = "reportlet";
+
+    private ReportXMLConst() {
+        // empty private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
new file mode 100644
index 0000000..1156d25
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
@@ -0,0 +1,128 @@
+/*
+ * 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.report;
+
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.report.StaticReportletConf;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.springframework.util.StringUtils;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(StaticReportletConf.class)
+public class StaticReportlet extends AbstractReportlet {
+
+    private StaticReportletConf conf;
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "staticAttributes", atts);
+
+        handler.startElement("", "", "string", atts);
+        handler.characters("string".toCharArray(), 0, "string".length());
+        handler.endElement("", "", "string");
+
+        handler.startElement("", "", "long", atts);
+        handler.characters("long".toCharArray(), 0, "long".length());
+        handler.endElement("", "", "long");
+
+        handler.startElement("", "", "double", atts);
+        handler.characters("double".toCharArray(), 0, "double".length());
+        handler.endElement("", "", "double");
+
+        handler.startElement("", "", "date", atts);
+        handler.characters("date".toCharArray(), 0, "date".length());
+        handler.endElement("", "", "date");
+
+        handler.startElement("", "", "double", atts);
+        handler.characters("double".toCharArray(), 0, "double".length());
+        handler.endElement("", "", "double");
+
+        handler.startElement("", "", "enum", atts);
+        handler.characters("enum".toCharArray(), 0, "enum".length());
+        handler.endElement("", "", "enum");
+
+        handler.startElement("", "", "list", atts);
+        handler.characters("list".toCharArray(), 0, "list".length());
+        handler.endElement("", "", "list");
+
+        handler.endElement("", "", "staticAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof StaticReportletConf) {
+            this.conf = StaticReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        if (StringUtils.hasText(this.conf.getStringField())) {
+            handler.startElement("", "", "string", null);
+            handler.characters(this.conf.getStringField().toCharArray(), 0, this.conf.getStringField().length());
+            handler.endElement("", "", "string");
+        }
+
+        if (this.conf.getLongField() != null) {
+            handler.startElement("", "", "long", null);
+            String printed = String.valueOf(this.conf.getLongField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "long");
+        }
+
+        if (this.conf.getDoubleField() != null) {
+            handler.startElement("", "", "double", null);
+            String printed = String.valueOf(this.conf.getDoubleField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "double");
+        }
+
+        if (this.conf.getDateField() != null) {
+            handler.startElement("", "", "date", null);
+            String printed = FormatUtils.format(this.conf.getDateField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "date");
+        }
+
+        if (this.conf.getTraceLevel() != null) {
+            handler.startElement("", "", "enum", null);
+            String printed = this.conf.getTraceLevel().name();
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "enum");
+        }
+
+        if (this.conf.getListField() != null && !this.conf.getListField().isEmpty()) {
+            handler.startElement("", "", "list", null);
+            for (String item : this.conf.getListField()) {
+                if (StringUtils.hasText(item)) {
+                    handler.startElement("", "", "string", null);
+                    handler.characters(item.toCharArray(), 0, item.length());
+                    handler.endElement("", "", "string");
+                }
+            }
+            handler.endElement("", "", "list");
+        }
+    }
+}


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

Posted by il...@apache.org.
[SYNCOPE-1103] Option to put Quartz scheduler in standby, from provisioning.properties


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

Branch: refs/heads/2_0_X
Commit: 67ecbea39eb9d54f73ab6fcd79fdbe57de399e3b
Parents: 69a0fe4
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Jun 6 15:01:54 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Jun 6 15:02:14 2017 +0200

----------------------------------------------------------------------
 core/logic/pom.xml                              |   4 -
 .../syncope/core/logic/AnyObjectLogic.java      |   1 +
 .../apache/syncope/core/logic/GroupLogic.java   |   4 +
 .../core/logic/LogicInvocationHandler.java      |  37 +-
 .../syncope/core/logic/NotificationLogic.java   |   2 +-
 .../apache/syncope/core/logic/ReportLogic.java  |  20 +-
 .../apache/syncope/core/logic/SyncopeLogic.java |   7 +-
 .../core/logic/SystemLoadReporterJob.java       |  57 --
 .../apache/syncope/core/logic/TaskLogic.java    |   2 +-
 .../apache/syncope/core/logic/UserLogic.java    |   2 +
 .../core/logic/cocoon/FopSerializer.java        |  82 +++
 .../core/logic/cocoon/TextSerializer.java       |  83 +++
 .../core/logic/cocoon/XSLTTransformer.java      | 193 +++++++
 .../syncope/core/logic/init/JobManagerImpl.java | 379 --------------
 .../logic/notification/NotificationJob.java     |  86 ---
 .../notification/NotificationJobDelegate.java   | 259 ---------
 .../core/logic/report/AbstractReportlet.java    |  52 --
 .../core/logic/report/AuditReportlet.java       | 141 -----
 .../core/logic/report/FopSerializer.java        |  82 ---
 .../core/logic/report/GroupReportlet.java       | 316 -----------
 .../logic/report/ReconciliationReportlet.java   | 519 -------------------
 .../core/logic/report/ReportException.java      |  32 --
 .../syncope/core/logic/report/ReportJob.java    |  80 ---
 .../core/logic/report/ReportJobDelegate.java    | 198 -------
 .../core/logic/report/ReportXMLConst.java       |  44 --
 .../core/logic/report/StaticReportlet.java      | 128 -----
 .../core/logic/report/TextSerializer.java       |  83 ---
 .../core/logic/report/UserReportlet.java        | 383 --------------
 .../core/logic/report/XSLTTransformer.java      | 193 -------
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |   9 +-
 .../core/persistence/jpa/dao/JPAGroupDAO.java   |  25 +-
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   7 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    |   9 +-
 .../test/resources/domains/MasterContent.xml    |  20 +-
 core/provisioning-api/pom.xml                   |   5 +
 .../core/provisioning/api/AuditManager.java     |  10 +-
 .../api/event/AfterHandlingEvent.java           | 115 ++++
 .../api/event/AnyCreatedUpdatedEvent.java       |  46 ++
 .../provisioning/api/event/AnyDeletedEvent.java |  57 ++
 .../api/notification/NotificationManager.java   |   8 +
 core/provisioning-java/pom.xml                  |   6 +-
 .../provisioning/java/AuditManagerImpl.java     |  20 +
 .../provisioning/java/ConnectorFacadeProxy.java |   5 +
 .../provisioning/java/job/JobManagerImpl.java   | 393 ++++++++++++++
 .../java/job/SystemLoadReporterJob.java         |  57 ++
 .../java/job/notification/NotificationJob.java  |  86 +++
 .../notification/NotificationJobDelegate.java   | 268 ++++++++++
 .../java/job/report/AbstractReportlet.java      |  52 ++
 .../java/job/report/AuditReportlet.java         | 141 +++++
 .../java/job/report/GroupReportlet.java         | 316 +++++++++++
 .../job/report/ReconciliationReportlet.java     | 519 +++++++++++++++++++
 .../java/job/report/ReportException.java        |  32 ++
 .../provisioning/java/job/report/ReportJob.java |  80 +++
 .../java/job/report/ReportJobDelegate.java      | 198 +++++++
 .../java/job/report/ReportXMLConst.java         |  44 ++
 .../java/job/report/StaticReportlet.java        | 128 +++++
 .../java/job/report/UserReportlet.java          | 383 ++++++++++++++
 .../notification/NotificationManagerImpl.java   |  20 +
 .../pushpull/AbstractPullResultHandler.java     |  20 +-
 .../pushpull/AbstractPushResultHandler.java     |  20 +-
 .../pushpull/AbstractRealmResultHandler.java    |   2 +-
 .../pushpull/AbstractSyncopeResultHandler.java  |  33 --
 .../AnyObjectPullResultHandlerImpl.java         |   5 +
 .../pushpull/GroupPullResultHandlerImpl.java    |   7 +-
 .../pushpull/RealmPushResultHandlerImpl.java    |  21 +-
 .../pushpull/UserPullResultHandlerImpl.java     |   5 +
 .../src/main/resources/provisioning.properties  |   1 +
 .../src/main/resources/provisioningContext.xml  |   3 +
 .../spring/event/AnyCreatedUpdatedEvent.java    |  47 --
 .../core/spring/event/AnyDeletedEvent.java      |  53 --
 .../src/main/resources/provisioning.properties  |   1 +
 .../client/ElasticsearchIndexManager.java       |   4 +-
 .../core/reference/ITImplementationLookup.java  |  10 +-
 .../main/resources/all/provisioning.properties  |   1 +
 .../resources/mariadb/provisioning.properties   |   1 +
 .../resources/mysql/provisioning.properties     |   1 +
 .../resources/oracle/provisioning.properties    |   1 +
 .../resources/postgres/provisioning.properties  |   1 +
 .../src/main/resources/provisioning.properties  |   1 +
 .../resources/sqlserver/provisioning.properties |   1 +
 .../syncope/fit/core/AbstractTaskITCase.java    |   2 +-
 .../syncope/fit/core/ConnectorITCase.java       |   2 -
 .../fit/core/NotificationTaskITCase.java        |   2 +-
 .../systemadministration/highavailability.adoc  |  26 +
 84 files changed, 3523 insertions(+), 3276 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/pom.xml
----------------------------------------------------------------------
diff --git a/core/logic/pom.xml b/core/logic/pom.xml
index 7baa863..0ab849f 100644
--- a/core/logic/pom.xml
+++ b/core/logic/pom.xml
@@ -51,10 +51,6 @@ 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.aspectj</groupId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index c4ee237..b4779df 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -136,6 +136,7 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectPatch
                 effectiveRealms, searchCond, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
         return CollectionUtils.collect(matchingAnyObjects, new Transformer<AnyObject, AnyObjectTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public AnyObjectTO transform(final AnyObject input) {
                 return binder.getAnyObjectTO(input, details);

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index c59907c..b93142d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -155,6 +155,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 userDAO.findAllGroups(userDAO.findByUsername(AuthContextUtils.getUsername())),
                 new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, true);
@@ -193,6 +194,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 page, size, orderBy),
                 new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, details);
@@ -220,6 +222,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
                 searchCondition, page, size, orderBy, AnyTypeKind.GROUP);
         return CollectionUtils.collect(matchingGroups, new Transformer<Group, GroupTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public GroupTO transform(final Group input) {
                 return binder.getGroupTO(input, details);
@@ -281,6 +284,7 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupPatch> {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
             sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
 
+                @Transactional(readOnly = true)
                 @Override
                 public String transform(final Group group) {
                     return group.getKey() + " " + group.getName();

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java b/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
index 87eda20..2d12682 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/LogicInvocationHandler.java
@@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -31,6 +32,7 @@ import org.aspectj.lang.reflect.MethodSignature;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 
 @Aspect
 public class LogicInvocationHandler {
@@ -43,6 +45,9 @@ public class LogicInvocationHandler {
     @Autowired
     private AuditManager auditManager;
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     @Around("execution(* org.apache.syncope.core.logic.AbstractLogic+.*(..))")
     public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
         Class<?> clazz = joinPoint.getTarget().getClass();
@@ -89,27 +94,17 @@ public class LogicInvocationHandler {
             LOG.debug("After throwing {}.{}", clazz.getSimpleName(), event);
             throw t;
         } finally {
-            if (notificationsAvailable) {
-                notificationManager.createTasks(AuditElements.EventCategoryType.LOGIC,
-                        category,
-                        null,
-                        event,
-                        condition,
-                        before,
-                        output,
-                        input);
-            }
-
-            if (auditRequested) {
-                auditManager.audit(AuditElements.EventCategoryType.LOGIC,
-                        category,
-                        null,
-                        event,
-                        condition,
-                        before,
-                        output,
-                        input);
-            }
+            publisher.publishEvent(new AfterHandlingEvent(this,
+                    notificationsAvailable,
+                    auditRequested,
+                    AuditElements.EventCategoryType.LOGIC,
+                    category,
+                    null,
+                    event,
+                    condition,
+                    before,
+                    output,
+                    input));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
index aa54c0d..3b867b4 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
@@ -30,12 +30,12 @@ import org.apache.syncope.common.lib.to.NotificationTO;
 import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
-import org.apache.syncope.core.logic.notification.NotificationJob;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 5a27343..1f232ad 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -40,29 +40,29 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobType;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.logic.cocoon.FopSerializer;
+import org.apache.syncope.core.logic.cocoon.TextSerializer;
+import org.apache.syncope.core.logic.cocoon.XSLTTransformer;
+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.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
 import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobNamer;
-import org.apache.syncope.core.logic.report.TextSerializer;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.JobTO;
-import org.apache.syncope.common.lib.types.JobAction;
-import org.apache.syncope.common.lib.types.JobType;
-import org.apache.syncope.common.lib.types.StandardEntitlement;
-import org.apache.syncope.core.logic.report.FopSerializer;
-import org.apache.syncope.core.logic.report.XSLTTransformer;
-import org.apache.syncope.core.persistence.api.dao.ConfDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.xmlgraphics.util.MimeConstants;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index bce3c09..149ddea 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -62,6 +62,8 @@ import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.PayloadApplicationEvent;
+import org.springframework.context.event.EventListener;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -237,10 +239,11 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
         }
     }
 
-    public void addLoadInstant(final SystemInfo.LoadInstant instant) {
+    @EventListener
+    public void addLoadInstant(final PayloadApplicationEvent<SystemInfo.LoadInstant> event) {
         synchronized (MONITOR) {
             initSystemInfo();
-            SYSTEM_INFO.getLoad().add(instant);
+            SYSTEM_INFO.getLoad().add(event.getPayload());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
deleted file mode 100644
index bb419bc..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SystemLoadReporterJob.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic;
-
-import java.lang.management.ManagementFactory;
-import org.apache.syncope.common.lib.info.SystemInfo;
-import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * Reports about system load.
- */
-@Component
-public class SystemLoadReporterJob extends AbstractInterruptableJob {
-
-    private static final Integer MB = 1024 * 1024;
-
-    @Autowired
-    private SyncopeLogic logic;
-
-    @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);
-
-        logic.addLoadInstant(instant);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 21cb4ee..a310e47 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -53,11 +53,11 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
 import org.apache.syncope.core.provisioning.api.job.JobNamer;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
-import org.apache.syncope.core.logic.notification.NotificationJobDelegate;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJobDelegate;
 import org.quartz.JobDataMap;
 import org.quartz.JobKey;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 1672cfd..7cc5414 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -118,6 +118,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
                 page, size, orderBy),
                 new Transformer<User, UserTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public UserTO transform(final User input) {
                 return binder.returnUserTO(binder.getUserTO(input, details));
@@ -160,6 +161,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
                 searchCondition, page, size, orderBy, AnyTypeKind.USER);
         return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() {
 
+            @Transactional(readOnly = true)
             @Override
             public UserTO transform(final User input) {
                 return binder.returnUserTO(binder.getUserTO(input, details));

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
new file mode 100644
index 0000000..bacec1f
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import java.io.File;
+import java.io.OutputStream;
+
+import org.apache.cocoon.pipeline.ProcessingException;
+import org.apache.cocoon.pipeline.caching.CacheKey;
+import org.apache.cocoon.pipeline.caching.SimpleCacheKey;
+import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
+import org.apache.cocoon.pipeline.util.StringRepresentation;
+import org.apache.cocoon.sax.AbstractSAXSerializer;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FopFactoryBuilder;
+import org.apache.xmlgraphics.util.MimeConstants;
+import org.xml.sax.ContentHandler;
+
+public class FopSerializer extends AbstractSAXSerializer implements CachingPipelineComponent {
+
+    private static final FopFactory FOP_FACTORY = new FopFactoryBuilder(new File(".").toURI()).build();
+
+    private String outputFormat;
+
+    /**
+     * Create a new FOP serializer that produces a PDF in output
+     */
+    public FopSerializer() {
+        this(MimeConstants.MIME_PDF);
+    }
+
+    /**
+     * Create a new FOP serializer that produces the specified mime
+     *
+     * @param outputFormat the output's mime type
+     */
+    public FopSerializer(final String outputFormat) {
+        if (outputFormat == null) {
+            throw new IllegalArgumentException("The parameter 'outputFormat' mustn't be null.");
+        }
+
+        this.outputFormat = outputFormat;
+    }
+
+    @Override
+    public CacheKey constructCacheKey() {
+        return new SimpleCacheKey();
+    }
+
+    @Override
+    public void setOutputStream(final OutputStream outputStream) {
+        try {
+            Fop fop = FOP_FACTORY.newFop(this.outputFormat, outputStream);
+            ContentHandler fopContentHandler = fop.getDefaultHandler();
+
+            this.setContentHandler(fopContentHandler);
+        } catch (FOPException e) {
+            throw new ProcessingException("Impossible to initialize FOPSerializer", e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return StringRepresentation.buildString(this, "outputFormat=" + this.outputFormat);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
new file mode 100644
index 0000000..ce4ffb4
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import org.apache.cocoon.sax.component.XMLSerializer;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * Converts XML into plain text. It omits all XML tags and writes only character events to the output. Input document
+ * must have at least one element - root element - which should wrap all the text inside it.
+ *
+ */
+public class TextSerializer extends XMLSerializer {
+
+    private static final String UTF_8 = "UTF-8";
+
+    private static final String TXT = "text";
+
+    public TextSerializer() {
+        super();
+        super.setOmitXmlDeclaration(true);
+    }
+
+    @Override
+    public void setDocumentLocator(final Locator locator) {
+        // nothing
+    }
+
+    @Override
+    public void processingInstruction(final String target, final String data)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void startDTD(final String name, final String publicId, final String systemId)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void endDTD() throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void startElement(final String uri, final String loc, final String raw, final Attributes atts)
+            throws SAXException {
+        // nothing
+    }
+
+    @Override
+    public void endElement(final String uri, final String name, final String raw)
+            throws SAXException {
+        // nothing
+    }
+
+    public static TextSerializer createPlainSerializer() {
+        final TextSerializer serializer = new TextSerializer();
+        serializer.setContentType("text/plain; charset=" + UTF_8);
+        serializer.setEncoding(UTF_8);
+        serializer.setMethod(TXT);
+        return serializer;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
new file mode 100644
index 0000000..1d8c6e2
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.syncope.core.logic.cocoon;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import org.apache.cocoon.pipeline.SetupException;
+import org.apache.cocoon.pipeline.caching.CacheKey;
+import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
+import org.apache.cocoon.pipeline.util.StringRepresentation;
+import org.apache.cocoon.sax.AbstractSAXTransformer;
+import org.apache.cocoon.sax.SAXConsumer;
+import org.apache.cocoon.sax.util.SAXConsumerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
+
+    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
+
+    /**
+     * A generic transformer factory to parse XSLTs.
+     */
+    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
+
+    /**
+     * The XSLT parameters name pattern.
+     */
+    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
+
+    /**
+     * The XSLT parameters reference.
+     */
+    private Map<String, Object> parameters;
+
+    /**
+     * The XSLT Template reference.
+     */
+    private Templates templates;
+
+    private Source source;
+
+    public XSLTTransformer(final Source source) {
+        super();
+        this.load(source, null);
+    }
+
+    /**
+     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
+     *
+     * This constructor is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param attributes the Transformer Factory attributes
+     */
+    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
+        super();
+        this.load(source, attributes);
+    }
+
+    /**
+     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
+     * Factory attributes.
+     *
+     * This method is useful when users want to perform XSLT transformation using <a
+     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
+     *
+     * @param source the XSLT source
+     * @param attributes the Transformer Factory attributes
+     */
+    private void load(final Source source, final Map<String, Object> attributes) {
+        if (source == null) {
+            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
+        }
+
+        this.source = source;
+
+        this.load(this.source, this.source.toString(), attributes);
+    }
+
+    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
+        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
+
+        // XSLT has to be parsed
+        final SAXTransformerFactory transformerFactory;
+        if (attributes == null || attributes.isEmpty()) {
+            transformerFactory = TRAX_FACTORY;
+        } else {
+            transformerFactory = createNewSAXTransformerFactory();
+            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
+                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
+            }
+        }
+
+        try {
+            this.templates = transformerFactory.newTemplates(source);
+        } catch (TransformerConfigurationException e) {
+            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
+        }
+    }
+
+    /**
+     * Sets the XSLT parameters to be applied to XSLT stylesheet.
+     *
+     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
+     */
+    public void setParameters(final Map<String, ? extends Object> parameters) {
+        if (parameters == null) {
+            this.parameters = null;
+        } else {
+            this.parameters = new HashMap<String, Object>(parameters);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void setSAXConsumer(final SAXConsumer consumer) {
+        TransformerHandler transformerHandler;
+        try {
+            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
+        } catch (Exception e) {
+            throw new SetupException("Could not initialize transformer handler.", e);
+        }
+
+        if (this.parameters != null) {
+            final Transformer transformer = transformerHandler.getTransformer();
+
+            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
+                final String name = entry.getKey();
+
+                // is valid XSLT parameter name
+                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
+                    transformer.setParameter(name, entry.getValue());
+                }
+            }
+        }
+
+        final SAXResult result = new SAXResult();
+        result.setHandler(consumer);
+        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
+        result.setLexicalHandler(consumer);
+        transformerHandler.setResult(result);
+
+        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
+        saxConsumerAdapter.setContentHandler(transformerHandler);
+        super.setSAXConsumer(saxConsumerAdapter);
+    }
+
+    @Override
+    public CacheKey constructCacheKey() {
+        return null;
+    }
+
+    /**
+     * Utility method to create a new transformer factory.
+     *
+     * @return a new transformer factory
+     */
+    private static SAXTransformerFactory createNewSAXTransformerFactory() {
+        return (SAXTransformerFactory) TransformerFactory.newInstance();
+    }
+
+    @Override
+    public String toString() {
+        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
deleted file mode 100644
index d8b08f1..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/JobManagerImpl.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.init;
-
-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.logic.SystemLoadReporterJob;
-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.logic.notification.NotificationJob;
-import org.apache.syncope.core.logic.report.ReportJob;
-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.job.TaskJob;
-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.stereotype.Component;
-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;
-
-@Component
-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 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() {
-        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/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
deleted file mode 100644
index 7a0270f..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.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...");
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
deleted file mode 100644
index ea9b1da..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJobDelegate.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.notification;
-
-import java.util.Date;
-import java.util.Properties;
-import javax.mail.internet.MimeMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-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.AuditManager;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@Component
-public class NotificationJobDelegate {
-
-    private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
-
-    /**
-     * Task DAO.
-     */
-    @Autowired
-    private TaskDAO taskDAO;
-
-    @Autowired
-    private JavaMailSender mailSender;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AuditManager auditManager;
-
-    @Autowired
-    private NotificationManager notificationManager;
-
-    private long maxRetries;
-
-    private void init() {
-        maxRetries = notificationManager.getMaxRetries();
-
-        if (mailSender instanceof JavaMailSenderImpl
-                && StringUtils.isNotBlank(((JavaMailSenderImpl) mailSender).getUsername())) {
-
-            Properties javaMailProperties = ((JavaMailSenderImpl) mailSender).getJavaMailProperties();
-            javaMailProperties.setProperty("mail.smtp.auth", "true");
-            ((JavaMailSenderImpl) mailSender).setJavaMailProperties(javaMailProperties);
-        }
-    }
-
-    @Transactional
-    public TaskExec executeSingle(final NotificationTask task) {
-        init();
-
-        TaskExec execution = entityFactory.newEntity(TaskExec.class);
-        execution.setTask(task);
-        execution.setStart(new Date());
-
-        boolean retryPossible = true;
-
-        if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
-                || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
-
-            String message = "Could not fetch all required information for sending e-mails:\n"
-                    + task.getRecipients() + "\n"
-                    + task.getSender() + "\n"
-                    + task.getSubject() + "\n"
-                    + task.getHtmlBody() + "\n"
-                    + task.getTextBody();
-            LOG.error(message);
-
-            execution.setStatus(NotificationJob.Status.NOT_SENT.name());
-            retryPossible = false;
-
-            if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
-                execution.setMessage(message);
-            }
-        } else {
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("About to send e-mails:\n"
-                        + task.getRecipients() + "\n"
-                        + task.getSender() + "\n"
-                        + task.getSubject() + "\n"
-                        + task.getHtmlBody() + "\n"
-                        + task.getTextBody() + "\n");
-            }
-
-            for (String to : task.getRecipients()) {
-                try {
-                    MimeMessage message = mailSender.createMimeMessage();
-                    MimeMessageHelper helper = new MimeMessageHelper(message, true);
-                    helper.setTo(to);
-                    helper.setFrom(task.getSender());
-                    helper.setSubject(task.getSubject());
-                    helper.setText(task.getTextBody(), task.getHtmlBody());
-
-                    mailSender.send(message);
-
-                    execution.setStatus(NotificationJob.Status.SENT.name());
-
-                    StringBuilder report = new StringBuilder();
-                    switch (task.getTraceLevel()) {
-                        case ALL:
-                            report.append("FROM: ").append(task.getSender()).append('\n').
-                                    append("TO: ").append(to).append('\n').
-                                    append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
-                                    append(task.getTextBody()).append('\n').append('\n').
-                                    append(task.getHtmlBody()).append('\n');
-                            break;
-
-                        case SUMMARY:
-                            report.append("E-mail sent to ").append(to).append('\n');
-                            break;
-
-                        case FAILURES:
-                        case NONE:
-                        default:
-                    }
-                    if (report.length() > 0) {
-                        execution.setMessage(report.toString());
-                    }
-
-                    auditManager.audit(
-                            AuditElements.EventCategoryType.TASK,
-                            "notification",
-                            null,
-                            "send",
-                            AuditElements.Result.SUCCESS,
-                            null,
-                            null,
-                            task,
-                            "Successfully sent notification to " + to);
-                } catch (Exception e) {
-                    LOG.error("Could not send e-mail", e);
-
-                    execution.setStatus(NotificationJob.Status.NOT_SENT.name());
-                    if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
-                        execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
-                    }
-
-                    auditManager.audit(
-                            AuditElements.EventCategoryType.TASK,
-                            "notification",
-                            null,
-                            "send",
-                            AuditElements.Result.FAILURE,
-                            null,
-                            null,
-                            task,
-                            "Could not send notification to " + to, e);
-                }
-
-                execution.setEnd(new Date());
-            }
-        }
-
-        if (hasToBeRegistered(execution)) {
-            execution = notificationManager.storeExec(execution);
-            if (retryPossible
-                    && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
-
-                handleRetries(execution);
-            }
-        } else {
-            notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
-        }
-
-        return execution;
-    }
-
-    @Transactional
-    public void execute() throws JobExecutionException {
-        for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
-            LOG.debug("Found notification task {} to be executed: starting...", task);
-            executeSingle(task);
-            LOG.debug("Notification task {} executed", task);
-        }
-    }
-
-    private boolean hasToBeRegistered(final TaskExec execution) {
-        NotificationTask task = (NotificationTask) execution.getTask();
-
-        // True if either failed and failures have to be registered, or if ALL
-        // has to be registered.
-        return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
-                && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
-                || task.getTraceLevel() == TraceLevel.ALL;
-    }
-
-    private void handleRetries(final TaskExec execution) {
-        if (maxRetries <= 0) {
-            return;
-        }
-
-        long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
-                execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
-
-        if (failedExecutionsCount <= maxRetries) {
-            LOG.debug("Execution of notification task {} will be retried [{}/{}]",
-                    execution.getTask(), failedExecutionsCount, maxRetries);
-            notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
-
-            auditManager.audit(
-                    AuditElements.EventCategoryType.TASK,
-                    "notification",
-                    null,
-                    "retry",
-                    AuditElements.Result.SUCCESS,
-                    null,
-                    null,
-                    execution,
-                    "Notification task " + execution.getTask().getKey() + " will be retried");
-        } else {
-            LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
-
-            auditManager.audit(
-                    AuditElements.EventCategoryType.TASK,
-                    "notification",
-                    null,
-                    "retry",
-                    AuditElements.Result.FAILURE,
-                    null,
-                    null,
-                    execution,
-                    "Giving up retries on notification task " + execution.getTask().getKey());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
deleted file mode 100644
index 69e6b4f..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-public abstract class AbstractReportlet implements Reportlet {
-
-    protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
-
-    protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
-
-    @Override
-    @Transactional(readOnly = true)
-    public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf == null) {
-            throw new ReportException(new IllegalArgumentException("No configuration provided"));
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-        atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, conf.getName());
-        atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
-        handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
-
-        doExtract(conf, handler);
-
-        handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
deleted file mode 100644
index ffd94a4..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AuditReportlet.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.List;
-import java.util.Map;
-import javax.sql.DataSource;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.syncope.common.lib.report.AuditReportletConf;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.provisioning.java.AuditEntry;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.DomainsHolder;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(AuditReportletConf.class)
-public class AuditReportlet extends AbstractReportlet {
-
-    @Autowired
-    private DomainsHolder domainsHolder;
-
-    private AuditReportletConf conf;
-
-    private DataSource datasource;
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
-        jdbcTemplate.setMaxRows(conf.getSize());
-        List<Map<String, Object>> rows = jdbcTemplate.
-                queryForList("SELECT * FROM SYNCOPEAUDIT ORDER BY EVENT_DATE DESC");
-
-        handler.startElement("", "", "events", null);
-        AttributesImpl atts = new AttributesImpl();
-        for (Map<String, Object> row : rows) {
-            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
-
-            atts.clear();
-            if (StringUtils.isNotBlank(auditEntry.getWho())) {
-                atts.addAttribute("", "", "who", ReportXMLConst.XSD_STRING, auditEntry.getWho());
-            }
-            handler.startElement("", "", "event", atts);
-
-            atts.clear();
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getCategory())) {
-                atts.addAttribute("", "", "category",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getCategory());
-            }
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getSubcategory())) {
-                atts.addAttribute("", "", "subcategory",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getSubcategory());
-            }
-            if (StringUtils.isNotBlank(auditEntry.getLogger().getEvent())) {
-                atts.addAttribute("", "", "event",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getEvent());
-            }
-            if (auditEntry.getLogger().getResult() != null) {
-                atts.addAttribute("", "", "result",
-                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getResult().name());
-            }
-            handler.startElement("", "", "logger", atts);
-            handler.endElement("", "", "logger");
-
-            if (auditEntry.getBefore() != null) {
-                char[] before = ToStringBuilder.reflectionToString(
-                        auditEntry.getBefore(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                handler.startElement("", "", "before", null);
-                handler.characters(before, 0, before.length);
-                handler.endElement("", "", "before");
-            }
-
-            if (auditEntry.getInput() != null) {
-                handler.startElement("", "", "inputs", null);
-                for (Object inputObj : auditEntry.getInput()) {
-                    char[] input = ToStringBuilder.reflectionToString(
-                            inputObj, ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                    handler.startElement("", "", "input", null);
-                    handler.characters(input, 0, input.length);
-                    handler.endElement("", "", "input");
-                }
-                handler.endElement("", "", "inputs");
-            }
-
-            if (auditEntry.getOutput() != null) {
-                char[] output = ToStringBuilder.reflectionToString(
-                        auditEntry.getOutput(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
-                handler.startElement("", "", "output", null);
-                handler.characters(output, 0, output.length);
-                handler.endElement("", "", "output");
-            }
-
-            handler.startElement("", "", "throwable", null);
-            char[] throwable = row.get("THROWABLE").toString().toCharArray();
-            handler.characters(throwable, 0, throwable.length);
-            handler.endElement("", "", "throwable");
-
-            handler.endElement("", "", "event");
-        }
-        handler.endElement("", "", "events");
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof AuditReportletConf) {
-            this.conf = AuditReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        datasource = domainsHolder.getDomains().get(AuthContextUtils.getDomain());
-        if (datasource == null) {
-            throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
-        }
-
-        doExtractConf(handler);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
deleted file mode 100644
index d90a619..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/FopSerializer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.io.File;
-import java.io.OutputStream;
-
-import org.apache.cocoon.pipeline.ProcessingException;
-import org.apache.cocoon.pipeline.caching.CacheKey;
-import org.apache.cocoon.pipeline.caching.SimpleCacheKey;
-import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
-import org.apache.cocoon.pipeline.util.StringRepresentation;
-import org.apache.cocoon.sax.AbstractSAXSerializer;
-import org.apache.fop.apps.FOPException;
-import org.apache.fop.apps.Fop;
-import org.apache.fop.apps.FopFactory;
-import org.apache.fop.apps.FopFactoryBuilder;
-import org.apache.xmlgraphics.util.MimeConstants;
-import org.xml.sax.ContentHandler;
-
-public class FopSerializer extends AbstractSAXSerializer implements CachingPipelineComponent {
-
-    private static final FopFactory FOP_FACTORY = new FopFactoryBuilder(new File(".").toURI()).build();
-
-    private String outputFormat;
-
-    /**
-     * Create a new FOP serializer that produces a PDF in output
-     */
-    public FopSerializer() {
-        this(MimeConstants.MIME_PDF);
-    }
-
-    /**
-     * Create a new FOP serializer that produces the specified mime
-     *
-     * @param outputFormat the output's mime type
-     */
-    public FopSerializer(final String outputFormat) {
-        if (outputFormat == null) {
-            throw new IllegalArgumentException("The parameter 'outputFormat' mustn't be null.");
-        }
-
-        this.outputFormat = outputFormat;
-    }
-
-    @Override
-    public CacheKey constructCacheKey() {
-        return new SimpleCacheKey();
-    }
-
-    @Override
-    public void setOutputStream(final OutputStream outputStream) {
-        try {
-            Fop fop = FOP_FACTORY.newFop(this.outputFormat, outputStream);
-            ContentHandler fopContentHandler = fop.getDefaultHandler();
-
-            this.setContentHandler(fopContentHandler);
-        } catch (FOPException e) {
-            throw new ProcessingException("Impossible to initialize FOPSerializer", e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return StringRepresentation.buildString(this, "outputFormat=" + this.outputFormat);
-    }
-}


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

Posted by il...@apache.org.
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/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
new file mode 100644
index 0000000..0d37d86
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
@@ -0,0 +1,268 @@
+/*
+ * 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 java.util.Date;
+import java.util.Properties;
+import javax.mail.internet.MimeMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+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.AuditManager;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class NotificationJobDelegate {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
+
+    /**
+     * Task DAO.
+     */
+    @Autowired
+    private TaskDAO taskDAO;
+
+    @Autowired
+    private JavaMailSender mailSender;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private AuditManager auditManager;
+
+    @Autowired
+    private NotificationManager notificationManager;
+
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
+    private long maxRetries;
+
+    private void init() {
+        maxRetries = notificationManager.getMaxRetries();
+
+        if (mailSender instanceof JavaMailSenderImpl
+                && StringUtils.isNotBlank(((JavaMailSenderImpl) mailSender).getUsername())) {
+
+            Properties javaMailProperties = ((JavaMailSenderImpl) mailSender).getJavaMailProperties();
+            javaMailProperties.setProperty("mail.smtp.auth", "true");
+            ((JavaMailSenderImpl) mailSender).setJavaMailProperties(javaMailProperties);
+        }
+    }
+
+    @Transactional
+    public TaskExec executeSingle(final NotificationTask task) {
+        init();
+
+        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        execution.setTask(task);
+        execution.setStart(new Date());
+
+        boolean retryPossible = true;
+
+        if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
+                || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
+
+            String message = "Could not fetch all required information for sending e-mails:\n"
+                    + task.getRecipients() + "\n"
+                    + task.getSender() + "\n"
+                    + task.getSubject() + "\n"
+                    + task.getHtmlBody() + "\n"
+                    + task.getTextBody();
+            LOG.error(message);
+
+            execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+            retryPossible = false;
+
+            if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+                execution.setMessage(message);
+            }
+        } else {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("About to send e-mails:\n"
+                        + task.getRecipients() + "\n"
+                        + task.getSender() + "\n"
+                        + task.getSubject() + "\n"
+                        + task.getHtmlBody() + "\n"
+                        + task.getTextBody() + "\n");
+            }
+
+            for (String to : task.getRecipients()) {
+                try {
+                    MimeMessage message = mailSender.createMimeMessage();
+                    MimeMessageHelper helper = new MimeMessageHelper(message, true);
+                    helper.setTo(to);
+                    helper.setFrom(task.getSender());
+                    helper.setSubject(task.getSubject());
+                    helper.setText(task.getTextBody(), task.getHtmlBody());
+
+                    mailSender.send(message);
+
+                    execution.setStatus(NotificationJob.Status.SENT.name());
+
+                    StringBuilder report = new StringBuilder();
+                    switch (task.getTraceLevel()) {
+                        case ALL:
+                            report.append("FROM: ").append(task.getSender()).append('\n').
+                                    append("TO: ").append(to).append('\n').
+                                    append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
+                                    append(task.getTextBody()).append('\n').append('\n').
+                                    append(task.getHtmlBody()).append('\n');
+                            break;
+
+                        case SUMMARY:
+                            report.append("E-mail sent to ").append(to).append('\n');
+                            break;
+
+                        case FAILURES:
+                        case NONE:
+                        default:
+                    }
+                    if (report.length() > 0) {
+                        execution.setMessage(report.toString());
+                    }
+
+                    publisher.publishEvent(new AfterHandlingEvent(this,
+                            true,
+                            true,
+                            AuditElements.EventCategoryType.TASK,
+                            "notification",
+                            null,
+                            "send",
+                            AuditElements.Result.SUCCESS,
+                            null,
+                            null,
+                            task,
+                            "Successfully sent notification to " + to));
+                } catch (Exception e) {
+                    LOG.error("Could not send e-mail", e);
+
+                    execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+                    if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+                        execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
+                    }
+
+                    publisher.publishEvent(new AfterHandlingEvent(this,
+                            true,
+                            true,
+                            AuditElements.EventCategoryType.TASK,
+                            "notification",
+                            null,
+                            "send",
+                            AuditElements.Result.FAILURE,
+                            null,
+                            null,
+                            task,
+                            "Could not send notification to " + to, e));
+                }
+
+                execution.setEnd(new Date());
+            }
+        }
+
+        if (hasToBeRegistered(execution)) {
+            execution = notificationManager.storeExec(execution);
+            if (retryPossible
+                    && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
+
+                handleRetries(execution);
+            }
+        } else {
+            notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
+        }
+
+        return execution;
+    }
+
+    @Transactional
+    public void execute() throws JobExecutionException {
+        for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
+            LOG.debug("Found notification task {} to be executed: starting...", task);
+            executeSingle(task);
+            LOG.debug("Notification task {} executed", task);
+        }
+    }
+
+    private boolean hasToBeRegistered(final TaskExec execution) {
+        NotificationTask task = (NotificationTask) execution.getTask();
+
+        // True if either failed and failures have to be registered, or if ALL
+        // has to be registered.
+        return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
+                && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+                || task.getTraceLevel() == TraceLevel.ALL;
+    }
+
+    private void handleRetries(final TaskExec execution) {
+        if (maxRetries <= 0) {
+            return;
+        }
+
+        long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
+                execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
+
+        if (failedExecutionsCount <= maxRetries) {
+            LOG.debug("Execution of notification task {} will be retried [{}/{}]",
+                    execution.getTask(), failedExecutionsCount, maxRetries);
+            notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.TASK,
+                    "notification",
+                    null,
+                    "retry",
+                    AuditElements.Result.SUCCESS,
+                    null,
+                    null,
+                    execution,
+                    "Notification task " + execution.getTask().getKey() + " will be retried");
+        } else {
+            LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.TASK,
+                    "notification",
+                    null,
+                    "retry",
+                    AuditElements.Result.FAILURE,
+                    null,
+                    null,
+                    execution,
+                    "Giving up retries on notification task " + execution.getTask().getKey());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
new file mode 100644
index 0000000..2c85b20
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.report;
+
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+public abstract class AbstractReportlet implements Reportlet {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
+
+    protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
+
+    @Override
+    @Transactional(readOnly = true)
+    public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf == null) {
+            throw new ReportException(new IllegalArgumentException("No configuration provided"));
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+        atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, conf.getName());
+        atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
+        handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
+
+        doExtract(conf, handler);
+
+        handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
new file mode 100644
index 0000000..96039f0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -0,0 +1,141 @@
+/*
+ * 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.report;
+
+import java.util.List;
+import java.util.Map;
+import javax.sql.DataSource;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.syncope.common.lib.report.AuditReportletConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.provisioning.java.AuditEntry;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(AuditReportletConf.class)
+public class AuditReportlet extends AbstractReportlet {
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    private AuditReportletConf conf;
+
+    private DataSource datasource;
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
+        jdbcTemplate.setMaxRows(conf.getSize());
+        List<Map<String, Object>> rows = jdbcTemplate.
+                queryForList("SELECT * FROM SYNCOPEAUDIT ORDER BY EVENT_DATE DESC");
+
+        handler.startElement("", "", "events", null);
+        AttributesImpl atts = new AttributesImpl();
+        for (Map<String, Object> row : rows) {
+            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
+
+            atts.clear();
+            if (StringUtils.isNotBlank(auditEntry.getWho())) {
+                atts.addAttribute("", "", "who", ReportXMLConst.XSD_STRING, auditEntry.getWho());
+            }
+            handler.startElement("", "", "event", atts);
+
+            atts.clear();
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getCategory())) {
+                atts.addAttribute("", "", "category",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getCategory());
+            }
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getSubcategory())) {
+                atts.addAttribute("", "", "subcategory",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getSubcategory());
+            }
+            if (StringUtils.isNotBlank(auditEntry.getLogger().getEvent())) {
+                atts.addAttribute("", "", "event",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getEvent());
+            }
+            if (auditEntry.getLogger().getResult() != null) {
+                atts.addAttribute("", "", "result",
+                        ReportXMLConst.XSD_STRING, auditEntry.getLogger().getResult().name());
+            }
+            handler.startElement("", "", "logger", atts);
+            handler.endElement("", "", "logger");
+
+            if (auditEntry.getBefore() != null) {
+                char[] before = ToStringBuilder.reflectionToString(
+                        auditEntry.getBefore(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                handler.startElement("", "", "before", null);
+                handler.characters(before, 0, before.length);
+                handler.endElement("", "", "before");
+            }
+
+            if (auditEntry.getInput() != null) {
+                handler.startElement("", "", "inputs", null);
+                for (Object inputObj : auditEntry.getInput()) {
+                    char[] input = ToStringBuilder.reflectionToString(
+                            inputObj, ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                    handler.startElement("", "", "input", null);
+                    handler.characters(input, 0, input.length);
+                    handler.endElement("", "", "input");
+                }
+                handler.endElement("", "", "inputs");
+            }
+
+            if (auditEntry.getOutput() != null) {
+                char[] output = ToStringBuilder.reflectionToString(
+                        auditEntry.getOutput(), ToStringStyle.MULTI_LINE_STYLE).toCharArray();
+                handler.startElement("", "", "output", null);
+                handler.characters(output, 0, output.length);
+                handler.endElement("", "", "output");
+            }
+
+            handler.startElement("", "", "throwable", null);
+            char[] throwable = row.get("THROWABLE").toString().toCharArray();
+            handler.characters(throwable, 0, throwable.length);
+            handler.endElement("", "", "throwable");
+
+            handler.endElement("", "", "event");
+        }
+        handler.endElement("", "", "events");
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof AuditReportletConf) {
+            this.conf = AuditReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        datasource = domainsHolder.getDomains().get(AuthContextUtils.getDomain());
+        if (datasource == null) {
+            throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
+        }
+
+        doExtractConf(handler);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
new file mode 100644
index 0000000..fa558b6
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
@@ -0,0 +1,316 @@
+/*
+ * 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.report;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.GroupReportletConf;
+import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(GroupReportletConf.class)
+public class GroupReportlet extends AbstractReportlet {
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private GroupDataBinder groupDataBinder;
+
+    private GroupReportletConf conf;
+
+    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
+            throws SAXException {
+
+        if (anyTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : anyTO.getResources()) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
+                handler.startElement("", "", "resource", atts);
+                handler.endElement("", "", "resource");
+            }
+
+            handler.endElement("", "", "resources");
+        }
+    }
+
+    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
+            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+        if (!attrs.isEmpty()) {
+            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
+
+            handler.startElement("", "", "attributes", null);
+            for (String attrName : attrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "attribute", atts);
+
+                if (attrMap.containsKey(attrName)) {
+                    for (String value : attrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
+
+            handler.startElement("", "", "derivedAttributes", null);
+            for (String attrName : derAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "derivedAttribute", atts);
+
+                if (derAttrMap.containsKey(attrName)) {
+                    for (String value : derAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
+
+            handler.startElement("", "", "virtualAttributes", null);
+            for (String attrName : virAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "virtualAttribute", atts);
+
+                if (virAttrMap.containsKey(attrName)) {
+                    for (String value : virAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        for (Group group : groups) {
+            atts.clear();
+
+            for (Feature feature : conf.getFeatures()) {
+                String type = null;
+                String value = null;
+                switch (feature) {
+                    case key:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getKey();
+                        break;
+
+                    case name:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = String.valueOf(group.getName());
+                        break;
+
+                    case groupOwner:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getGroupOwner().getKey();
+                        break;
+
+                    case userOwner:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = group.getUserOwner().getKey();
+                        break;
+
+                    default:
+                }
+
+                if (type != null && value != null) {
+                    atts.addAttribute("", "", feature.name(), type, value);
+                }
+            }
+
+            handler.startElement("", "", "group", atts);
+
+            // Using GroupTO for attribute values, since the conversion logic of
+            // values to String is already encapsulated there
+            GroupTO groupTO = groupDataBinder.getGroupTO(group, true);
+
+            doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+
+            // to get resources associated to a group
+            if (conf.getFeatures().contains(Feature.resources)) {
+                doExtractResources(handler, groupTO);
+            }
+            //to get users asscoiated to a group is preferred GroupDAO to GroupTO
+            if (conf.getFeatures().contains(Feature.users)) {
+                handler.startElement("", "", "users", null);
+
+                for (UMembership memb : groupDAO.findUMemberships(group)) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_STRING,
+                            memb.getLeftEnd().getKey());
+                    atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING,
+                            String.valueOf(memb.getLeftEnd().getUsername()));
+
+                    handler.startElement("", "", "user", atts);
+                    handler.endElement("", "", "user");
+                }
+
+                handler.endElement("", "", "users");
+            }
+
+            handler.endElement("", "", "group");
+        }
+    }
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        if (conf == null) {
+            LOG.debug("Report configuration is not present");
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "groupAttributes", atts);
+
+        if (conf != null) {
+            for (Feature feature : conf.getFeatures()) {
+                atts.clear();
+                handler.startElement("", "", "feature", atts);
+                handler.characters(feature.name().toCharArray(), 0, feature.name().length());
+                handler.endElement("", "", "feature");
+            }
+
+            for (String attr : conf.getPlainAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "attribute", atts);
+                handler.characters(attr.toCharArray(), 0, attr.length());
+                handler.endElement("", "", "attribute");
+            }
+
+            for (String derAttr : conf.getDerAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "derAttribute", atts);
+                handler.characters(derAttr.toCharArray(), 0, derAttr.length());
+                handler.endElement("", "", "derAttribute");
+            }
+
+            for (String virAttr : conf.getVirAttrs()) {
+                atts.clear();
+                handler.startElement("", "", "virAttribute", atts);
+                handler.characters(virAttr.toCharArray(), 0, virAttr.length());
+                handler.endElement("", "", "virAttribute");
+            }
+        }
+
+        handler.endElement("", "", "groupAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    private int count() {
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? groupDAO.count()
+                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP);
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof GroupReportletConf) {
+            this.conf = GroupReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<Group> groups;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                groups = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(this.conf.getMatchingCond()),
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
+            }
+
+            doExtract(handler, groups);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
new file mode 100644
index 0000000..f39ee1c
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
@@ -0,0 +1,519 @@
+/*
+ * 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.report;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.commons.collections4.Closure;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
+import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Reportlet for extracting information for the current reconciliation status, e.g. the coherence between Syncope
+ * information and mapped entities on external resources.
+ */
+@ReportletConfClass(ReconciliationReportletConf.class)
+public class ReconciliationReportlet extends AbstractReportlet {
+
+    private static final int PAGE_SIZE = 10;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private MappingManager mappingManager;
+
+    @Autowired
+    private ConnectorFactory connFactory;
+
+    @Autowired
+    private AnyUtilsFactory anyUtilsFactory;
+
+    private ReconciliationReportletConf conf;
+
+    private String getAnyElementName(final AnyTypeKind anyTypeKind) {
+        String elementName;
+
+        switch (anyTypeKind) {
+            case USER:
+                elementName = "user";
+                break;
+
+            case GROUP:
+                elementName = "group";
+                break;
+
+            case ANY_OBJECT:
+            default:
+                elementName = "anyObject";
+        }
+
+        return elementName;
+    }
+
+    private void doExtract(
+            final ContentHandler handler,
+            final Any<?> any,
+            final Set<Missing> missing,
+            final Set<Misaligned> misaligned)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+
+        for (Feature feature : conf.getFeatures()) {
+            String type = null;
+            String value = null;
+            switch (feature) {
+                case key:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getKey();
+                    break;
+
+                case username:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_STRING;
+                        value = User.class.cast(any).getUsername();
+                    }
+                    break;
+
+                case groupName:
+                    if (any instanceof Group) {
+                        type = ReportXMLConst.XSD_STRING;
+                        value = Group.class.cast(any).getName();
+                    }
+                    break;
+
+                case workflowId:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getWorkflowId();
+                    break;
+
+                case status:
+                    type = ReportXMLConst.XSD_STRING;
+                    value = any.getStatus();
+                    break;
+
+                case creationDate:
+                    type = ReportXMLConst.XSD_DATETIME;
+                    value = any.getCreationDate() == null
+                            ? StringUtils.EMPTY
+                            : FormatUtils.format(any.getCreationDate());
+                    break;
+
+                case lastLoginDate:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = User.class.cast(any).getLastLoginDate() == null
+                                ? StringUtils.EMPTY
+                                : FormatUtils.format(User.class.cast(any).getLastLoginDate());
+                    }
+                    break;
+
+                case changePwdDate:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = User.class.cast(any).getChangePwdDate() == null
+                                ? StringUtils.EMPTY
+                                : FormatUtils.format(User.class.cast(any).getChangePwdDate());
+                    }
+                    break;
+
+                case passwordHistorySize:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(User.class.cast(any).getPasswordHistory().size());
+                    }
+                    break;
+
+                case failedLoginCount:
+                    if (any instanceof User) {
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(User.class.cast(any).getFailedLogins());
+                    }
+                    break;
+
+                default:
+            }
+
+            if (type != null && value != null) {
+                atts.addAttribute("", "", feature.name(), type, value);
+            }
+        }
+
+        handler.startElement("", "", getAnyElementName(any.getType().getKind()), atts);
+
+        for (Missing item : missing) {
+            atts.clear();
+            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
+            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
+
+            handler.startElement("", "", "missing", atts);
+            handler.endElement("", "", "missing");
+        }
+        for (Misaligned item : misaligned) {
+            atts.clear();
+            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
+            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
+            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, item.getName());
+
+            handler.startElement("", "", "misaligned", atts);
+
+            handler.startElement("", "", "onSyncope", null);
+            if (item.getOnSyncope() != null) {
+                for (Object value : item.getOnSyncope()) {
+                    char[] asChars = value.toString().toCharArray();
+
+                    handler.startElement("", "", "value", null);
+                    handler.characters(asChars, 0, asChars.length);
+                    handler.endElement("", "", "value");
+                }
+            }
+            handler.endElement("", "", "onSyncope");
+
+            handler.startElement("", "", "onResource", null);
+            if (item.getOnResource() != null) {
+                for (Object value : item.getOnResource()) {
+                    char[] asChars = value.toString().toCharArray();
+
+                    handler.startElement("", "", "value", null);
+                    handler.characters(asChars, 0, asChars.length);
+                    handler.endElement("", "", "value");
+                }
+            }
+            handler.endElement("", "", "onResource");
+
+            handler.endElement("", "", "misaligned");
+        }
+
+        handler.endElement("", "", getAnyElementName(any.getType().getKind()));
+    }
+
+    private Set<Object> getValues(final Attribute attr) {
+        Set<Object> values;
+        if (attr.getValue() == null || attr.getValue().isEmpty()) {
+            values = Collections.emptySet();
+        } else if (attr.getValue().get(0) instanceof byte[]) {
+            values = new HashSet<>(attr.getValue().size());
+            for (Object single : attr.getValue()) {
+                values.add(Base64.encode((byte[]) single));
+            }
+        } else {
+            values = new HashSet<>(attr.getValue());
+        }
+
+        return values;
+    }
+
+    private void doExtract(final ContentHandler handler, final List<? extends Any<?>> anys)
+            throws SAXException, ReportException {
+
+        final Set<Missing> missing = new HashSet<>();
+        final Set<Misaligned> misaligned = new HashSet<>();
+
+        for (Any<?> any : anys) {
+            missing.clear();
+            misaligned.clear();
+
+            AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
+            for (final ExternalResource resource : anyUtils.getAllResources(any)) {
+                Provision provision = resource.getProvision(any.getType());
+                MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
+                final String connObjectKeyValue = connObjectKeyItem == null
+                        ? StringUtils.EMPTY
+                        : mappingManager.getConnObjectKeyValue(any, provision);
+                if (provision != null && connObjectKeyItem != null && StringUtils.isNotBlank(connObjectKeyValue)) {
+                    // 1. read from the underlying connector
+                    Connector connector = connFactory.getConnector(resource);
+                    ConnectorObject connectorObject = connector.getObject(provision.getObjectClass(),
+                            new Uid(connObjectKeyValue),
+                            MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
+
+                    if (connectorObject == null) {
+                        // 2. not found on resource?
+                        LOG.error("Object {} with class {} not found on resource {}",
+                                connObjectKeyValue, provision.getObjectClass(), resource);
+
+                        missing.add(new Missing(resource.getKey(), connObjectKeyValue));
+                    } else {
+                        // 3. found but misaligned?
+                        Pair<String, Set<Attribute>> preparedAttrs =
+                                mappingManager.prepareAttrs(any, null, false, null, provision);
+                        preparedAttrs.getRight().add(AttributeBuilder.build(
+                                Uid.NAME, preparedAttrs.getLeft()));
+                        preparedAttrs.getRight().add(AttributeBuilder.build(
+                                connObjectKeyItem.getExtAttrName(), preparedAttrs.getLeft()));
+
+                        final Map<String, Set<Object>> syncopeAttrs = new HashMap<>();
+                        for (Attribute attr : preparedAttrs.getRight()) {
+                            syncopeAttrs.put(attr.getName(), getValues(attr));
+                        }
+
+                        final Map<String, Set<Object>> resourceAttrs = new HashMap<>();
+                        for (Attribute attr : connectorObject.getAttributes()) {
+                            if (!OperationalAttributes.PASSWORD_NAME.equals(attr.getName())
+                                    && !OperationalAttributes.ENABLE_NAME.equals(attr.getName())) {
+
+                                resourceAttrs.put(attr.getName(), getValues(attr));
+                            }
+                        }
+
+                        IterableUtils.forEach(CollectionUtils.subtract(syncopeAttrs.keySet(), resourceAttrs.keySet()),
+                                new Closure<String>() {
+
+                            @Override
+                            public void execute(final String name) {
+                                misaligned.add(new Misaligned(
+                                        resource.getKey(),
+                                        connObjectKeyValue,
+                                        name,
+                                        syncopeAttrs.get(name),
+                                        Collections.emptySet()));
+                            }
+                        });
+
+                        for (Map.Entry<String, Set<Object>> entry : resourceAttrs.entrySet()) {
+                            if (syncopeAttrs.containsKey(entry.getKey())) {
+                                if (!Objects.equals(syncopeAttrs.get(entry.getKey()), entry.getValue())) {
+                                    misaligned.add(new Misaligned(
+                                            resource.getKey(),
+                                            connObjectKeyValue,
+                                            entry.getKey(),
+                                            syncopeAttrs.get(entry.getKey()),
+                                            entry.getValue()));
+                                }
+                            } else {
+                                misaligned.add(new Misaligned(
+                                        resource.getKey(),
+                                        connObjectKeyValue,
+                                        entry.getKey(),
+                                        Collections.emptySet(),
+                                        entry.getValue()));
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!missing.isEmpty() || !misaligned.isEmpty()) {
+                doExtract(handler, any, missing, misaligned);
+            }
+        }
+    }
+
+    private void doExtract(
+            final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
+            throws SAXException {
+
+        for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
+            List<AnyObject> anys = searchDAO.search(
+                    SyncopeConstants.FULL_ADMIN_REALMS,
+                    cond,
+                    page,
+                    PAGE_SIZE,
+                    Collections.<OrderByClause>emptyList(),
+                    anyTypeKind);
+
+            doExtract(handler, anys);
+        }
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof ReconciliationReportletConf) {
+            this.conf = ReconciliationReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        AttributesImpl atts = new AttributesImpl();
+
+        if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
+
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
+        } else {
+            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
+
+            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
+
+            doExtract(handler, count, cond, AnyTypeKind.USER);
+        }
+        handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
+
+        atts.clear();
+        if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
+
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
+        } else {
+            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
+
+            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
+            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
+
+            doExtract(handler, count, cond, AnyTypeKind.GROUP);
+        }
+        handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
+
+        for (AnyType anyType : anyTypeDAO.findAll()) {
+            if (!anyType.equals(anyTypeDAO.findUser()) && !anyType.equals(anyTypeDAO.findGroup())) {
+                AnyTypeCond anyTypeCond = new AnyTypeCond();
+                anyTypeCond.setAnyTypeKey(anyType.getKey());
+                SearchCond cond = StringUtils.isBlank(this.conf.getAnyObjectMatchingCond())
+                        ? SearchCond.getLeafCond(anyTypeCond)
+                        : SearchCond.getAndCond(
+                                SearchCond.getLeafCond(anyTypeCond),
+                                SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
+
+                int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+
+                atts.clear();
+                atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
+                atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+                handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
+
+                doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
+
+                handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
+            }
+        }
+    }
+
+    private static class Missing {
+
+        private final String resource;
+
+        private final String connObjectKeyValue;
+
+        Missing(final String resource, final String connObjectKeyValue) {
+            this.resource = resource;
+            this.connObjectKeyValue = connObjectKeyValue;
+        }
+
+        public String getResource() {
+            return resource;
+        }
+
+        public String getConnObjectKeyValue() {
+            return connObjectKeyValue;
+        }
+
+    }
+
+    private static class Misaligned extends Missing {
+
+        private final String name;
+
+        private final Set<Object> onSyncope;
+
+        private final Set<Object> onResource;
+
+        Misaligned(
+                final String resource,
+                final String connObjectKeyValue,
+                final String name,
+                final Set<Object> onSyncope,
+                final Set<Object> onResource) {
+
+            super(resource, connObjectKeyValue);
+
+            this.name = name;
+            this.onSyncope = onSyncope;
+            this.onResource = onResource;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Set<Object> getOnSyncope() {
+            return onSyncope;
+        }
+
+        public Set<Object> getOnResource() {
+            return onResource;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
new file mode 100644
index 0000000..db42152
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.report;
+
+public class ReportException extends RuntimeException {
+
+    private static final long serialVersionUID = 6719507778589395283L;
+
+    public ReportException(final Throwable cause) {
+        super(cause);
+    }
+
+    public ReportException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
new file mode 100644
index 0000000..78183d5
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -0,0 +1,80 @@
+/*
+ * 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.report;
+
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Quartz job for executing a given report.
+ */
+public class ReportJob extends AbstractInterruptableJob {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
+
+    /**
+     * Key, set by the caller, for identifying the report to be executed.
+     */
+    private String reportKey;
+
+    @Autowired
+    private ReportJobDelegate delegate;
+
+    /**
+     * Report id setter.
+     *
+     * @param reportKey to be set
+     */
+    public void setReportKey(final String reportKey) {
+        this.reportKey = reportKey;
+    }
+
+    @Override
+    public void execute(final JobExecutionContext context) throws JobExecutionException {
+        super.execute(context);
+
+        try {
+            AuthContextUtils.execWithAuthContext(context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY),
+                    new AuthContextUtils.Executable<Void>() {
+
+                @Override
+                public Void exec() {
+                    try {
+                        delegate.execute(reportKey);
+                    } catch (Exception e) {
+                        LOG.error("While executing report {}", reportKey, e);
+                        throw new RuntimeException(e);
+                    }
+
+                    return null;
+                }
+            });
+        } catch (RuntimeException e) {
+            LOG.error("While executing report {}", reportKey, e);
+            throw new JobExecutionException("While executing report " + reportKey, 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/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
new file mode 100644
index 0000000..28f4894
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
@@ -0,0 +1,198 @@
+/*
+ * 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.report;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.helpers.AttributesImpl;
+
+@Component
+public class ReportJobDelegate {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
+
+    /**
+     * Report DAO.
+     */
+    @Autowired
+    private ReportDAO reportDAO;
+
+    /**
+     * Report execution DAO.
+     */
+    @Autowired
+    private ReportExecDAO reportExecDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
+    @Transactional
+    public void execute(final String reportKey) throws JobExecutionException {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new JobExecutionException("Report " + reportKey + " not found");
+        }
+
+        if (!report.isActive()) {
+            LOG.info("Report {} not active, aborting...", reportKey);
+            return;
+        }
+
+        // 1. create execution
+        ReportExec execution = entityFactory.newEntity(ReportExec.class);
+        execution.setStatus(ReportExecStatus.STARTED);
+        execution.setStart(new Date());
+        execution.setReport(report);
+        execution = reportExecDAO.save(execution);
+
+        report.add(execution);
+        report = reportDAO.save(report);
+
+        // 2. define a SAX handler for generating result as XML
+        TransformerHandler handler;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ZipOutputStream zos = new ZipOutputStream(baos);
+        zos.setLevel(Deflater.BEST_COMPRESSION);
+        try {
+            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            handler = tFactory.newTransformerHandler();
+            Transformer serializer = handler.getTransformer();
+            serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
+            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            // a single ZipEntry in the ZipOutputStream
+            zos.putNextEntry(new ZipEntry(report.getName()));
+
+            // streaming SAX handler in a compressed byte array stream
+            handler.setResult(new StreamResult(zos));
+        } catch (Exception e) {
+            throw new JobExecutionException("While configuring for SAX generation", e, true);
+        }
+
+        execution.setStatus(ReportExecStatus.RUNNING);
+        execution = reportExecDAO.save(execution);
+
+        // 3. actual report execution
+        StringBuilder reportExecutionMessage = new StringBuilder();
+        try {
+            // report header
+            handler.startDocument();
+            AttributesImpl atts = new AttributesImpl();
+            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
+            handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
+
+            // iterate over reportlet instances defined for this report
+            for (ReportletConf reportletConf : report.getReportletConfs()) {
+                Class<? extends Reportlet> reportletClass =
+                        implementationLookup.getReportletClass(reportletConf.getClass());
+                if (reportletClass == null) {
+                    LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
+                } else {
+                    // fetch (or create) reportlet
+                    Reportlet reportlet;
+                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                getSingleton(reportletClass.getName());
+                    } else {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                        ApplicationContextProvider.getBeanFactory().
+                                registerSingleton(reportletClass.getName(), reportlet);
+                    }
+
+                    // invoke reportlet
+                    try {
+                        reportlet.extract(reportletConf, handler);
+                    } catch (Throwable t) {
+                        LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
+
+                        execution.setStatus(ReportExecStatus.FAILURE);
+
+                        Throwable effective = t instanceof ReportException
+                                ? t.getCause()
+                                : t;
+                        reportExecutionMessage.
+                                append(ExceptionUtils2.getFullStackTrace(effective)).
+                                append("\n==================\n");
+                    }
+                }
+            }
+
+            // report footer
+            handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
+            handler.endDocument();
+
+            if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
+                execution.setStatus(ReportExecStatus.SUCCESS);
+            }
+        } catch (Exception e) {
+            execution.setStatus(ReportExecStatus.FAILURE);
+            reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
+
+            throw new JobExecutionException(e, true);
+        } finally {
+            try {
+                zos.closeEntry();
+                IOUtils.closeQuietly(zos);
+                IOUtils.closeQuietly(baos);
+            } catch (IOException e) {
+                LOG.error("While closing StreamResult's backend", e);
+            }
+
+            execution.setExecResult(baos.toByteArray());
+            execution.setMessage(reportExecutionMessage.toString());
+            execution.setEnd(new Date());
+            reportExecDAO.save(execution);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
new file mode 100644
index 0000000..6b21695
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportXMLConst.java
@@ -0,0 +1,44 @@
+/*
+ * 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.report;
+
+public final class ReportXMLConst {
+
+    public static final String XSD_STRING = "xsd:string";
+
+    public static final String XSD_INT = "xsd:integer";
+
+    public static final String XSD_LONG = "xsd:long";
+
+    public static final String XSD_BOOLEAN = "xsd:boolean";
+
+    public static final String XSD_DATETIME = "xsd:dateTime";
+
+    public static final String ELEMENT_REPORT = "report";
+
+    public static final String ATTR_NAME = "name";
+
+    public static final String ATTR_CLASS = "class";
+
+    public static final String ELEMENT_REPORTLET = "reportlet";
+
+    private ReportXMLConst() {
+        // empty private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/67ecbea3/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
new file mode 100644
index 0000000..1156d25
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
@@ -0,0 +1,128 @@
+/*
+ * 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.report;
+
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.report.StaticReportletConf;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.springframework.util.StringUtils;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(StaticReportletConf.class)
+public class StaticReportlet extends AbstractReportlet {
+
+    private StaticReportletConf conf;
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "staticAttributes", atts);
+
+        handler.startElement("", "", "string", atts);
+        handler.characters("string".toCharArray(), 0, "string".length());
+        handler.endElement("", "", "string");
+
+        handler.startElement("", "", "long", atts);
+        handler.characters("long".toCharArray(), 0, "long".length());
+        handler.endElement("", "", "long");
+
+        handler.startElement("", "", "double", atts);
+        handler.characters("double".toCharArray(), 0, "double".length());
+        handler.endElement("", "", "double");
+
+        handler.startElement("", "", "date", atts);
+        handler.characters("date".toCharArray(), 0, "date".length());
+        handler.endElement("", "", "date");
+
+        handler.startElement("", "", "double", atts);
+        handler.characters("double".toCharArray(), 0, "double".length());
+        handler.endElement("", "", "double");
+
+        handler.startElement("", "", "enum", atts);
+        handler.characters("enum".toCharArray(), 0, "enum".length());
+        handler.endElement("", "", "enum");
+
+        handler.startElement("", "", "list", atts);
+        handler.characters("list".toCharArray(), 0, "list".length());
+        handler.endElement("", "", "list");
+
+        handler.endElement("", "", "staticAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof StaticReportletConf) {
+            this.conf = StaticReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        if (StringUtils.hasText(this.conf.getStringField())) {
+            handler.startElement("", "", "string", null);
+            handler.characters(this.conf.getStringField().toCharArray(), 0, this.conf.getStringField().length());
+            handler.endElement("", "", "string");
+        }
+
+        if (this.conf.getLongField() != null) {
+            handler.startElement("", "", "long", null);
+            String printed = String.valueOf(this.conf.getLongField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "long");
+        }
+
+        if (this.conf.getDoubleField() != null) {
+            handler.startElement("", "", "double", null);
+            String printed = String.valueOf(this.conf.getDoubleField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "double");
+        }
+
+        if (this.conf.getDateField() != null) {
+            handler.startElement("", "", "date", null);
+            String printed = FormatUtils.format(this.conf.getDateField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "date");
+        }
+
+        if (this.conf.getTraceLevel() != null) {
+            handler.startElement("", "", "enum", null);
+            String printed = this.conf.getTraceLevel().name();
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "enum");
+        }
+
+        if (this.conf.getListField() != null && !this.conf.getListField().isEmpty()) {
+            handler.startElement("", "", "list", null);
+            for (String item : this.conf.getListField()) {
+                if (StringUtils.hasText(item)) {
+                    handler.startElement("", "", "string", null);
+                    handler.characters(item.toCharArray(), 0, item.length());
+                    handler.endElement("", "", "string");
+                }
+            }
+            handler.endElement("", "", "list");
+        }
+    }
+}


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

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
deleted file mode 100644
index 0aeaf0a..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.GroupReportletConf;
-import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(GroupReportletConf.class)
-public class GroupReportlet extends AbstractReportlet {
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private GroupDataBinder groupDataBinder;
-
-    private GroupReportletConf conf;
-
-    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
-            throws SAXException {
-
-        if (anyTO.getResources().isEmpty()) {
-            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
-        } else {
-            AttributesImpl atts = new AttributesImpl();
-            handler.startElement("", "", "resources", null);
-
-            for (String resourceName : anyTO.getResources()) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
-                handler.startElement("", "", "resource", atts);
-                handler.endElement("", "", "resource");
-            }
-
-            handler.endElement("", "", "resources");
-        }
-    }
-
-    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
-            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-        if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
-
-            handler.startElement("", "", "attributes", null);
-            for (String attrName : attrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "attribute", atts);
-
-                if (attrMap.containsKey(attrName)) {
-                    for (String value : attrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "attribute");
-            }
-            handler.endElement("", "", "attributes");
-        }
-
-        if (!derAttrs.isEmpty()) {
-            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
-
-            handler.startElement("", "", "derivedAttributes", null);
-            for (String attrName : derAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "derivedAttribute", atts);
-
-                if (derAttrMap.containsKey(attrName)) {
-                    for (String value : derAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "derivedAttribute");
-            }
-            handler.endElement("", "", "derivedAttributes");
-        }
-
-        if (!virAttrs.isEmpty()) {
-            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
-
-            handler.startElement("", "", "virtualAttributes", null);
-            for (String attrName : virAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "virtualAttribute", atts);
-
-                if (virAttrMap.containsKey(attrName)) {
-                    for (String value : virAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "virtualAttribute");
-            }
-            handler.endElement("", "", "virtualAttributes");
-        }
-    }
-
-    private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        for (Group group : groups) {
-            atts.clear();
-
-            for (Feature feature : conf.getFeatures()) {
-                String type = null;
-                String value = null;
-                switch (feature) {
-                    case key:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getKey();
-                        break;
-
-                    case name:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = String.valueOf(group.getName());
-                        break;
-
-                    case groupOwner:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getGroupOwner().getKey();
-                        break;
-
-                    case userOwner:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = group.getUserOwner().getKey();
-                        break;
-
-                    default:
-                }
-
-                if (type != null && value != null) {
-                    atts.addAttribute("", "", feature.name(), type, value);
-                }
-            }
-
-            handler.startElement("", "", "group", atts);
-
-            // Using GroupTO for attribute values, since the conversion logic of
-            // values to String is already encapsulated there
-            GroupTO groupTO = groupDataBinder.getGroupTO(group, true);
-
-            doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
-
-            // to get resources associated to a group
-            if (conf.getFeatures().contains(Feature.resources)) {
-                doExtractResources(handler, groupTO);
-            }
-            //to get users asscoiated to a group is preferred GroupDAO to GroupTO
-            if (conf.getFeatures().contains(Feature.users)) {
-                handler.startElement("", "", "users", null);
-
-                for (UMembership memb : groupDAO.findUMemberships(group)) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_STRING,
-                            memb.getLeftEnd().getKey());
-                    atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING,
-                            String.valueOf(memb.getLeftEnd().getUsername()));
-
-                    handler.startElement("", "", "user", atts);
-                    handler.endElement("", "", "user");
-                }
-
-                handler.endElement("", "", "users");
-            }
-
-            handler.endElement("", "", "group");
-        }
-    }
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        if (conf == null) {
-            LOG.debug("Report configuration is not present");
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "groupAttributes", atts);
-
-        if (conf != null) {
-            for (Feature feature : conf.getFeatures()) {
-                atts.clear();
-                handler.startElement("", "", "feature", atts);
-                handler.characters(feature.name().toCharArray(), 0, feature.name().length());
-                handler.endElement("", "", "feature");
-            }
-
-            for (String attr : conf.getPlainAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "attribute", atts);
-                handler.characters(attr.toCharArray(), 0, attr.length());
-                handler.endElement("", "", "attribute");
-            }
-
-            for (String derAttr : conf.getDerAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "derAttribute", atts);
-                handler.characters(derAttr.toCharArray(), 0, derAttr.length());
-                handler.endElement("", "", "derAttribute");
-            }
-
-            for (String virAttr : conf.getVirAttrs()) {
-                atts.clear();
-                handler.startElement("", "", "virAttribute", atts);
-                handler.characters(virAttr.toCharArray(), 0, virAttr.length());
-                handler.endElement("", "", "virAttribute");
-            }
-        }
-
-        handler.endElement("", "", "groupAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    private int count() {
-        return StringUtils.isBlank(conf.getMatchingCond())
-                ? groupDAO.count()
-                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP);
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof GroupReportletConf) {
-            this.conf = GroupReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-            List<Group> groups;
-            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
-            } else {
-                groups = searchDAO.search(
-                        SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page,
-                        AnyDAO.DEFAULT_PAGE_SIZE,
-                        Collections.<OrderByClause>emptyList(),
-                        AnyTypeKind.USER);
-            }
-
-            doExtract(handler, groups);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
deleted file mode 100644
index 4922015..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import org.apache.commons.collections4.Closure;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.IterableUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
-import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.AnyType;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
-import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.api.Connector;
-import org.apache.syncope.core.provisioning.api.ConnectorFactory;
-import org.apache.syncope.core.provisioning.api.MappingManager;
-import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.identityconnectors.common.Base64;
-import org.identityconnectors.framework.common.objects.Attribute;
-import org.identityconnectors.framework.common.objects.AttributeBuilder;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.OperationalAttributes;
-import org.identityconnectors.framework.common.objects.Uid;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * Reportlet for extracting information for the current reconciliation status, e.g. the coherence between Syncope
- * information and mapped entities on external resources.
- */
-@ReportletConfClass(ReconciliationReportletConf.class)
-public class ReconciliationReportlet extends AbstractReportlet {
-
-    private static final int PAGE_SIZE = 10;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private AnyTypeDAO anyTypeDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private MappingManager mappingManager;
-
-    @Autowired
-    private ConnectorFactory connFactory;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
-    private ReconciliationReportletConf conf;
-
-    private String getAnyElementName(final AnyTypeKind anyTypeKind) {
-        String elementName;
-
-        switch (anyTypeKind) {
-            case USER:
-                elementName = "user";
-                break;
-
-            case GROUP:
-                elementName = "group";
-                break;
-
-            case ANY_OBJECT:
-            default:
-                elementName = "anyObject";
-        }
-
-        return elementName;
-    }
-
-    private void doExtract(
-            final ContentHandler handler,
-            final Any<?> any,
-            final Set<Missing> missing,
-            final Set<Misaligned> misaligned)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-
-        for (Feature feature : conf.getFeatures()) {
-            String type = null;
-            String value = null;
-            switch (feature) {
-                case key:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getKey();
-                    break;
-
-                case username:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_STRING;
-                        value = User.class.cast(any).getUsername();
-                    }
-                    break;
-
-                case groupName:
-                    if (any instanceof Group) {
-                        type = ReportXMLConst.XSD_STRING;
-                        value = Group.class.cast(any).getName();
-                    }
-                    break;
-
-                case workflowId:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getWorkflowId();
-                    break;
-
-                case status:
-                    type = ReportXMLConst.XSD_STRING;
-                    value = any.getStatus();
-                    break;
-
-                case creationDate:
-                    type = ReportXMLConst.XSD_DATETIME;
-                    value = any.getCreationDate() == null
-                            ? StringUtils.EMPTY
-                            : FormatUtils.format(any.getCreationDate());
-                    break;
-
-                case lastLoginDate:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = User.class.cast(any).getLastLoginDate() == null
-                                ? StringUtils.EMPTY
-                                : FormatUtils.format(User.class.cast(any).getLastLoginDate());
-                    }
-                    break;
-
-                case changePwdDate:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = User.class.cast(any).getChangePwdDate() == null
-                                ? StringUtils.EMPTY
-                                : FormatUtils.format(User.class.cast(any).getChangePwdDate());
-                    }
-                    break;
-
-                case passwordHistorySize:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(User.class.cast(any).getPasswordHistory().size());
-                    }
-                    break;
-
-                case failedLoginCount:
-                    if (any instanceof User) {
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(User.class.cast(any).getFailedLogins());
-                    }
-                    break;
-
-                default:
-            }
-
-            if (type != null && value != null) {
-                atts.addAttribute("", "", feature.name(), type, value);
-            }
-        }
-
-        handler.startElement("", "", getAnyElementName(any.getType().getKind()), atts);
-
-        for (Missing item : missing) {
-            atts.clear();
-            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
-            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
-
-            handler.startElement("", "", "missing", atts);
-            handler.endElement("", "", "missing");
-        }
-        for (Misaligned item : misaligned) {
-            atts.clear();
-            atts.addAttribute("", "", "resource", ReportXMLConst.XSD_STRING, item.getResource());
-            atts.addAttribute("", "", "connObjectKeyValue", ReportXMLConst.XSD_STRING, item.getConnObjectKeyValue());
-            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, item.getName());
-
-            handler.startElement("", "", "misaligned", atts);
-
-            handler.startElement("", "", "onSyncope", null);
-            if (item.getOnSyncope() != null) {
-                for (Object value : item.getOnSyncope()) {
-                    char[] asChars = value.toString().toCharArray();
-
-                    handler.startElement("", "", "value", null);
-                    handler.characters(asChars, 0, asChars.length);
-                    handler.endElement("", "", "value");
-                }
-            }
-            handler.endElement("", "", "onSyncope");
-
-            handler.startElement("", "", "onResource", null);
-            if (item.getOnResource() != null) {
-                for (Object value : item.getOnResource()) {
-                    char[] asChars = value.toString().toCharArray();
-
-                    handler.startElement("", "", "value", null);
-                    handler.characters(asChars, 0, asChars.length);
-                    handler.endElement("", "", "value");
-                }
-            }
-            handler.endElement("", "", "onResource");
-
-            handler.endElement("", "", "misaligned");
-        }
-
-        handler.endElement("", "", getAnyElementName(any.getType().getKind()));
-    }
-
-    private Set<Object> getValues(final Attribute attr) {
-        Set<Object> values;
-        if (attr.getValue() == null || attr.getValue().isEmpty()) {
-            values = Collections.emptySet();
-        } else if (attr.getValue().get(0) instanceof byte[]) {
-            values = new HashSet<>(attr.getValue().size());
-            for (Object single : attr.getValue()) {
-                values.add(Base64.encode((byte[]) single));
-            }
-        } else {
-            values = new HashSet<>(attr.getValue());
-        }
-
-        return values;
-    }
-
-    private void doExtract(final ContentHandler handler, final List<? extends Any<?>> anys)
-            throws SAXException, ReportException {
-
-        final Set<Missing> missing = new HashSet<>();
-        final Set<Misaligned> misaligned = new HashSet<>();
-
-        for (Any<?> any : anys) {
-            missing.clear();
-            misaligned.clear();
-
-            AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
-            for (final ExternalResource resource : anyUtils.getAllResources(any)) {
-                Provision provision = resource.getProvision(any.getType());
-                MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
-                final String connObjectKeyValue = connObjectKeyItem == null
-                        ? StringUtils.EMPTY
-                        : mappingManager.getConnObjectKeyValue(any, provision);
-                if (provision != null && connObjectKeyItem != null && StringUtils.isNotBlank(connObjectKeyValue)) {
-                    // 1. read from the underlying connector
-                    Connector connector = connFactory.getConnector(resource);
-                    ConnectorObject connectorObject = connector.getObject(provision.getObjectClass(),
-                            new Uid(connObjectKeyValue),
-                            MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
-
-                    if (connectorObject == null) {
-                        // 2. not found on resource?
-                        LOG.error("Object {} with class {} not found on resource {}",
-                                connObjectKeyValue, provision.getObjectClass(), resource);
-
-                        missing.add(new Missing(resource.getKey(), connObjectKeyValue));
-                    } else {
-                        // 3. found but misaligned?
-                        Pair<String, Set<Attribute>> preparedAttrs =
-                                mappingManager.prepareAttrs(any, null, false, null, provision);
-                        preparedAttrs.getRight().add(AttributeBuilder.build(
-                                Uid.NAME, preparedAttrs.getLeft()));
-                        preparedAttrs.getRight().add(AttributeBuilder.build(
-                                connObjectKeyItem.getExtAttrName(), preparedAttrs.getLeft()));
-
-                        final Map<String, Set<Object>> syncopeAttrs = new HashMap<>();
-                        for (Attribute attr : preparedAttrs.getRight()) {
-                            syncopeAttrs.put(attr.getName(), getValues(attr));
-                        }
-
-                        final Map<String, Set<Object>> resourceAttrs = new HashMap<>();
-                        for (Attribute attr : connectorObject.getAttributes()) {
-                            if (!OperationalAttributes.PASSWORD_NAME.equals(attr.getName())
-                                    && !OperationalAttributes.ENABLE_NAME.equals(attr.getName())) {
-
-                                resourceAttrs.put(attr.getName(), getValues(attr));
-                            }
-                        }
-
-                        IterableUtils.forEach(CollectionUtils.subtract(syncopeAttrs.keySet(), resourceAttrs.keySet()),
-                                new Closure<String>() {
-
-                            @Override
-                            public void execute(final String name) {
-                                misaligned.add(new Misaligned(
-                                        resource.getKey(),
-                                        connObjectKeyValue,
-                                        name,
-                                        syncopeAttrs.get(name),
-                                        Collections.emptySet()));
-                            }
-                        });
-
-                        for (Map.Entry<String, Set<Object>> entry : resourceAttrs.entrySet()) {
-                            if (syncopeAttrs.containsKey(entry.getKey())) {
-                                if (!Objects.equals(syncopeAttrs.get(entry.getKey()), entry.getValue())) {
-                                    misaligned.add(new Misaligned(
-                                            resource.getKey(),
-                                            connObjectKeyValue,
-                                            entry.getKey(),
-                                            syncopeAttrs.get(entry.getKey()),
-                                            entry.getValue()));
-                                }
-                            } else {
-                                misaligned.add(new Misaligned(
-                                        resource.getKey(),
-                                        connObjectKeyValue,
-                                        entry.getKey(),
-                                        Collections.emptySet(),
-                                        entry.getValue()));
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (!missing.isEmpty() || !misaligned.isEmpty()) {
-                doExtract(handler, any, missing, misaligned);
-            }
-        }
-    }
-
-    private void doExtract(
-            final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
-            throws SAXException {
-
-        for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
-            List<AnyObject> anys = searchDAO.search(
-                    SyncopeConstants.FULL_ADMIN_REALMS,
-                    cond,
-                    page,
-                    PAGE_SIZE,
-                    Collections.<OrderByClause>emptyList(),
-                    anyTypeKind);
-
-            doExtract(handler, anys);
-        }
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof ReconciliationReportletConf) {
-            this.conf = ReconciliationReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        AttributesImpl atts = new AttributesImpl();
-
-        if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
-
-            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
-            }
-        } else {
-            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
-
-            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
-
-            doExtract(handler, count, cond, AnyTypeKind.USER);
-        }
-        handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
-
-        atts.clear();
-        if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
-
-            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
-            }
-        } else {
-            SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
-
-            int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
-            atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-            handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
-
-            doExtract(handler, count, cond, AnyTypeKind.GROUP);
-        }
-        handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
-
-        for (AnyType anyType : anyTypeDAO.findAll()) {
-            if (!anyType.equals(anyTypeDAO.findUser()) && !anyType.equals(anyTypeDAO.findGroup())) {
-                AnyTypeCond anyTypeCond = new AnyTypeCond();
-                anyTypeCond.setAnyTypeKey(anyType.getKey());
-                SearchCond cond = StringUtils.isBlank(this.conf.getAnyObjectMatchingCond())
-                        ? SearchCond.getLeafCond(anyTypeCond)
-                        : SearchCond.getAndCond(
-                                SearchCond.getLeafCond(anyTypeCond),
-                                SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
-
-                int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
-
-                atts.clear();
-                atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
-                atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
-                handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
-
-                doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
-
-                handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
-            }
-        }
-    }
-
-    private static class Missing {
-
-        private final String resource;
-
-        private final String connObjectKeyValue;
-
-        Missing(final String resource, final String connObjectKeyValue) {
-            this.resource = resource;
-            this.connObjectKeyValue = connObjectKeyValue;
-        }
-
-        public String getResource() {
-            return resource;
-        }
-
-        public String getConnObjectKeyValue() {
-            return connObjectKeyValue;
-        }
-
-    }
-
-    private static class Misaligned extends Missing {
-
-        private final String name;
-
-        private final Set<Object> onSyncope;
-
-        private final Set<Object> onResource;
-
-        Misaligned(
-                final String resource,
-                final String connObjectKeyValue,
-                final String name,
-                final Set<Object> onSyncope,
-                final Set<Object> onResource) {
-
-            super(resource, connObjectKeyValue);
-
-            this.name = name;
-            this.onSyncope = onSyncope;
-            this.onResource = onResource;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public Set<Object> getOnSyncope() {
-            return onSyncope;
-        }
-
-        public Set<Object> getOnResource() {
-            return onResource;
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
deleted file mode 100644
index f4495ad..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-public class ReportException extends RuntimeException {
-
-    private static final long serialVersionUID = 6719507778589395283L;
-
-    public ReportException(final Throwable cause) {
-        super(cause);
-    }
-
-    public ReportException(final String message, final Throwable cause) {
-        super(message, cause);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
deleted file mode 100644
index 8b14024..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.apache.syncope.core.provisioning.api.job.JobManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Quartz job for executing a given report.
- */
-public class ReportJob extends AbstractInterruptableJob {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
-
-    /**
-     * Key, set by the caller, for identifying the report to be executed.
-     */
-    private String reportKey;
-
-    @Autowired
-    private ReportJobDelegate delegate;
-
-    /**
-     * Report id setter.
-     *
-     * @param reportKey to be set
-     */
-    public void setReportKey(final String reportKey) {
-        this.reportKey = reportKey;
-    }
-
-    @Override
-    public void execute(final JobExecutionContext context) throws JobExecutionException {
-        super.execute(context);
-
-        try {
-            AuthContextUtils.execWithAuthContext(context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY),
-                    new AuthContextUtils.Executable<Void>() {
-
-                @Override
-                public Void exec() {
-                    try {
-                        delegate.execute(reportKey);
-                    } catch (Exception e) {
-                        LOG.error("While executing report {}", reportKey, e);
-                        throw new RuntimeException(e);
-                    }
-
-                    return null;
-                }
-            });
-        } catch (RuntimeException e) {
-            LOG.error("While executing report {}", reportKey, e);
-            throw new JobExecutionException("While executing report " + reportKey, e);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
deleted file mode 100644
index 6d41686..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.zip.Deflater;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import org.apache.commons.io.IOUtils;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.persistence.api.ImplementationLookup;
-import org.apache.syncope.core.persistence.api.dao.ReportDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.Report;
-import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.helpers.AttributesImpl;
-
-@Component
-public class ReportJobDelegate {
-
-    private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
-
-    /**
-     * Report DAO.
-     */
-    @Autowired
-    private ReportDAO reportDAO;
-
-    /**
-     * Report execution DAO.
-     */
-    @Autowired
-    private ReportExecDAO reportExecDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private ImplementationLookup implementationLookup;
-
-    @Transactional
-    public void execute(final String reportKey) throws JobExecutionException {
-        Report report = reportDAO.find(reportKey);
-        if (report == null) {
-            throw new JobExecutionException("Report " + reportKey + " not found");
-        }
-
-        if (!report.isActive()) {
-            LOG.info("Report {} not active, aborting...", reportKey);
-            return;
-        }
-
-        // 1. create execution
-        ReportExec execution = entityFactory.newEntity(ReportExec.class);
-        execution.setStatus(ReportExecStatus.STARTED);
-        execution.setStart(new Date());
-        execution.setReport(report);
-        execution = reportExecDAO.save(execution);
-
-        report.add(execution);
-        report = reportDAO.save(report);
-
-        // 2. define a SAX handler for generating result as XML
-        TransformerHandler handler;
-
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ZipOutputStream zos = new ZipOutputStream(baos);
-        zos.setLevel(Deflater.BEST_COMPRESSION);
-        try {
-            SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
-            tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
-            handler = tFactory.newTransformerHandler();
-            Transformer serializer = handler.getTransformer();
-            serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
-            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
-
-            // a single ZipEntry in the ZipOutputStream
-            zos.putNextEntry(new ZipEntry(report.getName()));
-
-            // streaming SAX handler in a compressed byte array stream
-            handler.setResult(new StreamResult(zos));
-        } catch (Exception e) {
-            throw new JobExecutionException("While configuring for SAX generation", e, true);
-        }
-
-        execution.setStatus(ReportExecStatus.RUNNING);
-        execution = reportExecDAO.save(execution);
-
-        // 3. actual report execution
-        StringBuilder reportExecutionMessage = new StringBuilder();
-        try {
-            // report header
-            handler.startDocument();
-            AttributesImpl atts = new AttributesImpl();
-            atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
-            handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
-
-            // iterate over reportlet instances defined for this report
-            for (ReportletConf reportletConf : report.getReportletConfs()) {
-                Class<? extends Reportlet> reportletClass =
-                        implementationLookup.getReportletClass(reportletConf.getClass());
-                if (reportletClass == null) {
-                    LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
-                } else {
-                    // fetch (or create) reportlet
-                    Reportlet reportlet;
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(reportletClass.getName());
-                    } else {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(reportletClass.getName(), reportlet);
-                    }
-
-                    // invoke reportlet
-                    try {
-                        reportlet.extract(reportletConf, handler);
-                    } catch (Throwable t) {
-                        LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
-
-                        execution.setStatus(ReportExecStatus.FAILURE);
-
-                        Throwable effective = t instanceof ReportException
-                                ? t.getCause()
-                                : t;
-                        reportExecutionMessage.
-                                append(ExceptionUtils2.getFullStackTrace(effective)).
-                                append("\n==================\n");
-                    }
-                }
-            }
-
-            // report footer
-            handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
-            handler.endDocument();
-
-            if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
-                execution.setStatus(ReportExecStatus.SUCCESS);
-            }
-        } catch (Exception e) {
-            execution.setStatus(ReportExecStatus.FAILURE);
-            reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
-
-            throw new JobExecutionException(e, true);
-        } finally {
-            try {
-                zos.closeEntry();
-                IOUtils.closeQuietly(zos);
-                IOUtils.closeQuietly(baos);
-            } catch (IOException e) {
-                LOG.error("While closing StreamResult's backend", e);
-            }
-
-            execution.setExecResult(baos.toByteArray());
-            execution.setMessage(reportExecutionMessage.toString());
-            execution.setEnd(new Date());
-            reportExecDAO.save(execution);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
deleted file mode 100644
index 1bbae73..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-public final class ReportXMLConst {
-
-    public static final String XSD_STRING = "xsd:string";
-
-    public static final String XSD_INT = "xsd:integer";
-
-    public static final String XSD_LONG = "xsd:long";
-
-    public static final String XSD_BOOLEAN = "xsd:boolean";
-
-    public static final String XSD_DATETIME = "xsd:dateTime";
-
-    public static final String ELEMENT_REPORT = "report";
-
-    public static final String ATTR_NAME = "name";
-
-    public static final String ATTR_CLASS = "class";
-
-    public static final String ELEMENT_REPORTLET = "reportlet";
-
-    private ReportXMLConst() {
-        // empty private constructor for static utility class
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
deleted file mode 100644
index 404d086..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.report.StaticReportletConf;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.springframework.util.StringUtils;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(StaticReportletConf.class)
-public class StaticReportlet extends AbstractReportlet {
-
-    private StaticReportletConf conf;
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "staticAttributes", atts);
-
-        handler.startElement("", "", "string", atts);
-        handler.characters("string".toCharArray(), 0, "string".length());
-        handler.endElement("", "", "string");
-
-        handler.startElement("", "", "long", atts);
-        handler.characters("long".toCharArray(), 0, "long".length());
-        handler.endElement("", "", "long");
-
-        handler.startElement("", "", "double", atts);
-        handler.characters("double".toCharArray(), 0, "double".length());
-        handler.endElement("", "", "double");
-
-        handler.startElement("", "", "date", atts);
-        handler.characters("date".toCharArray(), 0, "date".length());
-        handler.endElement("", "", "date");
-
-        handler.startElement("", "", "double", atts);
-        handler.characters("double".toCharArray(), 0, "double".length());
-        handler.endElement("", "", "double");
-
-        handler.startElement("", "", "enum", atts);
-        handler.characters("enum".toCharArray(), 0, "enum".length());
-        handler.endElement("", "", "enum");
-
-        handler.startElement("", "", "list", atts);
-        handler.characters("list".toCharArray(), 0, "list".length());
-        handler.endElement("", "", "list");
-
-        handler.endElement("", "", "staticAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof StaticReportletConf) {
-            this.conf = StaticReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        if (StringUtils.hasText(this.conf.getStringField())) {
-            handler.startElement("", "", "string", null);
-            handler.characters(this.conf.getStringField().toCharArray(), 0, this.conf.getStringField().length());
-            handler.endElement("", "", "string");
-        }
-
-        if (this.conf.getLongField() != null) {
-            handler.startElement("", "", "long", null);
-            String printed = String.valueOf(this.conf.getLongField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "long");
-        }
-
-        if (this.conf.getDoubleField() != null) {
-            handler.startElement("", "", "double", null);
-            String printed = String.valueOf(this.conf.getDoubleField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "double");
-        }
-
-        if (this.conf.getDateField() != null) {
-            handler.startElement("", "", "date", null);
-            String printed = FormatUtils.format(this.conf.getDateField());
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "date");
-        }
-
-        if (this.conf.getTraceLevel() != null) {
-            handler.startElement("", "", "enum", null);
-            String printed = this.conf.getTraceLevel().name();
-            handler.characters(printed.toCharArray(), 0, printed.length());
-            handler.endElement("", "", "enum");
-        }
-
-        if (this.conf.getListField() != null && !this.conf.getListField().isEmpty()) {
-            handler.startElement("", "", "list", null);
-            for (String item : this.conf.getListField()) {
-                if (StringUtils.hasText(item)) {
-                    handler.startElement("", "", "string", null);
-                    handler.characters(item.toCharArray(), 0, item.length());
-                    handler.endElement("", "", "string");
-                }
-            }
-            handler.endElement("", "", "list");
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
deleted file mode 100644
index f1f3a9e..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.cocoon.sax.component.XMLSerializer;
-import org.xml.sax.Attributes;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-/**
- * Converts XML into plain text. It omits all XML tags and writes only character events to the output. Input document
- * must have at least one element - root element - which should wrap all the text inside it.
- *
- */
-public class TextSerializer extends XMLSerializer {
-
-    private static final String UTF_8 = "UTF-8";
-
-    private static final String TXT = "text";
-
-    public TextSerializer() {
-        super();
-        super.setOmitXmlDeclaration(true);
-    }
-
-    @Override
-    public void setDocumentLocator(final Locator locator) {
-        // nothing
-    }
-
-    @Override
-    public void processingInstruction(final String target, final String data)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void startDTD(final String name, final String publicId, final String systemId)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void endDTD() throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void startElement(final String uri, final String loc, final String raw, final Attributes atts)
-            throws SAXException {
-        // nothing
-    }
-
-    @Override
-    public void endElement(final String uri, final String name, final String raw)
-            throws SAXException {
-        // nothing
-    }
-
-    public static TextSerializer createPlainSerializer() {
-        final TextSerializer serializer = new TextSerializer();
-        serializer.setContentType("text/plain; charset=" + UTF_8);
-        serializer.setEncoding(UTF_8);
-        serializer.setMethod(TXT);
-        return serializer;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
deleted file mode 100644
index bb1048b..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.report.UserReportletConf;
-import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.common.lib.to.MembershipTO;
-import org.apache.syncope.common.lib.to.RelationshipTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AnyDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
-import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
-import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.persistence.api.entity.user.URelationship;
-import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
-import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-@ReportletConfClass(UserReportletConf.class)
-public class UserReportlet extends AbstractReportlet {
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private AnySearchDAO searchDAO;
-
-    @Autowired
-    private UserDataBinder userDataBinder;
-
-    @Autowired
-    private GroupDataBinder groupDataBinder;
-
-    @Autowired
-    private AnyObjectDataBinder anyObjectDataBinder;
-
-    private UserReportletConf conf;
-
-    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
-            throws SAXException {
-
-        if (anyTO.getResources().isEmpty()) {
-            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
-        } else {
-            AttributesImpl atts = new AttributesImpl();
-            handler.startElement("", "", "resources", null);
-
-            for (String resourceName : anyTO.getResources()) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
-                handler.startElement("", "", "resource", atts);
-                handler.endElement("", "", "resource");
-            }
-
-            handler.endElement("", "", "resources");
-        }
-    }
-
-    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
-            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
-            throws SAXException {
-
-        AttributesImpl atts = new AttributesImpl();
-        if (!attrs.isEmpty()) {
-            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
-
-            handler.startElement("", "", "attributes", null);
-            for (String attrName : attrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "attribute", atts);
-
-                if (attrMap.containsKey(attrName)) {
-                    for (String value : attrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "attribute");
-            }
-            handler.endElement("", "", "attributes");
-        }
-
-        if (!derAttrs.isEmpty()) {
-            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
-
-            handler.startElement("", "", "derivedAttributes", null);
-            for (String attrName : derAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "derivedAttribute", atts);
-
-                if (derAttrMap.containsKey(attrName)) {
-                    for (String value : derAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "derivedAttribute");
-            }
-            handler.endElement("", "", "derivedAttributes");
-        }
-
-        if (!virAttrs.isEmpty()) {
-            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
-
-            handler.startElement("", "", "virtualAttributes", null);
-            for (String attrName : virAttrs) {
-                atts.clear();
-
-                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
-                handler.startElement("", "", "virtualAttribute", atts);
-
-                if (virAttrMap.containsKey(attrName)) {
-                    for (String value : virAttrMap.get(attrName).getValues()) {
-                        handler.startElement("", "", "value", null);
-                        handler.characters(value.toCharArray(), 0, value.length());
-                        handler.endElement("", "", "value");
-                    }
-                } else {
-                    LOG.debug("{} not found for {}[{}]", attrName,
-                            anyTO.getClass().getSimpleName(), anyTO.getKey());
-                }
-
-                handler.endElement("", "", "virtualAttribute");
-            }
-            handler.endElement("", "", "virtualAttributes");
-        }
-    }
-
-    private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        for (User user : users) {
-            atts.clear();
-
-            for (Feature feature : conf.getFeatures()) {
-                String type = null;
-                String value = null;
-                switch (feature) {
-                    case key:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getKey();
-                        break;
-
-                    case username:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getUsername();
-                        break;
-
-                    case workflowId:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getWorkflowId();
-                        break;
-
-                    case status:
-                        type = ReportXMLConst.XSD_STRING;
-                        value = user.getStatus();
-                        break;
-
-                    case creationDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getCreationDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getCreationDate());
-                        break;
-
-                    case lastLoginDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getLastLoginDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getLastLoginDate());
-                        break;
-
-                    case changePwdDate:
-                        type = ReportXMLConst.XSD_DATETIME;
-                        value = user.getChangePwdDate() == null
-                                ? ""
-                                : FormatUtils.format(user.getChangePwdDate());
-                        break;
-
-                    case passwordHistorySize:
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(user.getPasswordHistory().size());
-                        break;
-
-                    case failedLoginCount:
-                        type = ReportXMLConst.XSD_INT;
-                        value = String.valueOf(user.getFailedLogins());
-                        break;
-
-                    default:
-                }
-
-                if (type != null && value != null) {
-                    atts.addAttribute("", "", feature.name(), type, value);
-                }
-            }
-
-            handler.startElement("", "", "user", atts);
-
-            // Using UserTO for attribute values, since the conversion logic of
-            // values to String is already encapsulated there
-            UserTO userTO = userDataBinder.getUserTO(user, true);
-
-            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
-
-            if (conf.getFeatures().contains(Feature.relationships)) {
-                handler.startElement("", "", "relationships", null);
-
-                for (RelationshipTO rel : userTO.getRelationships()) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "anyObjectKey",
-                            ReportXMLConst.XSD_STRING, rel.getRightKey());
-                    handler.startElement("", "", "relationship", atts);
-
-                    if (conf.getFeatures().contains(Feature.resources)) {
-                        for (URelationship actualRel : user.getRelationships(rel.getRightKey())) {
-                            doExtractResources(
-                                    handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true));
-                        }
-                    }
-
-                    handler.endElement("", "", "relationship");
-                }
-
-                handler.endElement("", "", "relationships");
-            }
-            if (conf.getFeatures().contains(Feature.memberships)) {
-                handler.startElement("", "", "memberships", null);
-
-                for (MembershipTO memb : userTO.getMemberships()) {
-                    atts.clear();
-
-                    atts.addAttribute("", "", "groupKey",
-                            ReportXMLConst.XSD_STRING, memb.getRightKey());
-                    atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, memb.getGroupName());
-                    handler.startElement("", "", "membership", atts);
-
-                    if (conf.getFeatures().contains(Feature.resources)) {
-                        UMembership actualMemb = user.getMembership(memb.getRightKey());
-                        if (actualMemb == null) {
-                            LOG.warn("Unexpected: cannot find membership for group {} for user {}",
-                                    memb.getRightKey(), user);
-                        } else {
-                            doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true));
-                        }
-                    }
-
-                    handler.endElement("", "", "membership");
-                }
-
-                handler.endElement("", "", "memberships");
-            }
-
-            if (conf.getFeatures().contains(Feature.resources)) {
-                doExtractResources(handler, userTO);
-            }
-
-            handler.endElement("", "", "user");
-        }
-    }
-
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
-        handler.startElement("", "", "configurations", null);
-        handler.startElement("", "", "userAttributes", atts);
-
-        for (Feature feature : conf.getFeatures()) {
-            atts.clear();
-            handler.startElement("", "", "feature", atts);
-            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
-            handler.endElement("", "", "feature");
-        }
-
-        for (String attr : conf.getPlainAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "attribute", atts);
-            handler.characters(attr.toCharArray(), 0, attr.length());
-            handler.endElement("", "", "attribute");
-        }
-
-        for (String derAttr : conf.getDerAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "derAttribute", atts);
-            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
-            handler.endElement("", "", "derAttribute");
-        }
-
-        for (String virAttr : conf.getVirAttrs()) {
-            atts.clear();
-            handler.startElement("", "", "virAttribute", atts);
-            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
-            handler.endElement("", "", "virAttribute");
-        }
-
-        handler.endElement("", "", "userAttributes");
-        handler.endElement("", "", "configurations");
-    }
-
-    private int count() {
-        return StringUtils.isBlank(conf.getMatchingCond())
-                ? userDAO.count()
-                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER);
-    }
-
-    @Override
-    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
-        if (conf instanceof UserReportletConf) {
-            this.conf = UserReportletConf.class.cast(conf);
-        } else {
-            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
-        }
-
-        doExtractConf(handler);
-
-        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-            List<User> users;
-            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
-            } else {
-                users = searchDAO.search(
-                        SyncopeConstants.FULL_ADMIN_REALMS,
-                        SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page,
-                        AnyDAO.DEFAULT_PAGE_SIZE,
-                        Collections.<OrderByClause>emptyList(),
-                        AnyTypeKind.USER);
-            }
-
-            doExtract(handler, users);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
deleted file mode 100644
index 3dd6d78..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/XSLTTransformer.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-import javax.xml.transform.Source;
-import javax.xml.transform.Templates;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import org.apache.cocoon.pipeline.SetupException;
-import org.apache.cocoon.pipeline.caching.CacheKey;
-import org.apache.cocoon.pipeline.component.CachingPipelineComponent;
-import org.apache.cocoon.pipeline.util.StringRepresentation;
-import org.apache.cocoon.sax.AbstractSAXTransformer;
-import org.apache.cocoon.sax.SAXConsumer;
-import org.apache.cocoon.sax.util.SAXConsumerAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class XSLTTransformer extends AbstractSAXTransformer implements CachingPipelineComponent {
-
-    private static final Logger LOG = LoggerFactory.getLogger(XSLTTransformer.class);
-
-    /**
-     * A generic transformer factory to parse XSLTs.
-     */
-    private static final SAXTransformerFactory TRAX_FACTORY = createNewSAXTransformerFactory();
-
-    /**
-     * The XSLT parameters name pattern.
-     */
-    private static final Pattern XSLT_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][\\w\\-\\.]*");
-
-    /**
-     * The XSLT parameters reference.
-     */
-    private Map<String, Object> parameters;
-
-    /**
-     * The XSLT Template reference.
-     */
-    private Templates templates;
-
-    private Source source;
-
-    public XSLTTransformer(final Source source) {
-        super();
-        this.load(source, null);
-    }
-
-    /**
-     * Creates a new transformer reading the XSLT from the Source source and setting the TransformerFactory attributes.
-     *
-     * This constructor is useful when users want to perform XSLT transformation using <a
-     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
-     *
-     * @param source the XSLT source
-     * @param attributes the Transformer Factory attributes
-     */
-    public XSLTTransformer(final Source source, final Map<String, Object> attributes) {
-        super();
-        this.load(source, attributes);
-    }
-
-    /**
-     * Method useful to create a new transformer reading the XSLT from the URL source and setting the Transformer
-     * Factory attributes.
-     *
-     * This method is useful when users want to perform XSLT transformation using <a
-     * href="http://xml.apache.org/xalan-j/xsltc_usage.html">xsltc</a>.
-     *
-     * @param source the XSLT source
-     * @param attributes the Transformer Factory attributes
-     */
-    private void load(final Source source, final Map<String, Object> attributes) {
-        if (source == null) {
-            throw new IllegalArgumentException("The parameter 'source' mustn't be null.");
-        }
-
-        this.source = source;
-
-        this.load(this.source, this.source.toString(), attributes);
-    }
-
-    private void load(final Source source, final String localCacheKey, final Map<String, Object> attributes) {
-        LOG.debug("{} local cache miss: {}", getClass().getSimpleName(), localCacheKey);
-
-        // XSLT has to be parsed
-        final SAXTransformerFactory transformerFactory;
-        if (attributes == null || attributes.isEmpty()) {
-            transformerFactory = TRAX_FACTORY;
-        } else {
-            transformerFactory = createNewSAXTransformerFactory();
-            for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
-                transformerFactory.setAttribute(attribute.getKey(), attribute.getValue());
-            }
-        }
-
-        try {
-            this.templates = transformerFactory.newTemplates(source);
-        } catch (TransformerConfigurationException e) {
-            throw new SetupException("Impossible to read XSLT from '" + source + "', see nested exception", e);
-        }
-    }
-
-    /**
-     * Sets the XSLT parameters to be applied to XSLT stylesheet.
-     *
-     * @param parameters the XSLT parameters to be applied to XSLT stylesheet
-     */
-    public void setParameters(final Map<String, ? extends Object> parameters) {
-        if (parameters == null) {
-            this.parameters = null;
-        } else {
-            this.parameters = new HashMap<String, Object>(parameters);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setSAXConsumer(final SAXConsumer consumer) {
-        TransformerHandler transformerHandler;
-        try {
-            transformerHandler = TRAX_FACTORY.newTransformerHandler(this.templates);
-        } catch (Exception e) {
-            throw new SetupException("Could not initialize transformer handler.", e);
-        }
-
-        if (this.parameters != null) {
-            final Transformer transformer = transformerHandler.getTransformer();
-
-            for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
-                final String name = entry.getKey();
-
-                // is valid XSLT parameter name
-                if (XSLT_PARAMETER_NAME_PATTERN.matcher(name).matches()) {
-                    transformer.setParameter(name, entry.getValue());
-                }
-            }
-        }
-
-        final SAXResult result = new SAXResult();
-        result.setHandler(consumer);
-        // According to TrAX specs, all TransformerHandlers are LexicalHandlers
-        result.setLexicalHandler(consumer);
-        transformerHandler.setResult(result);
-
-        final SAXConsumerAdapter saxConsumerAdapter = new SAXConsumerAdapter();
-        saxConsumerAdapter.setContentHandler(transformerHandler);
-        super.setSAXConsumer(saxConsumerAdapter);
-    }
-
-    @Override
-    public CacheKey constructCacheKey() {
-        return null;
-    }
-
-    /**
-     * Utility method to create a new transformer factory.
-     *
-     * @return a new transformer factory
-     */
-    private static SAXTransformerFactory createNewSAXTransformerFactory() {
-        return (SAXTransformerFactory) TransformerFactory.newInstance();
-    }
-
-    @Override
-    public String toString() {
-        return StringRepresentation.buildString(this, "src=<" + this.source + ">");
-    }
-}


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

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
new file mode 100644
index 0000000..200f64a
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
@@ -0,0 +1,383 @@
+/*
+ * 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.report;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(UserReportletConf.class)
+public class UserReportlet extends AbstractReportlet {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private AnySearchDAO searchDAO;
+
+    @Autowired
+    private UserDataBinder userDataBinder;
+
+    @Autowired
+    private GroupDataBinder groupDataBinder;
+
+    @Autowired
+    private AnyObjectDataBinder anyObjectDataBinder;
+
+    private UserReportletConf conf;
+
+    private void doExtractResources(final ContentHandler handler, final AnyTO anyTO)
+            throws SAXException {
+
+        if (anyTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : anyTO.getResources()) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName);
+                handler.startElement("", "", "resource", atts);
+                handler.endElement("", "", "resource");
+            }
+
+            handler.endElement("", "", "resources");
+        }
+    }
+
+    private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO,
+            final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs)
+            throws SAXException {
+
+        AttributesImpl atts = new AttributesImpl();
+        if (!attrs.isEmpty()) {
+            Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
+
+            handler.startElement("", "", "attributes", null);
+            for (String attrName : attrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "attribute", atts);
+
+                if (attrMap.containsKey(attrName)) {
+                    for (String value : attrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap();
+
+            handler.startElement("", "", "derivedAttributes", null);
+            for (String attrName : derAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "derivedAttribute", atts);
+
+                if (derAttrMap.containsKey(attrName)) {
+                    for (String value : derAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap();
+
+            handler.startElement("", "", "virtualAttributes", null);
+            for (String attrName : virAttrs) {
+                atts.clear();
+
+                atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName);
+                handler.startElement("", "", "virtualAttribute", atts);
+
+                if (virAttrMap.containsKey(attrName)) {
+                    for (String value : virAttrMap.get(attrName).getValues()) {
+                        handler.startElement("", "", "value", null);
+                        handler.characters(value.toCharArray(), 0, value.length());
+                        handler.endElement("", "", "value");
+                    }
+                } else {
+                    LOG.debug("{} not found for {}[{}]", attrName,
+                            anyTO.getClass().getSimpleName(), anyTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        for (User user : users) {
+            atts.clear();
+
+            for (Feature feature : conf.getFeatures()) {
+                String type = null;
+                String value = null;
+                switch (feature) {
+                    case key:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getKey();
+                        break;
+
+                    case username:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getUsername();
+                        break;
+
+                    case workflowId:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getWorkflowId();
+                        break;
+
+                    case status:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getStatus();
+                        break;
+
+                    case creationDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getCreationDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getCreationDate());
+                        break;
+
+                    case lastLoginDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getLastLoginDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getLastLoginDate());
+                        break;
+
+                    case changePwdDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getChangePwdDate() == null
+                                ? ""
+                                : FormatUtils.format(user.getChangePwdDate());
+                        break;
+
+                    case passwordHistorySize:
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(user.getPasswordHistory().size());
+                        break;
+
+                    case failedLoginCount:
+                        type = ReportXMLConst.XSD_INT;
+                        value = String.valueOf(user.getFailedLogins());
+                        break;
+
+                    default:
+                }
+
+                if (type != null && value != null) {
+                    atts.addAttribute("", "", feature.name(), type, value);
+                }
+            }
+
+            handler.startElement("", "", "user", atts);
+
+            // Using UserTO for attribute values, since the conversion logic of
+            // values to String is already encapsulated there
+            UserTO userTO = userDataBinder.getUserTO(user, true);
+
+            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs());
+
+            if (conf.getFeatures().contains(Feature.relationships)) {
+                handler.startElement("", "", "relationships", null);
+
+                for (RelationshipTO rel : userTO.getRelationships()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "anyObjectKey",
+                            ReportXMLConst.XSD_STRING, rel.getRightKey());
+                    handler.startElement("", "", "relationship", atts);
+
+                    if (conf.getFeatures().contains(Feature.resources)) {
+                        for (URelationship actualRel : user.getRelationships(rel.getRightKey())) {
+                            doExtractResources(
+                                    handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true));
+                        }
+                    }
+
+                    handler.endElement("", "", "relationship");
+                }
+
+                handler.endElement("", "", "relationships");
+            }
+            if (conf.getFeatures().contains(Feature.memberships)) {
+                handler.startElement("", "", "memberships", null);
+
+                for (MembershipTO memb : userTO.getMemberships()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "groupKey",
+                            ReportXMLConst.XSD_STRING, memb.getRightKey());
+                    atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, memb.getGroupName());
+                    handler.startElement("", "", "membership", atts);
+
+                    if (conf.getFeatures().contains(Feature.resources)) {
+                        UMembership actualMemb = user.getMembership(memb.getRightKey());
+                        if (actualMemb == null) {
+                            LOG.warn("Unexpected: cannot find membership for group {} for user {}",
+                                    memb.getRightKey(), user);
+                        } else {
+                            doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true));
+                        }
+                    }
+
+                    handler.endElement("", "", "membership");
+                }
+
+                handler.endElement("", "", "memberships");
+            }
+
+            if (conf.getFeatures().contains(Feature.resources)) {
+                doExtractResources(handler, userTO);
+            }
+
+            handler.endElement("", "", "user");
+        }
+    }
+
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
+        AttributesImpl atts = new AttributesImpl();
+        handler.startElement("", "", "configurations", null);
+        handler.startElement("", "", "userAttributes", atts);
+
+        for (Feature feature : conf.getFeatures()) {
+            atts.clear();
+            handler.startElement("", "", "feature", atts);
+            handler.characters(feature.name().toCharArray(), 0, feature.name().length());
+            handler.endElement("", "", "feature");
+        }
+
+        for (String attr : conf.getPlainAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "attribute", atts);
+            handler.characters(attr.toCharArray(), 0, attr.length());
+            handler.endElement("", "", "attribute");
+        }
+
+        for (String derAttr : conf.getDerAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "derAttribute", atts);
+            handler.characters(derAttr.toCharArray(), 0, derAttr.length());
+            handler.endElement("", "", "derAttribute");
+        }
+
+        for (String virAttr : conf.getVirAttrs()) {
+            atts.clear();
+            handler.startElement("", "", "virAttribute", atts);
+            handler.characters(virAttr.toCharArray(), 0, virAttr.length());
+            handler.endElement("", "", "virAttribute");
+        }
+
+        handler.endElement("", "", "userAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    private int count() {
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? userDAO.count()
+                : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER);
+    }
+
+    @Override
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof UserReportletConf) {
+            this.conf = UserReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
+        doExtractConf(handler);
+
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<User> users;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                users = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
+                        SearchCondConverter.convert(this.conf.getMatchingCond()),
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
+            }
+
+            doExtract(handler, users);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index 06df8d6..aecfe69 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
@@ -76,11 +76,14 @@ import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.notification.NotificationRecipientsProvider;
+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.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 @Component
@@ -276,6 +279,23 @@ public class NotificationManagerImpl implements NotificationManager {
         });
     }
 
+    @EventListener
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    @Override
+    public void createTasks(final AfterHandlingEvent event) {
+        if (event.isNotificationsAvailable()) {
+            createTasks(
+                    event.getType(),
+                    event.getCategory(),
+                    event.getSubcategory(),
+                    event.getEvent(),
+                    event.getCondition(),
+                    event.getBefore(),
+                    event.getOutput(),
+                    event.getInput());
+        }
+    }
+
     @Override
     public List<NotificationTask> createTasks(
             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/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index e487986..3491736 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -42,14 +42,17 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.identityconnectors.framework.common.objects.SyncDeltaType;
@@ -65,14 +68,23 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
     protected PullUtils pullUtils;
 
     @Autowired
-    protected VirSchemaDAO virSchemaDAO;
+    private NotificationManager notificationManager;
 
     @Autowired
-    protected VirAttrCache virAttrCache;
+    private AuditManager auditManager;
 
-    protected SyncopePullExecutor executor;
+    @Autowired
+    private ConnObjectUtils connObjectUtils;
+
+    @Autowired
+    private VirSchemaDAO virSchemaDAO;
+
+    @Autowired
+    private VirAttrCache virAttrCache;
+
+    private SyncopePullExecutor executor;
 
-    protected Result latestResult;
+    private Result latestResult;
 
     protected abstract String getName(AnyTO anyTO);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 9d4ab3e..a17c789 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -46,6 +46,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
@@ -55,6 +56,7 @@ import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -64,6 +66,9 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
     @Autowired
     protected MappingManager mappingManager;
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     protected abstract String getName(Any<?> any);
 
     protected void deprovision(final Any<?> any) {
@@ -357,22 +362,17 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
 
                 throw new JobExecutionException(e);
             } finally {
-                notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
-                        any.getType().getKind().name().toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        any);
-                auditManager.audit(AuditElements.EventCategoryType.PUSH,
+                publisher.publishEvent(new AfterHandlingEvent(this,
+                        true,
+                        true,
+                        AuditElements.EventCategoryType.PUSH,
                         any.getType().getKind().name().toLowerCase(),
                         profile.getTask().getResource().getKey(),
                         operation,
                         resultStatus,
                         beforeObj,
                         output,
-                        any);
+                        any));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
index 0df7f49..e914ce3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
@@ -38,7 +38,7 @@ public abstract class AbstractRealmResultHandler<T extends ProvisioningTask, A e
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
 
     protected static final String REALM_TYPE = "REALM";
-    
+
     @Autowired
     protected RealmDAO realmDAO;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
index 8f3224c..61e37e1 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
@@ -23,24 +23,18 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
-import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
-import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler;
-import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
-import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
@@ -64,24 +58,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
     protected GroupDAO groupDAO;
 
     /**
-     * ConnectorObject utils.
-     */
-    @Autowired
-    protected ConnObjectUtils connObjectUtils;
-
-    /**
-     * Notification Manager.
-     */
-    @Autowired
-    protected NotificationManager notificationManager;
-
-    /**
-     * Audit Manager.
-     */
-    @Autowired
-    protected AuditManager auditManager;
-
-    /**
      * Propagation manager.
      */
     @Autowired
@@ -117,15 +93,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
     protected GroupDataBinder groupDataBinder;
 
     @Autowired
-    protected AnyObjectProvisioningManager anyObjectProvisioningManager;
-
-    @Autowired
-    protected UserProvisioningManager userProvisioningManager;
-
-    @Autowired
-    protected GroupProvisioningManager groupProvisioningManager;
-
-    @Autowired
     protected AnyUtilsFactory anyUtilsFactory;
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
index 025b7e9..a328e56 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AnyObjectPullResultHandlerImpl.java
@@ -29,14 +29,19 @@ import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class AnyObjectPullResultHandlerImpl extends AbstractPullResultHandler implements AnyObjectPullResultHandler {
 
+    @Autowired
+    private AnyObjectProvisioningManager anyObjectProvisioningManager;
+
     @Override
     protected AnyUtils getAnyUtils() {
         return anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
index dc1613f..01af49f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GroupPullResultHandlerImpl.java
@@ -32,15 +32,20 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class GroupPullResultHandlerImpl extends AbstractPullResultHandler implements GroupPullResultHandler {
 
-    protected final Map<String, String> groupOwnerMap = new HashMap<>();
+    @Autowired
+    private GroupProvisioningManager groupProvisioningManager;
+
+    private final Map<String, String> groupOwnerMap = new HashMap<>();
 
     @Override
     public Map<String, String> getGroupOwnerMap() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
index 34e7530..4d7ad3c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/RealmPushResultHandlerImpl.java
@@ -34,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
 import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
@@ -44,6 +45,8 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.ResultsHandler;
 import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
 import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -51,6 +54,9 @@ public class RealmPushResultHandlerImpl
         extends AbstractRealmResultHandler<PushTask, PushActions>
         implements SyncopePushResultHandler {
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public boolean handle(final String realmKey) {
@@ -313,22 +319,17 @@ public class RealmPushResultHandlerImpl
 
                 throw new JobExecutionException(e);
             } finally {
-                notificationManager.createTasks(AuditElements.EventCategoryType.PUSH,
-                        REALM_TYPE.toLowerCase(),
-                        profile.getTask().getResource().getKey(),
-                        operation,
-                        resultStatus,
-                        beforeObj,
-                        output,
-                        realm);
-                auditManager.audit(AuditElements.EventCategoryType.PUSH,
+                publisher.publishEvent(new AfterHandlingEvent(this,
+                        true,
+                        true,
+                        AuditElements.EventCategoryType.PUSH,
                         REALM_TYPE.toLowerCase(),
                         profile.getTask().getResource().getKey(),
                         operation,
                         resultStatus,
                         beforeObj,
                         output,
-                        realm);
+                        realm));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
index 72855bb..91575e2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/UserPullResultHandlerImpl.java
@@ -31,13 +31,18 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.identityconnectors.framework.common.objects.SyncDelta;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
+import org.springframework.beans.factory.annotation.Autowired;
 
 public class UserPullResultHandlerImpl extends AbstractPullResultHandler implements UserPullResultHandler {
 
+    @Autowired
+    private UserProvisioningManager userProvisioningManager;
+
     @Override
     protected AnyUtils getAnyUtils() {
         return anyUtilsFactory.getInstance(AnyTypeKind.USER);

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioning.properties b/core/provisioning-java/src/main/resources/provisioning.properties
index b7e405d..29aca0d 100644
--- a/core/provisioning-java/src/main/resources/provisioning.properties
+++ b/core/provisioning-java/src/main/resources/provisioning.properties
@@ -28,3 +28,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml
index 37c8181..fab7099 100644
--- a/core/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -93,6 +93,9 @@ under the License.
     </property>
   </bean>
   <bean class="org.apache.syncope.core.provisioning.java.job.SchedulerShutdown"/>
+  <bean class="org.apache.syncope.core.provisioning.java.job.JobManagerImpl">
+    <property name="disableQuartzInstance" value="${quartz.disableInstance:false}"/>
+  </bean>
   
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
     <property name="defaultEncoding" value="${smtpEncoding}"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
deleted file mode 100644
index f71e59c..0000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.event;
-
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-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) {
-        super(source);
-        this.any = any;
-        this.domain = AuthContextUtils.getDomain();
-    }
-
-    public A getAny() {
-        return any;
-    }
-
-    public String getDomain() {
-        return domain;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
deleted file mode 100644
index 8d33609..0000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.event;
-
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-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) {
-        super(source);
-        this.anyTypeKind = anyTypeKind;
-        this.anyKey = anyKey;
-        this.domain = AuthContextUtils.getDomain();
-    }
-
-    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/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties b/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
index 41f21ad..5af0e91 100644
--- a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
+++ b/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
@@ -30,3 +30,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
index 1352f9e..1e34f6a 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -20,8 +20,8 @@ package org.apache.syncope.ext.elasticsearch.client;
 
 import java.io.IOException;
 import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
-import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.elasticsearch.action.delete.DeleteResponse;
 import org.elasticsearch.action.get.GetResponse;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index 5951117..ea86696 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -35,11 +35,11 @@ import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf;
 import org.apache.syncope.common.lib.to.SchedTaskTO;
 import org.apache.syncope.core.logic.TaskLogic;
-import org.apache.syncope.core.logic.report.AuditReportlet;
-import org.apache.syncope.core.logic.report.GroupReportlet;
-import org.apache.syncope.core.logic.report.ReconciliationReportlet;
-import org.apache.syncope.core.logic.report.StaticReportlet;
-import org.apache.syncope.core.logic.report.UserReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.AuditReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.GroupReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.ReconciliationReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.StaticReportlet;
+import org.apache.syncope.core.provisioning.java.job.report.UserReportlet;
 import org.apache.syncope.core.migration.MigrationPullActions;
 import org.apache.syncope.core.persistence.api.DomainsHolder;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/all/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/all/provisioning.properties b/fit/core-reference/src/main/resources/all/provisioning.properties
index 8147828..b66ef97 100644
--- a/fit/core-reference/src/main/resources/all/provisioning.properties
+++ b/fit/core-reference/src/main/resources/all/provisioning.properties
@@ -31,3 +31,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_h2.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/mariadb/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/mariadb/provisioning.properties b/fit/core-reference/src/main/resources/mariadb/provisioning.properties
index 5b6539d..cc042e7 100644
--- a/fit/core-reference/src/main/resources/mariadb/provisioning.properties
+++ b/fit/core-reference/src/main/resources/mariadb/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_mariadb.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/mysql/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/mysql/provisioning.properties
index acb8a8a..48f9ef6 100644
--- a/fit/core-reference/src/main/resources/mysql/provisioning.properties
+++ b/fit/core-reference/src/main/resources/mysql/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_mysql.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/oracle/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/oracle/provisioning.properties b/fit/core-reference/src/main/resources/oracle/provisioning.properties
index a71d41e..67bc013 100644
--- a/fit/core-reference/src/main/resources/oracle/provisioning.properties
+++ b/fit/core-reference/src/main/resources/oracle/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
 quartz.sql=tables_oracle.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/postgres/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/postgres/provisioning.properties b/fit/core-reference/src/main/resources/postgres/provisioning.properties
index 4b578ba..378e75e 100644
--- a/fit/core-reference/src/main/resources/postgres/provisioning.properties
+++ b/fit/core-reference/src/main/resources/postgres/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
 quartz.sql=tables_postgres.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/provisioning.properties b/fit/core-reference/src/main/resources/provisioning.properties
index dae8ca8..b2a8cb6 100644
--- a/fit/core-reference/src/main/resources/provisioning.properties
+++ b/fit/core-reference/src/main/resources/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 quartz.sql=tables_h2.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties b/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
index 510eae0..1a21fef 100644
--- a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
+++ b/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
@@ -29,3 +29,4 @@ virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
 quartz.jobstore=org.quartz.impl.jdbcjobstore.MSSQLDelegate
 quartz.sql=tables_sqlServer.sql
 quartz.scheduler.idleWaitTime=5000
+quartz.disableInstance=false

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
index d3b0192..1674621 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java
@@ -42,7 +42,7 @@ import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.TaskService;
-import org.apache.syncope.core.logic.notification.NotificationJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.apache.syncope.fit.AbstractITCase;
 
 public abstract class AbstractTaskITCase extends AbstractITCase {

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index 65f37f4..4cea200 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -559,7 +559,6 @@ public class ConnectorITCase extends AbstractITCase {
 
         objectClassInfo = connectorService.buildObjectClassInfo(ldap, true);
         assertNotNull(objectClassInfo);
-        assertEquals(2, objectClassInfo.size());
 
         Collection<String> objectClasses = CollectionUtils.collect(objectClassInfo,
                 new Transformer<ConnIdObjectClassTO, String>() {
@@ -570,7 +569,6 @@ public class ConnectorITCase extends AbstractITCase {
             }
 
         });
-        assertEquals(2, objectClasses.size());
         assertTrue(objectClasses.contains(ObjectClass.ACCOUNT_NAME));
         assertTrue(objectClasses.contains(ObjectClass.GROUP_NAME));
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
index 8f3a1cb..f4cd5d7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
@@ -51,7 +51,7 @@ import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
 import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.NotificationService;
-import org.apache.syncope.core.logic.notification.NotificationJob;
+import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob;
 import org.apache.syncope.fit.core.reference.TestNotificationRecipientsProvider;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;

http://git-wip-us.apache.org/repos/asf/syncope/blob/bd695da7/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
index 5f5484d..f9742cd 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/highavailability.adoc
@@ -18,6 +18,9 @@
 //
 ==== High-Availability
 
+[discrete]
+===== OpenJPA
+
 When deploying multiple Syncope <<core>> instances with a single database or database cluster, it is of
 fundamental importance that the contained OpenJPA instances are correctly configured for
 http://openjpa.apache.org/builds/2.4.2/apache-openjpa/docs/ref_guide_event.html[remote event notification^]. +
@@ -93,3 +96,26 @@ becomes:
 <entry key="openjpa.RemoteCommitProvider" value="tcp(Addresses=10.0.1.10;10.0.1.11)"/>
 ....
 ====
+
+[discrete]
+===== Quartz
+
+The http://www.quartz-scheduler.org[Quartz^] scheduler is largely used within Syncope <<core>> to schedule the execution
+of jobs, including <<tasks-pull,pull>>, <<tasks-push,push>>, <<tasks-notification,notification>> and 
+<<tasks-custom,custom>> tasks, and <<reportlets,reportlets>>.
+
+By default, Quartz is configured for
+http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/ConfigJDBCJobStoreClustering.html[clustering^],
+where each node is automatically handled via the underlying JDBC store, and all cluster nodes are equally selectable
+for processing jobs.
+
+There are deployment scenarios which might have different requirements: for example, there could be three Core nodes
+configured with OpenJPA remote commit provider (see above), where two of them are dedicated to serve REST requests,
+leaving the third for running Quartz jobs.
+
+In such cases, it is possible to prevent Quartz from running on a given node by setting the following parameter in
+`core/src/main/resources/provisioning.properties`:
+
+....
+quartz.disableInstance=true
+....


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

Posted by il...@apache.org.
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...");
+    }
+}


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

Posted by il...@apache.org.
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...");
+    }
+}