You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/01/08 14:17:25 UTC

[05/13] syncope git commit: [SYNCOPE-620] server logic in, tests missing

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/AbstractReportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/AbstractReportlet.java
new file mode 100644
index 0000000..8c8661b
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportException.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportException.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportException.java
new file mode 100644
index 0000000..b8d311b
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/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
new file mode 100644
index 0000000..ca59705
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportJob.java
@@ -0,0 +1,206 @@
+/*
+ * 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.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.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.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;
+
+    /**
+     * Report data binder.
+     */
+    @Autowired
+    private ReportDataBinder 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/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportXMLConst.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportXMLConst.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportXMLConst.java
new file mode 100644
index 0000000..3cd22cd
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/Reportlet.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/Reportlet.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/Reportlet.java
new file mode 100644
index 0000000..49438bc
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportletConfClass.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportletConfClass.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/ReportletConfClass.java
new file mode 100644
index 0000000..48896cc
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/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
new file mode 100644
index 0000000..3ffbdfb
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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.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.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 RoleDataBinder 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.getAttrMap();
+
+            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.getAttrs(), 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.getAttrs()) {
+            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/99369c31/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
new file mode 100644
index 0000000..bdc76f8
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.logic.report;
+
+import org.apache.syncope.common.lib.report.StaticReportletConf;
+import org.apache.syncope.server.utils.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/99369c31/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/TextSerializer.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/TextSerializer.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/report/TextSerializer.java
new file mode 100644
index 0000000..8ad63fc
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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/99369c31/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
new file mode 100644
index 0000000..ecae604
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/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.server.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.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.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.getAttrMap();
+
+            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.getAttrs(), 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.getAttrMap().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.getAttrs()) {
+            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/99369c31/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
new file mode 100644
index 0000000..662cd46
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondConverter.java
@@ -0,0 +1,50 @@
+/*
+ * 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/99369c31/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
new file mode 100644
index 0000000..5c9acdb
--- /dev/null
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/search/SearchCondVisitor.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.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/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/DuplicateException.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/DuplicateException.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/DuplicateException.java
new file mode 100644
index 0000000..517d64d
--- /dev/null
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/DuplicateException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.persistence.api.dao;
+
+/**
+ * Thrown when something is not found.
+ */
+public class DuplicateException extends RuntimeException {
+
+    private static final long serialVersionUID = -8200698688516957508L;
+
+    public DuplicateException(final String msg) {
+        super(msg);
+    }
+
+    public DuplicateException(final String msg, final Exception cause) {
+        super(msg, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/RoleDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/RoleDAO.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/RoleDAO.java
index b2d7dd3..d26615d 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/RoleDAO.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/RoleDAO.java
@@ -77,4 +77,6 @@ public interface RoleDAO extends SubjectDAO<RPlainAttr, RDerAttr, RVirAttr> {
     void delete(Role role);
 
     void delete(Long key);
+    
+    Role authFetchRole(Long key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/UserDAO.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/UserDAO.java
index 42ea10a..0f359c3 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/UserDAO.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/dao/UserDAO.java
@@ -62,4 +62,7 @@ public interface UserDAO extends SubjectDAO<UPlainAttr, UDerAttr, UVirAttr> {
 
     void delete(User user);
 
+    User authFecthUser(Long key);
+
+    User authFecthUser(String username);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtil.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtil.java
index ff1679c..3d12ec9 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtil.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtil.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.persistence.api.entity;
 
 import java.util.List;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 import org.apache.syncope.common.lib.types.AttributableType;
 import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
@@ -83,4 +85,7 @@ public interface AttributableUtil {
 
     <T extends MappingItem> Class<T> mappingItemClass();
 
+    <T extends AbstractAttributableTO> T newAttributableTO();
+
+    <T extends AbstractSubjectTO> T newSubjectTO();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtilFactory.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtilFactory.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtilFactory.java
new file mode 100644
index 0000000..1f42c17
--- /dev/null
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/AttributableUtilFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.persistence.api.entity;
+
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+
+public interface AttributableUtilFactory {
+
+    AttributableUtil getInstance(AttributableType type);
+
+    AttributableUtil getInstance(String attributableType);
+
+    AttributableUtil getInstance(ObjectClass objectClass);
+
+    AttributableUtil getInstance(Attributable<?, ?, ?> attributable);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/Entity.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/Entity.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/Entity.java
index b1b4e00..6c8afbd 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/Entity.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/Entity.java
@@ -18,7 +18,9 @@
  */
 package org.apache.syncope.persistence.api.entity;
 
-public interface Entity<KEY> {
+import java.io.Serializable;
+
+public interface Entity<KEY> extends Serializable {
 
     KEY getKey();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/EntityFactory.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/EntityFactory.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/EntityFactory.java
index 58af4f8..c36030b 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/EntityFactory.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/EntityFactory.java
@@ -23,4 +23,6 @@ public interface EntityFactory {
     <KEY, T extends Entity<KEY>> T newEntity(Class<T> reference);
 
     <T extends Policy> T newPolicy(Class<T> reference, boolean global);
+
+    ConnPoolConf newConnPoolConf();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/ExternalResource.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/ExternalResource.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/ExternalResource.java
index 965c1a5..8e55c26 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/ExternalResource.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/ExternalResource.java
@@ -27,7 +27,7 @@ import org.apache.syncope.common.lib.types.PropagationMode;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.identityconnectors.framework.common.objects.SyncToken;
 
-public interface ExternalResource extends Entity<String> {
+public interface ExternalResource extends AnnotatedEntity<String> {
 
     AccountPolicy getAccountPolicy();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/role/Role.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/role/Role.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/role/Role.java
index 0c3ac50..b61afdc 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/role/Role.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/role/Role.java
@@ -92,7 +92,7 @@ public interface Role extends Subject<RPlainAttr, RDerAttr, RVirAttr> {
 
     boolean isInheritAccountPolicy();
 
-    boolean isInheritAttrs();
+    boolean isInheritPlainAttrs();
 
     boolean isInheritDerAttrs();
 
@@ -108,7 +108,7 @@ public interface Role extends Subject<RPlainAttr, RDerAttr, RVirAttr> {
 
     void setInheritAccountPolicy(boolean condition);
 
-    void setInheritAttrs(boolean inheritAttrs);
+    void setInheritPlainAttrs(boolean inheritAttrs);
 
     void setInheritDerAttrs(boolean inheritDerAttrs);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtil.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtil.java
new file mode 100644
index 0000000..661c645
--- /dev/null
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtil.java
@@ -0,0 +1,36 @@
+/*
+ * 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.persistence.api.entity.task;
+
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.types.TaskType;
+
+public interface TaskUtil {
+
+    TaskType getType();
+
+    <T extends Task> T newTask();
+
+    <T extends AbstractTaskTO> T newTaskTO();
+
+    <T extends Task> Class<T> taskClass();
+
+    <T extends AbstractTaskTO> Class<T> taskTOClass();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtilFactory.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtilFactory.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtilFactory.java
new file mode 100644
index 0000000..b2b24e6
--- /dev/null
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/persistence/api/entity/task/TaskUtilFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.persistence.api.entity.task;
+
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.types.TaskType;
+
+public interface TaskUtilFactory {
+
+    TaskUtil getInstance(TaskType type);
+
+    TaskUtil getInstance(Task task);
+
+    TaskUtil getInstance(Class<? extends AbstractTaskTO> taskClass);
+
+    TaskUtil getInstance(AbstractTaskTO taskTO);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/pom.xml b/syncope620/server/persistence-jpa/pom.xml
index 17b6a92..b5f0bbe 100644
--- a/syncope620/server/persistence-jpa/pom.xml
+++ b/syncope620/server/persistence-jpa/pom.xml
@@ -91,7 +91,7 @@ under the License.
       
     <dependency>
       <groupId>org.apache.syncope.server</groupId>
-      <artifactId>syncope-provisioning-api</artifactId>
+      <artifactId>syncope-provisioning-common</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/99369c31/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/persistence/jpa/dao/AbstractSubjectDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/persistence/jpa/dao/AbstractSubjectDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/persistence/jpa/dao/AbstractSubjectDAO.java
index ac6ab68..64712c8 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/persistence/jpa/dao/AbstractSubjectDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/persistence/jpa/dao/AbstractSubjectDAO.java
@@ -89,10 +89,10 @@ abstract class AbstractSubjectDAO<P extends PlainAttr, D extends DerAttr, V exte
         final Parser parser = new Parser(new StringReader(expression));
 
         // Schema names
-        final List<String> identifiers = new ArrayList<String>();
+        final List<String> identifiers = new ArrayList<>();
 
         // Literals
-        final List<String> literals = new ArrayList<String>();
+        final List<String> literals = new ArrayList<>();
 
         // Get schema names and literals
         Token token;
@@ -131,18 +131,18 @@ abstract class AbstractSubjectDAO<P extends PlainAttr, D extends DerAttr, V exte
         final List<String> attrValues = split(value, literals);
 
         if (attrValues.size() != identifiers.size()) {
-            LOG.error("Ambiguous jexl expression resolution.");
+            LOG.error("Ambiguous JEXL expression resolution.");
             throw new IllegalArgumentException("literals and values have different size");
         }
 
         // clauses to be used with INTERSECTed queries
-        final Set<String> clauses = new HashSet<String>();
+        final Set<String> clauses = new HashSet<>();
 
         // builder to build the clauses
         final StringBuilder bld = new StringBuilder();
 
         // Contains used identifiers in order to avoid replications
-        final Set<String> used = new HashSet<String>();
+        final Set<String> used = new HashSet<>();
 
         // Create several clauses: one for eanch identifiers
         for (int i = 0; i < identifiers.size(); i++) {