syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [47/52] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Unit tests all in
Date Mon, 12 Jan 2015 16:32:26 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
index 8944777..2d74da0 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/JobInstanceLoader.java
@@ -23,26 +23,25 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.persistence.api.dao.ConfDAO;
-import org.apache.syncope.persistence.api.dao.NotFoundException;
-import org.apache.syncope.persistence.api.dao.ReportDAO;
-import org.apache.syncope.persistence.api.dao.TaskDAO;
-import org.apache.syncope.persistence.api.entity.Report;
-import org.apache.syncope.persistence.api.entity.conf.CPlainAttr;
-import org.apache.syncope.persistence.api.entity.task.PushTask;
-import org.apache.syncope.persistence.api.entity.task.SchedTask;
-import org.apache.syncope.persistence.api.entity.task.SyncTask;
-import org.apache.syncope.persistence.api.entity.task.Task;
-import org.apache.syncope.provisioning.api.job.SyncJob;
-import org.apache.syncope.provisioning.api.job.TaskJob;
-import org.apache.syncope.provisioning.api.sync.SyncActions;
+import org.apache.syncope.server.persistence.api.dao.ConfDAO;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.persistence.api.dao.ReportDAO;
+import org.apache.syncope.server.persistence.api.dao.TaskDAO;
+import org.apache.syncope.server.persistence.api.entity.Report;
+import org.apache.syncope.server.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+import org.apache.syncope.server.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.server.persistence.api.entity.task.Task;
+import org.apache.syncope.server.provisioning.api.job.JobNamer;
+import org.apache.syncope.server.provisioning.api.job.SyncJob;
+import org.apache.syncope.server.provisioning.api.job.TaskJob;
+import org.apache.syncope.server.provisioning.api.sync.SyncActions;
 import org.apache.syncope.server.logic.notification.NotificationJob;
 import org.apache.syncope.server.logic.report.ReportJob;
-import org.apache.syncope.server.spring.ApplicationContextProvider;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobKey;
@@ -76,43 +75,6 @@ public class JobInstanceLoader {
     @Autowired
     private ConfDAO confDAO;
 
-    private static Long getIdFromJobName(final String name, final String pattern, final int prefixLength) {
-        Long result = null;
-
-        Matcher jobMatcher = Pattern.compile(pattern).matcher(name);
-        if (jobMatcher.matches()) {
-            try {
-                result = Long.valueOf(name.substring(prefixLength));
-            } catch (NumberFormatException e) {
-                LOG.error("Unparsable id: {}", name.substring(prefixLength), e);
-            }
-        }
-
-        return result;
-    }
-
-    public static Long getTaskIdFromJobName(final String name) {
-        return getIdFromJobName("taskJob[0-9]+", name, 7);
-    }
-
-    public static Long getReportIdFromJobName(final String name) {
-        return getIdFromJobName("reportJob[0-9]+", name, 9);
-    }
-
-    public static String getJobName(final Task task) {
-        return task == null
-                ? "taskNotificationJob"
-                : "taskJob" + task.getKey();
-    }
-
-    public static String getJobName(final Report report) {
-        return "reportJob" + report.getKey();
-    }
-
-    public static String getTriggerName(final String jobName) {
-        return "Trigger_" + jobName;
-    }
-
     private void registerJob(final String jobName, final Job jobInstance, final String cronExpression)
             throws SchedulerException, ParseException {
 
@@ -152,7 +114,7 @@ public class JobInstanceLoader {
             scheduler.getScheduler().addJob(jobDetail, true);
         } else {
             CronTriggerImpl cronTrigger = new CronTriggerImpl();
-            cronTrigger.setName(getTriggerName(jobName));
+            cronTrigger.setName(JobNamer.getTriggerName(jobName));
             cronTrigger.setCronExpression(cronExpression);
 
             scheduler.getScheduler().scheduleJob(jobDetail, cronTrigger);
@@ -191,7 +153,7 @@ public class JobInstanceLoader {
             ((SyncJob) jobInstance).setActions(actions);
         }
 
-        registerJob(getJobName(task), jobInstance, cronExpression);
+        registerJob(JobNamer.getJobName(task), jobInstance, cronExpression);
     }
 
     @Transactional(readOnly = true)
@@ -211,7 +173,7 @@ public class JobInstanceLoader {
                 createBean(ReportJob.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
         ((ReportJob) jobInstance).setReportKey(report.getKey());
 
-        registerJob(getJobName(report), jobInstance, report.getCronExpression());
+        registerJob(JobNamer.getJobName(report), jobInstance, report.getCronExpression());
     }
 
     @Transactional(readOnly = true)
@@ -238,11 +200,11 @@ public class JobInstanceLoader {
     }
 
     public void unregisterJob(final Task task) {
-        unregisterJob(getJobName(task));
+        unregisterJob(JobNamer.getJobName(task));
     }
 
     public void unregisterJob(final Report report) {
-        unregisterJob(getJobName(report));
+        unregisterJob(JobNamer.getJobName(report));
     }
 
     @Transactional

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
index 77189e1..c91b827 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
@@ -26,12 +26,13 @@ import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.persistence.api.dao.TaskDAO;
-import org.apache.syncope.persistence.api.entity.EntityFactory;
-import org.apache.syncope.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.server.logic.audit.AuditManager;
-import org.apache.syncope.server.utils.ExceptionUtil;
+import org.apache.syncope.server.persistence.api.dao.TaskDAO;
+import org.apache.syncope.server.persistence.api.entity.EntityFactory;
+import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.server.misc.AuditManager;
+import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
+import org.apache.syncope.server.misc.ExceptionUtil;
 import org.quartz.DisallowConcurrentExecution;
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;
@@ -42,16 +43,18 @@ 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;
 
 /**
  * Periodically checks for notification to send.
  *
  * @see NotificationTask
  */
+@Component
 @DisallowConcurrentExecution
 public class NotificationJob implements Job {
 
-    enum Status {
+    public enum Status {
 
         SENT,
         NOT_SENT

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationManager.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationManager.java
deleted file mode 100644
index 68c3a78..0000000
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationManager.java
+++ /dev/null
@@ -1,441 +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.server.logic.notification;
-
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.common.lib.types.IntMappingType;
-import org.apache.syncope.common.lib.types.SubjectType;
-import org.apache.syncope.persistence.api.RoleEntitlementUtil;
-import org.apache.syncope.persistence.api.dao.ConfDAO;
-import org.apache.syncope.persistence.api.dao.EntitlementDAO;
-import org.apache.syncope.persistence.api.dao.NotificationDAO;
-import org.apache.syncope.persistence.api.dao.RoleDAO;
-import org.apache.syncope.persistence.api.dao.SubjectSearchDAO;
-import org.apache.syncope.persistence.api.dao.TaskDAO;
-import org.apache.syncope.persistence.api.dao.UserDAO;
-import org.apache.syncope.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.persistence.api.entity.Attributable;
-import org.apache.syncope.persistence.api.entity.AttributableUtilFactory;
-import org.apache.syncope.persistence.api.entity.EntityFactory;
-import org.apache.syncope.persistence.api.entity.Notification;
-import org.apache.syncope.persistence.api.entity.PlainAttr;
-import org.apache.syncope.persistence.api.entity.Subject;
-import org.apache.syncope.persistence.api.entity.role.Role;
-import org.apache.syncope.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.persistence.api.entity.user.UDerAttr;
-import org.apache.syncope.persistence.api.entity.user.UPlainAttr;
-import org.apache.syncope.persistence.api.entity.user.UVirAttr;
-import org.apache.syncope.persistence.api.entity.user.User;
-import org.apache.syncope.server.logic.data.RoleDataBinder;
-import org.apache.syncope.server.logic.data.UserDataBinder;
-import org.apache.syncope.server.logic.search.SearchCondConverter;
-import org.apache.syncope.server.utils.ConnObjectUtil;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.context.Context;
-import org.apache.velocity.exception.VelocityException;
-import org.apache.velocity.tools.ToolManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * Create notification tasks that will be executed by NotificationJob.
- *
- * @see NotificationTask
- */
-@Transactional(rollbackFor = { Throwable.class })
-public class NotificationManager {
-
-    /**
-     * Logger.
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
-
-    public static final String MAIL_TEMPLATES = "mailTemplates/";
-
-    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
-
-    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
-
-    /**
-     * Notification DAO.
-     */
-    @Autowired
-    private NotificationDAO notificationDAO;
-
-    /**
-     * Configuration DAO.
-     */
-    @Autowired
-    private ConfDAO confDAO;
-
-    /**
-     * User DAO.
-     */
-    @Autowired
-    private UserDAO userDAO;
-
-    /**
-     * Role DAO.
-     */
-    @Autowired
-    private RoleDAO roleDAO;
-
-    /**
-     * User data binder.
-     */
-    @Autowired
-    private UserDataBinder userDataBinder;
-
-    /**
-     * Role data binder.
-     */
-    @Autowired
-    private RoleDataBinder roleDataBinder;
-
-    /**
-     * User Search DAO.
-     */
-    @Autowired
-    private SubjectSearchDAO searchDAO;
-
-    /**
-     * Task DAO.
-     */
-    @Autowired
-    private TaskDAO taskDAO;
-
-    /**
-     * Velocity template engine.
-     */
-    @Autowired
-    private VelocityEngine velocityEngine;
-
-    /**
-     * Velocity tool manager.
-     */
-    @Autowired
-    private ToolManager velocityToolManager;
-
-    @Autowired
-    private EntitlementDAO entitlementDAO;
-
-    @Autowired
-    private ConnObjectUtil connObjectUtil;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AttributableUtilFactory attrUtilFactory;
-
-    @Transactional(readOnly = true)
-    public long getMaxRetries() {
-        return confDAO.find("notification.maxRetries", "0").getValues().get(0).getLongValue();
-    }
-
-    /**
-     * Create a notification task.
-     *
-     * @param notification notification to take as model
-     * @param attributable the user this task is about
-     * @param model Velocity model
-     * @return notification task, fully populated
-     */
-    private NotificationTask getNotificationTask(
-            final Notification notification,
-            final Attributable<?, ?, ?> attributable,
-            final Map<String, Object> model) {
-
-        if (attributable != null) {
-            connObjectUtil.retrieveVirAttrValues(attributable,
-                    attrUtilFactory.getInstance(
-                            attributable instanceof User ? AttributableType.USER : AttributableType.ROLE));
-        }
-
-        final List<User> recipients = new ArrayList<>();
-
-        if (notification.getRecipients() != null) {
-            recipients.addAll(searchDAO.<User>search(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
-                    SearchCondConverter.convert(notification.getRecipients()),
-                    Collections.<OrderByClause>emptyList(), SubjectType.USER));
-        }
-
-        if (notification.isSelfAsRecipient() && attributable instanceof User) {
-            recipients.add((User) attributable);
-        }
-
-        final Set<String> recipientEmails = new HashSet<>();
-        final List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
-        for (User recipient : recipients) {
-            connObjectUtil.retrieveVirAttrValues(recipient, attrUtilFactory.getInstance(AttributableType.USER));
-
-            String email = getRecipientEmail(notification.getRecipientAttrType(),
-                    notification.getRecipientAttrName(), recipient);
-            if (email == null) {
-                LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
-            } else {
-                recipientEmails.add(email);
-                recipientTOs.add(userDataBinder.getUserTO(recipient));
-            }
-        }
-
-        if (notification.getStaticRecipients() != null) {
-            recipientEmails.addAll(notification.getStaticRecipients());
-        }
-
-        model.put("recipients", recipientTOs);
-        model.put("syncopeConf", this.findAllSyncopeConfs());
-        model.put("events", notification.getEvents());
-
-        NotificationTask task = entityFactory.newEntity(NotificationTask.class);
-        task.setTraceLevel(notification.getTraceLevel());
-        task.getRecipients().addAll(recipientEmails);
-        task.setSender(notification.getSender());
-        task.setSubject(notification.getSubject());
-
-        String htmlBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, model);
-        String textBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, model);
-
-        task.setHtmlBody(htmlBody);
-        task.setTextBody(textBody);
-
-        return task;
-    }
-
-    private String mergeTemplateIntoString(final String templateLocation, final Map<String, Object> model) {
-        StringWriter result = new StringWriter();
-        try {
-            Context velocityContext = createVelocityContext(model);
-            velocityEngine.mergeTemplate(templateLocation, SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
-        } catch (VelocityException e) {
-            LOG.error("Could not get mail body", e);
-        } catch (RuntimeException e) {
-            // ensure same behaviour as by using Spring VelocityEngineUtils.mergeTemplateIntoString()
-            throw e;
-        } catch (Exception e) {
-            LOG.error("Could not get mail body", e);
-        }
-
-        return result.toString();
-    }
-
-    /**
-     * Create a Velocity Context for the given model, to be passed to the template for merging.
-     *
-     * @param model Velocity model
-     * @return Velocity context
-     */
-    protected Context createVelocityContext(Map<String, Object> model) {
-        Context toolContext = velocityToolManager.createContext();
-        return new VelocityContext(model, toolContext);
-    }
-
-    /**
-     * Create notification tasks for each notification matching the given user id and (some of) tasks performed.
-     */
-    public void createTasks(
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final String subcategory,
-            final String event,
-            final Result condition,
-            final Object before,
-            final Object output,
-            final Object... input) {
-
-        SubjectType subjectType = null;
-        Subject<?, ?, ?> subject = null;
-
-        if (before instanceof UserTO) {
-            subjectType = SubjectType.USER;
-            subject = userDAO.find(((UserTO) before).getKey());
-        } else if (output instanceof UserTO) {
-            subjectType = SubjectType.USER;
-            subject = userDAO.find(((UserTO) output).getKey());
-        } else if (before instanceof RoleTO) {
-            subjectType = SubjectType.ROLE;
-            subject = roleDAO.find(((RoleTO) before).getKey());
-        } else if (output instanceof RoleTO) {
-            subjectType = SubjectType.ROLE;
-            subject = roleDAO.find(((RoleTO) output).getKey());
-        }
-
-        LOG.debug("Search notification for [{}]{}", subjectType, subject);
-
-        for (Notification notification : notificationDAO.findAll()) {
-            LOG.debug("Notification available user about {}", notification.getUserAbout());
-            LOG.debug("Notification available role about {}", notification.getRoleAbout());
-            if (notification.isActive()) {
-
-                final Set<String> events = new HashSet<>(notification.getEvents());
-                events.retainAll(Collections.<String>singleton(AuditLoggerName.buildEvent(
-                        type, category, subcategory, event, condition)));
-
-                if (events.isEmpty()) {
-                    LOG.debug("No events found about {}", subject);
-                } else if (subjectType == null || subject == null
-                        || notification.getUserAbout() == null || notification.getRoleAbout() == null
-                        || searchDAO.matches(subject,
-                                SearchCondConverter.convert(notification.getUserAbout()), subjectType)
-                        || searchDAO.matches(subject,
-                                SearchCondConverter.convert(notification.getRoleAbout()), subjectType)) {
-
-                    LOG.debug("Creating notification task for events {} about {}", events, subject);
-
-                    final Map<String, Object> model = new HashMap<>();
-                    model.put("type", type);
-                    model.put("category", category);
-                    model.put("subcategory", subcategory);
-                    model.put("event", event);
-                    model.put("condition", condition);
-                    model.put("before", before);
-                    model.put("output", output);
-                    model.put("input", input);
-
-                    if (subject instanceof User) {
-                        model.put("user", userDataBinder.getUserTO((User) subject));
-                    } else if (subject instanceof Role) {
-                        model.put("role", roleDataBinder.getRoleTO((Role) subject));
-                    }
-
-                    taskDAO.save(getNotificationTask(notification, subject, model));
-                }
-            } else {
-                LOG.debug("Notification {}, userAbout {}, roleAbout {} is deactivated, "
-                        + "notification task will not be created", notification.getKey(),
-                        notification.getUserAbout(), notification.getRoleAbout());
-            }
-        }
-    }
-
-    private String getRecipientEmail(
-            final IntMappingType recipientAttrType, final String recipientAttrName, final User user) {
-
-        String email = null;
-
-        switch (recipientAttrType) {
-            case Username:
-                email = user.getUsername();
-                break;
-
-            case UserSchema:
-                UPlainAttr attr = user.getPlainAttr(recipientAttrName);
-                if (attr != null && !attr.getValuesAsStrings().isEmpty()) {
-                    email = attr.getValuesAsStrings().get(0);
-                }
-                break;
-
-            case UserVirtualSchema:
-                UVirAttr virAttr = user.getVirAttr(recipientAttrName);
-                if (virAttr != null && !virAttr.getValues().isEmpty()) {
-                    email = virAttr.getValues().get(0);
-                }
-                break;
-
-            case UserDerivedSchema:
-                UDerAttr derAttr = user.getDerAttr(recipientAttrName);
-                if (derAttr != null) {
-                    email = derAttr.getValue(user.getPlainAttrs());
-                }
-                break;
-
-            default:
-        }
-
-        return email;
-    }
-
-    /**
-     * Store execution of a NotificationTask.
-     *
-     * @param execution task execution.
-     * @return merged task execution.
-     */
-    public TaskExec storeExec(final TaskExec execution) {
-        NotificationTask task = taskDAO.find(execution.getTask().getKey());
-        task.addExec(execution);
-        task.setExecuted(true);
-        taskDAO.save(task);
-        // this flush call is needed to generate a value for the execution id
-        taskDAO.flush();
-        return execution;
-    }
-
-    /**
-     * Set execution state of NotificationTask with provided id.
-     *
-     * @param taskId task to be updated
-     * @param executed execution state
-     */
-    public void setTaskExecuted(final Long taskId, final boolean executed) {
-        NotificationTask task = taskDAO.find(taskId);
-        task.setExecuted(executed);
-        taskDAO.save(task);
-    }
-
-    /**
-     * Count the number of task executions of a given task with a given status.
-     *
-     * @param taskId task id
-     * @param status status
-     * @return number of task executions
-     */
-    public long countExecutionsWithStatus(final Long taskId, final String status) {
-        NotificationTask task = taskDAO.find(taskId);
-        long count = 0;
-        for (TaskExec taskExec : task.getExecs()) {
-            if (status == null) {
-                if (taskExec.getStatus() == null) {
-                    count++;
-                }
-            } else if (status.equals(taskExec.getStatus())) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    protected Map<String, String> findAllSyncopeConfs() {
-        Map<String, String> syncopeConfMap = new HashMap<>();
-        for (PlainAttr attr : confDAO.get().getPlainAttrs()) {
-            syncopeConfMap.put(attr.getSchema().getKey(), attr.getValuesAsStrings().get(0));
-        }
-        return syncopeConfMap;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java
index ca59705..1b8d65e 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java
@@ -33,14 +33,14 @@ import org.apache.commons.io.IOUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.persistence.api.dao.ReportDAO;
-import org.apache.syncope.persistence.api.dao.ReportExecDAO;
-import org.apache.syncope.persistence.api.entity.EntityFactory;
-import org.apache.syncope.persistence.api.entity.Report;
-import org.apache.syncope.persistence.api.entity.ReportExec;
-import org.apache.syncope.server.logic.data.ReportDataBinder;
-import org.apache.syncope.server.spring.ApplicationContextProvider;
-import org.apache.syncope.server.utils.ExceptionUtil;
+import org.apache.syncope.server.persistence.api.dao.ReportDAO;
+import org.apache.syncope.server.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.server.persistence.api.entity.EntityFactory;
+import org.apache.syncope.server.persistence.api.entity.Report;
+import org.apache.syncope.server.persistence.api.entity.ReportExec;
+import org.apache.syncope.server.logic.ReportLogic;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.server.misc.ExceptionUtil;
 import org.quartz.DisallowConcurrentExecution;
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;
@@ -75,11 +75,8 @@ public class ReportJob implements Job {
     @Autowired
     private ReportExecDAO reportExecDAO;
 
-    /**
-     * Report data binder.
-     */
     @Autowired
-    private ReportDataBinder dataBinder;
+    private ReportLogic dataBinder;
 
     @Autowired
     private EntityFactory entityFactory;

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
index 3ffbdfb..756f817 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/RoleReportlet.java
@@ -31,15 +31,15 @@ import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.types.SubjectType;
-import org.apache.syncope.persistence.api.RoleEntitlementUtil;
-import org.apache.syncope.persistence.api.dao.EntitlementDAO;
-import org.apache.syncope.persistence.api.dao.RoleDAO;
-import org.apache.syncope.persistence.api.dao.SubjectSearchDAO;
-import org.apache.syncope.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.persistence.api.entity.membership.Membership;
-import org.apache.syncope.persistence.api.entity.role.Role;
-import org.apache.syncope.server.logic.data.RoleDataBinder;
-import org.apache.syncope.server.logic.search.SearchCondConverter;
+import org.apache.syncope.server.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.RoleDAO;
+import org.apache.syncope.server.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.role.Role;
+import org.apache.syncope.server.provisioning.java.data.RoleDataBinderImpl;
+import org.apache.syncope.server.misc.search.SearchCondConverter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -60,7 +60,7 @@ public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
     private SubjectSearchDAO searchDAO;
 
     @Autowired
-    private RoleDataBinder roleDataBinder;
+    private RoleDataBinderImpl roleDataBinder;
 
     private List<Role> getPagedRoles(final int page) {
         final Set<Long> adminRoleIds = RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/StaticReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/StaticReportlet.java
index bdc76f8..5f5ee96 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/StaticReportlet.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/StaticReportlet.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.server.logic.report;
 
 import org.apache.syncope.common.lib.report.StaticReportletConf;
-import org.apache.syncope.server.utils.DataFormat;
+import org.apache.syncope.server.misc.DataFormat;
 import org.springframework.util.StringUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
index ecae604..ebdad43 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/UserReportlet.java
@@ -32,17 +32,17 @@ import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.SubjectType;
-import org.apache.syncope.persistence.api.RoleEntitlementUtil;
-import org.apache.syncope.persistence.api.dao.EntitlementDAO;
-import org.apache.syncope.persistence.api.dao.SubjectSearchDAO;
-import org.apache.syncope.persistence.api.dao.UserDAO;
-import org.apache.syncope.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.persistence.api.entity.membership.Membership;
-import org.apache.syncope.persistence.api.entity.user.User;
-import org.apache.syncope.server.logic.data.RoleDataBinder;
-import org.apache.syncope.server.logic.data.UserDataBinder;
-import org.apache.syncope.server.logic.search.SearchCondConverter;
-import org.apache.syncope.server.utils.DataFormat;
+import org.apache.syncope.server.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.server.persistence.api.dao.UserDAO;
+import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.misc.search.SearchCondConverter;
+import org.apache.syncope.server.misc.DataFormat;
+import org.apache.syncope.server.provisioning.api.data.RoleDataBinder;
+import org.apache.syncope.server.provisioning.api.data.UserDataBinder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondConverter.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondConverter.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondConverter.java
deleted file mode 100644
index 662cd46..0000000
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondConverter.java
+++ /dev/null
@@ -1,50 +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.server.logic.search;
-
-import org.apache.cxf.jaxrs.ext.search.SearchBean;
-import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser;
-import org.apache.syncope.common.lib.search.SyncopeFiqlSearchConditionBuilder;
-import org.apache.syncope.persistence.api.dao.search.SearchCond;
-
-/**
- * Converts FIQL expressions to Syncope's <tt>SearchCond</tt>.
- */
-public final class SearchCondConverter {
-
-    /**
-     * Parses a FIQL expression into Syncope's <tt>SearchCond</tt>, using CXF's <tt>FiqlParser</tt>.
-     *
-     * @param fiqlExpression FIQL string
-     * @return <tt>SearchCond</tt> instance for given FIQL expression
-     * @see FiqlParser
-     */
-    public static SearchCond convert(final String fiqlExpression) {
-        FiqlParser<SearchBean> fiqlParser = new FiqlParser<SearchBean>(
-                SearchBean.class, SyncopeFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES);
-        SearchCondVisitor searchCondVisitor = new SearchCondVisitor();
-
-        searchCondVisitor.visit(fiqlParser.parse(fiqlExpression));
-        return searchCondVisitor.getQuery();
-    }
-
-    private SearchCondConverter() {
-        // empty constructor for static utility class        
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondVisitor.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondVisitor.java
deleted file mode 100644
index 5c9acdb..0000000
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondVisitor.java
+++ /dev/null
@@ -1,203 +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.server.logic.search;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import org.apache.cxf.jaxrs.ext.search.ConditionType;
-import org.apache.cxf.jaxrs.ext.search.SearchBean;
-import org.apache.cxf.jaxrs.ext.search.SearchCondition;
-import org.apache.cxf.jaxrs.ext.search.SearchUtils;
-import org.apache.cxf.jaxrs.ext.search.visitor.AbstractSearchConditionVisitor;
-import org.apache.syncope.common.lib.search.SearchableFields;
-import org.apache.syncope.common.lib.search.SpecialAttr;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.persistence.api.dao.search.AttributeCond;
-import org.apache.syncope.persistence.api.dao.search.EntitlementCond;
-import org.apache.syncope.persistence.api.dao.search.MembershipCond;
-import org.apache.syncope.persistence.api.dao.search.ResourceCond;
-import org.apache.syncope.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.persistence.api.dao.search.SubjectCond;
-
-/**
- * Converts CXF's <tt>SearchCondition</tt> into internal <tt>SearchCond</tt>.
- */
-public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean, SearchCond> {
-
-    private static final List<String> ATTRIBUTABLE_FIELDS;
-
-    static {
-        ATTRIBUTABLE_FIELDS = new ArrayList<String>();
-        ATTRIBUTABLE_FIELDS.addAll(SearchableFields.get(UserTO.class));
-        ATTRIBUTABLE_FIELDS.addAll(SearchableFields.get(RoleTO.class));
-    }
-
-    private SearchCond searchCond;
-
-    public SearchCondVisitor() {
-        super(null);
-    }
-
-    public SearchCondVisitor(final Map<String, String> fieldMap) {
-        super(fieldMap);
-    }
-
-    private AttributeCond createAttributeCond(final String schema) {
-        AttributeCond attributeCond = ATTRIBUTABLE_FIELDS.contains(schema)
-                ? new SubjectCond()
-                : new AttributeCond();
-        attributeCond.setSchema(schema);
-        return attributeCond;
-    }
-
-    private SearchCond visitPrimitive(final SearchCondition<SearchBean> sc) {
-        String name = getRealPropertyName(sc.getStatement().getProperty());
-        SpecialAttr specialAttrName = SpecialAttr.fromString(name);
-
-        String value = SearchUtils.toSqlWildcardString(sc.getStatement().getValue().toString(), false).
-                replaceAll("\\\\_", "_");
-        SpecialAttr specialAttrValue = SpecialAttr.fromString(value);
-
-        AttributeCond attributeCond = createAttributeCond(name);
-        attributeCond.setExpression(value);
-
-        SearchCond leaf;
-        switch (sc.getConditionType()) {
-            case EQUALS:
-            case NOT_EQUALS:
-                if (specialAttrName == null) {
-                    if (specialAttrValue != null && specialAttrValue == SpecialAttr.NULL) {
-                        attributeCond.setType(AttributeCond.Type.ISNULL);
-                        attributeCond.setExpression(null);
-                    } else if (value.indexOf('%') == -1) {
-                        attributeCond.setType(AttributeCond.Type.EQ);
-                    } else {
-                        attributeCond.setType(AttributeCond.Type.LIKE);
-                    }
-
-                    leaf = SearchCond.getLeafCond(attributeCond);
-                } else {
-                    switch (specialAttrName) {
-                        case ROLES:
-                            MembershipCond membershipCond = new MembershipCond();
-                            membershipCond.setRoleId(Long.valueOf(value));
-                            leaf = SearchCond.getLeafCond(membershipCond);
-                            break;
-
-                        case RESOURCES:
-                            ResourceCond resourceCond = new ResourceCond();
-                            resourceCond.setResourceName(value);
-                            leaf = SearchCond.getLeafCond(resourceCond);
-                            break;
-
-                        case ENTITLEMENTS:
-                            EntitlementCond entitlementCond = new EntitlementCond();
-                            entitlementCond.setExpression(value);
-                            leaf = SearchCond.getLeafCond(entitlementCond);
-                            break;
-
-                        default:
-                            throw new IllegalArgumentException(
-                                    String.format("Special attr name %s is not supported", specialAttrName));
-                    }
-                }
-                if (sc.getConditionType() == ConditionType.NOT_EQUALS) {
-                    if (leaf.getAttributeCond() != null
-                            && leaf.getAttributeCond().getType() == AttributeCond.Type.ISNULL) {
-
-                        leaf.getAttributeCond().setType(AttributeCond.Type.ISNOTNULL);
-                    } else if (leaf.getSubjectCond() != null
-                            && leaf.getSubjectCond().getType() == SubjectCond.Type.ISNULL) {
-
-                        leaf.getSubjectCond().setType(AttributeCond.Type.ISNOTNULL);
-                    } else {
-                        leaf = SearchCond.getNotLeafCond(leaf);
-                    }
-                }
-                break;
-
-            case GREATER_OR_EQUALS:
-                attributeCond.setType(AttributeCond.Type.GE);
-                leaf = SearchCond.getLeafCond(attributeCond);
-                break;
-
-            case GREATER_THAN:
-                attributeCond.setType(AttributeCond.Type.GT);
-                leaf = SearchCond.getLeafCond(attributeCond);
-                break;
-
-            case LESS_OR_EQUALS:
-                attributeCond.setType(AttributeCond.Type.LE);
-                leaf = SearchCond.getLeafCond(attributeCond);
-                break;
-
-            case LESS_THAN:
-                attributeCond.setType(AttributeCond.Type.LT);
-                leaf = SearchCond.getLeafCond(attributeCond);
-                break;
-
-            default:
-                throw new IllegalArgumentException(
-                        String.format("Condition type %s is not supported", sc.getConditionType().name()));
-        }
-
-        return leaf;
-    }
-
-    private SearchCond visitCompount(final SearchCondition<SearchBean> sc) {
-        List<SearchCond> searchConds = new ArrayList<SearchCond>();
-        for (SearchCondition<SearchBean> searchCondition : sc.getSearchConditions()) {
-            searchConds.add(searchCondition.getStatement() == null
-                    ? visitCompount(searchCondition)
-                    : visitPrimitive(searchCondition));
-        }
-
-        SearchCond compound;
-        switch (sc.getConditionType()) {
-            case AND:
-                compound = SearchCond.getAndCond(searchConds);
-                break;
-
-            case OR:
-                compound = SearchCond.getOrCond(searchConds);
-                break;
-
-            default:
-                throw new IllegalArgumentException(
-                        String.format("Condition type %s is not supported", sc.getConditionType().name()));
-        }
-
-        return compound;
-    }
-
-    @Override
-    public void visit(final SearchCondition<SearchBean> sc) {
-        searchCond = sc.getStatement() == null
-                ? visitCompount(sc)
-                : visitPrimitive(sc);
-    }
-
-    @Override
-    public SearchCond getQuery() {
-        return searchCond;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/resources/logic.properties
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/resources/logic.properties b/syncope620/server/logic/src/main/resources/logic.properties
new file mode 100644
index 0000000..95dd278
--- /dev/null
+++ b/syncope620/server/logic/src/main/resources/logic.properties
@@ -0,0 +1,18 @@
+# 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.
+attributableTransformer=org.apache.syncope.server.provisioning.java.DefaultAttributableTransformer
+logicInvocationHandler=org.apache.syncope.server.logic.LogicInvocationHandler

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/main/resources/logicContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/resources/logicContext.xml b/syncope620/server/logic/src/main/resources/logicContext.xml
new file mode 100644
index 0000000..273b8a2
--- /dev/null
+++ b/syncope620/server/logic/src/main/resources/logicContext.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/aop 
+                           http://www.springframework.org/schema/aop/spring-aop.xsd
+                           http://www.springframework.org/schema/context
+                           http://www.springframework.org/schema/context/spring-context.xsd">
+  
+  <aop:aspectj-autoproxy/>
+
+  <context:component-scan base-package="org.apache.syncope.server.logic"/>
+
+  <bean class="${logicInvocationHandler}"/>
+  <bean class="${attributableTransformer}"/>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
new file mode 100644
index 0000000..4db9bba
--- /dev/null
+++ b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
@@ -0,0 +1,643 @@
+/*
+ * 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.server.logic;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import javax.annotation.Resource;
+import javax.mail.Flags.Flag;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Store;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.search.RoleFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.search.UserFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.server.persistence.api.dao.ConfDAO;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.server.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.server.persistence.api.dao.TaskDAO;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.server.persistence.api.entity.Entitlement;
+import org.apache.syncope.server.persistence.api.entity.EntityFactory;
+import org.apache.syncope.server.persistence.api.entity.Notification;
+import org.apache.syncope.server.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.server.persistence.api.entity.conf.CPlainSchema;
+import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
+import org.apache.syncope.server.logic.notification.NotificationJob;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {
+    "classpath:provisioningContext.xml",
+    "classpath:logicContext.xml",
+    "classpath:persistenceTest.xml",
+    "classpath:logicTest.xml"
+})
+@Transactional
+public class NotificationTest {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationTest.class);
+
+    private static final String SMTP_HOST = "localhost";
+
+    private static final int SMTP_PORT = 2525;
+
+    private static final String POP3_HOST = "localhost";
+
+    private static final int POP3_PORT = 1110;
+
+    private static final String MAIL_ADDRESS = "notificationtest@syncope.apache.org";
+
+    private static final String MAIL_PASSWORD = "password";
+
+    private static GreenMail greenMail;
+
+    @Resource(name = "adminUser")
+    private String adminUser;
+
+    @Autowired
+    private EntitlementDAO entitlementDAO;
+
+    @Autowired
+    private NotificationDAO notificationDAO;
+
+    @Autowired
+    private TaskDAO taskDAO;
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Autowired
+    private UserLogic userLogic;
+
+    @Autowired
+    private RoleLogic roleLogic;
+
+    @Autowired
+    private TaskLogic taskLogic;
+
+    @Autowired
+    private NotificationJob notificationJob;
+
+    @Autowired
+    private NotificationManager notificationManager;
+
+    @Autowired
+    private JavaMailSender mailSender;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private AttributableUtilFactory attrUtilFactory;
+
+    @BeforeClass
+    public static void startGreenMail() {
+        ServerSetup[] config = new ServerSetup[2];
+        config[0] = new ServerSetup(SMTP_PORT, SMTP_HOST, ServerSetup.PROTOCOL_SMTP);
+        config[1] = new ServerSetup(POP3_PORT, POP3_HOST, ServerSetup.PROTOCOL_POP3);
+        greenMail = new GreenMail(config);
+        greenMail.setUser(MAIL_ADDRESS, MAIL_PASSWORD);
+        greenMail.start();
+    }
+
+    @AfterClass
+    public static void stopGreenMail() {
+        if (greenMail != null) {
+            greenMail.stop();
+        }
+    }
+
+    private static UserTO getUniqueSampleTO(final String email) {
+        return getSampleTO(UUID.randomUUID().toString().substring(0, 8) + email);
+    }
+
+    private static AttrTO attributeTO(final String schema, final String value) {
+        AttrTO attr = new AttrTO();
+        attr.setSchema(schema);
+        attr.getValues().add(value);
+        return attr;
+    }
+
+    private static UserTO getSampleTO(final String email) {
+        String uid = email;
+        UserTO userTO = new UserTO();
+        userTO.setPassword("password123");
+        userTO.setUsername(uid);
+
+        userTO.getPlainAttrs().add(attributeTO("fullname", uid));
+        userTO.getPlainAttrs().add(attributeTO("firstname", uid));
+        userTO.getPlainAttrs().add(attributeTO("surname", "surname"));
+        userTO.getPlainAttrs().add(attributeTO("type", "a type"));
+        userTO.getPlainAttrs().add(attributeTO("userId", uid));
+        userTO.getPlainAttrs().add(attributeTO("email", uid));
+        userTO.getPlainAttrs().add(attributeTO("loginDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date())));
+        userTO.getDerAttrs().add(attributeTO("cn", null));
+        userTO.getVirAttrs().add(attributeTO("virtualdata", "virtualvalue"));
+        return userTO;
+    }
+
+    @Before
+    public void setupSecurity() {
+        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+        for (Entitlement entitlement : entitlementDAO.findAll()) {
+            authorities.add(new SimpleGrantedAuthority(entitlement.getKey()));
+        }
+
+        UserDetails userDetails = new User(adminUser, "FAKE_PASSWORD", true, true, true, true, authorities);
+        Authentication authentication = new TestingAuthenticationToken(userDetails, "FAKE_PASSWORD", authorities);
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+    }
+
+    @Before
+    public void setupSMTP() throws Exception {
+        JavaMailSenderImpl sender = (JavaMailSenderImpl) mailSender;
+        sender.setDefaultEncoding(SyncopeConstants.DEFAULT_ENCODING);
+        sender.setHost(SMTP_HOST);
+        sender.setPort(SMTP_PORT);
+    }
+
+    private boolean verifyMail(final String sender, final String subject) throws Exception {
+        LOG.info("Waiting for notification to be sent...");
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+        }
+
+        boolean found = false;
+        Session session = Session.getDefaultInstance(System.getProperties());
+        Store store = session.getStore("pop3");
+        store.connect(POP3_HOST, POP3_PORT, MAIL_ADDRESS, MAIL_PASSWORD);
+
+        Folder inbox = store.getFolder("INBOX");
+        assertNotNull(inbox);
+        inbox.open(Folder.READ_WRITE);
+
+        Message[] messages = inbox.getMessages();
+        for (int i = 0; i < messages.length; i++) {
+            if (sender.equals(messages[i].getFrom()[0].toString()) && subject.equals(messages[i].getSubject())) {
+                found = true;
+                messages[i].setFlag(Flag.DELETED, true);
+            }
+        }
+
+        inbox.close(true);
+        store.close();
+        return found;
+    }
+
+    @Test
+    public void notifyByMail() throws Exception {
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(new UserFiqlSearchConditionBuilder().hasRoles(7L).query());
+        notification.setRecipients(new UserFiqlSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        // 2. create user
+        UserTO userTO = getSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. force Quartz job execution and verify e-mail
+        notificationJob.execute(null);
+        assertTrue(verifyMail(sender, subject));
+
+        // 4. get NotificationTask id and text body
+        Long taskId = null;
+        String textBody = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                taskId = task.getKey();
+                textBody = task.getTextBody();
+            }
+        }
+        assertNotNull(taskId);
+        assertNotNull(textBody);
+        assertTrue("Notification mail text doesn't contain expected content.",
+                textBody.contains("Your email address is notificationtest@syncope.apache.org."));
+        assertTrue("Notification mail text doesn't contain expected content.",
+                textBody.contains("Your email address inside a link: "
+                        + "http://localhost/?email=notificationtest%40syncope.apache.org ."));
+
+        // 5. execute Notification task and verify e-mail
+        taskLogic.execute(taskId, false);
+        assertTrue(verifyMail(sender, subject));
+    }
+
+    @Test
+    public void issueSYNCOPE192() throws Exception {
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(new UserFiqlSearchConditionBuilder().hasRoles(7L).query());
+        notification.setRecipients(new UserFiqlSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncope192-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+        notification.setTraceLevel(TraceLevel.NONE);
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        // 2. create user
+        UserTO userTO = getSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. force Quartz job execution and verify e-mail
+        notificationJob.execute(null);
+        assertTrue(verifyMail(sender, subject));
+
+        // 4. get NotificationTask id
+        Long taskId = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                taskId = task.getKey();
+            }
+        }
+        assertNotNull(taskId);
+
+        // 5. verify that last exec status was updated
+        NotificationTaskTO task = (NotificationTaskTO) taskLogic.read(taskId);
+        assertNotNull(task);
+        assertTrue(task.getExecutions().isEmpty());
+        assertTrue(task.isExecuted());
+        assertTrue(StringUtils.isNotBlank(task.getLatestExecStatus()));
+    }
+
+    @Test
+    public void notifyByMailEmptyAbout() throws Exception {
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(null);
+        notification.setRecipients(new UserFiqlSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        // 2. create user
+        UserTO userTO = getSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. force Quartz job execution and verify e-mail
+        notificationJob.execute(null);
+        assertTrue(verifyMail(sender, subject));
+
+        // 4. get NotificationTask id
+        Long taskId = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                taskId = task.getKey();
+            }
+        }
+        assertNotNull(taskId);
+
+        // 5. execute Notification task and verify e-mail
+        taskLogic.execute(taskId, false);
+        assertTrue(verifyMail(sender, subject));
+    }
+
+    @Test
+    public void notifyByMailWithRetry() throws Exception {
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(null);
+        notification.setRecipients(new UserFiqlSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        // 2. create user
+        UserTO userTO = getSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. Set number of retries
+        CPlainAttr maxRetries = entityFactory.newEntity(CPlainAttr.class);
+        maxRetries.setSchema(plainSchemaDAO.find("notification.maxRetries", CPlainSchema.class));
+        maxRetries.addValue("5", attrUtilFactory.getInstance(AttributableType.CONFIGURATION));
+        confDAO.save(maxRetries);
+        confDAO.flush();
+
+        // 4. Stop mail server to force error sending mail
+        stopGreenMail();
+
+        // 5. force Quartz job execution multiple times
+        for (int i = 0; i < 10; i++) {
+            notificationJob.execute(null);
+        }
+
+        // 6. get NotificationTask, count number of executions
+        NotificationTask foundTask = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                foundTask = task;
+            }
+        }
+        assertNotNull(foundTask);
+        assertEquals(6, notificationManager.countExecutionsWithStatus(foundTask.getKey(),
+                NotificationJob.Status.NOT_SENT.name()));
+
+        // 7. start mail server again
+        startGreenMail();
+
+        // 8. reset number of retries
+        maxRetries = entityFactory.newEntity(CPlainAttr.class);
+        maxRetries.setSchema(plainSchemaDAO.find("notification.maxRetries", CPlainSchema.class));
+        maxRetries.addValue("0", attrUtilFactory.getInstance(AttributableType.CONFIGURATION));
+        confDAO.save(maxRetries);
+        confDAO.flush();
+    }
+
+    @Test
+    public void issueSYNCOPE445() throws Exception {
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(new UserFiqlSearchConditionBuilder().hasRoles(7L).query());
+        notification.setRecipients(new UserFiqlSearchConditionBuilder().hasRoles(8L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        notification.getStaticRecipients().add("syncope445@syncope.apache.org");
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        // 2. create user
+        UserTO userTO = getSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. force Quartz job execution and verify e-mail
+        notificationJob.execute(null);
+        assertTrue(verifyMail(sender, subject));
+
+        // 4. get NotificationTask id and text body
+        Long taskId = null;
+        String textBody = null;
+        Set<String> recipients = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                taskId = task.getKey();
+                textBody = task.getTextBody();
+                recipients = task.getRecipients();
+            }
+        }
+
+        assertNotNull(taskId);
+        assertNotNull(textBody);
+        assertTrue(recipients.contains("syncope445@syncope.apache.org"));
+
+        // 5. execute Notification task and verify e-mail
+        taskLogic.execute(taskId, false);
+        assertTrue(verifyMail(sender, subject));
+    }
+
+    @Test
+    public void issueSYNCOPE492() throws Exception {
+        // 1. create suitable disabled notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+        notification.setUserAbout(new UserFiqlSearchConditionBuilder().hasRoles(7L).query());
+        notification.setSelfAsRecipient(true);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        notification.getStaticRecipients().add("syncope492@syncope.apache.org");
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+        notification.setActive(false);
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        final int tasksNumberBefore = taskDAO.findAll(TaskType.NOTIFICATION).size();
+
+        // 2. create user
+        UserTO userTO = getUniqueSampleTO(MAIL_ADDRESS);
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setRoleId(7);
+        userTO.getMemberships().add(membershipTO);
+
+        userLogic.create(userTO, true);
+
+        // 3. force Quartz job execution
+        notificationJob.execute(null);
+
+        // 4. check if number of tasks is not incremented
+        assertEquals(tasksNumberBefore, taskDAO.findAll(TaskType.NOTIFICATION).size());
+    }
+
+    @Test
+    public void issueSYNCOPE446() throws Exception {
+
+        // 1. create suitable notification for subsequent tests
+        Notification notification = entityFactory.newEntity(Notification.class);
+        notification.addEvent("[REST]:[RoleLogic]:[]:[create]:[SUCCESS]");
+        notification.setRoleAbout(new RoleFiqlSearchConditionBuilder().is("name").equalTo("role446").query());
+        notification.setSelfAsRecipient(false);
+
+        notification.setRecipientAttrName("email");
+        notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+        notification.getStaticRecipients().add(MAIL_ADDRESS);
+
+        Random random = new Random(System.currentTimeMillis());
+        String sender = "syncopetest-" + random.nextLong() + "@syncope.apache.org";
+        notification.setSender(sender);
+        String subject = "Test notification " + random.nextLong();
+        notification.setSubject(subject);
+        notification.setTemplate("optin");
+
+        Notification actual = notificationDAO.save(notification);
+        assertNotNull(actual);
+
+        notificationDAO.flush();
+
+        // 2. create role
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName("role446");
+        roleTO.setParent(1L);
+
+        RoleTO createdRole = roleLogic.create(roleTO);
+        assertNotNull(createdRole);
+
+        // 3. force Quartz job execution and verify e-mail
+        notificationJob.execute(null);
+        assertTrue(verifyMail(sender, subject));
+
+        // 4. get NotificationTask id and text body
+        Long taskId = null;
+        String textBody = null;
+        Set<String> recipients = null;
+        for (NotificationTask task : taskDAO.<NotificationTask>findAll(TaskType.NOTIFICATION)) {
+            if (sender.equals(task.getSender())) {
+                taskId = task.getKey();
+                textBody = task.getTextBody();
+                recipients = task.getRecipients();
+            }
+        }
+
+        assertNotNull(taskId);
+        assertNotNull(textBody);
+        assertTrue(recipients.contains(MAIL_ADDRESS));
+
+        // 5. execute Notification task and verify e-mail
+        taskLogic.execute(taskId, false);
+        assertTrue(verifyMail(sender, subject));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/logic/src/test/resources/logicTest.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/test/resources/logicTest.xml b/syncope620/server/logic/src/test/resources/logicTest.xml
new file mode 100644
index 0000000..c3f5942
--- /dev/null
+++ b/syncope620/server/logic/src/test/resources/logicTest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+    
+  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+    <property name="locations">
+      <list>
+        <value>classpath:persistence.properties</value>
+        <value>classpath:security.properties</value>
+        <value>classpath:connid.properties</value>
+        <value>classpath:mail.properties</value>
+        <value>classpath:logic.properties</value>
+      </list>
+    </property>
+    <property name="ignoreResourceNotFound" value="true"/>
+    <property name="ignoreUnresolvablePlaceholders" value="true"/>
+  </bean>
+  
+  <bean id="contentXML" class="org.apache.syncope.server.misc.spring.ResourceWithFallbackLoader">
+    <property name="primary" value="file:${conf.directory}/content.xml"/>
+    <property name="fallback" value="classpath:content.xml"/>
+  </bean>
+  <bean class="org.apache.syncope.server.persistence.jpa.content.XMLContentLoader" init-method="load"/>
+  
+  <bean class="org.apache.syncope.server.workflow.java.DefaultUserWorkflowAdapter"/>
+  <bean class="org.apache.syncope.server.workflow.java.DefaultRoleWorkflowAdapter"/>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/misc/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/pom.xml b/syncope620/server/misc/pom.xml
new file mode 100644
index 0000000..fd4ae70
--- /dev/null
+++ b/syncope620/server/misc/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-server</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Server Misc</name>
+  <description>Apache Syncope Server Misc</description>
+  <groupId>org.apache.syncope.server</groupId>
+  <artifactId>syncope-server-misc</artifactId>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jexl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-afterburner</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-tx</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.jasypt</groupId>
+      <artifactId>jasypt</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <!-- TEST -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/AuditManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/AuditManager.java b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/AuditManager.java
new file mode 100644
index 0000000..db2a632
--- /dev/null
+++ b/syncope620/server/misc/src/main/java/org/apache/syncope/server/misc/AuditManager.java
@@ -0,0 +1,109 @@
+/*
+ * 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.server.misc;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.server.persistence.api.dao.LoggerDAO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AuditManager {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(AuditManager.class);
+
+    @Autowired
+    private LoggerDAO loggerDAO;
+
+    public void audit(
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final String event,
+            final Result result,
+            final Object before,
+            final Object output,
+            final Object... input) {
+
+        final Throwable throwable;
+        final StringBuilder message = new StringBuilder();
+
+        message.append("BEFORE:\n");
+        message.append("\t").append(before == null ? "unknown" : before).append("\n");
+
+        message.append("INPUT:\n");
+
+        if (ArrayUtils.isNotEmpty(input)) {
+            for (Object obj : input) {
+                message.append("\t").append(obj == null ? null : obj.toString()).append("\n");
+            }
+        } else {
+            message.append("\t").append("none").append("\n");
+        }
+
+        message.append("OUTPUT:\n");
+
+        if (output instanceof Throwable) {
+            throwable = (Throwable) output;
+            message.append("\t").append(throwable.getMessage());
+        } else {
+            throwable = null;
+            message.append("\t").append(output == null ? "none" : output.toString());
+        }
+
+        AuditLoggerName auditLoggerName = null;
+        try {
+            auditLoggerName = new AuditLoggerName(type, category, subcategory, event, result);
+        } catch (IllegalArgumentException e) {
+            LOG.error("Invalid audit parameters, aborting...", e);
+        }
+
+        if (auditLoggerName != null) {
+            org.apache.syncope.server.persistence.api.entity.Logger syncopeLogger =
+                    loggerDAO.find(auditLoggerName.toLoggerName());
+            if (syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG) {
+                StringBuilder auditMessage = new StringBuilder();
+
+                final SecurityContext ctx = SecurityContextHolder.getContext();
+                if (ctx != null && ctx.getAuthentication() != null) {
+                    auditMessage.append('[').append(ctx.getAuthentication().getName()).append(']').append(' ');
+                }
+                auditMessage.append(message);
+
+                final Logger logger = LoggerFactory.getLogger(auditLoggerName.toLoggerName());
+                if (throwable == null) {
+                    logger.debug(auditMessage.toString());
+                } else {
+                    logger.debug(auditMessage.toString(), throwable);
+                }
+            }
+        }
+    }
+}


Mime
View raw message