http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
new file mode 100644
index 0000000..88d42fd
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/notification/NotificationJob.java
@@ -0,0 +1,283 @@
+/*
+ * 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.AuditElements.Result;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+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.misc.AuditManager;
+import org.apache.syncope.core.misc.ExceptionUtil;
+import 
org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.Job;
+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.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 {
+
+    public enum Status {
+
+        SENT,
+        NOT_SENT
+
+    }
+
+    public static final String DEFAULT_CRON_EXP = "0 0/5 * * * ?";
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = 
LoggerFactory.getLogger(NotificationJob.class);
+
+    @Autowired
+    private AuditManager auditManager;
+
+    @Autowired
+    private NotificationManager notificationManager;
+
+    @Autowired
+    private JavaMailSender mailSender;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    /**
+     * Task DAO.
+     */
+    @Autowired
+    private TaskDAO taskDAO;
+
+    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);
+        }
+    }
+
+    public TaskExec executeSingle(final NotificationTask task) {
+        init();
+
+        TaskExec execution = entityFactory.newEntity(TaskExec.class);
+        execution.setTask(task);
+        execution.setStartDate(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(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(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",
+                            Result.SUCCESS,
+                            null,
+                            null,
+                            task,
+                            "Successfully sent notification to " + to);
+                } catch (Exception e) {
+                    LOG.error("Could not send e-mail", e);
+
+                    execution.setStatus(Status.NOT_SENT.name());
+                    if (task.getTraceLevel().ordinal() >= 
TraceLevel.FAILURES.ordinal()) {
+                        
execution.setMessage(ExceptionUtil.getFullStackTrace(e));
+                    }
+
+                    auditManager.audit(
+                            AuditElements.EventCategoryType.TASK,
+                            "notification",
+                            null,
+                            "send",
+                            Result.FAILURE,
+                            null,
+                            null,
+                            task,
+                            "Could not send notification to " + to, e);
+                }
+
+                execution.setEndDate(new Date());
+            }
+        }
+
+        if (hasToBeRegistered(execution)) {
+            execution = notificationManager.storeExec(execution);
+            if (retryPossible && (Status.valueOf(execution.getStatus()) == 
Status.NOT_SENT)) {
+                handleRetries(execution);
+            }
+        } else {
+            notificationManager.setTaskExecuted(execution.getTask().getKey(), 
true);
+        }
+
+        return execution;
+    }
+
+    @Override
+    public void execute(final JobExecutionContext context)
+            throws JobExecutionException {
+
+        LOG.debug("Waking up...");
+
+        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);
+        }
+
+        LOG.debug("Sleeping again...");
+    }
+
+    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 (Status.valueOf(execution.getStatus()) == 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(), 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",
+                    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",
+                    Result.FAILURE,
+                    null,
+                    null,
+                    execution,
+                    "Giving up retries on notification task " + 
execution.getTask().getKey());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
new file mode 100644
index 0000000..df32041
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.report;
+
+import org.apache.syncope.common.lib.report.AbstractReportletConf;
+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<T extends AbstractReportletConf> 
implements Reportlet<T> {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = 
LoggerFactory.getLogger(AbstractReportlet.class);
+
+    protected T conf;
+
+    public T getConf() {
+        return conf;
+    }
+
+    @Override
+    public void setConf(final T conf) {
+        this.conf = conf;
+    }
+
+    protected abstract void doExtract(ContentHandler handler) throws 
SAXException, ReportException;
+
+    @Override
+    @Transactional(readOnly = true)
+    public void extract(final ContentHandler handler) throws SAXException, 
ReportException {
+
+        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(handler);
+
+        handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportException.java
new file mode 100644
index 0000000..f4495ad
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/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.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/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
new file mode 100644
index 0000000..7d03b61
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJob.java
@@ -0,0 +1,203 @@
+/*
+ * 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.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.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+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.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.logic.ReportLogic;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.misc.ExceptionUtil;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.Job;
+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.beans.factory.support.AbstractBeanDefinition;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Quartz job for executing a given report.
+ */
+@SuppressWarnings("unchecked")
+@DisallowConcurrentExecution
+public class ReportJob implements Job {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
+
+    /**
+     * Report DAO.
+     */
+    @Autowired
+    private ReportDAO reportDAO;
+
+    /**
+     * Report execution DAO.
+     */
+    @Autowired
+    private ReportExecDAO reportExecDAO;
+
+    @Autowired
+    private ReportLogic dataBinder;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    /**
+     * Key, set by the caller, for identifying the report to be executed.
+     */
+    private Long reportKey;
+
+    /**
+     * Report id setter.
+     *
+     * @param reportKey to be set
+     */
+    public void setReportKey(final Long reportKey) {
+        this.reportKey = reportKey;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public void execute(final JobExecutionContext context) throws 
JobExecutionException {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new JobExecutionException("Report " + reportKey + " not 
found");
+        }
+
+        // 1. create execution
+        ReportExec execution = entityFactory.newEntity(ReportExec.class);
+        execution.setStatus(ReportExecStatus.STARTED);
+        execution.setStartDate(new Date());
+        execution.setReport(report);
+        execution = reportExecDAO.save(execution);
+
+        report.addExec(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();
+            handler = tFactory.newTransformerHandler();
+            Transformer serializer = handler.getTransformer();
+            serializer.setOutputProperty(OutputKeys.ENCODING, 
SyncopeConstants.DEFAULT_ENCODING);
+            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<Reportlet> reportletClass =
+                        
dataBinder.findReportletClassHavingConfClass(reportletConf.getClass());
+                if (reportletClass != null) {
+                    Reportlet<ReportletConf> autowired =
+                            (Reportlet<ReportletConf>) 
ApplicationContextProvider.getBeanFactory().
+                            createBean(reportletClass, 
AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                    autowired.setConf(reportletConf);
+
+                    // invoke reportlet
+                    try {
+                        autowired.extract(handler);
+                    } catch (Exception e) {
+                        execution.setStatus(ReportExecStatus.FAILURE);
+
+                        Throwable t = e instanceof ReportException
+                                ? e.getCause()
+                                : e;
+                        reportExecutionMessage.
+                                append(ExceptionUtil.getFullStackTrace(t)).
+                                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(ExceptionUtil.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.setEndDate(new Date());
+            reportExecDAO.save(execution);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportXMLConst.java
new file mode 100644
index 0000000..1bbae73
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/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.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/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
new file mode 100644
index 0000000..cd6dd25
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
@@ -0,0 +1,47 @@
+/*
+ * 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.common.lib.report.ReportletConf;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface for all elements that can be embedded in a report.
+ *
+ * @see org.apache.syncope.core.persistence.beans.Report
+ */
+public interface Reportlet<T extends ReportletConf> {
+
+    /**
+     * Set this reportlet configuration.
+     *
+     * @param conf configuration
+     */
+    void setConf(T conf);
+
+    /**
+     * Actual data extraction for reporting.
+     *
+     * @param handler SAX content handler for streaming result
+     * @throws SAXException if there is any problem in SAX handling
+     * @throws ReportException if anything goes wrong
+     */
+    void extract(ContentHandler handler) throws SAXException, ReportException;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
new file mode 100644
index 0000000..39f1c16
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.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.logic.report;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.syncope.common.lib.report.ReportletConf;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ReportletConfClass {
+
+    Class<? extends ReportletConf> value();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/RoleReportlet.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/RoleReportlet.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/RoleReportlet.java
new file mode 100644
index 0000000..5bada5d
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/RoleReportlet.java
@@ -0,0 +1,327 @@
+/*
+ * 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 java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.report.RoleReportletConf;
+import org.apache.syncope.common.lib.report.RoleReportletConf.Feature;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+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.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.provisioning.java.data.RoleDataBinderImpl;
+import org.apache.syncope.core.misc.search.SearchCondConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+@ReportletConfClass(RoleReportletConf.class)
+public class RoleReportlet extends AbstractReportlet<RoleReportletConf> {
+
+    private static final int PAGE_SIZE = 10;
+
+    @Autowired
+    private EntitlementDAO entitlementDAO;
+
+    @Autowired
+    private RoleDAO roleDAO;
+
+    @Autowired
+    private SubjectSearchDAO searchDAO;
+
+    @Autowired
+    private RoleDataBinderImpl roleDataBinder;
+
+    private List<Role> getPagedRoles(final int page) {
+        final Set<Long> adminRoleIds = 
RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());
+        final List<Role> result;
+        if (StringUtils.isBlank(conf.getMatchingCond())) {
+            result = roleDAO.findAll();
+        } else {
+            result = searchDAO.search(adminRoleIds, 
SearchCondConverter.convert(conf.getMatchingCond()),
+                    page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), 
SubjectType.ROLE);
+        }
+
+        return result;
+    }
+
+    private int count() {
+        Set<Long> adminRoleIds = 
RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());
+
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? roleDAO.findAll().size()
+                : searchDAO.count(adminRoleIds, 
SearchCondConverter.convert(conf.getMatchingCond()), SubjectType.ROLE);
+    }
+
+    private void doExtractResources(final ContentHandler handler, final 
AbstractSubjectTO subjectTO)
+            throws SAXException {
+
+        if (subjectTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", 
subjectTO.getClass().getSimpleName(), subjectTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : subjectTO.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 
AbstractAttributableTO attributableTO,
+            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 = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<Role> 
roles)
+            throws SAXException, ReportException {
+
+        AttributesImpl atts = new AttributesImpl();
+        for (Role role : roles) {
+            atts.clear();
+
+            for (Feature feature : conf.getFeatures()) {
+                String type = null;
+                String value = null;
+                switch (feature) {
+                    case key:
+                        type = ReportXMLConst.XSD_LONG;
+                        value = String.valueOf(role.getKey());
+                        break;
+
+                    case name:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = String.valueOf(role.getName());
+                        break;
+
+                    case roleOwner:
+                        type = ReportXMLConst.XSD_LONG;
+                        value = String.valueOf(role.getRoleOwner());
+                        break;
+
+                    case userOwner:
+                        type = ReportXMLConst.XSD_LONG;
+                        value = String.valueOf(role.getUserOwner());
+                        break;
+
+                    default:
+                }
+
+                if (type != null && value != null) {
+                    atts.addAttribute("", "", feature.name(), type, value);
+                }
+            }
+
+            handler.startElement("", "", "role", atts);
+
+            // Using RoleTO for attribute values, since the conversion logic of
+            // values to String is already encapsulated there
+            RoleTO roleTO = roleDataBinder.getRoleTO(role);
+
+            doExtractAttributes(handler, roleTO, conf.getPlainAttrs(), 
conf.getDerAttrs(), conf.getVirAttrs());
+
+            if (conf.getFeatures().contains(Feature.entitlements)) {
+                handler.startElement("", "", "entitlements", null);
+
+                for (String ent : roleTO.getEntitlements()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "id", ReportXMLConst.XSD_STRING, 
String.valueOf(ent));
+
+                    handler.startElement("", "", "entitlement", atts);
+                    handler.endElement("", "", "entitlement");
+                }
+
+                handler.endElement("", "", "entitlements");
+            }
+            // to get resources associated to a role
+            if (conf.getFeatures().contains(Feature.resources)) {
+                doExtractResources(handler, roleTO);
+            }
+            //to get users asscoiated to a role is preferred RoleDAO to RoleTO
+            if (conf.getFeatures().contains(Feature.users)) {
+                handler.startElement("", "", "users", null);
+
+                for (Membership memb : roleDAO.findMemberships(role)) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "key", ReportXMLConst.XSD_LONG,
+                            String.valueOf(memb.getUser().getKey()));
+                    atts.addAttribute("", "", "username", 
ReportXMLConst.XSD_STRING,
+                            String.valueOf(memb.getUser().getUsername()));
+
+                    handler.startElement("", "", "user", atts);
+                    handler.endElement("", "", "user");
+                }
+
+                handler.endElement("", "", "users");
+            }
+
+            handler.endElement("", "", "role");
+        }
+    }
+
+    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("", "", "roleAttributes", 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("", "", "roleAttributes");
+        handler.endElement("", "", "configurations");
+    }
+
+    @Override
+    protected void doExtract(final ContentHandler handler) throws 
SAXException, ReportException {
+        doExtractConf(handler);
+        for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
+            doExtract(handler, getPagedRoles(i));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
new file mode 100644
index 0000000..196f3e9
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
@@ -0,0 +1,120 @@
+/*
+ * 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.common.lib.report.StaticReportletConf;
+import org.apache.syncope.core.misc.DataFormat;
+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<StaticReportletConf> {
+
+    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
+    public void doExtract(final ContentHandler handler) throws SAXException, 
ReportException {
+
+        doExtractConf(handler);
+
+        if (StringUtils.hasText(conf.getStringField())) {
+            handler.startElement("", "", "string", null);
+            handler.characters(conf.getStringField().toCharArray(), 0, 
conf.getStringField().length());
+            handler.endElement("", "", "string");
+        }
+
+        if (conf.getLongField() != null) {
+            handler.startElement("", "", "long", null);
+            String printed = String.valueOf(conf.getLongField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "long");
+        }
+
+        if (conf.getDoubleField() != null) {
+            handler.startElement("", "", "double", null);
+            String printed = String.valueOf(conf.getDoubleField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "double");
+        }
+
+        if (conf.getDateField() != null) {
+            handler.startElement("", "", "date", null);
+            String printed = DataFormat.format(conf.getDateField());
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "date");
+        }
+
+        if (conf.getTraceLevel() != null) {
+            handler.startElement("", "", "enum", null);
+            String printed = conf.getTraceLevel().name();
+            handler.characters(printed.toCharArray(), 0, printed.length());
+            handler.endElement("", "", "enum");
+        }
+
+        if (conf.getListField() != null && !conf.getListField().isEmpty()) {
+            handler.startElement("", "", "list", null);
+            for (String item : 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/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
new file mode 100644
index 0000000..3805963
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/TextSerializer.java
@@ -0,0 +1,101 @@
+/*
+ * 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
+    }
+
+    @Override
+    public void endDocument() throws SAXException {
+        super.endDocument();
+    }
+
+    /**
+     * @throws SAXException if text is encountered before root element.
+     */
+    @Override
+    public void characters(final char buffer[], final int start, final int 
len) throws SAXException {
+        super.characters(buffer, start, len);
+    }
+
+    @Override
+    public void recycle() {
+        super.recycle();
+    }
+
+    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/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
new file mode 100644
index 0000000..76efe89
--- /dev/null
+++ 
b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -0,0 +1,359 @@
+/*
+ * 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 java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+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.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+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.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.misc.search.SearchCondConverter;
+import org.apache.syncope.core.misc.DataFormat;
+import org.apache.syncope.core.provisioning.api.data.RoleDataBinder;
+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<UserReportletConf> {
+
+    private static final int PAGE_SIZE = 10;
+
+    @Autowired
+    private EntitlementDAO entitlementDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private SubjectSearchDAO searchDAO;
+
+    @Autowired
+    private UserDataBinder userDataBinder;
+
+    @Autowired
+    private RoleDataBinder roleDataBinder;
+
+    private List<User> getPagedUsers(final int page) {
+        final Set<Long> adminRoleIds = 
RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());
+
+        final List<User> result;
+        if (StringUtils.isBlank(conf.getMatchingCond())) {
+            result = userDAO.findAll(adminRoleIds, page, PAGE_SIZE);
+        } else {
+            result = searchDAO.search(adminRoleIds, 
SearchCondConverter.convert(conf.getMatchingCond()),
+                    page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), 
SubjectType.USER);
+        }
+
+        return result;
+    }
+
+    private int count() {
+        Set<Long> adminRoleIds = 
RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll());
+
+        return StringUtils.isBlank(conf.getMatchingCond())
+                ? userDAO.count(adminRoleIds)
+                : searchDAO.count(adminRoleIds, 
SearchCondConverter.convert(conf.getMatchingCond()), SubjectType.USER);
+    }
+
+    private void doExtractResources(final ContentHandler handler, final 
AbstractSubjectTO subjectTO)
+            throws SAXException {
+
+        if (subjectTO.getResources().isEmpty()) {
+            LOG.debug("No resources found for {}[{}]", 
subjectTO.getClass().getSimpleName(), subjectTO.getKey());
+        } else {
+            AttributesImpl atts = new AttributesImpl();
+            handler.startElement("", "", "resources", null);
+
+            for (String resourceName : subjectTO.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 
AbstractAttributableTO attributableTO,
+            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 = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "attribute");
+            }
+            handler.endElement("", "", "attributes");
+        }
+
+        if (!derAttrs.isEmpty()) {
+            Map<String, AttrTO> derAttrMap = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "derivedAttribute");
+            }
+            handler.endElement("", "", "derivedAttributes");
+        }
+
+        if (!virAttrs.isEmpty()) {
+            Map<String, AttrTO> virAttrMap = attributableTO.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,
+                            attributableTO.getClass().getSimpleName(), 
attributableTO.getKey());
+                }
+
+                handler.endElement("", "", "virtualAttribute");
+            }
+            handler.endElement("", "", "virtualAttributes");
+        }
+    }
+
+    private void doExtract(final ContentHandler handler, final List<User> 
users)
+            throws SAXException, ReportException {
+
+        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_LONG;
+                        value = String.valueOf(user.getKey());
+                        break;
+
+                    case username:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getUsername();
+                        break;
+
+                    case workflowId:
+                        type = ReportXMLConst.XSD_LONG;
+                        value = String.valueOf(user.getWorkflowId());
+                        break;
+
+                    case status:
+                        type = ReportXMLConst.XSD_STRING;
+                        value = user.getStatus();
+                        break;
+
+                    case creationDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getCreationDate() == null
+                                ? ""
+                                : DataFormat.format(user.getCreationDate());
+                        break;
+
+                    case lastLoginDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getLastLoginDate() == null
+                                ? ""
+                                : DataFormat.format(user.getLastLoginDate());
+                        break;
+
+                    case changePwdDate:
+                        type = ReportXMLConst.XSD_DATETIME;
+                        value = user.getChangePwdDate() == null
+                                ? ""
+                                : DataFormat.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);
+
+            doExtractAttributes(handler, userTO, conf.getPlainAttrs(), 
conf.getDerAttrs(), conf.getVirAttrs());
+
+            if (conf.getFeatures().contains(Feature.memberships)) {
+                handler.startElement("", "", "memberships", null);
+
+                for (MembershipTO memb : userTO.getMemberships()) {
+                    atts.clear();
+
+                    atts.addAttribute("", "", "id", ReportXMLConst.XSD_LONG, 
String.valueOf(memb.getKey()));
+                    atts.addAttribute("", "", "roleId", 
ReportXMLConst.XSD_LONG, String.valueOf(memb.getRoleId()));
+                    atts.addAttribute("", "", "roleName", 
ReportXMLConst.XSD_STRING, String.valueOf(memb.getRoleName()));
+                    handler.startElement("", "", "membership", atts);
+
+                    doExtractAttributes(handler, memb, 
memb.getPlainAttrMap().keySet(), memb.getDerAttrMap()
+                            .keySet(), memb.getVirAttrMap().keySet());
+
+                    if (conf.getFeatures().contains(Feature.resources)) {
+                        Membership actualMemb = 
user.getMembership(memb.getRoleId());
+                        if (actualMemb == null) {
+                            LOG.warn("Unexpected: cannot find membership for 
role {} for user {}", memb.getRoleId(),
+                                    user);
+                        } else {
+                            doExtractResources(handler, 
roleDataBinder.getRoleTO(actualMemb.getRole()));
+                        }
+                    }
+
+                    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");
+    }
+
+    @Override
+    protected void doExtract(final ContentHandler handler) throws 
SAXException, ReportException {
+        doExtractConf(handler);
+        for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
+            doExtract(handler, getPagedUsers(i));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/logic.properties
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/resources/logic.properties 
b/syncope620/core/logic/src/main/resources/logic.properties
new file mode 100644
index 0000000..72f5b06
--- /dev/null
+++ b/syncope620/core/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.core.provisioning.java.DefaultAttributableTransformer
+logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/logicContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/resources/logicContext.xml 
b/syncope620/core/logic/src/main/resources/logicContext.xml
new file mode 100644
index 0000000..61eb724
--- /dev/null
+++ b/syncope620/core/logic/src/main/resources/logicContext.xml
@@ -0,0 +1,42 @@
+<?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";>
+  
+  <bean id="version" class="java.lang.String">
+    <constructor-arg value="${syncope.version}"/>
+  </bean>
+
+  <aop:aspectj-autoproxy/>
+
+  <context:component-scan base-package="org.apache.syncope.core.logic"/>
+
+  <bean class="${logicInvocationHandler}"/>
+  <bean class="${attributableTransformer}"/>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/report/report2csv.xsl
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/resources/report/report2csv.xsl 
b/syncope620/core/logic/src/main/resources/report/report2csv.xsl
new file mode 100644
index 0000000..b1e2c71
--- /dev/null
+++ b/syncope620/core/logic/src/main/resources/report/report2csv.xsl
@@ -0,0 +1,36 @@
+<?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.
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                version="1.0">
+
+  <xsl:import href="userReportlet2csv.xsl"/>
+  <xsl:import href="roleReportlet2csv.xsl"/>
+  <xsl:import href="staticReportlet2csv.xsl"/>
+ 
+  <xsl:param name="status"/>
+  <xsl:param name="message"/>
+  <xsl:param name="startDate"/>
+  <xsl:param name="endDate"/>
+    
+  <xsl:template match="/">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/report/report2fo.xsl
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/resources/report/report2fo.xsl 
b/syncope620/core/logic/src/main/resources/report/report2fo.xsl
new file mode 100644
index 0000000..7da9cab
--- /dev/null
+++ b/syncope620/core/logic/src/main/resources/report/report2fo.xsl
@@ -0,0 +1,97 @@
+<?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.
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                xmlns:fo="http://www.w3.org/1999/XSL/Format";
+                version="1.0">
+
+  <xsl:import href="userReportlet2fo.xsl"/>
+  <xsl:import href="roleReportlet2fo.xsl"/>
+  <xsl:import href="staticReportlet2fo.xsl"/>
+ 
+  <xsl:param name="status"/>
+  <xsl:param name="message"/>
+  <xsl:param name="startDate"/>
+  <xsl:param name="endDate"/>
+  
+  <xsl:template match="/">
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"; 
font-family="Helvetica" font-size="10pt">
+      
+      <!-- defines the layout master -->
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="first" page-height="29.7cm" 
page-width="21cm" 
+                               margin-top="1cm" margin-bottom="2cm" 
margin-left="2.5cm" margin-right="2.5cm">
+          <fo:region-body margin-top="1cm"/>
+          <fo:region-before extent="1cm"/>
+          <fo:region-after extent="1.5cm"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+
+      <!-- starts actual layout -->
+      <fo:page-sequence master-reference="first">
+       
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block font-size="24pt" font-weight="bold" text-align="center" 
space-after="1cm">
+            Apache Syncope Report - <xsl:value-of select="report/@name"/>
+          </fo:block>
+
+          <fo:table table-layout="fixed" border-width="0.5mm" 
border-style="solid" width="100%" space-after="1cm">
+            <fo:table-column column-width="proportional-column-width(1)"/>
+            <fo:table-column column-width="proportional-column-width(1)"/>
+            <fo:table-body>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">Report 
Name:</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">
+                    <xsl:value-of select="report/@name"/>
+                  </fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">Start 
Date:</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">
+                    <xsl:value-of select="$startDate"/>
+                  </fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">End 
Date:</fo:block>
+                </fo:table-cell>
+                <fo:table-cell>
+                  <fo:block font-size="18pt" font-weight="bold">
+                    <xsl:value-of select="$endDate"/>
+                  </fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+            </fo:table-body>
+          </fo:table>
+
+          <xsl:apply-templates/>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/report/report2html.xsl
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/resources/report/report2html.xsl 
b/syncope620/core/logic/src/main/resources/report/report2html.xsl
new file mode 100644
index 0000000..c1d6b67
--- /dev/null
+++ b/syncope620/core/logic/src/main/resources/report/report2html.xsl
@@ -0,0 +1,77 @@
+<?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.
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                version="1.0">
+
+  <xsl:import href="userReportlet2html.xsl"/>
+  <xsl:import href="roleReportlet2html.xsl"/>
+  <xsl:import href="staticReportlet2html.xsl"/>
+ 
+  <xsl:param name="status"/>
+  <xsl:param name="message"/>
+  <xsl:param name="startDate"/>
+  <xsl:param name="endDate"/>
+  
+  <xsl:template match="/">
+    <html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+      <head>
+        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+        <title>Apache Syncope Report - <xsl:value-of 
select="report/@name"/></title>
+      </head>
+      <body>
+        <table style="border: 1px solid black;">
+          <tr>
+            <td>
+              <h1>Report Name:</h1>
+            </td>
+            <td>
+              <h1>
+                <xsl:value-of select="report/@name"/>
+              </h1>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <h2>Start Date:</h2>
+            </td>
+            <td>
+              <h2>
+                <xsl:value-of select="$startDate"/>
+              </h2>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <h2>End Date:</h2>
+            </td>
+            <td>
+              <h2>
+                <xsl:value-of select="$endDate"/>
+              </h2>
+            </td>
+          </tr>
+        </table>
+
+        <xsl:apply-templates/>
+      </body>
+    </html>
+  </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/resources/report/roleReportlet2csv.xsl
----------------------------------------------------------------------
diff --git 
a/syncope620/core/logic/src/main/resources/report/roleReportlet2csv.xsl 
b/syncope620/core/logic/src/main/resources/report/roleReportlet2csv.xsl
new file mode 100644
index 0000000..ad092d5
--- /dev/null
+++ b/syncope620/core/logic/src/main/resources/report/roleReportlet2csv.xsl
@@ -0,0 +1,118 @@
+<?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.
+-->
+  
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                version="1.0">
+
+  <xsl:variable name="delimiter" select="';'"/>
+  
+  <xsl:template 
match="reportlet[@class='org.apache.syncope.core.report.RoleReportlet']">
+    
+    <xsl:call-template name="header">
+      <xsl:with-param name="node" select="configurations/roleAttributes"/>
+    </xsl:call-template>
+    <xsl:for-each select="role">
+      <xsl:call-template name="roleAttributes">
+        <xsl:with-param name="header" 
select="../configurations/roleAttributes"/>
+        <xsl:with-param name="attrs" select="."/>
+      </xsl:call-template>
+      <xsl:text>&#10;</xsl:text>
+    </xsl:for-each>
+  </xsl:template>
+  
+  <xsl:template name="header">
+    <xsl:param name="node"/>  
+    <xsl:for-each select="$node/*">
+      <xsl:value-of select="text()"/>   
+      <xsl:if test="position() != last()">
+        <xsl:value-of select="$delimiter"/>
+      </xsl:if>
+    </xsl:for-each>
+    <xsl:text>&#10;</xsl:text>
+  </xsl:template>
+    
+  <xsl:template name="roleAttributes">
+    <xsl:param name="header"/>
+    <xsl:param name="attrs"/>
+    
+    <xsl:for-each select="$header/*">
+      <xsl:variable name="nameAttr" select="text()"/>
+      
+      <xsl:choose> 
+        <xsl:when test="string-length($attrs/@*[name()=$nameAttr]) &gt; 0">
+          <xsl:variable name="roleAttr" select="$attrs/@*[name()=$nameAttr]"/>
+          <xsl:text>"</xsl:text>
+          <xsl:value-of select="$roleAttr/."/>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:when 
test="name($attrs/*[name(.)=$nameAttr]/*[name(.)='entitlement']) 
+                        and count($attrs/*[name(.)=$nameAttr]/node()) &gt; 0">
+          <xsl:text>"</xsl:text>       
+          <xsl:for-each select="$attrs/*/entitlement">
+            <xsl:variable name="value" select="@id"/>
+            <xsl:value-of select="$value"/>
+            <xsl:if test="position() != last()">
+              <xsl:value-of select="$delimiter"/>
+            </xsl:if>
+          </xsl:for-each>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:when 
test="name($attrs/*[name(.)=$nameAttr]/*[name(.)='resource']) 
+                        and count($attrs/*[name(.)=$nameAttr]/node()) &gt; 0">
+          <xsl:text>"</xsl:text>       
+          <xsl:for-each select="$attrs/*/resource">
+            <xsl:variable name="value" select="@name"/>
+            <xsl:value-of select="$value"/>
+            <xsl:if test="position() != last()">
+              <xsl:value-of select="$delimiter"/>
+            </xsl:if>
+          </xsl:for-each>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:when test="name($attrs/*[name(.)=$nameAttr]/*[name(.)='user']) 
+                        and count($attrs/*[name(.)=$nameAttr]/node()) &gt; 0">
+          <xsl:text>"</xsl:text>       
+          <xsl:for-each select="$attrs/*/user">
+            <xsl:variable name="value" select="@userUsername"/>
+            <xsl:value-of select="$value"/>
+            <xsl:if test="position() != last()">
+              <xsl:value-of select="$delimiter"/>
+            </xsl:if>
+          </xsl:for-each>
+          <xsl:text>"</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:text>"</xsl:text>
+          <xsl:if 
test="string-length($attrs/*/*[@name=$nameAttr]/value/text()) &gt; 0"> 
+            <xsl:variable name="value" 
select="$attrs/*/*[@name=$nameAttr]/value/text()"/>
+            <xsl:value-of select="$value"/>
+          </xsl:if>
+          <xsl:text>"</xsl:text>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:if test="position() != last()">
+        <xsl:value-of select="$delimiter"/>
+      </xsl:if>
+    
+    </xsl:for-each>
+  </xsl:template>
+  
+</xsl:stylesheet>
+

Reply via email to