You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/05/22 12:24:44 UTC
[5/5] syncope git commit: [SYNCOPE-670] Merge from 1_2_X
[SYNCOPE-670] Merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/22a9e12e
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/22a9e12e
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/22a9e12e
Branch: refs/heads/master
Commit: 22a9e12e5e001de4926deb9052ba28bd98a85d62
Parents: d489e8c 15cca15
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri May 22 12:24:30 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri May 22 12:24:30 2015 +0200
----------------------------------------------------------------------
.../common/rest/api/service/ReportService.java | 14 +--
.../common/rest/api/service/TaskService.java | 14 +--
.../syncope/core/logic/AbstractJobLogic.java | 109 +++++++++++--------
.../apache/syncope/core/logic/ReportLogic.java | 8 +-
.../apache/syncope/core/logic/TaskLogic.java | 12 +-
.../apache/syncope/core/logic/UserLogic.java | 2 +-
.../core/provisioning/java/VirAttrHandler.java | 2 +-
.../propagation/PropagationManagerImpl.java | 31 +++---
.../rest/cxf/service/ReportServiceImpl.java | 8 +-
.../core/rest/cxf/service/TaskServiceImpl.java | 8 +-
.../activiti/ActivitiUserWorkflowAdapter.java | 6 +-
.../core/workflow/activiti/task/Update.java | 11 +-
.../fit/core/reference/SchedTaskITCase.java | 28 ++---
13 files changed, 138 insertions(+), 115 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
----------------------------------------------------------------------
diff --cc common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
index fc37192,0000000..996f784
mode 100644,000000..100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
@@@ -1,182 -1,0 +1,182 @@@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobStatusType;
+import org.apache.syncope.common.lib.types.ReportExecExportFormat;
+import org.apache.syncope.common.lib.wrap.ReportletConfClass;
+import org.apache.syncope.common.rest.api.beans.ListQuery;
+
+/**
+ * REST operations for reports.
+ */
+@Path("reports")
+public interface ReportService extends JAXRSService {
+
+ /**
+ * Returns a list of available classes for reportlet configuration.
+ *
+ * @return list of available classes for reportlet configuration
+ */
+ @GET
+ @Path("reportletConfClasses")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ List<ReportletConfClass> getReportletConfClasses();
+
+ /**
+ * Returns report with matching key.
+ *
+ * @param reportKey key of report to be read
+ * @return report with matching key
+ */
+ @GET
+ @Path("{reportKey}")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ ReportTO read(@NotNull @PathParam("reportKey") Long reportKey);
+
+ /**
+ * Returns report execution with matching key.
+ *
+ * @param executionKey report execution id to be selected
+ * @return report execution with matching key
+ */
+ @GET
+ @Path("executions/{executionKey}")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ ReportExecTO readExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+ /**
+ * Returns a paged list of all existing reports matching the given query;
+ *
+ * @param listQuery query conditions
+ * @return paged list of existing reports matching the given query
+ */
+ @GET
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ PagedResult<ReportTO> list(@BeanParam ListQuery listQuery);
+
+ /**
+ * Creates a new report.
+ *
+ * @param reportTO report to be created
+ * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created report
+ */
+ @Descriptions({
+ @Description(target = DocTarget.RESPONSE, value = "Featuring <tt>Location</tt> header of created report")
+ })
+ @POST
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ Response create(@NotNull ReportTO reportTO);
+
+ /**
+ * Updates report with matching key.
+ *
+ * @param reportKey id for report to be updated
+ * @param reportTO report to be stored
+ */
+ @PUT
+ @Path("{reportKey}")
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ void update(@NotNull @PathParam("reportKey") Long reportKey, ReportTO reportTO);
+
+ /**
+ * Deletes report with matching key.
+ *
+ * @param reportKey Deletes report with matching key
+ */
+ @DELETE
+ @Path("{reportKey}")
+ void delete(@NotNull @PathParam("reportKey") Long reportKey);
+
+ /**
+ * Deletes report execution with matching key.
+ *
+ * @param executionKey key of execution report to be deleted
+ */
+ @DELETE
+ @Path("executions/{executionKey}")
+ void deleteExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+ /**
+ * Executes the report with matching key.
+ *
+ * @param reportKey key of report to be executed
+ * @return report execution result
+ */
+ @POST
+ @Path("{reportKey}/execute")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ ReportExecTO execute(@NotNull @PathParam("reportKey") Long reportKey);
+
+ /**
+ * Exports the report execution with matching key in the requested format.
+ *
+ * @param executionKey key of execution report to be selected
+ * @param fmt file-format selection
+ * @return a stream for content download
+ */
+ @GET
+ @Path("executions/{executionKey}/stream")
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ Response exportExecutionResult(@NotNull @PathParam("executionKey") Long executionKey,
+ @QueryParam("format") ReportExecExportFormat fmt);
+
+ /**
- * List report jobs of the given type
++ * List report jobs of the given type.
+ *
+ * @param type of report job
- * @return List of ReportExecTO
++ * @return list of report jobs of the given type
+ */
+ @GET
+ @Path("jobs")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- List<ReportExecTO> list(@MatrixParam("type") JobStatusType type);
++ List<ReportExecTO> listJobs(@MatrixParam("type") JobStatusType type);
+
+ /**
- * Execute a control action on an existing report
++ * Executes an action on an existing report's job.
+ *
++ * @param reportKey report key
+ * @param action
- * @param reportId id of report
+ */
+ @POST
- @Path("{reportId}")
- void process(@QueryParam("action") JobAction action, @PathParam("reportId") Long reportId);
++ @Path("{reportKey}")
++ void actionJob(@PathParam("reportKey") Long reportKey, @QueryParam("action") JobAction action);
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
----------------------------------------------------------------------
diff --cc common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
index 3d6c3f5,0000000..84b7cbf
mode 100644,000000..100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
@@@ -1,194 -1,0 +1,194 @@@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobStatusType;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.rest.api.beans.ListQuery;
+
+/**
+ * REST operations for tasks.
+ */
+@Path("tasks")
+public interface TaskService extends JAXRSService {
+
+ /**
+ * Returns the task matching the given key.
+ *
+ * @param taskKey key of task to be read
+ * @param <T> type of taskTO
+ * @return task with matching id
+ */
+ @GET
+ @Path("{taskKey}")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ <T extends AbstractTaskTO> T read(@NotNull @PathParam("taskKey") Long taskKey);
+
+ /**
+ * Returns the task execution with the given id.
+ *
+ * @param executionKey key of task execution to be read
+ * @return task execution with matching Id
+ */
+ @GET
+ @Path("executions/{executionKey}")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ TaskExecTO readExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+ /**
+ * Returns a paged list of existing tasks matching type and the given query.
+ *
+ * @param taskType type of tasks to be listed
+ * @param listQuery query conditions
+ * @param <T> type of taskTO
+ * @return paged list of existing tasks matching type and the given query
+ */
+ @GET
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ <T extends AbstractTaskTO> PagedResult<T> list(
+ @NotNull @MatrixParam("type") TaskType taskType,
+ @BeanParam ListQuery listQuery);
+
+ /**
+ * Creates a new task.
+ *
+ * @param taskTO task to be created
+ * @param <T> type of taskTO
+ * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created task
+ */
+ @Descriptions({
+ @Description(target = DocTarget.RESPONSE, value = "Featuring <tt>Location</tt> header of created task")
+ })
+ @POST
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ <T extends SchedTaskTO> Response create(@NotNull T taskTO);
+
+ /**
+ * Updates the task matching the provided key.
+ *
+ * @param taskKey key of task to be updated
+ * @param taskTO updated task to be stored
+ */
+ @PUT
+ @Path("{taskKey}")
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ void update(@NotNull @PathParam("taskKey") Long taskKey, @NotNull AbstractTaskTO taskTO);
+
+ /**
+ * Deletes the task matching the provided key.
+ *
+ * @param taskKey key of task to be deleted
+ */
+ @DELETE
+ @Path("{taskKey}")
+ void delete(@NotNull @PathParam("taskKey") Long taskKey);
+
+ /**
+ * Deletes the task execution matching the provided key.
+ *
+ * @param executionKey key of task execution to be deleted
+ */
+ @DELETE
+ @Path("executions/{executionKey}")
+ void deleteExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+ /**
+ * Executes the task matching the given id.
+ *
+ * @param taskKey key of task to be executed
+ * @param dryRun if true, task will only be simulated
+ * @return execution report for the task matching the given id
+ */
+ @POST
+ @Path("{taskKey}/execute")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ TaskExecTO execute(@NotNull @PathParam("taskKey") Long taskKey,
+ @QueryParam("dryRun") @DefaultValue("false") boolean dryRun);
+
+ /**
+ * Reports task execution result.
+ *
+ * @param executionKey key of task execution being reported
+ * @param reportExec execution being reported
+ */
+ @POST
+ @Path("executions/{executionKey}/report")
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ void report(@NotNull @PathParam("executionKey") Long executionKey, @NotNull ReportExecTO reportExec);
+
+ /**
+ * Executes the provided bulk action.
+ *
+ * @param bulkAction list of task ids against which the bulk action will be performed.
+ * @return Bulk action result
+ */
+ @POST
+ @Path("bulk")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ BulkActionResult bulk(@NotNull BulkAction bulkAction);
+
+ /**
- * List task jobs of the given type
++ * List task jobs of the given type.
+ *
+ * @param type of task job
- * @return List of TaskExecTO
++ * @return list task jobs of the given type
+ */
+ @GET
+ @Path("jobs")
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- List<TaskExecTO> list(@MatrixParam("type") JobStatusType type);
++ List<TaskExecTO> listJobs(@MatrixParam("type") JobStatusType type);
+
+ /**
- * Execute a control action on an existing task
++ * Executes an action on an existing task's job.
+ *
++ * @param taskKey task key
+ * @param action
- * @param taskId id of task
+ */
+ @POST
- @Path("{taskId}")
- void process(@QueryParam("action") JobAction action, @PathParam("taskId") Long taskId);
++ @Path("{taskKey}")
++ void actionJob(@PathParam("taskKey") Long taskKey, @QueryParam("action") JobAction action);
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java
index 9f20ee1,0000000..e423174
mode 100644,000000..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
@@@ -1,155 -1,0 +1,174 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.to.AbstractExecTO;
++import org.apache.syncope.common.lib.to.ReportExecTO;
++import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobStatusType;
+import org.quartz.JobExecutionContext;
+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.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+
+abstract class AbstractJobLogic<T extends AbstractBaseBean> extends AbstractTransactionalLogic<T> {
+
+ @Autowired
+ protected SchedulerFactoryBean scheduler;
+
+ protected abstract Long getKeyFromJobName(final JobKey jobKey);
+
- public <E extends AbstractExecTO> List<E> list(final JobStatusType type, final Class<E> reference) {
- List<E> jobExecTOs = new ArrayList<E>();
++ private <E extends AbstractExecTO> void setTaskOrReportKey(final E jobExecTO, final Long taskOrReportKey) {
++ if (jobExecTO instanceof TaskExecTO) {
++ ((TaskExecTO) jobExecTO).setTask(taskOrReportKey);
++ } else if (jobExecTO instanceof ReportExecTO) {
++ ((ReportExecTO) jobExecTO).setReport(taskOrReportKey);
++ }
++ }
++
++ public <E extends AbstractExecTO> List<E> listJobs(final JobStatusType type, final Class<E> reference) {
++ List<E> jobExecTOs = new ArrayList<>();
+
+ switch (type) {
+ case ALL:
+ try {
+ for (String groupName : scheduler.getScheduler().getJobGroupNames()) {
- for (JobKey jobKey : scheduler.getScheduler().getJobKeys(GroupMatcher.
- jobGroupEquals(groupName))) {
++ for (JobKey jobKey
++ : scheduler.getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
+
- Long jobId = getKeyFromJobName(jobKey);
- if (jobId != null) {
++ Long key = getKeyFromJobName(jobKey);
++ if (key != null) {
+ List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
- if (jobTriggers.size() > 0) {
++ if (jobTriggers.isEmpty()) {
++ E jobExecTO = reference.newInstance();
++ setTaskOrReportKey(jobExecTO, key);
++ jobExecTO.setStatus("Not Scheduled");
++
++ jobExecTOs.add(jobExecTO);
++ } else {
+ for (Trigger t : jobTriggers) {
+ E jobExecTO = reference.newInstance();
- jobExecTO.setKey(jobId);
++ jobExecTO.setKey(key);
+ jobExecTO.
+ setStatus(scheduler.getScheduler().getTriggerState(t.getKey()).name());
+ jobExecTO.setStartDate(t.getStartTime());
++
+ jobExecTOs.add(jobExecTO);
+ }
- } else {
- E jobExecTO = reference.newInstance();
- jobExecTO.setKey(jobId);
- jobExecTO.setStatus("Not Scheduled");
- jobExecTOs.add(jobExecTO);
+ }
+ }
+ }
+ }
- } catch (SchedulerException ex) {
- LOG.debug("Problems during retrieving all scheduled jobs {}", ex);
- } catch (InstantiationException ex) {
- LOG.debug("Problems during instantiating {} {}", reference, ex);
- } catch (IllegalAccessException ex) {
- LOG.debug("Problems during accessing {} {}", reference, ex);
++ } catch (SchedulerException e) {
++ LOG.debug("Problems while retrieving all scheduled jobs", e);
++ } catch (InstantiationException e) {
++ LOG.debug("Problems while instantiating {}", reference, e);
++ } catch (IllegalAccessException e) {
++ LOG.debug("Problems while accessing {}", reference, e);
+ }
+ break;
++
+ case RUNNING:
+ try {
+ for (JobExecutionContext jec : scheduler.getScheduler().getCurrentlyExecutingJobs()) {
- Long jobId = getKeyFromJobName(jec.getJobDetail().getKey());
- if (jobId != null) {
++ Long key = getKeyFromJobName(jec.getJobDetail().getKey());
++ if (key != null) {
+ E jobExecTO = reference.newInstance();
- jobExecTO.setKey(jobId);
- jobExecTO.setStatus(scheduler.getScheduler().getTriggerState(jec.getTrigger().getKey()).
- name());
++ setTaskOrReportKey(jobExecTO, key);
++ jobExecTO.setStatus(
++ scheduler.getScheduler().getTriggerState(jec.getTrigger().getKey()).name());
+ jobExecTO.setStartDate(jec.getFireTime());
++
+ jobExecTOs.add(jobExecTO);
+ }
+ }
- } catch (SchedulerException ex) {
- LOG.debug("Problems during retrieving all currently executing jobs {}", ex);
- } catch (InstantiationException ex) {
- LOG.debug("Problems during instantiating {} {}", reference, ex);
- } catch (IllegalAccessException ex) {
- LOG.debug("Problems during accessing {} {}", reference, ex);
++ } catch (SchedulerException e) {
++ LOG.debug("Problems while retrieving all currently executing jobs", e);
++ } catch (InstantiationException e) {
++ LOG.debug("Problems while instantiating {}", reference, e);
++ } catch (IllegalAccessException e) {
++ LOG.debug("Problems while accessing {}", reference, e);
+ }
+ break;
++
+ case SCHEDULED:
+ try {
+ for (String groupName : scheduler.getScheduler().getJobGroupNames()) {
- for (JobKey jobKey : scheduler.getScheduler().getJobKeys(GroupMatcher.
- jobGroupEquals(groupName))) {
- Long jobId = getKeyFromJobName(jobKey);
- if (jobId != null) {
++ for (JobKey jobKey
++ : scheduler.getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
++
++ Long key = getKeyFromJobName(jobKey);
++ if (key != null) {
+ List<? extends Trigger> jobTriggers = scheduler.getScheduler().getTriggersOfJob(jobKey);
+ for (Trigger t : jobTriggers) {
+ E jobExecTO = reference.newInstance();
- jobExecTO.setKey(jobId);
++ setTaskOrReportKey(jobExecTO, key);
+ jobExecTO.setStatus(scheduler.getScheduler().getTriggerState(t.getKey()).name());
+ jobExecTO.setStartDate(t.getStartTime());
++
+ jobExecTOs.add(jobExecTO);
+ }
+ }
+ }
+ }
- } catch (SchedulerException ex) {
- LOG.debug("Problems during retrieving all scheduled jobs {}", ex);
- } catch (InstantiationException ex) {
- LOG.debug("Problems during instantiating {} {}", reference, ex);
- } catch (IllegalAccessException ex) {
- LOG.debug("Problems during accessing {} {}", reference, ex);
++ } catch (SchedulerException e) {
++ LOG.debug("Problems while retrieving all scheduled jobs", e);
++ } catch (InstantiationException e) {
++ LOG.debug("Problems while instantiating {}", reference, e);
++ } catch (IllegalAccessException e) {
++ LOG.debug("Problems while accessing {}", reference, e);
+ }
+ break;
++
+ default:
+ }
+ return jobExecTOs;
+ }
+
- protected void process(final JobAction action, final String jobName) {
-
++ protected void actionJob(final String jobName, final JobAction action) {
+ if (jobName != null) {
+ JobKey jobKey = new JobKey(jobName, Scheduler.DEFAULT_GROUP);
+ try {
+ if (scheduler.getScheduler().checkExists(jobKey)) {
+ switch (action) {
+ case START:
+ scheduler.getScheduler().triggerJob(jobKey);
+ break;
++
+ case STOP:
+ scheduler.getScheduler().interrupt(jobKey);
+ break;
++
+ default:
+ }
+ }
- } catch (SchedulerException ex) {
- LOG.debug("Problems during {} operation on job with id {}", action.toString(), ex);
++ } catch (SchedulerException e) {
++ LOG.debug("Problems during {} operation on job {}", action.toString(), jobName, e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index 33e2102,0000000..4c3230c
mode 100644,000000..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
@@@ -1,424 -1,0 +1,424 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.io.ByteArrayInputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.ZipInputStream;
+import org.apache.cocoon.optional.pipeline.components.sax.fop.FopSerializer;
+import org.apache.cocoon.pipeline.NonCachingPipeline;
+import org.apache.cocoon.pipeline.Pipeline;
+import org.apache.cocoon.sax.SAXPipelineComponent;
+import org.apache.cocoon.sax.component.XMLGenerator;
+import org.apache.cocoon.sax.component.XMLSerializer;
+import org.apache.cocoon.sax.component.XSLTTransformer;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.PredicateUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ReportExecExportFormat;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
+import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.logic.init.ImplementationClassNamesLoader;
+import org.apache.syncope.core.provisioning.api.job.JobInstanceLoader;
+import org.apache.syncope.core.logic.report.Reportlet;
+import org.apache.syncope.core.logic.report.ReportletConfClass;
+import org.apache.syncope.core.logic.report.TextSerializer;
+import org.apache.syncope.common.lib.CollectionUtils2;
+import org.apache.syncope.common.lib.to.AbstractExecTO;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobStatusType;
+import org.apache.xmlgraphics.util.MimeConstants;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ClassUtils;
+
+@Component
+public class ReportLogic extends AbstractJobLogic<ReportTO> {
+
+ @Autowired
+ private ReportDAO reportDAO;
+
+ @Autowired
+ private ReportExecDAO reportExecDAO;
+
+ @Autowired
+ private JobInstanceLoader jobInstanceLoader;
+
+ @Autowired
+ private ReportDataBinder binder;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Autowired
+ private ImplementationClassNamesLoader classNamesLoader;
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_CREATE + "')")
+ public ReportTO create(final ReportTO reportTO) {
+ Report report = entityFactory.newEntity(Report.class);
+ binder.getReport(report, reportTO);
+ report = reportDAO.save(report);
+
+ try {
+ jobInstanceLoader.registerJob(report);
+ } catch (Exception e) {
+ LOG.error("While registering quartz job for report " + report.getKey(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ return binder.getReportTO(report);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_UPDATE + "')")
+ public ReportTO update(final ReportTO reportTO) {
+ Report report = reportDAO.find(reportTO.getKey());
+ if (report == null) {
+ throw new NotFoundException("Report " + reportTO.getKey());
+ }
+
+ binder.getReport(report, reportTO);
+ report = reportDAO.save(report);
+
+ try {
+ jobInstanceLoader.registerJob(report);
+ } catch (Exception e) {
+ LOG.error("While registering quartz job for report " + report.getKey(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ return binder.getReportTO(report);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_LIST + "')")
+ public int count() {
+ return reportDAO.count();
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_LIST + "')")
+ public List<ReportTO> list(final int page, final int size, final List<OrderByClause> orderByClauses) {
+ return CollectionUtils.collect(reportDAO.findAll(page, size, orderByClauses),
+ new Transformer<Report, ReportTO>() {
+
+ @Override
+ public ReportTO transform(final Report input) {
+ return binder.getReportTO(input);
+ }
+ }, new ArrayList<ReportTO>());
+ }
+
+ private Class<? extends ReportletConf> getReportletConfClass(final Class<Reportlet> reportletClass) {
+ Class<? extends ReportletConf> result = null;
+
+ ReportletConfClass annotation = reportletClass.getAnnotation(ReportletConfClass.class);
+ if (annotation != null) {
+ result = annotation.value();
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings({ "rawtypes" })
+ private Set<Class<Reportlet>> getAllReportletClasses() {
+ return CollectionUtils2.collect(classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.REPORTLET),
+ new Transformer<String, Class<Reportlet>>() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<Reportlet> transform(final String className) {
+ Class<Reportlet> result = null;
+ try {
+ Class reportletClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
+ result = reportletClass;
+ } catch (ClassNotFoundException e) {
+ LOG.warn("Could not load class {}", className);
+ } catch (LinkageError e) {
+ LOG.warn("Could not link class {}", className);
+ }
+
+ return result;
+ }
+ },
+ PredicateUtils.notNullPredicate(), new HashSet<Class<Reportlet>>());
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_LIST + "')")
+ public Set<String> getReportletConfClasses() {
+ return CollectionUtils2.collect(getAllReportletClasses(),
+ new Transformer<Class<Reportlet>, String>() {
+
+ @Override
+ public String transform(final Class<Reportlet> reportletClass) {
+ Class<? extends ReportletConf> reportletConfClass = getReportletConfClass(reportletClass);
+ return reportletConfClass == null ? null : reportletConfClass.getName();
+ }
+ }, PredicateUtils.notNullPredicate(), new HashSet<String>());
+ }
+
+ public Class<Reportlet> findReportletClassHavingConfClass(final Class<? extends ReportletConf> reportletConfClass) {
+ Class<Reportlet> result = null;
+ for (Class<Reportlet> reportletClass : getAllReportletClasses()) {
+ Class<? extends ReportletConf> found = getReportletConfClass(reportletClass);
+ if (found != null && found.equals(reportletConfClass)) {
+ result = reportletClass;
+ }
+ }
+
+ return result;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_READ + "')")
+ public ReportTO read(final Long reportKey) {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new NotFoundException("Report " + reportKey);
+ }
+ return binder.getReportTO(report);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_READ + "')")
+ @Transactional(readOnly = true)
+ public ReportExecTO readExecution(final Long executionKey) {
+ ReportExec reportExec = reportExecDAO.find(executionKey);
+ if (reportExec == null) {
+ throw new NotFoundException("Report execution " + executionKey);
+ }
+ return binder.getReportExecTO(reportExec);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_READ + "')")
+ public void exportExecutionResult(final OutputStream os, final ReportExec reportExec,
+ final ReportExecExportFormat format) {
+
+ // streaming SAX handler from a compressed byte array stream
+ ByteArrayInputStream bais = new ByteArrayInputStream(reportExec.getExecResult());
+ ZipInputStream zis = new ZipInputStream(bais);
+ try {
+ // a single ZipEntry in the ZipInputStream (see ReportJob)
+ zis.getNextEntry();
+
+ Pipeline<SAXPipelineComponent> pipeline = new NonCachingPipeline<>();
+ pipeline.addComponent(new XMLGenerator(zis));
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("status", reportExec.getStatus());
+ parameters.put("message", reportExec.getMessage());
+ parameters.put("startDate", reportExec.getStartDate());
+ parameters.put("endDate", reportExec.getEndDate());
+
+ switch (format) {
+ case HTML:
+ XSLTTransformer xsl2html = new XSLTTransformer(getClass().getResource("/report/report2html.xsl"));
+ xsl2html.setParameters(parameters);
+ pipeline.addComponent(xsl2html);
+ pipeline.addComponent(XMLSerializer.createXHTMLSerializer());
+ break;
+
+ case PDF:
+ XSLTTransformer xsl2pdf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
+ xsl2pdf.setParameters(parameters);
+ pipeline.addComponent(xsl2pdf);
+ pipeline.addComponent(new FopSerializer(MimeConstants.MIME_PDF));
+ break;
+
+ case RTF:
+ XSLTTransformer xsl2rtf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
+ xsl2rtf.setParameters(parameters);
+ pipeline.addComponent(xsl2rtf);
+ pipeline.addComponent(new FopSerializer(MimeConstants.MIME_RTF));
+ break;
+
+ case CSV:
+ XSLTTransformer xsl2csv = new XSLTTransformer(getClass().getResource("/report/report2csv.xsl"));
+ xsl2csv.setParameters(parameters);
+ pipeline.addComponent(xsl2csv);
+ pipeline.addComponent(new TextSerializer());
+ break;
+
+ case XML:
+ default:
+ pipeline.addComponent(XMLSerializer.createXMLSerializer());
+ }
+
+ pipeline.setup(os);
+ pipeline.execute();
+
+ LOG.debug("Result of {} successfully exported as {}", reportExec, format);
+ } catch (Exception e) {
+ LOG.error("While exporting content", e);
+ } finally {
+ IOUtils.closeQuietly(zis);
+ IOUtils.closeQuietly(bais);
+ }
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_READ + "')")
+ public ReportExec getAndCheckReportExec(final Long executionKey) {
+ ReportExec reportExec = reportExecDAO.find(executionKey);
+ if (reportExec == null) {
+ throw new NotFoundException("Report execution " + executionKey);
+ }
+ if (!ReportExecStatus.SUCCESS.name().equals(reportExec.getStatus()) || reportExec.getExecResult() == null) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidReportExec);
+ sce.getElements().add(reportExec.getExecResult() == null
+ ? "No report data produced"
+ : "Report did not run successfully");
+ throw sce;
+ }
+ return reportExec;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_EXECUTE + "')")
+ public ReportExecTO execute(final Long reportKey) {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new NotFoundException("Report " + reportKey);
+ }
+
+ try {
+ jobInstanceLoader.registerJob(report);
+
+ scheduler.getScheduler().triggerJob(
+ new JobKey(JobNamer.getJobName(report), Scheduler.DEFAULT_GROUP));
+ } catch (Exception e) {
+ LOG.error("While executing report {}", report, e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ ReportExecTO result = new ReportExecTO();
+ result.setReport(reportKey);
+ result.setStartDate(new Date());
+ result.setStatus(ReportExecStatus.STARTED.name());
+ result.setMessage("Job fired; waiting for results...");
+
+ return result;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_DELETE + "')")
+ public ReportTO delete(final Long reportKey) {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new NotFoundException("Report " + reportKey);
+ }
+
+ ReportTO deletedReport = binder.getReportTO(report);
+ jobInstanceLoader.unregisterJob(report);
+ reportDAO.delete(report);
+ return deletedReport;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_DELETE + "')")
+ public ReportExecTO deleteExecution(final Long executionKey) {
+ ReportExec reportExec = reportExecDAO.find(executionKey);
+ if (reportExec == null) {
+ throw new NotFoundException("Report execution " + executionKey);
+ }
+
+ ReportExecTO reportExecToDelete = binder.getReportExecTO(reportExec);
+ reportExecDAO.delete(reportExec);
+ return reportExecToDelete;
+ }
+
+ @Override
+ protected ReportTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+
+ Long key = null;
+
+ if (ArrayUtils.isNotEmpty(args) && ("create".equals(method.getName())
+ || "update".equals(method.getName())
+ || "delete".equals(method.getName()))) {
+ for (int i = 0; key == null && i < args.length; i++) {
+ if (args[i] instanceof Long) {
+ key = (Long) args[i];
+ } else if (args[i] instanceof ReportTO) {
+ key = ((ReportTO) args[i]).getKey();
+ }
+ }
+ }
+
+ if ((key != null) && !key.equals(0L)) {
+ try {
+ return binder.getReportTO(reportDAO.find(key));
+ } catch (Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+
+ throw new UnresolvedReferenceException();
+ }
+
+ @Override
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_LIST + "')")
- public <E extends AbstractExecTO> List<E> list(final JobStatusType type, final Class<E> reference) {
- return super.list(type, reference);
++ public <E extends AbstractExecTO> List<E> listJobs(final JobStatusType type, final Class<E> reference) {
++ return super.listJobs(type, reference);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.REPORT_EXECUTE + "')")
- public void process(final JobAction action, final Long reportKey) {
++ public void actionJob(final Long reportKey, final JobAction action) {
+ Report report = reportDAO.find(reportKey);
+ if (report == null) {
+ throw new NotFoundException("Report " + reportKey);
+ }
+ String jobName = JobNamer.getJobName(report);
- process(action, jobName);
++ actionJob(jobName, action);
+ }
+
+ @Override
+ protected Long getKeyFromJobName(final JobKey jobKey) {
+ return JobNamer.getReportKeyFromJobName(jobKey.getName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index e12c2da,0000000..82dc8d1
mode 100644,000000..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
@@@ -1,363 -1,0 +1,363 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractExecTO;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.SyncTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.common.lib.types.JobAction;
+import org.apache.syncope.common.lib.types.JobStatusType;
+import org.apache.syncope.common.lib.types.PropagationMode;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.Task;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.entity.task.TaskUtils;
+import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
+import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
+import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.provisioning.api.job.TaskJob;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.api.job.JobInstanceLoader;
+import org.apache.syncope.core.logic.notification.NotificationJob;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TaskLogic extends AbstractJobLogic<AbstractTaskTO> {
+
+ @Autowired
+ private TaskDAO taskDAO;
+
+ @Autowired
+ private TaskExecDAO taskExecDAO;
+
+ @Autowired
+ private TaskDataBinder binder;
+
+ @Autowired
+ private PropagationTaskExecutor taskExecutor;
+
+ @Autowired
+ private NotificationJob notificationJob;
+
+ @Autowired
+ private JobInstanceLoader jobInstanceLoader;
+
+ @Autowired
+ private SchedulerFactoryBean scheduler;
+
+ @Autowired
+ private TaskUtilsFactory taskUtilsFactory;
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_CREATE + "')")
+ public <T extends SchedTaskTO> T createSchedTask(final T taskTO) {
+ TaskUtils taskUtils = taskUtilsFactory.getInstance(taskTO);
+
+ SchedTask task = binder.createSchedTask(taskTO, taskUtils);
+ task = taskDAO.save(task);
+
+ try {
+ jobInstanceLoader.registerJob(task, task.getJobClassName(), task.getCronExpression());
+ } catch (Exception e) {
+ LOG.error("While registering quartz job for task " + task.getKey(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ return binder.getTaskTO(task, taskUtils);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_UPDATE + "')")
+ public SyncTaskTO updateSync(final SyncTaskTO taskTO) {
+ return updateSched(taskTO);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_UPDATE + "')")
+ public <T extends SchedTaskTO> T updateSched(final SchedTaskTO taskTO) {
+ SchedTask task = taskDAO.find(taskTO.getKey());
+ if (task == null) {
+ throw new NotFoundException("Task " + taskTO.getKey());
+ }
+
+ TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
+
+ binder.updateSchedTask(task, taskTO, taskUtils);
+ task = taskDAO.save(task);
+
+ try {
+ jobInstanceLoader.registerJob(task, task.getJobClassName(), task.getCronExpression());
+ } catch (Exception e) {
+ LOG.error("While registering quartz job for task " + task.getKey(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ return binder.getTaskTO(task, taskUtils);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_LIST + "')")
+ public int count(final TaskType taskType) {
+ return taskDAO.count(taskType);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_LIST + "')")
+ @SuppressWarnings("unchecked")
+ public <T extends AbstractTaskTO> List<T> list(final TaskType taskType,
+ final int page, final int size, final List<OrderByClause> orderByClauses) {
+
+ final TaskUtils taskUtilss = taskUtilsFactory.getInstance(taskType);
+
+ return CollectionUtils.collect(taskDAO.findAll(page, size, orderByClauses, taskType),
+ new Transformer<Task, T>() {
+
+ @Override
+ public T transform(final Task task) {
+ return (T) binder.getTaskTO(task, taskUtilss);
+ }
+ }, new ArrayList<T>());
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_READ + "')")
+ public <T extends AbstractTaskTO> T read(final Long taskId) {
+ Task task = taskDAO.find(taskId);
+ if (task == null) {
+ throw new NotFoundException("Task " + taskId);
+ }
+ return binder.getTaskTO(task, taskUtilsFactory.getInstance(task));
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_READ + "')")
+ public TaskExecTO readExecution(final Long executionId) {
+ TaskExec taskExec = taskExecDAO.find(executionId);
+ if (taskExec == null) {
+ throw new NotFoundException("Task execution " + executionId);
+ }
+ return binder.getTaskExecTO(taskExec);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_EXECUTE + "')")
+ public TaskExecTO execute(final Long taskId, final boolean dryRun) {
+ Task task = taskDAO.find(taskId);
+ if (task == null) {
+ throw new NotFoundException("Task " + taskId);
+ }
+ TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
+
+ TaskExecTO result = null;
+ switch (taskUtils.getType()) {
+ case PROPAGATION:
+ final TaskExec propExec = taskExecutor.execute((PropagationTask) task);
+ result = binder.getTaskExecTO(propExec);
+ break;
+
+ case NOTIFICATION:
+ final TaskExec notExec = notificationJob.executeSingle((NotificationTask) task);
+ result = binder.getTaskExecTO(notExec);
+ break;
+
+ case SCHEDULED:
+ case SYNCHRONIZATION:
+ case PUSH:
+ try {
+ jobInstanceLoader.registerJob(task,
+ ((SchedTask) task).getJobClassName(),
+ ((SchedTask) task).getCronExpression());
+
+ JobDataMap map = new JobDataMap();
+ map.put(TaskJob.DRY_RUN_JOBDETAIL_KEY, dryRun);
+
+ scheduler.getScheduler().triggerJob(
+ new JobKey(JobNamer.getJobName(task), Scheduler.DEFAULT_GROUP), map);
+ } catch (Exception e) {
+ LOG.error("While executing task {}", task, e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+
+ result = new TaskExecTO();
+ result.setTask(taskId);
+ result.setStartDate(new Date());
+ result.setStatus("JOB_FIRED");
+ result.setMessage("Job fired; waiting for results...");
+ break;
+
+ default:
+ }
+
+ return result;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_READ + "')")
+ public TaskExecTO report(final Long executionId, final PropagationTaskExecStatus status, final String message) {
+ TaskExec exec = taskExecDAO.find(executionId);
+ if (exec == null) {
+ throw new NotFoundException("Task execution " + executionId);
+ }
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPropagationTaskExecReport);
+
+ TaskUtils taskUtils = taskUtilsFactory.getInstance(exec.getTask());
+ if (TaskType.PROPAGATION == taskUtils.getType()) {
+ PropagationTask task = (PropagationTask) exec.getTask();
+ if (task.getPropagationMode() != PropagationMode.TWO_PHASES) {
+ sce.getElements().add("Propagation mode: " + task.getPropagationMode());
+ }
+ } else {
+ sce.getElements().add("Task type: " + taskUtils);
+ }
+
+ switch (status) {
+ case SUCCESS:
+ case FAILURE:
+ break;
+
+ case CREATED:
+ case SUBMITTED:
+ case UNSUBMITTED:
+ sce.getElements().add("Execution status to be set: " + status);
+ break;
+
+ default:
+ }
+
+ if (!sce.isEmpty()) {
+ throw sce;
+ }
+
+ exec.setStatus(status.toString());
+ exec.setMessage(message);
+ return binder.getTaskExecTO(taskExecDAO.save(exec));
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_DELETE + "')")
+ public <T extends AbstractTaskTO> T delete(final Long taskId) {
+ Task task = taskDAO.find(taskId);
+ if (task == null) {
+ throw new NotFoundException("Task " + taskId);
+ }
+ TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
+
+ T taskToDelete = binder.getTaskTO(task, taskUtils);
+
+ if (TaskType.SCHEDULED == taskUtils.getType()
+ || TaskType.SYNCHRONIZATION == taskUtils.getType()
+ || TaskType.PUSH == taskUtils.getType()) {
+
+ jobInstanceLoader.unregisterJob(task);
+ }
+
+ taskDAO.delete(task);
+ return taskToDelete;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_DELETE + "')")
+ public TaskExecTO deleteExecution(final Long executionId) {
+ TaskExec taskExec = taskExecDAO.find(executionId);
+ if (taskExec == null) {
+ throw new NotFoundException("Task execution " + executionId);
+ }
+
+ TaskExecTO taskExecutionToDelete = binder.getTaskExecTO(taskExec);
+ taskExecDAO.delete(taskExec);
+ return taskExecutionToDelete;
+ }
+
+ @Override
+ protected AbstractTaskTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+
+ Long key = null;
+
+ if (ArrayUtils.isNotEmpty(args)
+ && !"deleteExecution".equals(method.getName()) && !"readExecution".equals(method.getName())) {
+
+ for (int i = 0; key == null && i < args.length; i++) {
+ if (args[i] instanceof Long) {
+ key = (Long) args[i];
+ } else if (args[i] instanceof AbstractTaskTO) {
+ key = ((AbstractTaskTO) args[i]).getKey();
+ }
+ }
+ }
+
+ if ((key != null) && !key.equals(0L)) {
+ try {
+ final Task task = taskDAO.find(key);
+ return binder.getTaskTO(task, taskUtilsFactory.getInstance(task));
+ } catch (Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+
+ throw new UnresolvedReferenceException();
+ }
+
+ @Override
+ @PreAuthorize("hasRole('" + Entitlement.TASK_LIST + "')")
- public <E extends AbstractExecTO> List<E> list(final JobStatusType type, final Class<E> reference) {
- return super.list(type, reference);
++ public <E extends AbstractExecTO> List<E> listJobs(final JobStatusType type, final Class<E> reference) {
++ return super.listJobs(type, reference);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.TASK_EXECUTE + "')")
- public void process(final JobAction action, final Long taskId) {
- Task task = taskDAO.find(taskId);
++ public void actionJob(final Long taskKey, final JobAction action) {
++ Task task = taskDAO.find(taskKey);
+ if (task == null) {
- throw new NotFoundException("Task " + taskId);
++ throw new NotFoundException("Task " + taskKey);
+ }
+ String jobName = JobNamer.getJobName(task);
- process(action, jobName);
++ actionJob(jobName, action);
+ }
+
+ @Override
+ protected Long getKeyFromJobName(final JobKey jobKey) {
+ return JobNamer.getTaskKeyFromJobName(jobKey.getName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/22a9e12e/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index abad867,0000000..53d974e
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@@ -1,475 -1,0 +1,475 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.AttributableTransformer;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.java.VirAttrHandler;
+import org.apache.syncope.core.misc.security.AuthContextUtils;
+import org.apache.syncope.core.misc.security.UnauthorizedException;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+/**
+ * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
+ * Spring's Transactional logic at class level.
+ */
+@Component
+public class UserLogic extends AbstractSubjectLogic<UserTO, UserMod> {
+
+ @Autowired
+ protected UserDAO userDAO;
+
+ @Autowired
+ protected GroupDAO groupDAO;
+
+ @Autowired
+ protected SubjectSearchDAO searchDAO;
+
+ @Autowired
+ protected UserDataBinder binder;
+
+ @Autowired
+ protected VirAttrHandler virtAttrHandler;
+
+ @Autowired
+ protected PropagationManager propagationManager;
+
+ @Autowired
+ protected PropagationTaskExecutor taskExecutor;
+
+ @Autowired
+ protected AttributableTransformer attrTransformer;
+
+ @Autowired
+ protected UserProvisioningManager provisioningManager;
+
+ @Autowired
+ protected SyncopeLogic syncopeLogic;
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
+ public String getUsername(final Long key) {
+ return binder.getUserTO(key).getUsername();
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
+ public Long getKey(final String username) {
+ return binder.getUserTO(username).getKey();
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
+ @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+ @Override
+ public int count(final List<String> realms) {
+ return userDAO.count(
+ getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms));
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
+ @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+ @Override
+ public List<UserTO> list(
+ final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) {
+
+ return CollectionUtils.collect(userDAO.findAll(
+ getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms),
+ page, size, orderBy),
+ new Transformer<User, UserTO>() {
+
+ @Override
+ public UserTO transform(final User input) {
+ return binder.getUserTO(input);
+ }
+ }, new ArrayList<UserTO>());
+ }
+
+ @PreAuthorize("isAuthenticated()")
+ @Transactional(readOnly = true)
+ public Pair<String, UserTO> readSelf() {
+ return ImmutablePair.of(
+ POJOHelper.serialize(AuthContextUtils.getAuthorizations()),
+ binder.getAuthenticatedUserTO());
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
+ @Transactional(readOnly = true)
+ @Override
+ public UserTO read(final Long key) {
+ return binder.getUserTO(key);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
+ @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+ @Override
+ public int searchCount(final SearchCond searchCondition, final List<String> realms) {
+ return searchDAO.count(
+ getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
+ searchCondition, SubjectType.USER);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
+ @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+ @Override
+ public List<UserTO> search(final SearchCond searchCondition, final int page, final int size,
+ final List<OrderByClause> orderBy, final List<String> realms) {
+
+ List<User> matchingUsers = searchDAO.search(
+ getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
+ searchCondition, page, size, orderBy, SubjectType.USER);
+ return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() {
+
+ @Override
+ public UserTO transform(final User input) {
+ return binder.getUserTO(input);
+ }
+ }, new ArrayList<UserTO>());
+ }
+
+ @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
+ public UserTO createSelf(final UserTO userTO, final boolean storePassword) {
+ return doCreate(userTO, storePassword);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
+ public UserTO create(final UserTO userTO, final boolean storePassword) {
+ if (userTO.getRealm() == null) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+ throw sce;
+ }
+ Set<String> effectiveRealms = getEffectiveRealms(
+ AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
+ Collections.singleton(userTO.getRealm()));
+ if (effectiveRealms.isEmpty()) {
+ throw new UnauthorizedException(SubjectType.USER, null);
+ }
+
+ return doCreate(userTO, storePassword);
+ }
+
+ protected UserTO doCreate(final UserTO userTO, final boolean storePassword) {
+ // Attributable transformation (if configured)
+ UserTO actual = attrTransformer.transform(userTO);
+ LOG.debug("Transformed: {}", actual);
+
+ Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(actual, storePassword);
+
+ final UserTO savedTO = binder.getUserTO(created.getKey());
+ savedTO.getPropagationStatusTOs().addAll(created.getValue());
+ return savedTO;
+ }
+
+ @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
+ public UserTO updateSelf(final UserMod userMod) {
+ UserTO userTO = binder.getAuthenticatedUserTO();
+
+ if (userTO.getKey() != userMod.getKey()) {
+ throw new AccessControlException("Not allowed for user with key " + userMod.getKey());
+ }
+
+ return update(userMod);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Override
+ public UserTO update(final UserMod userMod) {
+ // AttributableMod transformation (if configured)
+ UserMod actual = attrTransformer.transform(userMod);
+ LOG.debug("Transformed: {}", actual);
+
+ // SYNCOPE-501: check if there are memberships to be removed with virtual attributes assigned
+ boolean removeMemberships = false;
+ for (Long membershipId : actual.getMembershipsToRemove()) {
+ if (!virtAttrHandler.fillMembershipVirtual(
+ null,
+ null,
+ membershipId,
+ Collections.<String>emptySet(),
+ Collections.<AttrMod>emptySet(),
+ true).isEmpty()) {
+
+ removeMemberships = true;
+ }
+ }
+
+ Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(actual, removeMemberships);
+
- final UserTO updatedTO = binder.getUserTO(updated.getKey());
++ UserTO updatedTO = binder.getUserTO(updated.getKey());
+ updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
+ return updatedTO;
+ }
+
+ protected Map.Entry<Long, List<PropagationStatus>> setStatusOnWfAdapter(final User user,
+ final StatusMod statusMod) {
+ Map.Entry<Long, List<PropagationStatus>> updated;
+
+ switch (statusMod.getType()) {
+ case SUSPEND:
+ updated = provisioningManager.suspend(user, statusMod);
+ break;
+
+ case REACTIVATE:
+ updated = provisioningManager.reactivate(user, statusMod);
+ break;
+
+ case ACTIVATE:
+ default:
+ updated = provisioningManager.activate(user, statusMod);
+ break;
+
+ }
+
+ return updated;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ public UserTO status(final StatusMod statusMod) {
+ User user = userDAO.authFetch(statusMod.getKey());
+
+ Map.Entry<Long, List<PropagationStatus>> updated = setStatusOnWfAdapter(user, statusMod);
+ final UserTO savedTO = binder.getUserTO(updated.getKey());
+ savedTO.getPropagationStatusTOs().addAll(updated.getValue());
+ return savedTO;
+ }
+
+ @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
+ @Transactional
+ public void requestPasswordReset(final String username, final String securityAnswer) {
+ if (username == null) {
+ throw new NotFoundException("Null username");
+ }
+
+ User user = userDAO.find(username);
+ if (user == null) {
+ throw new NotFoundException("User " + username);
+ }
+
+ if (syncopeLogic.isPwdResetRequiringSecurityQuestions()
+ && (securityAnswer == null || !securityAnswer.equals(user.getSecurityAnswer()))) {
+
+ throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer);
+ }
+
+ provisioningManager.requestPasswordReset(user.getKey());
+ }
+
+ @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
+ @Transactional
+ public void confirmPasswordReset(final String token, final String password) {
+ User user = userDAO.findByToken(token);
+ if (user == null) {
+ throw new NotFoundException("User with token " + token);
+ }
+ provisioningManager.confirmPasswordReset(user, token, password);
+ }
+
+ @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
+ public UserTO deleteSelf() {
+ UserTO userTO = binder.getAuthenticatedUserTO();
+
+ return delete(userTO.getKey());
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')")
+ @Override
+ public UserTO delete(final Long key) {
+ List<Group> ownedGroups = groupDAO.findOwnedByUser(key);
+ if (!ownedGroups.isEmpty()) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
+ sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
+
+ @Override
+ public String transform(final Group group) {
+ return group.getKey() + " " + group.getName();
+ }
+ }, new ArrayList<String>()));
+ throw sce;
+ }
+
+ List<PropagationStatus> statuses = provisioningManager.delete(key);
+
+ final UserTO deletedTO;
+ User deleted = userDAO.find(key);
+ if (deleted == null) {
+ deletedTO = new UserTO();
+ deletedTO.setKey(key);
+ } else {
+ deletedTO = binder.getUserTO(key);
+ }
+ deletedTO.getPropagationStatusTOs().addAll(statuses);
+
+ return deletedTO;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ @Override
+ public UserTO unlink(final Long key, final Collection<String> resources) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(key);
+ userMod.getResourcesToRemove().addAll(resources);
+ Long updatedKey = provisioningManager.unlink(userMod);
+
+ return binder.getUserTO(updatedKey);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ @Override
+ public UserTO link(final Long key, final Collection<String> resources) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(key);
+ userMod.getResourcesToAdd().addAll(resources);
+ return binder.getUserTO(provisioningManager.link(userMod));
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ @Override
+ public UserTO unassign(final Long key, final Collection<String> resources) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(key);
+ userMod.getResourcesToRemove().addAll(resources);
+ return update(userMod);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ @Override
+ public UserTO assign(
+ final Long key,
+ final Collection<String> resources,
+ final boolean changepwd,
+ final String password) {
+
+ final UserMod userMod = new UserMod();
+ userMod.setKey(key);
+ userMod.getResourcesToAdd().addAll(resources);
+
+ if (changepwd) {
+ StatusMod statusMod = new StatusMod();
+ statusMod.setOnSyncope(false);
+ statusMod.getResourceNames().addAll(resources);
+ userMod.setPwdPropRequest(statusMod);
+ userMod.setPassword(password);
+ }
+
+ return update(userMod);
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(rollbackFor = { Throwable.class })
+ @Override
+ public UserTO deprovision(final Long key, final Collection<String> resources) {
+ final User user = userDAO.authFetch(key);
+
+ List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
+
+ final UserTO updatedUserTO = binder.getUserTO(user);
+ updatedUserTO.getPropagationStatusTOs().addAll(statuses);
+ return updatedUserTO;
+ }
+
+ @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
+ @Transactional(readOnly = true)
+ @Override
+ public UserTO provision(
+ final Long key,
+ final Collection<String> resources,
+ final boolean changePwd,
+ final String password) {
+
+ final UserTO original = binder.getUserTO(key);
+
+ //trick: assign and retrieve propagation statuses ...
+ original.getPropagationStatusTOs().addAll(
+ assign(key, resources, changePwd, password).getPropagationStatusTOs());
+
+ // .... rollback.
+ TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
+ return original;
+ }
+
+ @Override
+ protected UserTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
+ Object key = null;
+
+ if (!"confirmPasswordReset".equals(method.getName()) && ArrayUtils.isNotEmpty(args)) {
+ for (int i = 0; key == null && i < args.length; i++) {
+ if (args[i] instanceof Long) {
+ key = (Long) args[i];
+ } else if (args[i] instanceof String) {
+ key = (String) args[i];
+ } else if (args[i] instanceof UserTO) {
+ key = ((UserTO) args[i]).getKey();
+ } else if (args[i] instanceof UserMod) {
+ key = ((UserMod) args[i]).getKey();
+ }
+ }
+ }
+
+ if ((key != null) && !key.equals(0L)) {
+ try {
+ return key instanceof Long ? binder.getUserTO((Long) key) : binder.getUserTO((String) key);
+ } catch (Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+
+ throw new UnresolvedReferenceException();
+ }
+}