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 2018/03/02 11:47:20 UTC
[01/11] syncope git commit: Small improvements to build-tools,
including Swagger
Repository: syncope
Updated Branches:
refs/heads/2_0_X de3696469 -> 799f079f1
refs/heads/master 2d2702a0a -> 772206a4c
Small improvements to build-tools, including Swagger
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/3e2ebb92
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/3e2ebb92
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/3e2ebb92
Branch: refs/heads/2_0_X
Commit: 3e2ebb925774d3542e40d54983395ec785f4710a
Parents: de36964
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 1 15:47:13 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Mar 1 15:47:13 2018 +0100
----------------------------------------------------------------------
fit/build-tools/pom.xml | 16 +++++++++++++++-
.../syncope/fit/buildtools/cxf/UserService.java | 3 ++-
.../fit/buildtools/cxf/UserServiceImpl.java | 13 +++++++++++--
.../META-INF/cxf/org.apache.cxf.Logger | 1 +
.../src/main/resources/cxfContext.xml | 20 ++++++++++++++++++++
5 files changed, 49 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/3e2ebb92/fit/build-tools/pom.xml
----------------------------------------------------------------------
diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml
index 4c63a9c..95f2365 100644
--- a/fit/build-tools/pom.xml
+++ b/fit/build-tools/pom.xml
@@ -45,6 +45,11 @@ under the License.
</dependency>
<dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-all</artifactId>
</dependency>
@@ -88,13 +93,22 @@ under the License.
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
- </dependency>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-service-description-swagger</artifactId>
+ </dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
+ <groupId>org.webjars</groupId>
+ <artifactId>swagger-ui</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
http://git-wip-us.apache.org/repos/asf/syncope/blob/3e2ebb92/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
index 5d64cfc..f02cae4 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
@@ -30,6 +30,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
@Path("users")
public interface UserService {
@@ -44,7 +45,7 @@ public interface UserService {
@POST
@Consumes({ MediaType.APPLICATION_JSON })
- void create(User user);
+ Response create(User user);
@PUT
@Path("{key}")
http://git-wip-us.apache.org/repos/asf/syncope/blob/3e2ebb92/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
index dea0f63..6b04515 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
@@ -23,8 +23,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.ws.rs.ClientErrorException;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.springframework.stereotype.Service;
@Service
@@ -32,6 +36,9 @@ public class UserServiceImpl implements UserService {
private static final Map<UUID, User> USERS = new HashMap<UUID, User>();
+ @Context
+ private UriInfo uriInfo;
+
@Override
public List<User> list() {
return new ArrayList<>(USERS.values());
@@ -47,14 +54,16 @@ public class UserServiceImpl implements UserService {
}
@Override
- public void create(final User user) {
+ public Response create(final User user) {
if (user.getKey() == null) {
user.setKey(UUID.randomUUID());
}
if (USERS.containsKey(user.getKey())) {
- throw new IllegalArgumentException("User already exists: " + user.getKey());
+ throw new ClientErrorException("User already exists: " + user.getKey(), Response.Status.CONFLICT);
}
USERS.put(user.getKey(), user);
+
+ return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getKey().toString()).build()).build();
}
@Override
http://git-wip-us.apache.org/repos/asf/syncope/blob/3e2ebb92/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger b/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
new file mode 100644
index 0000000..6e7bd36
--- /dev/null
+++ b/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
@@ -0,0 +1 @@
+org.apache.cxf.common.logging.Slf4jLogger
http://git-wip-us.apache.org/repos/asf/syncope/blob/3e2ebb92/fit/build-tools/src/main/resources/cxfContext.xml
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/resources/cxfContext.xml b/fit/build-tools/src/main/resources/cxfContext.xml
index 9a6c959..7e53a71 100644
--- a/fit/build-tools/src/main/resources/cxfContext.xml
+++ b/fit/build-tools/src/main/resources/cxfContext.xml
@@ -38,6 +38,23 @@ under the License.
<jaxws:endpoint id="soapProvisioning" address="/soap" implementor="#provisioningImpl"/>
+ <bean id="swagger2customizer" class="org.apache.cxf.jaxrs.swagger.Swagger2Customizer">
+ <property name="dynamicBasePath" value="true"/>
+ <property name="replaceTags" value="false"/>
+ </bean>
+ <bean id="swagger2Feature" class="org.apache.cxf.jaxrs.swagger.Swagger2Feature">
+ <property name="title" value="Apache Syncope FIT Build Tools"/>
+ <property name="version" value="${syncope.version}"/>
+ <property name="description" value="Apache Syncope ${syncope.version}"/>
+ <property name="contact" value="dev@syncope.apache.org"/>
+
+ <property name="resourcePackage" value="org.apache.syncope.fit.buildtools.cxf"/>
+ <property name="scanAllResources" value="true"/>
+ <property name="activateOnlyIfJaxrsSupported" value="true"/>
+
+ <property name="customizer" ref="swagger2customizer"/>
+ </bean>
+
<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
<jaxrs:server id="restProvisioning" address="/rest"
basePackages="org.apache.syncope.fit.buildtools.cxf"
@@ -45,6 +62,9 @@ under the License.
<jaxrs:providers>
<ref bean="jsonProvider"/>
</jaxrs:providers>
+ <jaxrs:features>
+ <ref bean="swagger2Feature"/>
+ </jaxrs:features>
</jaxrs:server>
</beans>
[07/11] syncope git commit: Upgrading Spring Security and Groovy
Posted by il...@apache.org.
Upgrading Spring Security and Groovy
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/d5056275
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/d5056275
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/d5056275
Branch: refs/heads/master
Commit: d50562757b1f5e2c90294b763bac920025e208a7
Parents: dffa086
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 2 11:52:32 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 11:52:32 2018 +0100
----------------------------------------------------------------------
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/d5056275/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 522db78..a66b036 100644
--- a/pom.xml
+++ b/pom.xml
@@ -370,7 +370,7 @@ under the License.
<jackson.version>2.9.4</jackson.version>
<spring.version>5.0.4.RELEASE</spring.version>
- <spring-security.version>5.0.2.RELEASE</spring-security.version>
+ <spring-security.version>5.0.3.RELEASE</spring-security.version>
<openjpa.version>3.0.0-SNAPSHOT</openjpa.version>
<hikaricp.version>2.7.8</hikaricp.version>
@@ -382,7 +382,7 @@ under the License.
<cocoon.version>3.0.0-alpha-3</cocoon.version>
- <groovy.version>2.5.0-beta-3</groovy.version>
+ <groovy.version>3.0.0-alpha-1</groovy.version>
<flowable.version>6.2.1</flowable.version>
[03/11] syncope git commit: Upgrading groovy
Posted by il...@apache.org.
Upgrading groovy
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/988dfee0
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/988dfee0
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/988dfee0
Branch: refs/heads/2_0_X
Commit: 988dfee008e14fe642be68b5d0064e25c74cd599
Parents: 990a465
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 2 09:08:40 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 09:08:40 2018 +0100
----------------------------------------------------------------------
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/988dfee0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index b6b9ad1..f95e579 100644
--- a/pom.xml
+++ b/pom.xml
@@ -382,7 +382,7 @@ under the License.
<cocoon.version>3.0.0-alpha-3</cocoon.version>
- <groovy.version>2.4.13</groovy.version>
+ <groovy.version>2.4.14</groovy.version>
<activiti.version>5.22.0</activiti.version>
<flowable.version>5.23.0</flowable.version>
[05/11] syncope git commit: [SYNCOPE-1279] Now providing runtime
status updates from running Tasks and Reports
Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
deleted file mode 100644
index c019639..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.job.notification;
-
-import java.io.PrintStream;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Properties;
-import javax.mail.Session;
-import javax.mail.internet.MimeMessage;
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.LogOutputStream;
-import org.apache.syncope.common.lib.PropertyUtils;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.provisioning.api.AuditManager;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
-import org.apache.syncope.core.spring.security.Encryptor;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@Component
-public class NotificationJobDelegate implements InitializingBean {
-
- private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
-
- @Autowired
- private TaskDAO taskDAO;
-
- @Autowired
- private JavaMailSender mailSender;
-
- @Autowired
- private EntityFactory entityFactory;
-
- @Autowired
- private AuditManager auditManager;
-
- @Autowired
- private NotificationManager notificationManager;
-
- @Override
- public void afterPropertiesSet() throws Exception {
- if (mailSender instanceof JavaMailSenderImpl) {
- JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender;
-
- Properties javaMailProperties = javaMailSender.getJavaMailProperties();
-
- Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory").getLeft();
- for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
- String prop = (String) e.nextElement();
- if (prop.startsWith("mail.smtp.")) {
- javaMailProperties.setProperty(prop, props.getProperty(prop));
- }
- }
-
- if (StringUtils.isNotBlank(javaMailSender.getUsername())) {
- javaMailProperties.setProperty("mail.smtp.auth", "true");
- }
-
- javaMailSender.setJavaMailProperties(javaMailProperties);
-
- String mailDebug = props.getProperty("mail.debug", "false");
- if (BooleanUtils.toBoolean(mailDebug)) {
- Session session = javaMailSender.getSession();
- session.setDebug(true);
- session.setDebugOut(new PrintStream(new LogOutputStream(LOG)));
- }
- }
- }
-
- @Transactional
- public TaskExec executeSingle(final NotificationTask task) {
- TaskExec execution = entityFactory.newEntity(TaskExec.class);
- execution.setTask(task);
- execution.setStart(new Date());
-
- boolean retryPossible = true;
-
- if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
- || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
-
- String message = "Could not fetch all required information for sending e-mails:\n"
- + task.getRecipients() + "\n"
- + task.getSender() + "\n"
- + task.getSubject() + "\n"
- + task.getHtmlBody() + "\n"
- + task.getTextBody();
- LOG.error(message);
-
- execution.setStatus(NotificationJob.Status.NOT_SENT.name());
- retryPossible = false;
-
- if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
- execution.setMessage(message);
- }
- } else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("About to send e-mails:\n"
- + task.getRecipients() + "\n"
- + task.getSender() + "\n"
- + task.getSubject() + "\n"
- + task.getHtmlBody() + "\n"
- + task.getTextBody() + "\n");
- }
-
- for (String to : task.getRecipients()) {
- try {
- MimeMessage message = mailSender.createMimeMessage();
- MimeMessageHelper helper = new MimeMessageHelper(message, true);
- helper.setTo(to);
- helper.setFrom(task.getSender());
- helper.setSubject(task.getSubject());
- helper.setText(task.getTextBody(), task.getHtmlBody());
-
- mailSender.send(message);
-
- execution.setStatus(NotificationJob.Status.SENT.name());
-
- StringBuilder report = new StringBuilder();
- switch (task.getTraceLevel()) {
- case ALL:
- report.append("FROM: ").append(task.getSender()).append('\n').
- append("TO: ").append(to).append('\n').
- append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
- append(task.getTextBody()).append('\n').append('\n').
- append(task.getHtmlBody()).append('\n');
- break;
-
- case SUMMARY:
- report.append("E-mail sent to ").append(to).append('\n');
- break;
-
- case FAILURES:
- case NONE:
- default:
- }
- if (report.length() > 0) {
- execution.setMessage(report.toString());
- }
-
- notificationManager.createTasks(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "send",
- AuditElements.Result.SUCCESS,
- null,
- null,
- task,
- "Successfully sent notification to " + to);
- } catch (Exception e) {
- LOG.error("Could not send e-mail", e);
-
- execution.setStatus(NotificationJob.Status.NOT_SENT.name());
- if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
- execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
- }
-
- notificationManager.createTasks(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "send",
- AuditElements.Result.FAILURE,
- null,
- null,
- task,
- "Could not send notification to " + to, e);
- }
-
- execution.setEnd(new Date());
- }
- }
-
- if (hasToBeRegistered(execution)) {
- execution = notificationManager.storeExec(execution);
- if (retryPossible
- && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
-
- handleRetries(execution);
- }
- } else {
- notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
- }
-
- return execution;
- }
-
- @Transactional
- public void execute() throws JobExecutionException {
- for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
- LOG.debug("Found notification task {} to be executed: starting...", task);
- executeSingle(task);
- LOG.debug("Notification task {} executed", task);
- }
- }
-
- private boolean hasToBeRegistered(final TaskExec execution) {
- NotificationTask task = (NotificationTask) execution.getTask();
-
- // True if either failed and failures have to be registered, or if ALL
- // has to be registered.
- return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
- && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
- || task.getTraceLevel() == TraceLevel.ALL;
- }
-
- private void handleRetries(final TaskExec execution) {
- if (notificationManager.getMaxRetries() <= 0) {
- return;
- }
-
- long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
- execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
-
- if (failedExecutionsCount <= notificationManager.getMaxRetries()) {
- LOG.debug("Execution of notification task {} will be retried [{}/{}]",
- execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries());
- notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
-
- auditManager.audit(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "retry",
- AuditElements.Result.SUCCESS,
- null,
- null,
- execution,
- "Notification task " + execution.getTask().getKey() + " will be retried");
- } else {
- LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
-
- auditManager.audit(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "retry",
- AuditElements.Result.FAILURE,
- null,
- null,
- execution,
- "Giving up retries on notification task " + execution.getTask().getKey());
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
index 2c85b20..81a108c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.Reportlet;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.slf4j.Logger;
@@ -31,11 +32,17 @@ public abstract class AbstractReportlet implements Reportlet {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
- protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
+ protected abstract void doExtract(ReportletConf conf, ContentHandler handler, AtomicReference<String> status)
+ throws SAXException;
@Override
@Transactional(readOnly = true)
- public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ public void extract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf == null) {
throw new ReportException(new IllegalArgumentException("No configuration provided"));
}
@@ -45,7 +52,7 @@ public abstract class AbstractReportlet implements Reportlet {
atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
- doExtract(conf, handler);
+ doExtract(conf, handler, status);
handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
index d6f0ec5..6ba9a53 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.job.report;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -47,7 +48,9 @@ public class AuditReportlet extends AbstractReportlet {
private DataSource datasource;
- private void doExtractConf(final ContentHandler handler) throws SAXException {
+ private void doExtractConf(final ContentHandler handler, final AtomicReference<String> status) throws SAXException {
+ status.set("Fetching " + conf.getSize() + " rows from the SYNCOPEAUDIT table");
+
JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
jdbcTemplate.setMaxRows(conf.getSize());
List<Map<String, Object>> rows = jdbcTemplate.
@@ -120,10 +123,17 @@ public class AuditReportlet extends AbstractReportlet {
handler.endElement("", "", "event");
}
handler.endElement("", "", "events");
+
+ status.set("Fetched " + conf.getSize() + " rows from the SYNCOPEAUDIT table");
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof AuditReportletConf) {
this.conf = AuditReportletConf.class.cast(conf);
} else {
@@ -135,7 +145,7 @@ public class AuditReportlet extends AbstractReportlet {
throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
}
- doExtractConf(handler);
+ doExtractConf(handler, status);
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
new file mode 100644
index 0000000..a9aeab0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.report;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicReference;
+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.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.helpers.AttributesImpl;
+
+@Component
+public class DefaultReportJobDelegate implements ReportJobDelegate {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
+
+ /**
+ * Report DAO.
+ */
+ @Autowired
+ private ReportDAO reportDAO;
+
+ /**
+ * Report execution DAO.
+ */
+ @Autowired
+ private ReportExecDAO reportExecDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Autowired
+ private ImplementationLookup implementationLookup;
+
+ private final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
+ @Transactional
+ @Override
+ public void execute(final String reportKey) throws JobExecutionException {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new JobExecutionException("Report " + reportKey + " not found");
+ }
+
+ if (!report.isActive()) {
+ LOG.info("Report {} not active, aborting...", reportKey);
+ return;
+ }
+
+ // 1. create execution
+ ReportExec execution = entityFactory.newEntity(ReportExec.class);
+ execution.setStatus(ReportExecStatus.STARTED);
+ execution.setStart(new Date());
+ execution.setReport(report);
+ execution = reportExecDAO.save(execution);
+
+ report.add(execution);
+ report = reportDAO.save(report);
+
+ // 2. define a SAX handler for generating result as XML
+ TransformerHandler handler;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream zos = new ZipOutputStream(baos);
+ zos.setLevel(Deflater.BEST_COMPRESSION);
+ try {
+ SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ handler = tFactory.newTransformerHandler();
+ Transformer serializer = handler.getTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
+ serializer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ // a single ZipEntry in the ZipOutputStream
+ zos.putNextEntry(new ZipEntry(report.getName()));
+
+ // streaming SAX handler in a compressed byte array stream
+ handler.setResult(new StreamResult(zos));
+ } catch (Exception e) {
+ throw new JobExecutionException("While configuring for SAX generation", e, true);
+ }
+
+ execution.setStatus(ReportExecStatus.RUNNING);
+ execution = reportExecDAO.save(execution);
+
+ status.set("Starting");
+
+ // 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);
+
+ status.set("Generating report header");
+
+ // iterate over reportlet instances defined for this report
+ for (ReportletConf reportletConf : report.getReportletConfs()) {
+ Class<? extends Reportlet> reportletClass =
+ implementationLookup.getReportletClass(reportletConf.getClass());
+ if (reportletClass == null) {
+ LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
+ } else {
+ // fetch (or create) reportlet
+ Reportlet reportlet;
+ if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
+ reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+ getSingleton(reportletClass.getName());
+ } else {
+ reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+ createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+ ApplicationContextProvider.getBeanFactory().
+ registerSingleton(reportletClass.getName(), reportlet);
+ }
+
+ // invoke reportlet
+ try {
+ status.set("Invoking reportlet " + reportletClass.getName());
+ reportlet.extract(reportletConf, handler, status);
+ } catch (Throwable t) {
+ LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
+
+ execution.setStatus(ReportExecStatus.FAILURE);
+
+ Throwable effective = t instanceof ReportException
+ ? t.getCause()
+ : t;
+ reportExecutionMessage.
+ append(ExceptionUtils2.getFullStackTrace(effective)).
+ append("\n==================\n");
+ }
+ }
+ }
+
+ // report footer
+ status.set("Generating report footer");
+
+ handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
+ handler.endDocument();
+
+ if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
+ execution.setStatus(ReportExecStatus.SUCCESS);
+ }
+ } catch (Exception e) {
+ execution.setStatus(ReportExecStatus.FAILURE);
+ reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
+
+ throw new JobExecutionException(e, true);
+ } finally {
+ status.set("Completed");
+
+ try {
+ zos.closeEntry();
+ zos.close();
+ baos.close();
+ } catch (IOException e) {
+ LOG.error("While closing StreamResult's backend", e);
+ }
+
+ execution.setExecResult(baos.toByteArray());
+ execution.setMessage(reportExecutionMessage.toString());
+ execution.setEnd(new Date());
+ reportExecDAO.save(execution);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
index 44a4dfa..b34e271 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -288,7 +289,12 @@ public class GroupReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof GroupReportletConf) {
this.conf = GroupReportletConf.class.cast(conf);
} else {
@@ -297,7 +303,14 @@ public class GroupReportlet extends AbstractReportlet {
doExtractConf(handler);
- for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ int total = count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
List<Group> groups;
if (StringUtils.isBlank(this.conf.getMatchingCond())) {
groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
@@ -312,6 +325,8 @@ public class GroupReportlet extends AbstractReportlet {
}
doExtract(handler, groups);
+
+ status.set("Processed " + total + " groups: page " + page + " of " + pages);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
index 0328e62..aac1888 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections4.Closure;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
@@ -50,7 +51,6 @@ import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
@@ -373,25 +373,13 @@ public class ReconciliationReportlet extends AbstractReportlet {
}
}
- private void doExtract(
- final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
+ @Override
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
throws SAXException {
- for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
- List<AnyObject> anys = searchDAO.search(
- SyncopeConstants.FULL_ADMIN_REALMS,
- cond,
- page,
- PAGE_SIZE,
- Collections.<OrderByClause>emptyList(),
- anyTypeKind);
-
- doExtract(handler, anys);
- }
- }
-
- @Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
if (conf instanceof ReconciliationReportletConf) {
this.conf = ReconciliationReportletConf.class.cast(conf);
} else {
@@ -401,39 +389,81 @@ public class ReconciliationReportlet extends AbstractReportlet {
AttributesImpl atts = new AttributesImpl();
if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
+ int total = userDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
- for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
}
} else {
SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.USER);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.USER));
+ }
}
handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
atts.clear();
if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
+ int total = groupDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
- for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
}
} else {
SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.GROUP);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.GROUP));
+ }
}
handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
@@ -447,14 +477,28 @@ public class ReconciliationReportlet extends AbstractReportlet {
SearchCond.getLeafCond(anyTypeCond),
SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " any objects " + anyType.getKey() + " in " + pages + " pages");
atts.clear();
atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " any objects " + anyType.getKey()
+ + ": page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.ANY_OBJECT));
+ }
handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
index 78183d5..5ef6785 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -18,12 +18,14 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +54,11 @@ public class ReportJob extends AbstractInterruptableJob {
}
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
deleted file mode 100644
index 40d9250..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.job.report;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.zip.Deflater;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.persistence.api.ImplementationLookup;
-import org.apache.syncope.core.persistence.api.dao.ReportDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.Report;
-import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.helpers.AttributesImpl;
-
-@Component
-public class ReportJobDelegate {
-
- private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
-
- /**
- * Report DAO.
- */
- @Autowired
- private ReportDAO reportDAO;
-
- /**
- * Report execution DAO.
- */
- @Autowired
- private ReportExecDAO reportExecDAO;
-
- @Autowired
- private EntityFactory entityFactory;
-
- @Autowired
- private ImplementationLookup implementationLookup;
-
- @Transactional
- public void execute(final String reportKey) throws JobExecutionException {
- Report report = reportDAO.find(reportKey);
- if (report == null) {
- throw new JobExecutionException("Report " + reportKey + " not found");
- }
-
- if (!report.isActive()) {
- LOG.info("Report {} not active, aborting...", reportKey);
- return;
- }
-
- // 1. create execution
- ReportExec execution = entityFactory.newEntity(ReportExec.class);
- execution.setStatus(ReportExecStatus.STARTED);
- execution.setStart(new Date());
- execution.setReport(report);
- execution = reportExecDAO.save(execution);
-
- report.add(execution);
- report = reportDAO.save(report);
-
- // 2. define a SAX handler for generating result as XML
- TransformerHandler handler;
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream zos = new ZipOutputStream(baos);
- zos.setLevel(Deflater.BEST_COMPRESSION);
- try {
- SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
- tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
- handler = tFactory.newTransformerHandler();
- Transformer serializer = handler.getTransformer();
- serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
- serializer.setOutputProperty(OutputKeys.INDENT, "yes");
-
- // a single ZipEntry in the ZipOutputStream
- zos.putNextEntry(new ZipEntry(report.getName()));
-
- // streaming SAX handler in a compressed byte array stream
- handler.setResult(new StreamResult(zos));
- } catch (Exception e) {
- throw new JobExecutionException("While configuring for SAX generation", e, true);
- }
-
- execution.setStatus(ReportExecStatus.RUNNING);
- execution = reportExecDAO.save(execution);
-
- // 3. actual report execution
- StringBuilder reportExecutionMessage = new StringBuilder();
- try {
- // report header
- handler.startDocument();
- AttributesImpl atts = new AttributesImpl();
- atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
- handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
-
- // iterate over reportlet instances defined for this report
- for (ReportletConf reportletConf : report.getReportletConfs()) {
- Class<? extends Reportlet> reportletClass =
- implementationLookup.getReportletClass(reportletConf.getClass());
- if (reportletClass == null) {
- LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
- } else {
- // fetch (or create) reportlet
- Reportlet reportlet;
- if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
- reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
- getSingleton(reportletClass.getName());
- } else {
- reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
- createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
- ApplicationContextProvider.getBeanFactory().
- registerSingleton(reportletClass.getName(), reportlet);
- }
-
- // invoke reportlet
- try {
- reportlet.extract(reportletConf, handler);
- } catch (Throwable t) {
- LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
-
- execution.setStatus(ReportExecStatus.FAILURE);
-
- Throwable effective = t instanceof ReportException
- ? t.getCause()
- : t;
- reportExecutionMessage.
- append(ExceptionUtils2.getFullStackTrace(effective)).
- append("\n==================\n");
- }
- }
- }
-
- // report footer
- handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
- handler.endDocument();
-
- if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
- execution.setStatus(ReportExecStatus.SUCCESS);
- }
- } catch (Exception e) {
- execution.setStatus(ReportExecStatus.FAILURE);
- reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
-
- throw new JobExecutionException(e, true);
- } finally {
- try {
- zos.closeEntry();
- zos.close();
- baos.close();
- } catch (IOException e) {
- LOG.error("While closing StreamResult's backend", e);
- }
-
- execution.setExecResult(baos.toByteArray());
- execution.setMessage(reportExecutionMessage.toString());
- execution.setEnd(new Date());
- reportExecDAO.save(execution);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
index 1156d25..49e5e2a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.apache.syncope.common.lib.report.StaticReportletConf;
@@ -70,7 +71,12 @@ public class StaticReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof StaticReportletConf) {
this.conf = StaticReportletConf.class.cast(conf);
} else {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
index 5ab0e78..0c13b27 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -355,7 +356,12 @@ public class UserReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof UserReportletConf) {
this.conf = UserReportletConf.class.cast(conf);
} else {
@@ -364,7 +370,14 @@ public class UserReportlet extends AbstractReportlet {
doExtractConf(handler);
- for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ int total = count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
List<User> users;
if (StringUtils.isBlank(this.conf.getMatchingCond())) {
users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 66e864f..3357335 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -28,6 +28,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.to.ExecTO;
@@ -209,7 +210,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
final PropagationTask task,
final ConnectorObject beforeObj,
final Connector connector,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
// set of attributes to be propagated
Set<Attribute> attributes = new HashSet<>(task.getAttributes());
@@ -299,7 +300,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
final PropagationTask task,
final ConnectorObject beforeObj,
final Connector connector,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result;
if (beforeObj == null) {
@@ -392,7 +393,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
String failureReason = null;
// Flag to state whether any propagation has been attempted
- Boolean[] propagationAttempted = new Boolean[] { false };
+ AtomicReference<Boolean> propagationAttempted = new AtomicReference<>(false);
ConnectorObject beforeObj = null;
ConnectorObject afterObj = null;
@@ -431,7 +432,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
default:
}
- execution.setStatus(propagationAttempted[0]
+ execution.setStatus(propagationAttempted.get()
? PropagationTaskExecStatus.SUCCESS.name()
: PropagationTaskExecStatus.NOT_ATTEMPTED.name());
@@ -463,7 +464,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
LOG.error("While executing KO action on {}", execution, wft);
}
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
for (PropagationActions action : actions) {
action.onError(task, execution, e);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index d1853c2..ef32fc9 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -130,6 +130,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
}
doHandle(delta, provision);
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
LOG.debug("Successfully handled {}", delta);
@@ -159,6 +160,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
LOG.warn("Ignoring during pull", e);
executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken());
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
return true;
} catch (JobExecutionException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index c274dc6..6ff95fc 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -89,6 +89,7 @@ public class DefaultRealmPullResultHandler
}
doHandle(delta, orgUnit);
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
LOG.debug("Successfully handled {}", delta);
@@ -117,6 +118,7 @@ public class DefaultRealmPullResultHandler
LOG.warn("Ignoring during pull", e);
executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken());
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
return true;
} catch (JobExecutionException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index b8c1fa2..dfb526e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.syncope.common.lib.policy.PullPolicySpec;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -54,6 +55,7 @@ import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.SyncToken;
@@ -74,6 +76,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
protected final Map<ObjectClass, SyncToken> latestSyncTokens = new HashMap<>();
+ protected final Map<ObjectClass, MutablePair<Integer, String>> handled = new HashMap<>();
+
protected ProvisioningProfile<PullTask, PullActions> profile;
protected RealmPullResultHandler rhandler;
@@ -89,6 +93,34 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
latestSyncTokens.put(objectClass, latestSyncToken);
}
+ @Override
+ public void reportHandled(final ObjectClass objectClass, final Name name) {
+ MutablePair<Integer, String> pair = handled.get(objectClass);
+ if (pair == null) {
+ pair = MutablePair.of(0, null);
+ handled.put(objectClass, pair);
+ }
+ pair.setLeft(pair.getLeft() + 1);
+ pair.setRight(name.getNameValue());
+ }
+
+ @Override
+ public String currentStatus() {
+ synchronized (status) {
+ if (!handled.isEmpty()) {
+ StringBuilder builder = new StringBuilder("Processed:\n");
+ for (Map.Entry<ObjectClass, MutablePair<Integer, String>> entry : handled.entrySet()) {
+ builder.append(' ').append(entry.getValue().getLeft()).append('\t').
+ append(entry.getKey().getObjectClassValue()).
+ append("\t/ latest: ").append(entry.getValue().getRight()).
+ append('\n');
+ }
+ status.set(builder.toString());
+ }
+ }
+ return status.get();
+ }
+
protected void setGroupOwners(final GroupPullResultHandler ghandler) {
for (Map.Entry<String, String> entry : ghandler.getGroupOwnerMap().entrySet()) {
Group group = groupDAO.find(entry.getKey());
@@ -195,8 +227,12 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
}
}
+ status.set("Initialization completed");
+
// First realms...
if (pullTask.getResource().getOrgUnit() != null) {
+ status.set("Pulling " + pullTask.getResource().getOrgUnit().getObjectClass().getObjectClassValue());
+
OrgUnit orgUnit = pullTask.getResource().getOrgUnit();
OperationOptions options = MappingUtils.buildOperationOptions(
MappingUtils.getPullItems(orgUnit.getItems()).iterator());
@@ -252,6 +288,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
for (Provision provision : pullTask.getResource().getProvisions()) {
if (provision.getMapping() != null) {
+ status.set("Pulling " + provision.getObjectClass().getObjectClassValue());
+
SyncopePullResultHandler handler;
switch (provision.getAnyType().getKind()) {
case USER:
@@ -331,6 +369,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
}
}
+ status.set("Pull done");
+
String result = createReport(profile.getResults(), pullTask.getResource(), dryRun);
LOG.debug("Pull result: {}", result);
return result;
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 5c8e788..abbb765 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -20,8 +20,12 @@ package org.apache.syncope.core.provisioning.java.pushpull;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.MutablePair;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -35,9 +39,12 @@ import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
@@ -78,6 +85,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
protected ProvisioningProfile<PushTask, PushActions> profile;
+ protected final Map<String, MutablePair<Integer, String>> handled = new HashMap<>();
+
protected RealmPushResultHandler rhandler;
protected AnyObjectPushResultHandler ahandler;
@@ -86,6 +95,33 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
protected GroupPushResultHandler ghandler;
+ protected void reportHandled(final String anyType, final String key) {
+ MutablePair<Integer, String> pair = handled.get(anyType);
+ if (pair == null) {
+ pair = MutablePair.of(0, null);
+ handled.put(anyType, pair);
+ }
+ pair.setLeft(pair.getLeft() + 1);
+ pair.setRight(key);
+ }
+
+ @Override
+ public String currentStatus() {
+ synchronized (status) {
+ if (!handled.isEmpty()) {
+ StringBuilder builder = new StringBuilder("Processed:\n");
+ for (Map.Entry<String, MutablePair<Integer, String>> entry : handled.entrySet()) {
+ builder.append(' ').append(entry.getValue().getLeft()).append('\t').
+ append(entry.getKey()).
+ append("\t/ latest: ").append(entry.getValue().getRight()).
+ append('\n');
+ }
+ status.set(builder.toString());
+ }
+ }
+ return status.get();
+ }
+
protected AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) {
AnyDAO<?> result;
switch (anyTypeKind) {
@@ -114,6 +150,13 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
for (Any<?> any : anys) {
try {
handler.handle(any.getKey());
+ reportHandled(
+ any.getType().getKey(),
+ (any instanceof User
+ ? ((User) any).getUsername()
+ : any instanceof Group
+ ? ((Group) any).getName()
+ : ((AnyObject) any).getName()));
} catch (Exception e) {
LOG.warn("Failure pushing '{}' on '{}'", any, resource, e);
throw new JobExecutionException("While pushing " + any + " on " + resource, e);
@@ -185,8 +228,12 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
}
}
+ status.set("Initialization completed");
+
// First realms...
if (pushTask.getResource().getOrgUnit() != null) {
+ status.set("Pushing realms");
+
rhandler = buildRealmHandler();
for (Realm realm : realmDAO.findDescendants(profile.getTask().getSourceRealm())) {
@@ -194,6 +241,7 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
if (realm.getParent() != null) {
try {
rhandler.handle(realm.getKey());
+ reportHandled(SyncopeConstants.REALM_ANYTYPE, realm.getName());
} catch (Exception e) {
LOG.warn("Failure pushing '{}' on '{}'", realm, pushTask.getResource(), e);
throw new JobExecutionException("While pushing " + realm + " on " + pushTask.getResource(), e);
@@ -209,6 +257,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
for (Provision provision : pushTask.getResource().getProvisions()) {
if (provision.getMapping() != null) {
+ status.set("Pushing " + provision.getAnyType().getKey());
+
AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind());
SyncopePushResultHandler handler;
@@ -255,6 +305,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
}
}
+ status.set("Push done");
+
String result = createReport(profile.getResults(), pushTask.getResource(), dryRun);
LOG.debug("Push result: {}", result);
return result;
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
index aadbaf3..81ef47c 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
@@ -71,6 +71,11 @@ public abstract class AbstractExecutableService extends AbstractServiceImpl impl
}
@Override
+ public JobTO getJob(final String key) {
+ return getExecutableLogic().getJob(key);
+ }
+
+ @Override
public List<JobTO> listJobs() {
return getExecutableLogic().listJobs();
}
[06/11] syncope git commit: [SYNCOPE-1279] Now providing runtime
status updates from running Tasks and Reports
Posted by il...@apache.org.
[SYNCOPE-1279] Now providing runtime status updates from running Tasks and Reports
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/799f079f
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/799f079f
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/799f079f
Branch: refs/heads/2_0_X
Commit: 799f079f132d6a2f9dc36a0bfd20475eb988febc
Parents: 988dfee
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 2 11:45:34 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 11:45:34 2018 +0100
----------------------------------------------------------------------
.../console/reports/ReportDirectoryPanel.java | 56 ++++
.../console/rest/AnyObjectRestClient.java | 2 -
.../client/console/rest/ReportRestClient.java | 4 +
.../client/console/rest/TaskRestClient.java | 4 +
.../tasks/ProvisioningTaskDirectoryPanel.java | 61 ++++
.../client/console/widgets/JobActionPanel.java | 23 +-
.../client/console/widgets/JobWidget.java | 4 +-
.../META-INF/resources/css/syncopeConsole.css | 6 +-
.../client/console/widgets/JobActionPanel.html | 2 +
.../org/apache/syncope/common/lib/to/JobTO.java | 11 +
.../rest/api/service/ExecutableService.java | 11 +
.../core/logic/AbstractExecutableLogic.java | 2 +
.../syncope/core/logic/AbstractJobLogic.java | 63 ++--
.../apache/syncope/core/logic/ReportLogic.java | 25 ++
.../apache/syncope/core/logic/TaskLogic.java | 27 +-
.../core/persistence/api/dao/Reportlet.java | 4 +-
.../core/provisioning/api/Connector.java | 7 +-
.../core/provisioning/api/job/JobDelegate.java | 27 ++
.../api/job/SchedTaskJobDelegate.java | 2 +-
.../api/job/report/ReportJobDelegate.java | 27 ++
.../notification/NotificationJobDelegate.java | 31 ++
.../api/pushpull/SyncopePullExecutor.java | 3 +
.../provisioning/java/ConnectorFacadeProxy.java | 13 +-
.../java/job/AbstractInterruptableJob.java | 21 +-
.../java/job/AbstractSchedTaskJobDelegate.java | 12 +
.../GroupMemberProvisionTaskJobDelegate.java | 28 +-
.../java/job/IdentityRecertification.java | 9 +-
.../core/provisioning/java/job/TaskJob.java | 19 +-
.../DefaultNotificationJobDelegate.java | 296 +++++++++++++++++++
.../java/job/notification/NotificationJob.java | 7 +
.../notification/NotificationJobDelegate.java | 278 -----------------
.../java/job/report/AbstractReportlet.java | 13 +-
.../java/job/report/AuditReportlet.java | 16 +-
.../job/report/DefaultReportJobDelegate.java | 216 ++++++++++++++
.../java/job/report/GroupReportlet.java | 19 +-
.../job/report/ReconciliationReportlet.java | 106 +++++--
.../provisioning/java/job/report/ReportJob.java | 7 +
.../java/job/report/ReportJobDelegate.java | 197 ------------
.../java/job/report/StaticReportlet.java | 8 +-
.../java/job/report/UserReportlet.java | 17 +-
.../AbstractPropagationTaskExecutor.java | 11 +-
.../pushpull/AbstractPullResultHandler.java | 2 +
.../pushpull/DefaultRealmPullResultHandler.java | 2 +
.../java/pushpull/PullJobDelegate.java | 40 +++
.../java/pushpull/PushJobDelegate.java | 52 ++++
.../cxf/service/AbstractExecutableService.java | 5 +
46 files changed, 1215 insertions(+), 581 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
index 01f77db..e0c5d6f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
@@ -34,27 +34,36 @@ import org.apache.syncope.client.console.pages.BasePage;
import org.apache.syncope.client.console.panels.DirectoryPanel;
import org.apache.syncope.client.console.panels.MultilevelPanel;
import org.apache.syncope.client.console.rest.ReportRestClient;
+import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.widgets.JobActionPanel;
import org.apache.syncope.client.console.wizards.AjaxWizard;
import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ReportTO;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.util.time.Duration;
/**
* Reports page.
@@ -76,6 +85,17 @@ public abstract class ReportDirectoryPanel
modal.size(Modal.Size.Large);
initResultTable();
+ container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+ private static final long serialVersionUID = -4661303265651934868L;
+
+ @Override
+ protected void onTimer(final AjaxRequestTarget target) {
+ container.modelChanged();
+ target.add(container);
+ }
+ });
+
startAt = new ReportStartAtTogglePanel(container, pageRef);
addInnerObject(startAt);
}
@@ -107,10 +127,46 @@ public abstract class ReportDirectoryPanel
columns.add(new BooleanPropertyColumn<ReportTO>(
new StringResourceModel("active", this), "active", "active"));
+ columns.add(new AbstractColumn<ReportTO, String>(new Model<>(""), "running") {
+
+ private static final long serialVersionUID = 4209532514416998046L;
+
+ @Override
+ public void populateItem(
+ final Item<ICellPopulator<ReportTO>> cellItem,
+ final String componentId,
+ final IModel<ReportTO> rowModel) {
+
+ JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
+ JobActionPanel panel = new JobActionPanel(
+ componentId, jobTO, false, ReportDirectoryPanel.this, pageRef);
+ MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
+ String.format("%s,%s",
+ StandardEntitlement.TASK_EXECUTE,
+ StandardEntitlement.TASK_UPDATE));
+ cellItem.add(panel);
+ }
+
+ @Override
+ public String getCssClass() {
+ return "col-xs-1";
+ }
+ });
+
return columns;
}
@Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof JobActionPanel.JobActionPayload) {
+ container.modelChanged();
+ JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container);
+ } else {
+ super.onEvent(event);
+ }
+ }
+
+ @Override
public ActionsPanel<ReportTO> getActions(final IModel<ReportTO> model) {
final ActionsPanel<ReportTO> panel = super.getActions(model);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
index 31cd3a8..a885764 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
@@ -18,8 +18,6 @@
*/
package org.apache.syncope.client.console.rest;
-import static org.apache.syncope.client.console.rest.BaseRestClient.getService;
-
import java.util.List;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
index 0c96627..e65ce7e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
@@ -54,6 +54,10 @@ public class ReportRestClient extends BaseRestClient
return getService(ReportService.class).list();
}
+ public JobTO getJob(final String key) {
+ return getService(ReportService.class).getJob(key);
+ }
+
public List<JobTO> listJobs() {
return getService(ReportService.class).listJobs();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
index d184085..723ae2e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
@@ -47,6 +47,10 @@ public class TaskRestClient extends BaseRestClient implements ExecutionRestClien
private static final long serialVersionUID = 6284485820911028843L;
+ public JobTO getJob(final String key) {
+ return getService(TaskService.class).getJob(key);
+ }
+
public List<JobTO> listJobs() {
return getService(TaskService.class).listJobs();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
index 91f17b8..cacd6f7 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
@@ -23,18 +23,32 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.widgets.JobActionPanel;
+import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ProvisioningTaskTO;
import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.util.time.Duration;
/**
* Tasks page.
@@ -63,6 +77,17 @@ public abstract class ProvisioningTaskDirectoryPanel<T extends ProvisioningTaskT
// super in order to call the parent implementation
super.initResultTable();
+
+ container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+ private static final long serialVersionUID = -4661303265651934868L;
+
+ @Override
+ protected void onTimer(final AjaxRequestTarget target) {
+ container.modelChanged();
+ target.add(container);
+ }
+ });
}
@Override
@@ -103,9 +128,45 @@ public abstract class ProvisioningTaskDirectoryPanel<T extends ProvisioningTaskT
columns.add(new BooleanPropertyColumn<T>(
new StringResourceModel("active", this), "active", "active"));
+ columns.add(new AbstractColumn<T, String>(new Model<>(""), "running") {
+
+ private static final long serialVersionUID = -4008579357070833846L;
+
+ @Override
+ public void populateItem(
+ final Item<ICellPopulator<T>> cellItem,
+ final String componentId,
+ final IModel<T> rowModel) {
+
+ JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
+ JobActionPanel panel = new JobActionPanel(
+ componentId, jobTO, false, ProvisioningTaskDirectoryPanel.this, pageRef);
+ MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
+ String.format("%s,%s",
+ StandardEntitlement.TASK_EXECUTE,
+ StandardEntitlement.TASK_UPDATE));
+ cellItem.add(panel);
+ }
+
+ @Override
+ public String getCssClass() {
+ return "col-xs-1";
+ }
+ });
+
return columns;
}
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof JobActionPanel.JobActionPayload) {
+ container.modelChanged();
+ JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container);
+ } else {
+ super.onEvent(event);
+ }
+ }
+
protected class ProvisioningTasksProvider<T extends ProvisioningTaskTO> extends SchedTasksProvider<T> {
private static final long serialVersionUID = 4725679400450513556L;
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
index 82665f8..5e03e2d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
@@ -18,6 +18,9 @@
*/
package org.apache.syncope.client.console.widgets;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverBehavior;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -30,10 +33,13 @@ import org.apache.syncope.client.console.wicket.ajax.markup.html.IndicatorAjaxLi
import org.apache.syncope.client.console.wizards.WizardMgtPanel;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,14 +58,21 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
public JobActionPanel(
final String id,
final JobTO jobTO,
- final JobWidget widget,
+ final boolean showNotRunning,
+ final Component container,
final PageReference pageRef) {
+
super(id, true);
setOutputMarkupId(true);
Fragment controls;
if (jobTO.isRunning()) {
controls = new Fragment("controls", "runningFragment", this);
+ controls.add(new Label("status", Model.of()).add(new PopoverBehavior(
+ Model.<String>of(),
+ Model.of("<pre>" + (jobTO.getStatus() == null ? StringUtils.EMPTY : jobTO.getStatus()) + "</pre>"),
+ new PopoverConfig().withAnimation(true).withHoverTrigger().withHtml(true).
+ withPlacement(TooltipConfig.Placement.left))));
controls.add(new IndicatorAjaxLink<Void>("stop") {
private static final long serialVersionUID = -7978723352517770644L;
@@ -83,7 +96,7 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
default:
}
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
- send(widget, Broadcast.EXACT, new JobActionPayload(target));
+ send(container, Broadcast.EXACT, new JobActionPayload(target));
} catch (Exception e) {
LOG.error("While stopping {}", jobTO.getRefDesc(), e);
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName()
@@ -117,7 +130,7 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
default:
}
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
- send(widget, Broadcast.EXACT, new JobActionPayload(target));
+ send(container, Broadcast.EXACT, new JobActionPayload(target));
} catch (Exception e) {
LOG.error("While starting {}", jobTO.getRefDesc(), e);
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName()
@@ -126,6 +139,10 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
((BasePage) getPage()).getNotificationPanel().refresh(target);
}
});
+ if (!showNotRunning) {
+ controls.setOutputMarkupPlaceholderTag(true);
+ controls.setVisible(false);
+ }
}
addInnerObject(controls);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
index cd10c70..98a53e4 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
@@ -357,9 +357,9 @@ public class JobWidget extends BaseWidget {
final IModel<JobTO> rowModel) {
JobTO jobTO = rowModel.getObject();
- JobActionPanel panel = new JobActionPanel(componentId, jobTO, JobWidget.this, pageRef);
+ JobActionPanel panel = new JobActionPanel(componentId, jobTO, true, JobWidget.this, pageRef);
MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
- String.format("%s,%s%s,%s",
+ String.format("%s,%s,%s,%s",
StandardEntitlement.TASK_EXECUTE,
StandardEntitlement.REPORT_EXECUTE,
StandardEntitlement.TASK_UPDATE,
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
index 294c978..c2dcf2c 100644
--- a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
+++ b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
@@ -879,6 +879,10 @@ li.todoitem a {
cursor: default;
}
+.popover{
+ max-width: 100%;
+}
+
#popover:hover {
cursor: pointer;
}
@@ -1157,4 +1161,4 @@ div#inline-actions ul.menu i, div#tablehandling ul.menu i {
div#tablehandling ul.menu li a {
padding: 0px !important;
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
index e94f292..8e31f36 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
@@ -22,6 +22,8 @@ under the License.
<wicket:fragment wicket:id="runningFragment">
<i id="actionLink" class="fa fa-refresh fa-spin"></i>
+ <div wicket:id="status" class="fa fa-binoculars"/>
+
<a href="#" wicket:id="stop" class="fa fa-stop-circle"></a>
</wicket:fragment>
<wicket:fragment wicket:id="notRunningFragment">
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
index 2cb5690..cee8db2 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
@@ -42,6 +42,8 @@ public class JobTO extends AbstractBaseBean {
private Date start;
+ private String status;
+
public JobType getType() {
return type;
}
@@ -93,4 +95,13 @@ public class JobTO extends AbstractBaseBean {
? null
: new Date(start.getTime());
}
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(final String status) {
+ this.status = status;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
index e7271b2..37301bd 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
@@ -101,6 +101,17 @@ public interface ExecutableService extends JAXRSService {
ExecTO execute(@BeanParam ExecuteQuery query);
/**
+ * Returns job (running or scheduled) for the executable matching the given key.
+ *
+ * @param key executable key
+ * @return job (running or scheduled) for the given key
+ */
+ @GET
+ @Path("jobs/{key}")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ JobTO getJob(@PathParam("key") String key);
+
+ /**
* List jobs (running and / or scheduled).
*
* @return jobs (running and / or scheduled)
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
index d1b7cb3..6b34bc7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
@@ -42,6 +42,8 @@ public abstract class AbstractExecutableLogic<T extends AbstractBaseBean> extend
public abstract BulkActionResult deleteExecutions(
String key, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
+ public abstract JobTO getJob(String key);
+
public abstract List<JobTO> listJobs();
public abstract void actionJob(String key, JobAction action);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
index a93ae2d..844d353 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
@@ -26,11 +26,14 @@ import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.JobAction;
import org.apache.syncope.common.lib.types.JobType;
import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.matchers.GroupMatcher;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@@ -44,32 +47,54 @@ abstract class AbstractJobLogic<T extends AbstractBaseBean> extends AbstractTran
protected abstract Triple<JobType, String, String> getReference(final JobKey jobKey);
- protected List<JobTO> doListJobs() {
- List<JobTO> jobTOs = new ArrayList<>();
+ protected JobTO getJobTO(final JobKey jobKey) throws SchedulerException {
+ JobTO jobTO = null;
- try {
- for (JobKey jobKey : scheduler.getScheduler().
- getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP))) {
+ Triple<JobType, String, String> reference = getReference(jobKey);
+ if (reference != null) {
+ jobTO = new JobTO();
- JobTO jobTO = new JobTO();
+ jobTO.setType(reference.getLeft());
+ jobTO.setRefKey(reference.getMiddle());
+ jobTO.setRefDesc(reference.getRight());
- Triple<JobType, String, String> reference = getReference(jobKey);
- if (reference != null) {
- jobTOs.add(jobTO);
+ List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
+ if (jobTriggers.isEmpty()) {
+ jobTO.setScheduled(false);
+ } else {
+ jobTO.setScheduled(true);
+ jobTO.setStart(jobTriggers.get(0).getStartTime());
+ }
+
+ jobTO.setRunning(jobManager.isRunning(jobKey));
- jobTO.setType(reference.getLeft());
- jobTO.setRefKey(reference.getMiddle());
- jobTO.setRefDesc(reference.getRight());
+ jobTO.setStatus("UNKNOWN");
+ if (jobTO.isRunning()) {
+ try {
+ Object job = ApplicationContextProvider.getBeanFactory().getBean(jobKey.getName());
+ if (job instanceof AbstractInterruptableJob
+ && ((AbstractInterruptableJob) job).getDelegate() != null) {
- List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
- if (jobTriggers.isEmpty()) {
- jobTO.setScheduled(false);
- } else {
- jobTO.setScheduled(true);
- jobTO.setStart(jobTriggers.get(0).getStartTime());
+ jobTO.setStatus(((AbstractInterruptableJob) job).getDelegate().currentStatus());
}
+ } catch (NoSuchBeanDefinitionException e) {
+ LOG.warn("Could not find job {} implementation", jobKey, e);
+ }
+ }
+ }
+
+ return jobTO;
+ }
+
+ protected List<JobTO> doListJobs() {
+ List<JobTO> jobTOs = new ArrayList<>();
+ try {
+ for (JobKey jobKey : scheduler.getScheduler().
+ getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP))) {
- jobTO.setRunning(jobManager.isRunning(jobKey));
+ JobTO jobTO = getJobTO(jobKey);
+ if (jobTO != null) {
+ jobTOs.add(jobTO);
}
}
} catch (SchedulerException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 9505303..da8f2b7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -66,6 +66,7 @@ import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
import org.apache.syncope.core.provisioning.api.job.JobNamer;
import org.apache.xmlgraphics.util.MimeConstants;
import org.quartz.JobKey;
+import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -387,6 +388,30 @@ public class ReportLogic extends AbstractExecutableLogic<ReportTO> {
return super.doListJobs();
}
+ @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_READ + "')")
+ @Override
+ public JobTO getJob(final String key) {
+ Report report = reportDAO.find(key);
+ if (report == null) {
+ throw new NotFoundException("Report " + key);
+ }
+
+ JobTO jobTO = null;
+ try {
+ jobTO = getJobTO(JobNamer.getJobKey(report));
+ } catch (SchedulerException e) {
+ LOG.error("Problems while retrieving scheduled job {}", JobNamer.getJobKey(report), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ if (jobTO == null) {
+ throw new NotFoundException("Job for report " + key);
+ }
+ return jobTO;
+ }
+
@PreAuthorize("hasRole('" + StandardEntitlement.REPORT_EXECUTE + "')")
@Override
public void actionJob(final String key, final JobAction action) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 9603bc8..25f06a1 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -57,10 +57,11 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
import org.apache.syncope.core.persistence.api.dao.ConfDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.java.job.TaskJob;
-import org.apache.syncope.core.provisioning.java.job.notification.NotificationJobDelegate;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
+import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -402,6 +403,30 @@ public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
return super.doListJobs();
}
+ @PreAuthorize("hasRole('" + StandardEntitlement.TASK_READ + "')")
+ @Override
+ public JobTO getJob(final String key) {
+ Task task = taskDAO.find(key);
+ if (task == null) {
+ throw new NotFoundException("Task " + key);
+ }
+
+ JobTO jobTO = null;
+ try {
+ jobTO = getJobTO(JobNamer.getJobKey(task));
+ } catch (SchedulerException e) {
+ LOG.error("Problems while retrieving scheduled job {}", JobNamer.getJobKey(task), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ if (jobTO == null) {
+ throw new NotFoundException("Job for task " + key);
+ }
+ return jobTO;
+ }
+
@PreAuthorize("hasRole('" + StandardEntitlement.TASK_EXECUTE + "')")
@Override
public void actionJob(final String key, final JobAction action) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
index 8180831..11e6b25 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.api.dao;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@@ -34,7 +35,8 @@ public interface Reportlet {
*
* @param conf configuration
* @param handler SAX content handler for streaming result
+ * @param status current report status (for job reporting)
* @throws SAXException if there is any problem in SAX handling
*/
- void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
+ void extract(ReportletConf conf, ContentHandler handler, AtomicReference<String> status) throws SAXException;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
index 901cdc7..2098466 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.api;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -63,7 +64,7 @@ public interface Connector {
ObjectClass objectClass,
Set<Attribute> attrs,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Update user / group on a connector instance.
@@ -80,7 +81,7 @@ public interface Connector {
Uid uid,
Set<Attribute> attrs,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Delete user / group on a connector instance.
@@ -94,7 +95,7 @@ public interface Connector {
ObjectClass objectClass,
Uid uid,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Fetches all remote objects (for use during full reconciliation).
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
new file mode 100644
index 0000000..3bfa292
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.job;
+
+/**
+ * Implementations of this interface will perform the actual operations required to Quartz's {@link org.quartz.Job}.
+ */
+public interface JobDelegate {
+
+ String currentStatus();
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
index a03f36b..bb69b10 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.provisioning.api.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
-public interface SchedTaskJobDelegate {
+public interface SchedTaskJobDelegate extends JobDelegate {
void execute(String taskKey, boolean dryRun, JobExecutionContext context) throws JobExecutionException;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
new file mode 100644
index 0000000..bbf455f
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.job.report;
+
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.quartz.JobExecutionException;
+
+public interface ReportJobDelegate extends JobDelegate {
+
+ void execute(String reportKey) throws JobExecutionException;
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
new file mode 100644
index 0000000..3dfcddd
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.notification;
+
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.quartz.JobExecutionException;
+
+public interface NotificationJobDelegate extends JobDelegate {
+
+ TaskExec executeSingle(NotificationTask task);
+
+ void execute() throws JobExecutionException;
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
index ab02282..39eed32 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
@@ -18,10 +18,13 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
+import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.SyncToken;
public interface SyncopePullExecutor {
void setLatestSyncToken(ObjectClass objectClass, SyncToken latestSyncToken);
+
+ void reportHandled(ObjectClass objectClass, Name name);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index 7e94c5b..aea4e27 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.syncope.common.lib.types.ConnConfProperty;
@@ -165,12 +166,12 @@ public class ConnectorFacadeProxy implements Connector {
final ObjectClass objectClass,
final Set<Attribute> attrs,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result = null;
if (connInstance.getCapabilities().contains(ConnectorCapability.CREATE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.create(connector, objectClass, attrs, options);
try {
@@ -200,12 +201,12 @@ public class ConnectorFacadeProxy implements Connector {
final Uid uid,
final Set<Attribute> attrs,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result = null;
if (connInstance.getCapabilities().contains(ConnectorCapability.UPDATE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.update(connector, objectClass, uid, attrs, options);
@@ -236,10 +237,10 @@ public class ConnectorFacadeProxy implements Connector {
final ObjectClass objectClass,
final Uid uid,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
if (connInstance.getCapabilities().contains(ConnectorCapability.DELETE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.delete(connector, objectClass, uid, options);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
index 19bbf1e..86a7f49 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.job;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.quartz.DisallowConcurrentExecution;
@@ -40,13 +41,25 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
*/
private final AtomicReference<Thread> runningThread = new AtomicReference<>();
+ private final JobDelegate embeddedDelegate = new JobDelegate() {
+
+ @Override
+ public String currentStatus() {
+ return "RUNNING THREAD: " + runningThread.get();
+ }
+ };
+
private long interruptMaxRetries = 1;
+ public JobDelegate getDelegate() {
+ return embeddedDelegate;
+ }
+
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
- this.runningThread.set(Thread.currentThread());
+ runningThread.set(Thread.currentThread());
try {
- this.interruptMaxRetries = context.getMergedJobDataMap().getLong(JobManager.INTERRUPT_MAX_RETRIES_KEY);
+ interruptMaxRetries = context.getMergedJobDataMap().getLong(JobManager.INTERRUPT_MAX_RETRIES_KEY);
} catch (Exception e) {
LOG.debug("Could not set {}, defaults to {}", JobManager.INTERRUPT_MAX_RETRIES_KEY, interruptMaxRetries, e);
}
@@ -54,7 +67,7 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
@Override
public void interrupt() throws UnableToInterruptJobException {
- Thread thread = this.runningThread.getAndSet(null);
+ Thread thread = runningThread.getAndSet(null);
if (thread == null) {
LOG.warn("Unable to retrieve the thread of the current job execution");
} else {
@@ -68,7 +81,7 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
}
// if the thread is still alive, it should be available in the next stop
if (thread.isAlive()) {
- this.runningThread.set(thread);
+ runningThread.set(thread);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
index 5fc5405..7103b7b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
@@ -19,6 +19,7 @@
package org.apache.syncope.core.provisioning.java.job;
import java.util.Date;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.types.AuditElements;
import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
@@ -72,6 +73,13 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
@Autowired
protected AuditManager auditManager;
+ protected final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
@Transactional
@Override
public void execute(final String taskKey, final boolean dryRun, final JobExecutionContext context)
@@ -90,6 +98,8 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
execution.setStart(new Date());
execution.setTask(task);
+ status.set("Initialization completed");
+
AuditElements.Result result;
try {
@@ -110,6 +120,8 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
}
task = taskDAO.save(task);
+ status.set("Done");
+
notificationManager.createTasks(
AuditElements.EventCategoryType.TASK,
this.getClass().getSimpleName(),
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
index 4868bbc..4f1a1eb 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
@@ -82,20 +82,25 @@ public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDel
}
result.append("provision\n\n");
+ status.set(result.toString());
+
MembershipCond membershipCond = new MembershipCond();
membershipCond.setGroup(groupKey);
List<User> users = searchDAO.search(SearchCond.getLeafCond(membershipCond), AnyTypeKind.USER);
Collection<String> groupResourceKeys = groupDAO.findAllResourceKeys(groupKey);
+ status.set("About to "
+ + (actionType == BulkMembersActionType.DEPROVISION ? "de" : "") + "provision "
+ + users.size() + " users from " + groupResourceKeys);
for (User user : users) {
List<PropagationStatus> statuses = actionType == BulkMembersActionType.DEPROVISION
? userProvisioningManager.deprovision(user.getKey(), groupResourceKeys, false)
: userProvisioningManager.provision(user.getKey(), true, null, groupResourceKeys, false);
- for (PropagationStatus status : statuses) {
+ for (PropagationStatus propagationStatus : statuses) {
result.append("User ").append(user.getKey()).append('\t').
- append("Resource ").append(status.getResource()).append('\t').
- append(status.getStatus());
- if (StringUtils.isNotBlank(status.getFailureReason())) {
- result.append('\n').append(status.getFailureReason()).append('\n');
+ append("Resource ").append(propagationStatus.getResource()).append('\t').
+ append(propagationStatus.getStatus());
+ if (StringUtils.isNotBlank(propagationStatus.getFailureReason())) {
+ result.append('\n').append(propagationStatus.getFailureReason()).append('\n');
}
result.append("\n");
}
@@ -105,17 +110,20 @@ public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDel
membershipCond = new MembershipCond();
membershipCond.setGroup(groupKey);
List<AnyObject> anyObjects = searchDAO.search(SearchCond.getLeafCond(membershipCond), AnyTypeKind.ANY_OBJECT);
+ status.set("About to "
+ + (actionType == BulkMembersActionType.DEPROVISION ? "de" : "") + "provision "
+ + anyObjects.size() + " any objects from " + groupResourceKeys);
for (AnyObject anyObject : anyObjects) {
List<PropagationStatus> statuses = actionType == BulkMembersActionType.DEPROVISION
? anyObjectProvisioningManager.deprovision(anyObject.getKey(), groupResourceKeys, false)
: anyObjectProvisioningManager.provision(anyObject.getKey(), groupResourceKeys, false);
- for (PropagationStatus status : statuses) {
+ for (PropagationStatus propagationStatus : statuses) {
result.append(anyObject.getType().getKey()).append(' ').append(anyObject.getKey()).append('\t').
- append("Resource ").append(status.getResource()).append('\t').
- append(status.getStatus());
- if (StringUtils.isNotBlank(status.getFailureReason())) {
- result.append('\n').append(status.getFailureReason()).append('\n');
+ append("Resource ").append(propagationStatus.getResource()).append('\t').
+ append(propagationStatus.getStatus());
+ if (StringUtils.isNotBlank(propagationStatus.getFailureReason())) {
+ result.append('\n').append(propagationStatus.getFailureReason()).append('\n');
}
result.append("\n");
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
index 8b3f4e5..332af19 100755
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
@@ -87,8 +87,15 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
return "DRY RUN";
}
+ int total = userDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
long now = System.currentTimeMillis();
- for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
LOG.debug("Processing user: {}", user.getUsername());
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
index 041d3b4..d688179 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
@@ -19,6 +19,7 @@
package org.apache.syncope.core.provisioning.java.job;
import org.apache.commons.lang3.ClassUtils;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
@@ -52,6 +53,8 @@ public class TaskJob extends AbstractInterruptableJob {
*/
private String taskKey;
+ private SchedTaskJobDelegate delegate;
+
/**
* Task key setter.
*
@@ -62,6 +65,11 @@ public class TaskJob extends AbstractInterruptableJob {
}
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
@@ -75,11 +83,12 @@ public class TaskJob extends AbstractInterruptableJob {
Class<?> delegateClass =
ClassUtils.getClass(context.getMergedJobDataMap().getString(DELEGATE_CLASS_KEY));
- ((SchedTaskJobDelegate) ApplicationContextProvider.getBeanFactory().
- createBean(delegateClass, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false)).
- execute(taskKey,
- context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY),
- context);
+ delegate = ((SchedTaskJobDelegate) ApplicationContextProvider.getBeanFactory().
+ createBean(delegateClass, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false));
+ delegate.execute(
+ taskKey,
+ context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY),
+ context);
} catch (Exception e) {
LOG.error("While executing task {}", taskKey, e);
throw new RuntimeException(e);
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
new file mode 100644
index 0000000..7ab218b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.notification;
+
+import java.io.PrintStream;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.LogOutputStream;
+import org.apache.syncope.common.lib.PropertyUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.spring.security.Encryptor;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class DefaultNotificationJobDelegate implements InitializingBean, NotificationJobDelegate {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
+
+ @Autowired
+ private TaskDAO taskDAO;
+
+ @Autowired
+ private JavaMailSender mailSender;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Autowired
+ private AuditManager auditManager;
+
+ @Autowired
+ private NotificationManager notificationManager;
+
+ private final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (mailSender instanceof JavaMailSenderImpl) {
+ JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender;
+
+ Properties javaMailProperties = javaMailSender.getJavaMailProperties();
+
+ Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory").getLeft();
+ for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
+ String prop = (String) e.nextElement();
+ if (prop.startsWith("mail.smtp.")) {
+ javaMailProperties.setProperty(prop, props.getProperty(prop));
+ }
+ }
+
+ if (StringUtils.isNotBlank(javaMailSender.getUsername())) {
+ javaMailProperties.setProperty("mail.smtp.auth", "true");
+ }
+
+ javaMailSender.setJavaMailProperties(javaMailProperties);
+
+ String mailDebug = props.getProperty("mail.debug", "false");
+ if (BooleanUtils.toBoolean(mailDebug)) {
+ Session session = javaMailSender.getSession();
+ session.setDebug(true);
+ session.setDebugOut(new PrintStream(new LogOutputStream(LOG)));
+ }
+ }
+ }
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
+ @Transactional
+ @Override
+ public TaskExec executeSingle(final NotificationTask task) {
+ TaskExec execution = entityFactory.newEntity(TaskExec.class);
+ execution.setTask(task);
+ execution.setStart(new Date());
+
+ boolean retryPossible = true;
+
+ if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
+ || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
+
+ String message = "Could not fetch all required information for sending e-mails:\n"
+ + task.getRecipients() + "\n"
+ + task.getSender() + "\n"
+ + task.getSubject() + "\n"
+ + task.getHtmlBody() + "\n"
+ + task.getTextBody();
+ LOG.error(message);
+
+ execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+ retryPossible = false;
+
+ if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+ execution.setMessage(message);
+ }
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("About to send e-mails:\n"
+ + task.getRecipients() + "\n"
+ + task.getSender() + "\n"
+ + task.getSubject() + "\n"
+ + task.getHtmlBody() + "\n"
+ + task.getTextBody() + "\n");
+ }
+
+ status.set("Sending notifications to " + task.getRecipients());
+
+ for (String to : task.getRecipients()) {
+ try {
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setTo(to);
+ helper.setFrom(task.getSender());
+ helper.setSubject(task.getSubject());
+ helper.setText(task.getTextBody(), task.getHtmlBody());
+
+ mailSender.send(message);
+
+ execution.setStatus(NotificationJob.Status.SENT.name());
+
+ StringBuilder report = new StringBuilder();
+ switch (task.getTraceLevel()) {
+ case ALL:
+ report.append("FROM: ").append(task.getSender()).append('\n').
+ append("TO: ").append(to).append('\n').
+ append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
+ append(task.getTextBody()).append('\n').append('\n').
+ append(task.getHtmlBody()).append('\n');
+ break;
+
+ case SUMMARY:
+ report.append("E-mail sent to ").append(to).append('\n');
+ break;
+
+ case FAILURES:
+ case NONE:
+ default:
+ }
+ if (report.length() > 0) {
+ execution.setMessage(report.toString());
+ }
+
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "send",
+ AuditElements.Result.SUCCESS,
+ null,
+ null,
+ task,
+ "Successfully sent notification to " + to);
+ } catch (Exception e) {
+ LOG.error("Could not send e-mail", e);
+
+ execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+ if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+ execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
+ }
+
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "send",
+ AuditElements.Result.FAILURE,
+ null,
+ null,
+ task,
+ "Could not send notification to " + to, e);
+ }
+
+ execution.setEnd(new Date());
+ }
+ }
+
+ if (hasToBeRegistered(execution)) {
+ execution = notificationManager.storeExec(execution);
+ if (retryPossible
+ && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
+
+ handleRetries(execution);
+ }
+ } else {
+ notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
+ }
+
+ return execution;
+ }
+
+ @Transactional
+ @Override
+ public void execute() throws JobExecutionException {
+ List<NotificationTask> tasks = taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION);
+
+ status.set("Sending out " + tasks.size() + " notifications");
+
+ for (NotificationTask task : tasks) {
+ LOG.debug("Found notification task {} to be executed: starting...", task);
+ executeSingle(task);
+ LOG.debug("Notification task {} executed", task);
+ }
+ }
+
+ private boolean hasToBeRegistered(final TaskExec execution) {
+ NotificationTask task = (NotificationTask) execution.getTask();
+
+ // True if either failed and failures have to be registered, or if ALL
+ // has to be registered.
+ return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
+ && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || task.getTraceLevel() == TraceLevel.ALL;
+ }
+
+ private void handleRetries(final TaskExec execution) {
+ if (notificationManager.getMaxRetries() <= 0) {
+ return;
+ }
+
+ long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
+ execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
+
+ if (failedExecutionsCount <= notificationManager.getMaxRetries()) {
+ LOG.debug("Execution of notification task {} will be retried [{}/{}]",
+ execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries());
+ notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "retry",
+ AuditElements.Result.SUCCESS,
+ null,
+ null,
+ execution,
+ "Notification task " + execution.getTask().getKey() + " will be retried");
+ } else {
+ LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "retry",
+ AuditElements.Result.FAILURE,
+ null,
+ null,
+ execution,
+ "Giving up retries on notification task " + execution.getTask().getKey());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/799f079f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
index 153a221..7edcce2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
@@ -20,6 +20,8 @@ package org.apache.syncope.core.provisioning.java.job.notification;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@@ -54,6 +56,11 @@ public class NotificationJob extends AbstractInterruptableJob {
private NotificationJobDelegate delegate;
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
[10/11] syncope git commit: [SYNCOPE-1279] Now providing runtime
status updates from running Tasks and Reports
Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
deleted file mode 100644
index c019639..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.job.notification;
-
-import java.io.PrintStream;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Properties;
-import javax.mail.Session;
-import javax.mail.internet.MimeMessage;
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.LogOutputStream;
-import org.apache.syncope.common.lib.PropertyUtils;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.persistence.api.dao.TaskDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.provisioning.api.AuditManager;
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
-import org.apache.syncope.core.spring.security.Encryptor;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.JavaMailSenderImpl;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@Component
-public class NotificationJobDelegate implements InitializingBean {
-
- private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
-
- @Autowired
- private TaskDAO taskDAO;
-
- @Autowired
- private JavaMailSender mailSender;
-
- @Autowired
- private EntityFactory entityFactory;
-
- @Autowired
- private AuditManager auditManager;
-
- @Autowired
- private NotificationManager notificationManager;
-
- @Override
- public void afterPropertiesSet() throws Exception {
- if (mailSender instanceof JavaMailSenderImpl) {
- JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender;
-
- Properties javaMailProperties = javaMailSender.getJavaMailProperties();
-
- Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory").getLeft();
- for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
- String prop = (String) e.nextElement();
- if (prop.startsWith("mail.smtp.")) {
- javaMailProperties.setProperty(prop, props.getProperty(prop));
- }
- }
-
- if (StringUtils.isNotBlank(javaMailSender.getUsername())) {
- javaMailProperties.setProperty("mail.smtp.auth", "true");
- }
-
- javaMailSender.setJavaMailProperties(javaMailProperties);
-
- String mailDebug = props.getProperty("mail.debug", "false");
- if (BooleanUtils.toBoolean(mailDebug)) {
- Session session = javaMailSender.getSession();
- session.setDebug(true);
- session.setDebugOut(new PrintStream(new LogOutputStream(LOG)));
- }
- }
- }
-
- @Transactional
- public TaskExec executeSingle(final NotificationTask task) {
- TaskExec execution = entityFactory.newEntity(TaskExec.class);
- execution.setTask(task);
- execution.setStart(new Date());
-
- boolean retryPossible = true;
-
- if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
- || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
-
- String message = "Could not fetch all required information for sending e-mails:\n"
- + task.getRecipients() + "\n"
- + task.getSender() + "\n"
- + task.getSubject() + "\n"
- + task.getHtmlBody() + "\n"
- + task.getTextBody();
- LOG.error(message);
-
- execution.setStatus(NotificationJob.Status.NOT_SENT.name());
- retryPossible = false;
-
- if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
- execution.setMessage(message);
- }
- } else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("About to send e-mails:\n"
- + task.getRecipients() + "\n"
- + task.getSender() + "\n"
- + task.getSubject() + "\n"
- + task.getHtmlBody() + "\n"
- + task.getTextBody() + "\n");
- }
-
- for (String to : task.getRecipients()) {
- try {
- MimeMessage message = mailSender.createMimeMessage();
- MimeMessageHelper helper = new MimeMessageHelper(message, true);
- helper.setTo(to);
- helper.setFrom(task.getSender());
- helper.setSubject(task.getSubject());
- helper.setText(task.getTextBody(), task.getHtmlBody());
-
- mailSender.send(message);
-
- execution.setStatus(NotificationJob.Status.SENT.name());
-
- StringBuilder report = new StringBuilder();
- switch (task.getTraceLevel()) {
- case ALL:
- report.append("FROM: ").append(task.getSender()).append('\n').
- append("TO: ").append(to).append('\n').
- append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
- append(task.getTextBody()).append('\n').append('\n').
- append(task.getHtmlBody()).append('\n');
- break;
-
- case SUMMARY:
- report.append("E-mail sent to ").append(to).append('\n');
- break;
-
- case FAILURES:
- case NONE:
- default:
- }
- if (report.length() > 0) {
- execution.setMessage(report.toString());
- }
-
- notificationManager.createTasks(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "send",
- AuditElements.Result.SUCCESS,
- null,
- null,
- task,
- "Successfully sent notification to " + to);
- } catch (Exception e) {
- LOG.error("Could not send e-mail", e);
-
- execution.setStatus(NotificationJob.Status.NOT_SENT.name());
- if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
- execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
- }
-
- notificationManager.createTasks(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "send",
- AuditElements.Result.FAILURE,
- null,
- null,
- task,
- "Could not send notification to " + to, e);
- }
-
- execution.setEnd(new Date());
- }
- }
-
- if (hasToBeRegistered(execution)) {
- execution = notificationManager.storeExec(execution);
- if (retryPossible
- && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
-
- handleRetries(execution);
- }
- } else {
- notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
- }
-
- return execution;
- }
-
- @Transactional
- public void execute() throws JobExecutionException {
- for (NotificationTask task : taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION)) {
- LOG.debug("Found notification task {} to be executed: starting...", task);
- executeSingle(task);
- LOG.debug("Notification task {} executed", task);
- }
- }
-
- private boolean hasToBeRegistered(final TaskExec execution) {
- NotificationTask task = (NotificationTask) execution.getTask();
-
- // True if either failed and failures have to be registered, or if ALL
- // has to be registered.
- return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
- && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
- || task.getTraceLevel() == TraceLevel.ALL;
- }
-
- private void handleRetries(final TaskExec execution) {
- if (notificationManager.getMaxRetries() <= 0) {
- return;
- }
-
- long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
- execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
-
- if (failedExecutionsCount <= notificationManager.getMaxRetries()) {
- LOG.debug("Execution of notification task {} will be retried [{}/{}]",
- execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries());
- notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
-
- auditManager.audit(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "retry",
- AuditElements.Result.SUCCESS,
- null,
- null,
- execution,
- "Notification task " + execution.getTask().getKey() + " will be retried");
- } else {
- LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
-
- auditManager.audit(
- AuditElements.EventCategoryType.TASK,
- "notification",
- null,
- "retry",
- AuditElements.Result.FAILURE,
- null,
- null,
- execution,
- "Giving up retries on notification task " + execution.getTask().getKey());
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
index ede3535..cb52fd0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.Reportlet;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.slf4j.Logger;
@@ -38,17 +39,18 @@ public abstract class AbstractReportlet implements Reportlet {
this.conf = conf;
}
- protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
+ protected abstract void doExtract(ReportletConf conf, ContentHandler handler, AtomicReference<String> status)
+ throws SAXException;
@Override
@Transactional(readOnly = true)
- public void extract(final ContentHandler handler) throws SAXException {
+ public void extract(final ContentHandler handler, final AtomicReference<String> status) throws SAXException {
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, conf.getName());
atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
- doExtract(conf, handler);
+ doExtract(conf, handler, status);
handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
index d6f0ec5..6ba9a53 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.job.report;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -47,7 +48,9 @@ public class AuditReportlet extends AbstractReportlet {
private DataSource datasource;
- private void doExtractConf(final ContentHandler handler) throws SAXException {
+ private void doExtractConf(final ContentHandler handler, final AtomicReference<String> status) throws SAXException {
+ status.set("Fetching " + conf.getSize() + " rows from the SYNCOPEAUDIT table");
+
JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
jdbcTemplate.setMaxRows(conf.getSize());
List<Map<String, Object>> rows = jdbcTemplate.
@@ -120,10 +123,17 @@ public class AuditReportlet extends AbstractReportlet {
handler.endElement("", "", "event");
}
handler.endElement("", "", "events");
+
+ status.set("Fetched " + conf.getSize() + " rows from the SYNCOPEAUDIT table");
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof AuditReportletConf) {
this.conf = AuditReportletConf.class.cast(conf);
} else {
@@ -135,7 +145,7 @@ public class AuditReportlet extends AbstractReportlet {
throw new ReportException(new IllegalArgumentException("Could not get to DataSource"));
}
- doExtractConf(handler);
+ doExtractConf(handler, status);
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
new file mode 100644
index 0000000..13d8dde
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.report;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+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.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.apache.syncope.core.spring.ImplementationManager;
+import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.xml.sax.helpers.AttributesImpl;
+
+@Component
+public class DefaultReportJobDelegate implements ReportJobDelegate {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
+
+ /**
+ * Report DAO.
+ */
+ @Autowired
+ private ReportDAO reportDAO;
+
+ /**
+ * Report execution DAO.
+ */
+ @Autowired
+ private ReportExecDAO reportExecDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ private final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
+ @Transactional
+ @Override
+ public void execute(final String reportKey) throws JobExecutionException {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new JobExecutionException("Report " + reportKey + " not found");
+ }
+
+ if (!report.isActive()) {
+ LOG.info("Report {} not active, aborting...", reportKey);
+ return;
+ }
+
+ // 1. create execution
+ ReportExec execution = entityFactory.newEntity(ReportExec.class);
+ execution.setStatus(ReportExecStatus.STARTED);
+ execution.setStart(new Date());
+ execution.setReport(report);
+ execution = reportExecDAO.save(execution);
+
+ report.add(execution);
+ report = reportDAO.save(report);
+
+ // 2. define a SAX handler for generating result as XML
+ TransformerHandler handler;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream zos = new ZipOutputStream(baos);
+ zos.setLevel(Deflater.BEST_COMPRESSION);
+ try {
+ SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ handler = tFactory.newTransformerHandler();
+ Transformer serializer = handler.getTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
+ serializer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ // a single ZipEntry in the ZipOutputStream
+ zos.putNextEntry(new ZipEntry(report.getName()));
+
+ // streaming SAX handler in a compressed byte array stream
+ handler.setResult(new StreamResult(zos));
+ } catch (Exception e) {
+ throw new JobExecutionException("While configuring for SAX generation", e, true);
+ }
+
+ execution.setStatus(ReportExecStatus.RUNNING);
+ execution = reportExecDAO.save(execution);
+
+ status.set("Starting");
+
+ // 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);
+
+ status.set("Generating report header");
+
+ // iterate over reportlet instances defined for this report
+ for (Implementation impl : report.getReportlets()) {
+ Optional<Reportlet> reportlet = ImplementationManager.buildReportlet(impl);
+ if (reportlet.isPresent()) {
+ try {
+ status.set("Invoking reportlet " + impl.getKey());
+ reportlet.get().extract(handler, status);
+ } catch (Throwable t) {
+ LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
+
+ execution.setStatus(ReportExecStatus.FAILURE);
+
+ Throwable effective = t instanceof ReportException
+ ? t.getCause()
+ : t;
+ reportExecutionMessage.
+ append(ExceptionUtils2.getFullStackTrace(effective)).
+ append("\n==================\n");
+ }
+ }
+ }
+
+ // report footer
+ status.set("Generating report footer");
+
+ handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
+ handler.endDocument();
+
+ if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
+ execution.setStatus(ReportExecStatus.SUCCESS);
+ }
+ } catch (Exception e) {
+ execution.setStatus(ReportExecStatus.FAILURE);
+ reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
+
+ throw new JobExecutionException(e, true);
+ } finally {
+ status.set("Completed");
+
+ try {
+ zos.closeEntry();
+ zos.close();
+ baos.close();
+ } catch (IOException e) {
+ LOG.error("While closing StreamResult's backend", e);
+ }
+
+ execution.setExecResult(baos.toByteArray());
+ execution.setMessage(reportExecutionMessage.toString());
+ execution.setEnd(new Date());
+ reportExecDAO.save(execution);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
index 44a4dfa..b34e271 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/GroupReportlet.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -288,7 +289,12 @@ public class GroupReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof GroupReportletConf) {
this.conf = GroupReportletConf.class.cast(conf);
} else {
@@ -297,7 +303,14 @@ public class GroupReportlet extends AbstractReportlet {
doExtractConf(handler);
- for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ int total = count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
List<Group> groups;
if (StringUtils.isBlank(this.conf.getMatchingCond())) {
groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
@@ -312,6 +325,8 @@ public class GroupReportlet extends AbstractReportlet {
}
doExtract(handler, groups);
+
+ status.set("Processed " + total + " groups: page " + page + " of " + pages);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
index 25da1f4..da358fd 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReconciliationReportlet.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -49,7 +50,6 @@ import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
@@ -366,25 +366,13 @@ public class ReconciliationReportlet extends AbstractReportlet {
}
}
- private void doExtract(
- final ContentHandler handler, final int count, final SearchCond cond, final AnyTypeKind anyTypeKind)
+ @Override
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
throws SAXException {
- for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
- List<AnyObject> anys = searchDAO.search(
- SyncopeConstants.FULL_ADMIN_REALMS,
- cond,
- page,
- PAGE_SIZE,
- Collections.<OrderByClause>emptyList(),
- anyTypeKind);
-
- doExtract(handler, anys);
- }
- }
-
- @Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
if (conf instanceof ReconciliationReportletConf) {
this.conf = ReconciliationReportletConf.class.cast(conf);
} else {
@@ -394,39 +382,81 @@ public class ReconciliationReportlet extends AbstractReportlet {
AttributesImpl atts = new AttributesImpl();
if (StringUtils.isBlank(this.conf.getUserMatchingCond())) {
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
+ int total = userDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
- for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
}
} else {
SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.USER);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.USER));
+ }
}
handler.endElement("", "", getAnyElementName(AnyTypeKind.USER) + "s");
atts.clear();
if (StringUtils.isBlank(this.conf.getGroupMatchingCond())) {
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
+ int total = groupDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
- for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
}
} else {
SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.GROUP);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " groups in " + pages + " pages");
+
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.GROUP);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " groups: page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.GROUP));
+ }
}
handler.endElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s");
@@ -440,14 +470,28 @@ public class ReconciliationReportlet extends AbstractReportlet {
SearchCond.getLeafCond(anyTypeCond),
SearchCondConverter.convert(this.conf.getAnyObjectMatchingCond()));
- int count = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+ int total = searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.ANY_OBJECT);
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " any objects " + anyType.getKey() + " in " + pages + " pages");
atts.clear();
atts.addAttribute("", "", "type", ReportXMLConst.XSD_STRING, anyType.getKey());
- atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(count));
+ atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(total));
handler.startElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s", atts);
- doExtract(handler, count, cond, AnyTypeKind.ANY_OBJECT);
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " any objects " + anyType.getKey()
+ + ": page " + page + " of " + pages);
+
+ doExtract(handler, searchDAO.search(
+ SyncopeConstants.FULL_ADMIN_REALMS,
+ cond,
+ page,
+ PAGE_SIZE,
+ Collections.<OrderByClause>emptyList(),
+ AnyTypeKind.ANY_OBJECT));
+ }
handler.endElement("", "", getAnyElementName(AnyTypeKind.ANY_OBJECT) + "s");
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
index b05c507..be42b1b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -18,12 +18,14 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +54,11 @@ public class ReportJob extends AbstractInterruptableJob {
}
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
deleted file mode 100644
index 7a9cfc7..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJobDelegate.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.job.report;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.Optional;
-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.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
-import org.apache.syncope.core.persistence.api.dao.ReportDAO;
-import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.Report;
-import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.apache.syncope.core.spring.ImplementationManager;
-import org.quartz.JobExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import org.xml.sax.helpers.AttributesImpl;
-
-@Component
-public class ReportJobDelegate {
-
- private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
-
- /**
- * Report DAO.
- */
- @Autowired
- private ReportDAO reportDAO;
-
- /**
- * Report execution DAO.
- */
- @Autowired
- private ReportExecDAO reportExecDAO;
-
- @Autowired
- private EntityFactory entityFactory;
-
- @Transactional
- public void execute(final String reportKey) throws JobExecutionException {
- Report report = reportDAO.find(reportKey);
- if (report == null) {
- throw new JobExecutionException("Report " + reportKey + " not found");
- }
-
- if (!report.isActive()) {
- LOG.info("Report {} not active, aborting...", reportKey);
- return;
- }
-
- // 1. create execution
- ReportExec execution = entityFactory.newEntity(ReportExec.class);
- execution.setStatus(ReportExecStatus.STARTED);
- execution.setStart(new Date());
- execution.setReport(report);
- execution = reportExecDAO.save(execution);
-
- report.add(execution);
- report = reportDAO.save(report);
-
- // 2. define a SAX handler for generating result as XML
- TransformerHandler handler;
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream zos = new ZipOutputStream(baos);
- zos.setLevel(Deflater.BEST_COMPRESSION);
- try {
- SAXTransformerFactory tFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
- tFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
- handler = tFactory.newTransformerHandler();
- Transformer serializer = handler.getTransformer();
- serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
- serializer.setOutputProperty(OutputKeys.INDENT, "yes");
-
- // a single ZipEntry in the ZipOutputStream
- zos.putNextEntry(new ZipEntry(report.getName()));
-
- // streaming SAX handler in a compressed byte array stream
- handler.setResult(new StreamResult(zos));
- } catch (Exception e) {
- throw new JobExecutionException("While configuring for SAX generation", e, true);
- }
-
- execution.setStatus(ReportExecStatus.RUNNING);
- execution = reportExecDAO.save(execution);
-
- // 3. actual report execution
- StringBuilder reportExecutionMessage = new StringBuilder();
- try {
- // report header
- handler.startDocument();
- AttributesImpl atts = new AttributesImpl();
- atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, report.getName());
- handler.startElement("", "", ReportXMLConst.ELEMENT_REPORT, atts);
-
- // iterate over reportlet instances defined for this report
- for (Implementation impl : report.getReportlets()) {
- Optional<Reportlet> reportlet = ImplementationManager.buildReportlet(impl);
- if (reportlet.isPresent()) {
- try {
- reportlet.get().extract(handler);
- } catch (Throwable t) {
- LOG.error("While executing reportlet {} for report {}", reportlet, reportKey, t);
-
- execution.setStatus(ReportExecStatus.FAILURE);
-
- Throwable effective = t instanceof ReportException
- ? t.getCause()
- : t;
- reportExecutionMessage.
- append(ExceptionUtils2.getFullStackTrace(effective)).
- append("\n==================\n");
- }
- }
- }
-
- // report footer
- handler.endElement("", "", ReportXMLConst.ELEMENT_REPORT);
- handler.endDocument();
-
- if (!ReportExecStatus.FAILURE.name().equals(execution.getStatus())) {
- execution.setStatus(ReportExecStatus.SUCCESS);
- }
- } catch (Exception e) {
- execution.setStatus(ReportExecStatus.FAILURE);
- reportExecutionMessage.append(ExceptionUtils2.getFullStackTrace(e));
-
- throw new JobExecutionException(e, true);
- } finally {
- try {
- zos.closeEntry();
- zos.close();
- baos.close();
- } catch (IOException e) {
- LOG.error("While closing StreamResult's backend", e);
- }
-
- execution.setExecResult(baos.toByteArray());
- execution.setMessage(reportExecutionMessage.toString());
- execution.setEnd(new Date());
- reportExecDAO.save(execution);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
index 1156d25..49e5e2a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/StaticReportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.job.report;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.apache.syncope.common.lib.report.StaticReportletConf;
@@ -70,7 +71,12 @@ public class StaticReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof StaticReportletConf) {
this.conf = StaticReportletConf.class.cast(conf);
} else {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
index 3f9384b..8d51e4a 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/UserReportlet.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.EntityTOUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -355,7 +356,12 @@ public class UserReportlet extends AbstractReportlet {
}
@Override
- protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+ protected void doExtract(
+ final ReportletConf conf,
+ final ContentHandler handler,
+ final AtomicReference<String> status)
+ throws SAXException {
+
if (conf instanceof UserReportletConf) {
this.conf = UserReportletConf.class.cast(conf);
} else {
@@ -364,7 +370,14 @@ public class UserReportlet extends AbstractReportlet {
doExtractConf(handler);
- for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ int total = count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
List<User> users;
if (StringUtils.isBlank(this.conf.getMatchingCond())) {
users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index b22d175..3e12970 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.collections.IteratorChain;
import org.apache.syncope.common.lib.to.ExecTO;
@@ -179,7 +180,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
final PropagationTask task,
final ConnectorObject beforeObj,
final Connector connector,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
// set of attributes to be propagated
Set<Attribute> attributes = new HashSet<>(task.getAttributes());
@@ -270,7 +271,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
final PropagationTask task,
final ConnectorObject beforeObj,
final Connector connector,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result;
if (beforeObj == null) {
@@ -363,7 +364,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
String failureReason = null;
// Flag to state whether any propagation has been attempted
- Boolean[] propagationAttempted = new Boolean[] { false };
+ AtomicReference<Boolean> propagationAttempted = new AtomicReference<>(false);
ConnectorObject beforeObj = null;
ConnectorObject afterObj = null;
@@ -402,7 +403,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
default:
}
- execution.setStatus(propagationAttempted[0]
+ execution.setStatus(propagationAttempted.get()
? PropagationTaskExecStatus.SUCCESS.name()
: PropagationTaskExecStatus.NOT_ATTEMPTED.name());
@@ -434,7 +435,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
LOG.error("While executing KO action on {}", execution, wft);
}
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
actions.forEach(action -> {
action.onError(task, execution, e);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 0a74cb3..38a7145 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -130,6 +130,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
}
doHandle(delta, provision);
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
LOG.debug("Successfully handled {}", delta);
@@ -159,6 +160,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
LOG.warn("Ignoring during pull", e);
executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken());
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
return true;
} catch (JobExecutionException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index c274dc6..6ff95fc 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -89,6 +89,7 @@ public class DefaultRealmPullResultHandler
}
doHandle(delta, orgUnit);
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
LOG.debug("Successfully handled {}", delta);
@@ -117,6 +118,7 @@ public class DefaultRealmPullResultHandler
LOG.warn("Ignoring during pull", e);
executor.setLatestSyncToken(delta.getObjectClass(), delta.getToken());
+ executor.reportHandled(delta.getObjectClass(), delta.getObject().getName());
return true;
} catch (JobExecutionException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index 63e02de..e1fd52c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.collections.IteratorChain;
import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -52,6 +53,7 @@ import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.SyncToken;
@@ -74,6 +76,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
protected final Map<ObjectClass, SyncToken> latestSyncTokens = new HashMap<>();
+ protected final Map<ObjectClass, MutablePair<Integer, String>> handled = new HashMap<>();
+
protected ProvisioningProfile<PullTask, PullActions> profile;
protected RealmPullResultHandler rhandler;
@@ -89,6 +93,34 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
latestSyncTokens.put(objectClass, latestSyncToken);
}
+ @Override
+ public void reportHandled(final ObjectClass objectClass, final Name name) {
+ MutablePair<Integer, String> pair = handled.get(objectClass);
+ if (pair == null) {
+ pair = MutablePair.of(0, null);
+ handled.put(objectClass, pair);
+ }
+ pair.setLeft(pair.getLeft() + 1);
+ pair.setRight(name.getNameValue());
+ }
+
+ @Override
+ public String currentStatus() {
+ synchronized (status) {
+ if (!handled.isEmpty()) {
+ StringBuilder builder = new StringBuilder("Processed:\n");
+ handled.forEach((key, value) -> {
+ builder.append(' ').append(value.getLeft()).append('\t').
+ append(key.getObjectClassValue()).
+ append("\t/ latest: ").append(value.getRight()).
+ append('\n');
+ });
+ status.set(builder.toString());
+ }
+ }
+ return status.get();
+ }
+
protected void setGroupOwners(final GroupPullResultHandler ghandler) {
ghandler.getGroupOwnerMap().entrySet().stream().map(entry -> {
Group group = groupDAO.find(entry.getKey());
@@ -193,8 +225,12 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
}
}
+ status.set("Initialization completed");
+
// First realms...
if (pullTask.getResource().getOrgUnit() != null) {
+ status.set("Pulling " + pullTask.getResource().getOrgUnit().getObjectClass().getObjectClassValue());
+
OrgUnit orgUnit = pullTask.getResource().getOrgUnit();
OperationOptions options = MappingUtils.buildOperationOptions(
MappingUtils.getPullItems(orgUnit.getItems()).iterator());
@@ -248,6 +284,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
for (Provision provision : pullTask.getResource().getProvisions()) {
if (provision.getMapping() != null) {
+ status.set("Pulling " + provision.getObjectClass().getObjectClassValue());
+
SyncopePullResultHandler handler;
switch (provision.getAnyType().getKind()) {
case USER:
@@ -322,6 +360,8 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
}
}
+ status.set("Pull done");
+
String result = createReport(profile.getResults(), pullTask.getResource(), dryRun);
LOG.debug("Pull result: {}", result);
return result;
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index b1a8b07..1967487 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -20,9 +20,13 @@ package org.apache.syncope.core.provisioning.java.pushpull;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.MutablePair;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -36,10 +40,13 @@ import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.task.PushTask;
import org.apache.syncope.core.persistence.api.entity.task.PushTaskAnyFilter;
+import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
@@ -81,6 +88,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
protected ProvisioningProfile<PushTask, PushActions> profile;
+ protected final Map<String, MutablePair<Integer, String>> handled = new HashMap<>();
+
protected RealmPushResultHandler rhandler;
protected AnyObjectPushResultHandler ahandler;
@@ -89,6 +98,33 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
protected GroupPushResultHandler ghandler;
+ protected void reportHandled(final String anyType, final String key) {
+ MutablePair<Integer, String> pair = handled.get(anyType);
+ if (pair == null) {
+ pair = MutablePair.of(0, null);
+ handled.put(anyType, pair);
+ }
+ pair.setLeft(pair.getLeft() + 1);
+ pair.setRight(key);
+ }
+
+ @Override
+ public String currentStatus() {
+ synchronized (status) {
+ if (!handled.isEmpty()) {
+ StringBuilder builder = new StringBuilder("Processed:\n");
+ handled.forEach((key, value) -> {
+ builder.append(' ').append(value.getLeft()).append('\t').
+ append(key).
+ append("\t/ latest: ").append(value.getRight()).
+ append('\n');
+ });
+ status.set(builder.toString());
+ }
+ }
+ return status.get();
+ }
+
protected AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) {
AnyDAO<?> result;
switch (anyTypeKind) {
@@ -117,6 +153,13 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
for (Any<?> any : anys) {
try {
handler.handle(any.getKey());
+ reportHandled(
+ any.getType().getKey(),
+ (any instanceof User
+ ? ((User) any).getUsername()
+ : any instanceof Group
+ ? ((Group) any).getName()
+ : ((AnyObject) any).getName()));
} catch (Exception e) {
LOG.warn("Failure pushing '{}' on '{}'", any, resource, e);
throw new JobExecutionException("While pushing " + any + " on " + resource, e);
@@ -184,8 +227,12 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
}
}
+ status.set("Initialization completed");
+
// First realms...
if (pushTask.getResource().getOrgUnit() != null) {
+ status.set("Pushing realms");
+
rhandler = buildRealmHandler();
for (Realm realm : realmDAO.findDescendants(profile.getTask().getSourceRealm())) {
@@ -193,6 +240,7 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
if (realm.getParent() != null) {
try {
rhandler.handle(realm.getKey());
+ reportHandled(SyncopeConstants.REALM_ANYTYPE, realm.getName());
} catch (Exception e) {
LOG.warn("Failure pushing '{}' on '{}'", realm, pushTask.getResource(), e);
throw new JobExecutionException("While pushing " + realm + " on " + pushTask.getResource(), e);
@@ -208,6 +256,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
for (Provision provision : pushTask.getResource().getProvisions()) {
if (provision.getMapping() != null) {
+ status.set("Pushing " + provision.getAnyType().getKey());
+
AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind());
SyncopePushResultHandler handler;
@@ -255,6 +305,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
}
}
+ status.set("Push done");
+
String result = createReport(profile.getResults(), pushTask.getResource(), dryRun);
LOG.debug("Push result: {}", result);
return result;
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
index aadbaf3..81ef47c 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
@@ -71,6 +71,11 @@ public abstract class AbstractExecutableService extends AbstractServiceImpl impl
}
@Override
+ public JobTO getJob(final String key) {
+ return getExecutableLogic().getJob(key);
+ }
+
+ @Override
public List<JobTO> listJobs() {
return getExecutableLogic().listJobs();
}
[02/11] syncope git commit: Adding pagination support to sample
groovy script for usage with Scripted REST connector
Posted by il...@apache.org.
Adding pagination support to sample groovy script for usage with Scripted REST connector
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/990a4651
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/990a4651
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/990a4651
Branch: refs/heads/2_0_X
Commit: 990a4651596a30eab683e5051149b600e5973439
Parents: 3e2ebb9
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 1 17:21:15 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Mar 1 17:21:15 2018 +0100
----------------------------------------------------------------------
.../src/test/resources/rest/SearchScript.groovy | 40 ++++++++++++++++++--
1 file changed, 37 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/990a4651/fit/core-reference/src/test/resources/rest/SearchScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rest/SearchScript.groovy b/fit/core-reference/src/test/resources/rest/SearchScript.groovy
index 118da8a..ba7a2e8 100644
--- a/fit/core-reference/src/test/resources/rest/SearchScript.groovy
+++ b/fit/core-reference/src/test/resources/rest/SearchScript.groovy
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode
import javax.ws.rs.core.Response
import org.apache.cxf.jaxrs.client.WebClient
import org.identityconnectors.common.security.GuardedString
+import org.identityconnectors.framework.common.objects.OperationOptions;
// Parameters:
// The connector sends the following:
@@ -66,6 +67,24 @@ def buildConnectorObject(node) {
log.info("Entering " + action + " Script");
+// ----------------
+// Manage pagination
+// ----------------
+def offset = options[OperationOptions.OP_PAGED_RESULTS_COOKIE] == null
+? 0
+: options[OperationOptions.OP_PAGED_RESULTS_COOKIE].toInteger();
+
+def pageSize = options[OperationOptions.OP_PAGE_SIZE] == null
+? 100
+: options[OperationOptions.OP_PAGE_SIZE].toInteger();
+
+def limit = offset + pageSize;
+
+log.ok("pagedResultsCookie: " + offset);
+log.ok("pageSize: " + pageSize);
+log.ok("limit: " + limit);
+// ----------------
+
WebClient webClient = client;
ObjectMapper mapper = new ObjectMapper();
@@ -76,10 +95,11 @@ case "__ACCOUNT__":
if (query == null || (!query.get("left").equals("__UID__") && !query.get("conditionType").equals("EQUALS"))) {
webClient.path("/users");
Response response = webClient.get();
- ArrayNode node = mapper.readTree(response.getEntity());
+ ArrayNode nodes = mapper.readTree(response.getEntity());
- for (i = 0; i < node.size(); i++) {
- result.add(buildConnectorObject(node.get(i)));
+ // beware: this is not enforcing any server-side pagination feature
+ for (i = offset; i < (limit < nodes.size() ? limit: nodes.size()); i++) {
+ result.add(buildConnectorObject(nodes.get(i)));
}
} else {
webClient.path("/users/" + query.get("right"));
@@ -98,4 +118,18 @@ default:
result;
}
+// ----------------
+// Return paged result cookie
+// ----------------
+def pagedResultCookieLine = [:]
+if (pageSize > result.size()) {
+ // no more results
+ pagedResultCookieLine.put(OperationOptions.OP_PAGED_RESULTS_COOKIE, null);
+} else {
+ pagedResultCookieLine.put(OperationOptions.OP_PAGED_RESULTS_COOKIE, "" + limit);
+}
+
+result.add(pagedResultCookieLine);
+// ----------------
+
return result;
[04/11] syncope git commit: Upgrading groovy
Posted by il...@apache.org.
Upgrading groovy
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/dffa0869
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/dffa0869
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/dffa0869
Branch: refs/heads/master
Commit: dffa08696cf244eb83210c2a5ac9405ed94b2f09
Parents: 2d2702a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 2 09:09:55 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 09:09:55 2018 +0100
----------------------------------------------------------------------
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/dffa0869/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0844233..522db78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -382,7 +382,7 @@ under the License.
<cocoon.version>3.0.0-alpha-3</cocoon.version>
- <groovy.version>2.5.0-beta-2</groovy.version>
+ <groovy.version>2.5.0-beta-3</groovy.version>
<flowable.version>6.2.1</flowable.version>
[09/11] syncope git commit: Adding pagination support to sample
groovy script for usage with Scripted REST connector
Posted by il...@apache.org.
Adding pagination support to sample groovy script for usage with Scripted REST connector
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/4f6fa1a2
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/4f6fa1a2
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/4f6fa1a2
Branch: refs/heads/master
Commit: 4f6fa1a252f399b225df4c7a73c66201b1ffbd51
Parents: e073cc4
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 1 17:21:15 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 12:13:41 2018 +0100
----------------------------------------------------------------------
.../src/test/resources/rest/SearchScript.groovy | 40 ++++++++++++++++++--
1 file changed, 37 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/4f6fa1a2/fit/core-reference/src/test/resources/rest/SearchScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rest/SearchScript.groovy b/fit/core-reference/src/test/resources/rest/SearchScript.groovy
index 118da8a..ba7a2e8 100644
--- a/fit/core-reference/src/test/resources/rest/SearchScript.groovy
+++ b/fit/core-reference/src/test/resources/rest/SearchScript.groovy
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode
import javax.ws.rs.core.Response
import org.apache.cxf.jaxrs.client.WebClient
import org.identityconnectors.common.security.GuardedString
+import org.identityconnectors.framework.common.objects.OperationOptions;
// Parameters:
// The connector sends the following:
@@ -66,6 +67,24 @@ def buildConnectorObject(node) {
log.info("Entering " + action + " Script");
+// ----------------
+// Manage pagination
+// ----------------
+def offset = options[OperationOptions.OP_PAGED_RESULTS_COOKIE] == null
+? 0
+: options[OperationOptions.OP_PAGED_RESULTS_COOKIE].toInteger();
+
+def pageSize = options[OperationOptions.OP_PAGE_SIZE] == null
+? 100
+: options[OperationOptions.OP_PAGE_SIZE].toInteger();
+
+def limit = offset + pageSize;
+
+log.ok("pagedResultsCookie: " + offset);
+log.ok("pageSize: " + pageSize);
+log.ok("limit: " + limit);
+// ----------------
+
WebClient webClient = client;
ObjectMapper mapper = new ObjectMapper();
@@ -76,10 +95,11 @@ case "__ACCOUNT__":
if (query == null || (!query.get("left").equals("__UID__") && !query.get("conditionType").equals("EQUALS"))) {
webClient.path("/users");
Response response = webClient.get();
- ArrayNode node = mapper.readTree(response.getEntity());
+ ArrayNode nodes = mapper.readTree(response.getEntity());
- for (i = 0; i < node.size(); i++) {
- result.add(buildConnectorObject(node.get(i)));
+ // beware: this is not enforcing any server-side pagination feature
+ for (i = offset; i < (limit < nodes.size() ? limit: nodes.size()); i++) {
+ result.add(buildConnectorObject(nodes.get(i)));
}
} else {
webClient.path("/users/" + query.get("right"));
@@ -98,4 +118,18 @@ default:
result;
}
+// ----------------
+// Return paged result cookie
+// ----------------
+def pagedResultCookieLine = [:]
+if (pageSize > result.size()) {
+ // no more results
+ pagedResultCookieLine.put(OperationOptions.OP_PAGED_RESULTS_COOKIE, null);
+} else {
+ pagedResultCookieLine.put(OperationOptions.OP_PAGED_RESULTS_COOKIE, "" + limit);
+}
+
+result.add(pagedResultCookieLine);
+// ----------------
+
return result;
[11/11] syncope git commit: [SYNCOPE-1279] Now providing runtime
status updates from running Tasks and Reports
Posted by il...@apache.org.
[SYNCOPE-1279] Now providing runtime status updates from running Tasks and Reports
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/772206a4
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/772206a4
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/772206a4
Branch: refs/heads/master
Commit: 772206a4cb0cadb5ace021e3608d5fcd5b78f505
Parents: 4f6fa1a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Mar 2 11:45:34 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 12:47:00 2018 +0100
----------------------------------------------------------------------
.../console/reports/ReportDirectoryPanel.java | 56 ++++
.../console/rest/AnyObjectRestClient.java | 2 -
.../client/console/rest/ReportRestClient.java | 4 +
.../client/console/rest/TaskRestClient.java | 4 +
.../tasks/ProvisioningTaskDirectoryPanel.java | 61 ++++
.../client/console/widgets/JobActionPanel.java | 23 +-
.../client/console/widgets/JobWidget.java | 4 +-
.../META-INF/resources/css/syncopeConsole.css | 6 +-
.../client/console/widgets/JobActionPanel.html | 2 +
.../org/apache/syncope/common/lib/to/JobTO.java | 11 +
.../rest/api/service/ExecutableService.java | 11 +
.../core/logic/AbstractExecutableLogic.java | 2 +
.../syncope/core/logic/AbstractJobLogic.java | 63 ++--
.../apache/syncope/core/logic/ReportLogic.java | 25 ++
.../apache/syncope/core/logic/TaskLogic.java | 27 +-
.../core/persistence/api/dao/Reportlet.java | 4 +-
.../core/provisioning/api/Connector.java | 7 +-
.../core/provisioning/api/job/JobDelegate.java | 27 ++
.../api/job/SchedTaskJobDelegate.java | 2 +-
.../api/job/report/ReportJobDelegate.java | 27 ++
.../notification/NotificationJobDelegate.java | 31 ++
.../api/pushpull/SyncopePullExecutor.java | 3 +
.../provisioning/java/ConnectorFacadeProxy.java | 13 +-
.../java/job/AbstractInterruptableJob.java | 21 +-
.../java/job/AbstractSchedTaskJobDelegate.java | 12 +
.../GroupMemberProvisionTaskJobDelegate.java | 28 +-
.../java/job/IdentityRecertification.java | 9 +-
.../core/provisioning/java/job/TaskJob.java | 21 +-
.../DefaultNotificationJobDelegate.java | 296 +++++++++++++++++++
.../java/job/notification/NotificationJob.java | 7 +
.../notification/NotificationJobDelegate.java | 278 -----------------
.../java/job/report/AbstractReportlet.java | 8 +-
.../java/job/report/AuditReportlet.java | 16 +-
.../job/report/DefaultReportJobDelegate.java | 196 ++++++++++++
.../java/job/report/GroupReportlet.java | 19 +-
.../job/report/ReconciliationReportlet.java | 106 +++++--
.../provisioning/java/job/report/ReportJob.java | 7 +
.../java/job/report/ReportJobDelegate.java | 177 -----------
.../java/job/report/StaticReportlet.java | 8 +-
.../java/job/report/UserReportlet.java | 17 +-
.../AbstractPropagationTaskExecutor.java | 11 +-
.../pushpull/AbstractPullResultHandler.java | 2 +
.../pushpull/DefaultRealmPullResultHandler.java | 2 +
.../java/pushpull/PullJobDelegate.java | 40 +++
.../java/pushpull/PushJobDelegate.java | 52 ++++
.../cxf/service/AbstractExecutableService.java | 5 +
46 files changed, 1191 insertions(+), 562 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
index 4d1c1c2..21b1563 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
@@ -34,27 +34,36 @@ import org.apache.syncope.client.console.pages.BasePage;
import org.apache.syncope.client.console.panels.DirectoryPanel;
import org.apache.syncope.client.console.panels.MultilevelPanel;
import org.apache.syncope.client.console.rest.ReportRestClient;
+import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.widgets.JobActionPanel;
import org.apache.syncope.client.console.wizards.AjaxWizard;
import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ReportTO;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.util.time.Duration;
/**
* Reports page.
@@ -76,6 +85,17 @@ public abstract class ReportDirectoryPanel
modal.size(Modal.Size.Large);
initResultTable();
+ container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+ private static final long serialVersionUID = -4661303265651934868L;
+
+ @Override
+ protected void onTimer(final AjaxRequestTarget target) {
+ container.modelChanged();
+ target.add(container);
+ }
+ });
+
startAt = new ReportStartAtTogglePanel(container, pageRef);
addInnerObject(startAt);
}
@@ -105,10 +125,46 @@ public abstract class ReportDirectoryPanel
columns.add(new BooleanPropertyColumn<>(
new StringResourceModel("active", this), "active", "active"));
+ columns.add(new AbstractColumn<ReportTO, String>(new Model<>(""), "running") {
+
+ private static final long serialVersionUID = 4209532514416998046L;
+
+ @Override
+ public void populateItem(
+ final Item<ICellPopulator<ReportTO>> cellItem,
+ final String componentId,
+ final IModel<ReportTO> rowModel) {
+
+ JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
+ JobActionPanel panel = new JobActionPanel(
+ componentId, jobTO, false, ReportDirectoryPanel.this, pageRef);
+ MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
+ String.format("%s,%s",
+ StandardEntitlement.TASK_EXECUTE,
+ StandardEntitlement.TASK_UPDATE));
+ cellItem.add(panel);
+ }
+
+ @Override
+ public String getCssClass() {
+ return "col-xs-1";
+ }
+ });
+
return columns;
}
@Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof JobActionPanel.JobActionPayload) {
+ container.modelChanged();
+ JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container);
+ } else {
+ super.onEvent(event);
+ }
+ }
+
+ @Override
public ActionsPanel<ReportTO> getActions(final IModel<ReportTO> model) {
final ActionsPanel<ReportTO> panel = super.getActions(model);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
index 31cd3a8..a885764 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyObjectRestClient.java
@@ -18,8 +18,6 @@
*/
package org.apache.syncope.client.console.rest;
-import static org.apache.syncope.client.console.rest.BaseRestClient.getService;
-
import java.util.List;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
index 0c96627..e65ce7e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
@@ -54,6 +54,10 @@ public class ReportRestClient extends BaseRestClient
return getService(ReportService.class).list();
}
+ public JobTO getJob(final String key) {
+ return getService(ReportService.class).getJob(key);
+ }
+
public List<JobTO> listJobs() {
return getService(ReportService.class).listJobs();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
index d184085..723ae2e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
@@ -47,6 +47,10 @@ public class TaskRestClient extends BaseRestClient implements ExecutionRestClien
private static final long serialVersionUID = 6284485820911028843L;
+ public JobTO getJob(final String key) {
+ return getService(TaskService.class).getJob(key);
+ }
+
public List<JobTO> listJobs() {
return getService(TaskService.class).listJobs();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
index 07c2ef3..f7f0b2e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java
@@ -23,18 +23,32 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.widgets.JobActionPanel;
+import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ProvisioningTaskTO;
import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.util.time.Duration;
/**
* Tasks page.
@@ -63,6 +77,17 @@ public abstract class ProvisioningTaskDirectoryPanel<T extends ProvisioningTaskT
// super in order to call the parent implementation
super.initResultTable();
+
+ container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+ private static final long serialVersionUID = -4661303265651934868L;
+
+ @Override
+ protected void onTimer(final AjaxRequestTarget target) {
+ container.modelChanged();
+ target.add(container);
+ }
+ });
}
@Override
@@ -103,9 +128,45 @@ public abstract class ProvisioningTaskDirectoryPanel<T extends ProvisioningTaskT
columns.add(new BooleanPropertyColumn<>(
new StringResourceModel("active", this), "active", "active"));
+ columns.add(new AbstractColumn<T, String>(new Model<>(""), "running") {
+
+ private static final long serialVersionUID = -4008579357070833846L;
+
+ @Override
+ public void populateItem(
+ final Item<ICellPopulator<T>> cellItem,
+ final String componentId,
+ final IModel<T> rowModel) {
+
+ JobTO jobTO = restClient.getJob(rowModel.getObject().getKey());
+ JobActionPanel panel = new JobActionPanel(
+ componentId, jobTO, false, ProvisioningTaskDirectoryPanel.this, pageRef);
+ MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
+ String.format("%s,%s",
+ StandardEntitlement.TASK_EXECUTE,
+ StandardEntitlement.TASK_UPDATE));
+ cellItem.add(panel);
+ }
+
+ @Override
+ public String getCssClass() {
+ return "col-xs-1";
+ }
+ });
+
return columns;
}
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof JobActionPanel.JobActionPayload) {
+ container.modelChanged();
+ JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container);
+ } else {
+ super.onEvent(event);
+ }
+ }
+
protected class ProvisioningTasksProvider<T extends ProvisioningTaskTO> extends SchedTasksProvider<T> {
private static final long serialVersionUID = 4725679400450513556L;
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
index 82665f8..5e03e2d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobActionPanel.java
@@ -18,6 +18,9 @@
*/
package org.apache.syncope.client.console.widgets;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverBehavior;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -30,10 +33,13 @@ import org.apache.syncope.client.console.wicket.ajax.markup.html.IndicatorAjaxLi
import org.apache.syncope.client.console.wizards.WizardMgtPanel;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,14 +58,21 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
public JobActionPanel(
final String id,
final JobTO jobTO,
- final JobWidget widget,
+ final boolean showNotRunning,
+ final Component container,
final PageReference pageRef) {
+
super(id, true);
setOutputMarkupId(true);
Fragment controls;
if (jobTO.isRunning()) {
controls = new Fragment("controls", "runningFragment", this);
+ controls.add(new Label("status", Model.of()).add(new PopoverBehavior(
+ Model.<String>of(),
+ Model.of("<pre>" + (jobTO.getStatus() == null ? StringUtils.EMPTY : jobTO.getStatus()) + "</pre>"),
+ new PopoverConfig().withAnimation(true).withHoverTrigger().withHtml(true).
+ withPlacement(TooltipConfig.Placement.left))));
controls.add(new IndicatorAjaxLink<Void>("stop") {
private static final long serialVersionUID = -7978723352517770644L;
@@ -83,7 +96,7 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
default:
}
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
- send(widget, Broadcast.EXACT, new JobActionPayload(target));
+ send(container, Broadcast.EXACT, new JobActionPayload(target));
} catch (Exception e) {
LOG.error("While stopping {}", jobTO.getRefDesc(), e);
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName()
@@ -117,7 +130,7 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
default:
}
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
- send(widget, Broadcast.EXACT, new JobActionPayload(target));
+ send(container, Broadcast.EXACT, new JobActionPayload(target));
} catch (Exception e) {
LOG.error("While starting {}", jobTO.getRefDesc(), e);
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName()
@@ -126,6 +139,10 @@ public class JobActionPanel extends WizardMgtPanel<Serializable> {
((BasePage) getPage()).getNotificationPanel().refresh(target);
}
});
+ if (!showNotRunning) {
+ controls.setOutputMarkupPlaceholderTag(true);
+ controls.setVisible(false);
+ }
}
addInnerObject(controls);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
index 5441b13..8575079 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
@@ -357,9 +357,9 @@ public class JobWidget extends BaseWidget {
final IModel<JobTO> rowModel) {
JobTO jobTO = rowModel.getObject();
- JobActionPanel panel = new JobActionPanel(componentId, jobTO, JobWidget.this, pageRef);
+ JobActionPanel panel = new JobActionPanel(componentId, jobTO, true, JobWidget.this, pageRef);
MetaDataRoleAuthorizationStrategy.authorize(panel, WebPage.ENABLE,
- String.format("%s,%s%s,%s",
+ String.format("%s,%s,%s,%s",
StandardEntitlement.TASK_EXECUTE,
StandardEntitlement.REPORT_EXECUTE,
StandardEntitlement.TASK_UPDATE,
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
index 294c978..c2dcf2c 100644
--- a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
+++ b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
@@ -879,6 +879,10 @@ li.todoitem a {
cursor: default;
}
+.popover{
+ max-width: 100%;
+}
+
#popover:hover {
cursor: pointer;
}
@@ -1157,4 +1161,4 @@ div#inline-actions ul.menu i, div#tablehandling ul.menu i {
div#tablehandling ul.menu li a {
padding: 0px !important;
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
index e94f292..8e31f36 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobActionPanel.html
@@ -22,6 +22,8 @@ under the License.
<wicket:fragment wicket:id="runningFragment">
<i id="actionLink" class="fa fa-refresh fa-spin"></i>
+ <div wicket:id="status" class="fa fa-binoculars"/>
+
<a href="#" wicket:id="stop" class="fa fa-stop-circle"></a>
</wicket:fragment>
<wicket:fragment wicket:id="notRunningFragment">
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
index 2cb5690..cee8db2 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/JobTO.java
@@ -42,6 +42,8 @@ public class JobTO extends AbstractBaseBean {
private Date start;
+ private String status;
+
public JobType getType() {
return type;
}
@@ -93,4 +95,13 @@ public class JobTO extends AbstractBaseBean {
? null
: new Date(start.getTime());
}
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(final String status) {
+ this.status = status;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
index 2b09d49..16bc38f 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ExecutableService.java
@@ -101,6 +101,17 @@ public interface ExecutableService extends JAXRSService {
ExecTO execute(@BeanParam ExecuteQuery query);
/**
+ * Returns job (running or scheduled) for the executable matching the given key.
+ *
+ * @param key executable key
+ * @return job (running or scheduled) for the given key
+ */
+ @GET
+ @Path("jobs/{key}")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ JobTO getJob(@PathParam("key") String key);
+
+ /**
* List jobs (running and / or scheduled).
*
* @return jobs (running and / or scheduled)
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
index d1b7cb3..6b34bc7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
@@ -42,6 +42,8 @@ public abstract class AbstractExecutableLogic<T extends AbstractBaseBean> extend
public abstract BulkActionResult deleteExecutions(
String key, Date startedBefore, Date startedAfter, Date endedBefore, Date endedAfter);
+ public abstract JobTO getJob(String key);
+
public abstract List<JobTO> listJobs();
public abstract void actionJob(String key, JobAction action);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
index a93ae2d..844d353 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
@@ -26,11 +26,14 @@ import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.JobAction;
import org.apache.syncope.common.lib.types.JobType;
import org.apache.syncope.core.provisioning.api.job.JobManager;
+import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.matchers.GroupMatcher;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@@ -44,32 +47,54 @@ abstract class AbstractJobLogic<T extends AbstractBaseBean> extends AbstractTran
protected abstract Triple<JobType, String, String> getReference(final JobKey jobKey);
- protected List<JobTO> doListJobs() {
- List<JobTO> jobTOs = new ArrayList<>();
+ protected JobTO getJobTO(final JobKey jobKey) throws SchedulerException {
+ JobTO jobTO = null;
- try {
- for (JobKey jobKey : scheduler.getScheduler().
- getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP))) {
+ Triple<JobType, String, String> reference = getReference(jobKey);
+ if (reference != null) {
+ jobTO = new JobTO();
- JobTO jobTO = new JobTO();
+ jobTO.setType(reference.getLeft());
+ jobTO.setRefKey(reference.getMiddle());
+ jobTO.setRefDesc(reference.getRight());
- Triple<JobType, String, String> reference = getReference(jobKey);
- if (reference != null) {
- jobTOs.add(jobTO);
+ List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
+ if (jobTriggers.isEmpty()) {
+ jobTO.setScheduled(false);
+ } else {
+ jobTO.setScheduled(true);
+ jobTO.setStart(jobTriggers.get(0).getStartTime());
+ }
+
+ jobTO.setRunning(jobManager.isRunning(jobKey));
- jobTO.setType(reference.getLeft());
- jobTO.setRefKey(reference.getMiddle());
- jobTO.setRefDesc(reference.getRight());
+ jobTO.setStatus("UNKNOWN");
+ if (jobTO.isRunning()) {
+ try {
+ Object job = ApplicationContextProvider.getBeanFactory().getBean(jobKey.getName());
+ if (job instanceof AbstractInterruptableJob
+ && ((AbstractInterruptableJob) job).getDelegate() != null) {
- List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
- if (jobTriggers.isEmpty()) {
- jobTO.setScheduled(false);
- } else {
- jobTO.setScheduled(true);
- jobTO.setStart(jobTriggers.get(0).getStartTime());
+ jobTO.setStatus(((AbstractInterruptableJob) job).getDelegate().currentStatus());
}
+ } catch (NoSuchBeanDefinitionException e) {
+ LOG.warn("Could not find job {} implementation", jobKey, e);
+ }
+ }
+ }
+
+ return jobTO;
+ }
+
+ protected List<JobTO> doListJobs() {
+ List<JobTO> jobTOs = new ArrayList<>();
+ try {
+ for (JobKey jobKey : scheduler.getScheduler().
+ getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP))) {
- jobTO.setRunning(jobManager.isRunning(jobKey));
+ JobTO jobTO = getJobTO(jobKey);
+ if (jobTO != null) {
+ jobTOs.add(jobTO);
}
}
} catch (SchedulerException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 534a537..6a5a442 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -64,6 +64,7 @@ import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
import org.apache.syncope.core.provisioning.api.job.JobNamer;
import org.apache.xmlgraphics.util.MimeConstants;
import org.quartz.JobKey;
+import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -368,6 +369,30 @@ public class ReportLogic extends AbstractExecutableLogic<ReportTO> {
return super.doListJobs();
}
+ @PreAuthorize("hasRole('" + StandardEntitlement.REPORT_READ + "')")
+ @Override
+ public JobTO getJob(final String key) {
+ Report report = reportDAO.find(key);
+ if (report == null) {
+ throw new NotFoundException("Report " + key);
+ }
+
+ JobTO jobTO = null;
+ try {
+ jobTO = getJobTO(JobNamer.getJobKey(report));
+ } catch (SchedulerException e) {
+ LOG.error("Problems while retrieving scheduled job {}", JobNamer.getJobKey(report), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ if (jobTO == null) {
+ throw new NotFoundException("Job for report " + key);
+ }
+ return jobTO;
+ }
+
@PreAuthorize("hasRole('" + StandardEntitlement.REPORT_EXECUTE + "')")
@Override
public void actionJob(final String key, final JobAction action) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index a9ac3d3..baad7e8 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -55,10 +55,11 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
import org.apache.syncope.core.persistence.api.dao.ConfDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.java.job.TaskJob;
-import org.apache.syncope.core.provisioning.java.job.notification.NotificationJobDelegate;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
+import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -385,6 +386,30 @@ public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
return super.doListJobs();
}
+ @PreAuthorize("hasRole('" + StandardEntitlement.TASK_READ + "')")
+ @Override
+ public JobTO getJob(final String key) {
+ Task task = taskDAO.find(key);
+ if (task == null) {
+ throw new NotFoundException("Task " + key);
+ }
+
+ JobTO jobTO = null;
+ try {
+ jobTO = getJobTO(JobNamer.getJobKey(task));
+ } catch (SchedulerException e) {
+ LOG.error("Problems while retrieving scheduled job {}", JobNamer.getJobKey(task), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ if (jobTO == null) {
+ throw new NotFoundException("Job for task " + key);
+ }
+ return jobTO;
+ }
+
@PreAuthorize("hasRole('" + StandardEntitlement.TASK_EXECUTE + "')")
@Override
public void actionJob(final String key, final JobAction action) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
index ab05f4a..2fb2a86 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.api.dao;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.report.ReportletConf;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
@@ -41,7 +42,8 @@ public interface Reportlet {
* Actual data extraction for reporting.
*
* @param handler SAX content handler for streaming result
+ * @param status current report status (for job reporting)
* @throws SAXException if there is any problem in SAX handling
*/
- void extract(ContentHandler handler) throws SAXException;
+ void extract(ContentHandler handler, AtomicReference<String> status) throws SAXException;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
index 7b84fc5..a0b21cb 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.api;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -63,7 +64,7 @@ public interface Connector {
ObjectClass objectClass,
Set<Attribute> attrs,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Update user / group on a connector instance.
@@ -80,7 +81,7 @@ public interface Connector {
Uid uid,
Set<Attribute> attrs,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Delete user / group on a connector instance.
@@ -94,7 +95,7 @@ public interface Connector {
ObjectClass objectClass,
Uid uid,
OperationOptions options,
- Boolean[] propagationAttempted);
+ AtomicReference<Boolean> propagationAttempted);
/**
* Fetches all remote objects (for use during full reconciliation).
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
new file mode 100644
index 0000000..3bfa292
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.job;
+
+/**
+ * Implementations of this interface will perform the actual operations required to Quartz's {@link org.quartz.Job}.
+ */
+public interface JobDelegate {
+
+ String currentStatus();
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
index a03f36b..bb69b10 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.provisioning.api.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
-public interface SchedTaskJobDelegate {
+public interface SchedTaskJobDelegate extends JobDelegate {
void execute(String taskKey, boolean dryRun, JobExecutionContext context) throws JobExecutionException;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
new file mode 100644
index 0000000..bbf455f
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/report/ReportJobDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.job.report;
+
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.quartz.JobExecutionException;
+
+public interface ReportJobDelegate extends JobDelegate {
+
+ void execute(String reportKey) throws JobExecutionException;
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
new file mode 100644
index 0000000..3dfcddd
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationJobDelegate.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.notification;
+
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.quartz.JobExecutionException;
+
+public interface NotificationJobDelegate extends JobDelegate {
+
+ TaskExec executeSingle(NotificationTask task);
+
+ void execute() throws JobExecutionException;
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
index ab02282..39eed32 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullExecutor.java
@@ -18,10 +18,13 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
+import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.SyncToken;
public interface SyncopePullExecutor {
void setLatestSyncToken(ObjectClass objectClass, SyncToken latestSyncToken);
+
+ void reportHandled(ObjectClass objectClass, Name name);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index 90645b2..523ecc0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@ -25,6 +25,7 @@ import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.types.ConnectorCapability;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
@@ -162,12 +163,12 @@ public class ConnectorFacadeProxy implements Connector {
final ObjectClass objectClass,
final Set<Attribute> attrs,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result = null;
if (connInstance.getCapabilities().contains(ConnectorCapability.CREATE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.create(connector, objectClass, attrs, options);
try {
@@ -197,12 +198,12 @@ public class ConnectorFacadeProxy implements Connector {
final Uid uid,
final Set<Attribute> attrs,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
Uid result = null;
if (connInstance.getCapabilities().contains(ConnectorCapability.UPDATE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.update(connector, objectClass, uid, attrs, options);
@@ -233,10 +234,10 @@ public class ConnectorFacadeProxy implements Connector {
final ObjectClass objectClass,
final Uid uid,
final OperationOptions options,
- final Boolean[] propagationAttempted) {
+ final AtomicReference<Boolean> propagationAttempted) {
if (connInstance.getCapabilities().contains(ConnectorCapability.DELETE)) {
- propagationAttempted[0] = true;
+ propagationAttempted.set(true);
Future<Uid> future = asyncFacade.delete(connector, objectClass, uid, options);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
index 19bbf1e..86a7f49 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.job;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.quartz.DisallowConcurrentExecution;
@@ -40,13 +41,25 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
*/
private final AtomicReference<Thread> runningThread = new AtomicReference<>();
+ private final JobDelegate embeddedDelegate = new JobDelegate() {
+
+ @Override
+ public String currentStatus() {
+ return "RUNNING THREAD: " + runningThread.get();
+ }
+ };
+
private long interruptMaxRetries = 1;
+ public JobDelegate getDelegate() {
+ return embeddedDelegate;
+ }
+
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
- this.runningThread.set(Thread.currentThread());
+ runningThread.set(Thread.currentThread());
try {
- this.interruptMaxRetries = context.getMergedJobDataMap().getLong(JobManager.INTERRUPT_MAX_RETRIES_KEY);
+ interruptMaxRetries = context.getMergedJobDataMap().getLong(JobManager.INTERRUPT_MAX_RETRIES_KEY);
} catch (Exception e) {
LOG.debug("Could not set {}, defaults to {}", JobManager.INTERRUPT_MAX_RETRIES_KEY, interruptMaxRetries, e);
}
@@ -54,7 +67,7 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
@Override
public void interrupt() throws UnableToInterruptJobException {
- Thread thread = this.runningThread.getAndSet(null);
+ Thread thread = runningThread.getAndSet(null);
if (thread == null) {
LOG.warn("Unable to retrieve the thread of the current job execution");
} else {
@@ -68,7 +81,7 @@ public abstract class AbstractInterruptableJob implements InterruptableJob {
}
// if the thread is still alive, it should be available in the next stop
if (thread.isAlive()) {
- this.runningThread.set(thread);
+ runningThread.set(thread);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
index 5fc5405..7103b7b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
@@ -19,6 +19,7 @@
package org.apache.syncope.core.provisioning.java.job;
import java.util.Date;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.types.AuditElements;
import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
@@ -72,6 +73,13 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
@Autowired
protected AuditManager auditManager;
+ protected final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
@Transactional
@Override
public void execute(final String taskKey, final boolean dryRun, final JobExecutionContext context)
@@ -90,6 +98,8 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
execution.setStart(new Date());
execution.setTask(task);
+ status.set("Initialization completed");
+
AuditElements.Result result;
try {
@@ -110,6 +120,8 @@ public abstract class AbstractSchedTaskJobDelegate implements SchedTaskJobDelega
}
task = taskDAO.save(task);
+ status.set("Done");
+
notificationManager.createTasks(
AuditElements.EventCategoryType.TASK,
this.getClass().getSimpleName(),
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
index 4868bbc..4f1a1eb 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java
@@ -82,20 +82,25 @@ public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDel
}
result.append("provision\n\n");
+ status.set(result.toString());
+
MembershipCond membershipCond = new MembershipCond();
membershipCond.setGroup(groupKey);
List<User> users = searchDAO.search(SearchCond.getLeafCond(membershipCond), AnyTypeKind.USER);
Collection<String> groupResourceKeys = groupDAO.findAllResourceKeys(groupKey);
+ status.set("About to "
+ + (actionType == BulkMembersActionType.DEPROVISION ? "de" : "") + "provision "
+ + users.size() + " users from " + groupResourceKeys);
for (User user : users) {
List<PropagationStatus> statuses = actionType == BulkMembersActionType.DEPROVISION
? userProvisioningManager.deprovision(user.getKey(), groupResourceKeys, false)
: userProvisioningManager.provision(user.getKey(), true, null, groupResourceKeys, false);
- for (PropagationStatus status : statuses) {
+ for (PropagationStatus propagationStatus : statuses) {
result.append("User ").append(user.getKey()).append('\t').
- append("Resource ").append(status.getResource()).append('\t').
- append(status.getStatus());
- if (StringUtils.isNotBlank(status.getFailureReason())) {
- result.append('\n').append(status.getFailureReason()).append('\n');
+ append("Resource ").append(propagationStatus.getResource()).append('\t').
+ append(propagationStatus.getStatus());
+ if (StringUtils.isNotBlank(propagationStatus.getFailureReason())) {
+ result.append('\n').append(propagationStatus.getFailureReason()).append('\n');
}
result.append("\n");
}
@@ -105,17 +110,20 @@ public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDel
membershipCond = new MembershipCond();
membershipCond.setGroup(groupKey);
List<AnyObject> anyObjects = searchDAO.search(SearchCond.getLeafCond(membershipCond), AnyTypeKind.ANY_OBJECT);
+ status.set("About to "
+ + (actionType == BulkMembersActionType.DEPROVISION ? "de" : "") + "provision "
+ + anyObjects.size() + " any objects from " + groupResourceKeys);
for (AnyObject anyObject : anyObjects) {
List<PropagationStatus> statuses = actionType == BulkMembersActionType.DEPROVISION
? anyObjectProvisioningManager.deprovision(anyObject.getKey(), groupResourceKeys, false)
: anyObjectProvisioningManager.provision(anyObject.getKey(), groupResourceKeys, false);
- for (PropagationStatus status : statuses) {
+ for (PropagationStatus propagationStatus : statuses) {
result.append(anyObject.getType().getKey()).append(' ').append(anyObject.getKey()).append('\t').
- append("Resource ").append(status.getResource()).append('\t').
- append(status.getStatus());
- if (StringUtils.isNotBlank(status.getFailureReason())) {
- result.append('\n').append(status.getFailureReason()).append('\n');
+ append("Resource ").append(propagationStatus.getResource()).append('\t').
+ append(propagationStatus.getStatus());
+ if (StringUtils.isNotBlank(propagationStatus.getFailureReason())) {
+ result.append('\n').append(propagationStatus.getFailureReason()).append('\n');
}
result.append("\n");
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
index f9d310f..60a1956 100755
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
@@ -89,8 +89,15 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
return "DRY RUN";
}
+ int total = userDAO.count();
+ int pages = (total / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ status.set("Processing " + total + " users in " + pages + " pages");
+
long now = System.currentTimeMillis();
- for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+ for (int page = 1; page <= pages; page++) {
+ status.set("Processing " + total + " users: page " + page + " of " + pages);
+
for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
LOG.debug("Processing user: {}", user.getUsername());
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
index 6f926cf..c2af5e5 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.job;
import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
@@ -53,6 +54,8 @@ public class TaskJob extends AbstractInterruptableJob {
*/
private String taskKey;
+ private SchedTaskJobDelegate delegate;
+
/**
* Task key setter.
*
@@ -63,6 +66,11 @@ public class TaskJob extends AbstractInterruptableJob {
}
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
@@ -72,16 +80,17 @@ public class TaskJob extends AbstractInterruptableJob {
try {
ImplementationDAO implementationDAO =
ApplicationContextProvider.getApplicationContext().getBean(ImplementationDAO.class);
- Implementation taskJobDelegate = implementationDAO.find(
+ Implementation implementation = implementationDAO.find(
context.getMergedJobDataMap().getString(DELEGATE_IMPLEMENTATION));
- if (taskJobDelegate == null) {
+ if (implementation == null) {
LOG.error("Could not find Implementation '{}', aborting",
context.getMergedJobDataMap().getString(DELEGATE_IMPLEMENTATION));
} else {
- ImplementationManager.<SchedTaskJobDelegate>build(taskJobDelegate).
- execute(taskKey,
- context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY),
- context);
+ delegate = ImplementationManager.<SchedTaskJobDelegate>build(implementation);
+ delegate.execute(
+ taskKey,
+ context.getMergedJobDataMap().getBoolean(DRY_RUN_JOBDETAIL_KEY),
+ context);
}
} catch (Exception e) {
LOG.error("While executing task {}", taskKey, e);
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
new file mode 100644
index 0000000..7ab218b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job.notification;
+
+import java.io.PrintStream;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.LogOutputStream;
+import org.apache.syncope.common.lib.PropertyUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.spring.security.Encryptor;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class DefaultNotificationJobDelegate implements InitializingBean, NotificationJobDelegate {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class);
+
+ @Autowired
+ private TaskDAO taskDAO;
+
+ @Autowired
+ private JavaMailSender mailSender;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Autowired
+ private AuditManager auditManager;
+
+ @Autowired
+ private NotificationManager notificationManager;
+
+ private final AtomicReference<String> status = new AtomicReference<>();
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (mailSender instanceof JavaMailSenderImpl) {
+ JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender;
+
+ Properties javaMailProperties = javaMailSender.getJavaMailProperties();
+
+ Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory").getLeft();
+ for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
+ String prop = (String) e.nextElement();
+ if (prop.startsWith("mail.smtp.")) {
+ javaMailProperties.setProperty(prop, props.getProperty(prop));
+ }
+ }
+
+ if (StringUtils.isNotBlank(javaMailSender.getUsername())) {
+ javaMailProperties.setProperty("mail.smtp.auth", "true");
+ }
+
+ javaMailSender.setJavaMailProperties(javaMailProperties);
+
+ String mailDebug = props.getProperty("mail.debug", "false");
+ if (BooleanUtils.toBoolean(mailDebug)) {
+ Session session = javaMailSender.getSession();
+ session.setDebug(true);
+ session.setDebugOut(new PrintStream(new LogOutputStream(LOG)));
+ }
+ }
+ }
+
+ @Override
+ public String currentStatus() {
+ return status.get();
+ }
+
+ @Transactional
+ @Override
+ public TaskExec executeSingle(final NotificationTask task) {
+ TaskExec execution = entityFactory.newEntity(TaskExec.class);
+ execution.setTask(task);
+ execution.setStart(new Date());
+
+ boolean retryPossible = true;
+
+ if (StringUtils.isBlank(task.getSubject()) || task.getRecipients().isEmpty()
+ || StringUtils.isBlank(task.getHtmlBody()) || StringUtils.isBlank(task.getTextBody())) {
+
+ String message = "Could not fetch all required information for sending e-mails:\n"
+ + task.getRecipients() + "\n"
+ + task.getSender() + "\n"
+ + task.getSubject() + "\n"
+ + task.getHtmlBody() + "\n"
+ + task.getTextBody();
+ LOG.error(message);
+
+ execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+ retryPossible = false;
+
+ if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+ execution.setMessage(message);
+ }
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("About to send e-mails:\n"
+ + task.getRecipients() + "\n"
+ + task.getSender() + "\n"
+ + task.getSubject() + "\n"
+ + task.getHtmlBody() + "\n"
+ + task.getTextBody() + "\n");
+ }
+
+ status.set("Sending notifications to " + task.getRecipients());
+
+ for (String to : task.getRecipients()) {
+ try {
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setTo(to);
+ helper.setFrom(task.getSender());
+ helper.setSubject(task.getSubject());
+ helper.setText(task.getTextBody(), task.getHtmlBody());
+
+ mailSender.send(message);
+
+ execution.setStatus(NotificationJob.Status.SENT.name());
+
+ StringBuilder report = new StringBuilder();
+ switch (task.getTraceLevel()) {
+ case ALL:
+ report.append("FROM: ").append(task.getSender()).append('\n').
+ append("TO: ").append(to).append('\n').
+ append("SUBJECT: ").append(task.getSubject()).append('\n').append('\n').
+ append(task.getTextBody()).append('\n').append('\n').
+ append(task.getHtmlBody()).append('\n');
+ break;
+
+ case SUMMARY:
+ report.append("E-mail sent to ").append(to).append('\n');
+ break;
+
+ case FAILURES:
+ case NONE:
+ default:
+ }
+ if (report.length() > 0) {
+ execution.setMessage(report.toString());
+ }
+
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "send",
+ AuditElements.Result.SUCCESS,
+ null,
+ null,
+ task,
+ "Successfully sent notification to " + to);
+ } catch (Exception e) {
+ LOG.error("Could not send e-mail", e);
+
+ execution.setStatus(NotificationJob.Status.NOT_SENT.name());
+ if (task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) {
+ execution.setMessage(ExceptionUtils2.getFullStackTrace(e));
+ }
+
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "send",
+ AuditElements.Result.FAILURE,
+ null,
+ null,
+ task,
+ "Could not send notification to " + to, e);
+ }
+
+ execution.setEnd(new Date());
+ }
+ }
+
+ if (hasToBeRegistered(execution)) {
+ execution = notificationManager.storeExec(execution);
+ if (retryPossible
+ && (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT)) {
+
+ handleRetries(execution);
+ }
+ } else {
+ notificationManager.setTaskExecuted(execution.getTask().getKey(), true);
+ }
+
+ return execution;
+ }
+
+ @Transactional
+ @Override
+ public void execute() throws JobExecutionException {
+ List<NotificationTask> tasks = taskDAO.<NotificationTask>findToExec(TaskType.NOTIFICATION);
+
+ status.set("Sending out " + tasks.size() + " notifications");
+
+ for (NotificationTask task : tasks) {
+ LOG.debug("Found notification task {} to be executed: starting...", task);
+ executeSingle(task);
+ LOG.debug("Notification task {} executed", task);
+ }
+ }
+
+ private boolean hasToBeRegistered(final TaskExec execution) {
+ NotificationTask task = (NotificationTask) execution.getTask();
+
+ // True if either failed and failures have to be registered, or if ALL
+ // has to be registered.
+ return (NotificationJob.Status.valueOf(execution.getStatus()) == NotificationJob.Status.NOT_SENT
+ && task.getTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || task.getTraceLevel() == TraceLevel.ALL;
+ }
+
+ private void handleRetries(final TaskExec execution) {
+ if (notificationManager.getMaxRetries() <= 0) {
+ return;
+ }
+
+ long failedExecutionsCount = notificationManager.countExecutionsWithStatus(
+ execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name());
+
+ if (failedExecutionsCount <= notificationManager.getMaxRetries()) {
+ LOG.debug("Execution of notification task {} will be retried [{}/{}]",
+ execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries());
+ notificationManager.setTaskExecuted(execution.getTask().getKey(), false);
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "retry",
+ AuditElements.Result.SUCCESS,
+ null,
+ null,
+ execution,
+ "Notification task " + execution.getTask().getKey() + " will be retried");
+ } else {
+ LOG.error("Maximum number of retries reached for task {} - giving up", execution.getTask());
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.TASK,
+ "notification",
+ null,
+ "retry",
+ AuditElements.Result.FAILURE,
+ null,
+ null,
+ execution,
+ "Giving up retries on notification task " + execution.getTask().getKey());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/772206a4/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
index 13d45b0..0137e8f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
@@ -20,6 +20,8 @@ package org.apache.syncope.core.provisioning.java.job.notification;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@@ -54,6 +56,11 @@ public class NotificationJob extends AbstractInterruptableJob {
private NotificationJobDelegate delegate;
@Override
+ public JobDelegate getDelegate() {
+ return delegate;
+ }
+
+ @Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
super.execute(context);
[08/11] syncope git commit: Small improvements to build-tools,
including Swagger
Posted by il...@apache.org.
Small improvements to build-tools, including Swagger
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/e073cc43
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/e073cc43
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/e073cc43
Branch: refs/heads/master
Commit: e073cc43225bbeb4c8ea670750b8f5a0829d6676
Parents: d505627
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 1 15:47:13 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Mar 2 12:13:18 2018 +0100
----------------------------------------------------------------------
fit/build-tools/pom.xml | 16 ++++++++++++-
.../syncope/fit/buildtools/cxf/UserService.java | 3 ++-
.../fit/buildtools/cxf/UserServiceImpl.java | 13 ++++++++--
.../META-INF/cxf/org.apache.cxf.Logger | 1 +
.../src/main/resources/cxfContext.xml | 25 ++++++++++++++++++++
5 files changed, 54 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/e073cc43/fit/build-tools/pom.xml
----------------------------------------------------------------------
diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml
index 9f0d783..a4172e7 100644
--- a/fit/build-tools/pom.xml
+++ b/fit/build-tools/pom.xml
@@ -45,6 +45,11 @@ under the License.
</dependency>
<dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-all</artifactId>
</dependency>
@@ -88,13 +93,22 @@ under the License.
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
- </dependency>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
+ </dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
+ <groupId>org.webjars</groupId>
+ <artifactId>swagger-ui</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
http://git-wip-us.apache.org/repos/asf/syncope/blob/e073cc43/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
index 5d64cfc..f02cae4 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserService.java
@@ -30,6 +30,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
@Path("users")
public interface UserService {
@@ -44,7 +45,7 @@ public interface UserService {
@POST
@Consumes({ MediaType.APPLICATION_JSON })
- void create(User user);
+ Response create(User user);
@PUT
@Path("{key}")
http://git-wip-us.apache.org/repos/asf/syncope/blob/e073cc43/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
index dea0f63..6b04515 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/cxf/UserServiceImpl.java
@@ -23,8 +23,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.ws.rs.ClientErrorException;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.springframework.stereotype.Service;
@Service
@@ -32,6 +36,9 @@ public class UserServiceImpl implements UserService {
private static final Map<UUID, User> USERS = new HashMap<UUID, User>();
+ @Context
+ private UriInfo uriInfo;
+
@Override
public List<User> list() {
return new ArrayList<>(USERS.values());
@@ -47,14 +54,16 @@ public class UserServiceImpl implements UserService {
}
@Override
- public void create(final User user) {
+ public Response create(final User user) {
if (user.getKey() == null) {
user.setKey(UUID.randomUUID());
}
if (USERS.containsKey(user.getKey())) {
- throw new IllegalArgumentException("User already exists: " + user.getKey());
+ throw new ClientErrorException("User already exists: " + user.getKey(), Response.Status.CONFLICT);
}
USERS.put(user.getKey(), user);
+
+ return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getKey().toString()).build()).build();
}
@Override
http://git-wip-us.apache.org/repos/asf/syncope/blob/e073cc43/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger b/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
new file mode 100644
index 0000000..6e7bd36
--- /dev/null
+++ b/fit/build-tools/src/main/resources/META-INF/cxf/org.apache.cxf.Logger
@@ -0,0 +1 @@
+org.apache.cxf.common.logging.Slf4jLogger
http://git-wip-us.apache.org/repos/asf/syncope/blob/e073cc43/fit/build-tools/src/main/resources/cxfContext.xml
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/resources/cxfContext.xml b/fit/build-tools/src/main/resources/cxfContext.xml
index 9a6c959..7384e55 100644
--- a/fit/build-tools/src/main/resources/cxfContext.xml
+++ b/fit/build-tools/src/main/resources/cxfContext.xml
@@ -38,6 +38,28 @@ under the License.
<jaxws:endpoint id="soapProvisioning" address="/soap" implementor="#provisioningImpl"/>
+ <bean id="openApiCustomizer" class="org.apache.cxf.jaxrs.openapi.OpenApiCustomizer">
+ <property name="dynamicBasePath" value="true"/>
+ <property name="replaceTags" value="false"/>
+ </bean>
+ <bean id="openapiFeature" class="org.apache.cxf.jaxrs.openapi.OpenApiFeature">
+ <property name="title" value="Apache Syncope FIT Build Tools"/>
+ <property name="version" value="${syncope.version}"/>
+ <property name="description" value="Apache Syncope ${syncope.version}"/>
+ <property name="contactName" value="The Apache Syncope community"/>
+ <property name="contactEmail" value="dev@syncope.apache.org"/>
+ <property name="contactUrl" value="http://syncope.apache.org"/>
+
+ <property name="scan" value="false"/>
+ <property name="resourcePackages">
+ <set>
+ <value>org.apache.syncope.fit.buildtools.cxf</value>
+ </set>
+ </property>
+
+ <property name="customizer" ref="openApiCustomizer"/>
+ </bean>
+
<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
<jaxrs:server id="restProvisioning" address="/rest"
basePackages="org.apache.syncope.fit.buildtools.cxf"
@@ -45,6 +67,9 @@ under the License.
<jaxrs:providers>
<ref bean="jsonProvider"/>
</jaxrs:providers>
+ <jaxrs:features>
+ <ref bean="openapiFeature"/>
+ </jaxrs:features>
</jaxrs:server>
</beans>