You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/03/18 09:49:13 UTC

[2/5] syncope git commit: [SYNCOPE-648]Merge from 1_2_X

http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
index d51b26f,0000000..c160556
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
@@@ -1,155 -1,0 +1,162 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.syncope.common.lib.mod.RoleMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.to.RoleTO;
++import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
++import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
 +import org.apache.syncope.core.persistence.api.entity.Mapping;
 +import org.apache.syncope.core.persistence.api.entity.MappingItem;
 +import org.apache.syncope.core.persistence.api.entity.Subject;
 +import org.apache.syncope.core.persistence.api.entity.role.Role;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import org.apache.syncope.core.provisioning.api.sync.RolePushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class RolePushResultHandlerImpl extends AbstractPushResultHandler implements RolePushResultHandler {
 +
 +    @Override
++    protected AttributableUtil getAttributableUtil() {
++        return attrUtilFactory.getInstance(AttributableType.ROLE);
++    }
++
++    @Override
 +    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
 +        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
 +
 +        final List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getRoleDeleteTaskIds(before.getKey(), noPropResources));
 +
 +        return roleDAO.authFetch(before.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
 +        final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
 +
 +        final List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        final PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getRoleCreateTaskIds(
 +                before.getKey(),
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                propByRes,
 +                noPropResources));
 +
 +        return roleDAO.authFetch(before.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
 +        final RoleMod roleMod = new RoleMod();
 +        roleMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        rwfAdapter.update(roleMod);
 +
 +        return roleDAO.authFetch(sbj.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
 +        final RoleMod roleMod = new RoleMod();
 +        roleMod.setKey(sbj.getKey());
 +        roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        rwfAdapter.update(roleMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
 +        final RoleMod roleMod = new RoleMod();
 +        roleMod.setKey(sbj.getKey());
 +        roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        rwfAdapter.update(roleMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Subject<?, ?, ?> subject) {
 +        return Role.class.cast(subject).getName();
 +    }
 +
 +    @Override
 +    protected AbstractSubjectTO getSubjectTO(final long key) {
 +        try {
 +            return roleTransfer.getRoleTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> getSubject(final long key) {
 +        try {
 +            return roleDAO.authFetch(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving role {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String accountId) {
 +        ConnectorObject obj = null;
 +
 +        try {
 +            final Uid uid = new Uid(accountId);
 +
 +            obj = profile.getConnector().getObject(
 +                    ObjectClass.GROUP,
 +                    uid,
 +                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", accountId, ignore);
 +        }
 +        return obj;
 +    }
 +
 +    @Override
 +    protected Mapping<?> getMapping() {
 +        return profile.getTask().getResource().getRmapping();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
index 0c1ad8b,0000000..61637a4
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
@@@ -1,160 -1,0 +1,167 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.to.UserTO;
++import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
++import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
 +import org.apache.syncope.core.persistence.api.entity.Mapping;
 +import org.apache.syncope.core.persistence.api.entity.MappingItem;
 +import org.apache.syncope.core.persistence.api.entity.Subject;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler {
 +
 +    @Override
++    protected AttributableUtil getAttributableUtil() {
++        return attrUtilFactory.getInstance(AttributableType.USER);
++    }
++
++    @Override
 +    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
 +        final UserTO before = userTransfer.getUserTO(sbj.getKey());
 +
 +        final List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
 +                Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
 +
 +        return userDAO.authFetch(before.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
 +        final UserTO before = userTransfer.getUserTO(sbj.getKey());
 +
 +        final List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        final PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getUserCreateTaskIds(
 +                before.getKey(),
 +                enabled,
 +                propByRes,
 +                null,
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                Collections.unmodifiableCollection(before.getMemberships()),
 +                noPropResources));
 +
 +        return userDAO.authFetch(before.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        uwfAdapter.update(userMod);
 +
 +        return userDAO.authFetch(userMod.getKey());
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(sbj.getKey());
 +        userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        uwfAdapter.update(userMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(sbj.getKey());
 +        userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        uwfAdapter.update(userMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Subject<?, ?, ?> subject) {
 +        return User.class.cast(subject).getUsername();
 +    }
 +
 +    @Override
 +    protected AbstractSubjectTO getSubjectTO(final long key) {
 +        try {
 +            return userTransfer.getUserTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Subject<?, ?, ?> getSubject(final long key) {
 +        try {
 +            return userDAO.authFetch(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String accountId) {
 +        ConnectorObject obj = null;
 +
 +        try {
 +            final Uid uid = new Uid(accountId);
 +
 +            obj = profile.getConnector().getObject(
 +                    ObjectClass.ACCOUNT,
 +                    uid,
 +                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", accountId, ignore);
 +        }
 +        return obj;
 +    }
 +
 +    @Override
 +    protected Mapping<?> getMapping() {
 +        return profile.getTask().getResource().getUmapping();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
index 6ba1dd8,0000000..669d088
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
@@@ -1,137 -1,0 +1,155 @@@
 +/*
 + * 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.fit.core.reference;
 +
++import static org.apache.syncope.fit.core.reference.AbstractITCase.taskService;
 +import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.fail;
 +
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import java.util.concurrent.Callable;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
 +import java.util.concurrent.Future;
 +import java.util.concurrent.TimeUnit;
 +import org.apache.syncope.common.lib.to.AbstractTaskTO;
++import org.apache.syncope.common.lib.to.NotificationTaskTO;
++import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.TaskExecTO;
 +import org.apache.syncope.common.lib.to.UserTO;
++import org.apache.syncope.common.lib.types.TaskType;
 +
 +public abstract class AbstractTaskITCase extends AbstractITCase {
 +
 +    protected static final Long SYNC_TASK_ID = 4L;
 +
 +    protected static final Long SCHED_TASK_ID = 5L;
 +
 +    protected static class ThreadExec implements Callable<TaskExecTO> {
 +
 +        private final AbstractTaskITCase test;
 +
 +        private final Long taskKey;
 +
 +        private final int maxWaitSeconds;
 +
 +        private final boolean dryRun;
 +
 +        public ThreadExec(AbstractTaskITCase test, Long taskKey, int maxWaitSeconds, boolean dryRun) {
 +            this.test = test;
 +            this.taskKey = taskKey;
 +            this.maxWaitSeconds = maxWaitSeconds;
 +            this.dryRun = dryRun;
 +        }
 +
 +        @Override
 +        public TaskExecTO call() throws Exception {
 +            return test.execSyncTask(taskKey, maxWaitSeconds, dryRun);
 +        }
 +    }
 +
 +    /**
 +     * Remove initial and synchronized users to make test re-runnable.
 +     */
 +    protected void removeTestUsers() {
 +        for (int i = 0; i < 10; i++) {
 +            String cUserName = "test" + i;
 +            try {
 +                UserTO cUserTO = readUser(cUserName);
 +                userService.delete(cUserTO.getKey());
 +            } catch (Exception e) {
 +                // Ignore
 +            }
 +        }
 +    }
 +
 +    protected TaskExecTO execSyncTask(final Long taskKey, final int maxWaitSeconds, final boolean dryRun) {
 +        AbstractTaskTO taskTO = taskService.read(taskKey);
 +        assertNotNull(taskTO);
 +        assertNotNull(taskTO.getExecutions());
 +
 +        int preSyncSize = taskTO.getExecutions().size();
 +        TaskExecTO execution = taskService.execute(taskTO.getKey(), dryRun);
 +        assertEquals("JOB_FIRED", execution.getStatus());
 +
 +        int i = 0;
 +        int maxit = maxWaitSeconds;
 +
 +        // wait for sync completion (executions incremented)
 +        do {
 +            try {
 +                Thread.sleep(1000);
 +            } catch (InterruptedException e) {
 +            }
 +
 +            taskTO = taskService.read(taskTO.getKey());
 +
 +            assertNotNull(taskTO);
 +            assertNotNull(taskTO.getExecutions());
 +
 +            i++;
 +        } while (preSyncSize == taskTO.getExecutions().size() && i < maxit);
 +        if (i == maxit) {
 +            fail("Timeout when executing task " + taskKey);
 +        }
 +        return taskTO.getExecutions().get(taskTO.getExecutions().size() - 1);
 +    }
 +
 +    protected Map<Long, TaskExecTO> execSyncTasks(
 +            final Set<Long> taskKeys, final int maxWaitSeconds, final boolean dryRun) throws Exception {
 +
 +        final ExecutorService service = Executors.newFixedThreadPool(taskKeys.size());
 +        final List<Future<TaskExecTO>> futures = new ArrayList<>();
 +
 +        for (Long key : taskKeys) {
 +            futures.add(service.submit(new ThreadExec(this, key, maxWaitSeconds, dryRun)));
 +        }
 +
 +        final Map<Long, TaskExecTO> res = new HashMap<>();
 +
 +        for (Future<TaskExecTO> future : futures) {
 +            TaskExecTO taskExecTO = future.get(100, TimeUnit.SECONDS);
 +            res.put(taskExecTO.getTask(), taskExecTO);
 +        }
 +
 +        service.shutdownNow();
 +
 +        return res;
 +    }
 +
++    protected NotificationTaskTO findNotificationTaskBySender(final String sender) {
++        PagedResult<NotificationTaskTO> tasks = taskService.list(TaskType.NOTIFICATION);
++        assertNotNull(tasks);
++        assertFalse(tasks.getResult().isEmpty());
++        NotificationTaskTO taskTO = null;
++        for (NotificationTaskTO task : tasks.getResult()) {
++            if (sender.equals(task.getSender())) {
++                taskTO = task;
++            }
++        }
++        return taskTO;
++    }
++
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
index 87a49fb,0000000..ee75de9
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
@@@ -1,155 -1,0 +1,140 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertTrue;
 +
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.NotificationTO;
 +import org.apache.syncope.common.lib.to.NotificationTaskTO;
- import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.TaskExecTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.IntMappingType;
- import org.apache.syncope.common.lib.types.TaskType;
 +import org.apache.syncope.common.lib.types.TraceLevel;
 +import org.apache.syncope.common.rest.api.service.NotificationService;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class NotificationTaskITCase extends AbstractTaskITCase {
 +
 +    @Test
 +    public void issueSYNCOPE81() {
 +        String sender = "syncope81@syncope.apache.org";
 +        createNotificationTask(sender);
 +        NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
 +        assertNotNull(taskTO);
 +
 +        assertTrue(taskTO.getExecutions().isEmpty());
 +
 +        // generate an execution in order to verify the deletion of a notification task with one or more executions
 +        TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
 +        assertEquals("NOT_SENT", execution.getStatus());
 +
 +        int i = 0;
 +        int maxit = 50;
 +        int executions = 0;
 +
 +        // wait for task exec completion (executions incremented)
 +        do {
 +            try {
 +                Thread.sleep(1000);
 +            } catch (InterruptedException e) {
 +            }
 +
 +            taskTO = taskService.read(taskTO.getKey());
 +
 +            assertNotNull(taskTO);
 +            assertNotNull(taskTO.getExecutions());
 +
 +            i++;
 +        } while (executions == taskTO.getExecutions().size() && i < maxit);
 +
 +        assertFalse(taskTO.getExecutions().isEmpty());
 +
 +        taskService.delete(taskTO.getKey());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE86() {
 +        // 1. create notification task
 +        String sender = "syncope86@syncope.apache.org";
 +        createNotificationTask(sender);
 +
 +        // 2. get NotificationTaskTO for user just created
 +        NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
 +        assertNotNull(taskTO);
 +        assertTrue(taskTO.getExecutions().isEmpty());
 +
 +        try {
 +            // 3. execute the generated NotificationTask
 +            TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
 +            assertNotNull(execution);
 +
 +            // 4. verify
 +            taskTO = taskService.read(taskTO.getKey());
 +            assertNotNull(taskTO);
 +            assertEquals(1, taskTO.getExecutions().size());
 +        } finally {
 +            // Remove execution to make test re-runnable
 +            taskService.deleteExecution(taskTO.getExecutions().get(0).getKey());
 +        }
 +    }
 +
-     private NotificationTaskTO findNotificationTaskBySender(final String sender) {
-         PagedResult<NotificationTaskTO> tasks = taskService.list(TaskType.NOTIFICATION);
-         assertNotNull(tasks);
-         assertFalse(tasks.getResult().isEmpty());
-         NotificationTaskTO taskTO = null;
-         for (NotificationTaskTO task : tasks.getResult()) {
-             if (sender.equals(task.getSender())) {
-                 taskTO = task;
-             }
-         }
-         return taskTO;
-     }
- 
 +    private void createNotificationTask(final String sender) {
 +        // 1. Create notification
 +        NotificationTO notification = new NotificationTO();
 +        notification.setTraceLevel(TraceLevel.FAILURES);
 +        notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
 +
 +        notification.setUserAbout(SyncopeClient.getUserSearchConditionBuilder().hasRoles(7L).query());
 +
 +        notification.setRecipients(SyncopeClient.getUserSearchConditionBuilder().hasRoles(8L).query());
 +        notification.setSelfAsRecipient(true);
 +
 +        notification.setRecipientAttrName("email");
 +        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
 +
 +        notification.setSender(sender);
 +        String subject = "Test notification";
 +        notification.setSubject(subject);
 +        notification.setTemplate("optin");
 +        notification.setActive(true);
 +
 +        Response response = notificationService.create(notification);
 +        notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
 +        assertNotNull(notification);
 +
 +        // 2. create user
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope@syncope.apache.org");
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRoleId(7);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
index d7d354f,0000000..3427a1d
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
@@@ -1,353 -1,0 +1,401 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.to.AbstractTaskTO;
 +import org.apache.syncope.common.lib.to.MappingItemTO;
 +import org.apache.syncope.common.lib.to.MappingTO;
++import org.apache.syncope.common.lib.to.NotificationTO;
++import org.apache.syncope.common.lib.to.NotificationTaskTO;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.PlainSchemaTO;
 +import org.apache.syncope.common.lib.to.PushTaskTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.syncope.common.lib.to.RoleTO;
 +import org.apache.syncope.common.lib.to.TaskExecTO;
 +import org.apache.syncope.common.lib.types.AttrSchemaType;
 +import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.types.MappingPurpose;
 +import org.apache.syncope.common.lib.types.MatchingRule;
 +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 +import org.apache.syncope.common.lib.types.SchemaType;
 +import org.apache.syncope.common.lib.types.SubjectType;
 +import org.apache.syncope.common.lib.types.TaskType;
++import org.apache.syncope.common.lib.types.TraceLevel;
 +import org.apache.syncope.common.lib.types.UnmatchingRule;
++import org.apache.syncope.common.rest.api.service.NotificationService;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.TaskService;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +import org.springframework.jdbc.core.JdbcTemplate;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class PushTaskITCase extends AbstractTaskITCase {
 +
 +    @Test
 +    public void getPushActionsClasses() {
 +        List<String> actions = syncopeService.info().getPushActions();
 +        assertNotNull(actions);
 +    }
 +
 +    @Test
 +    public void read() {
 +        PushTaskTO pushTaskTO = taskService.<PushTaskTO>read(17L);
 +        assertEquals(UnmatchingRule.ASSIGN, pushTaskTO.getUnmatchingRule());
 +        assertEquals(MatchingRule.UPDATE, pushTaskTO.getMatchingRule());
 +    }
 +
 +    @Test
 +    public void list() {
 +        final PagedResult<PushTaskTO> tasks = taskService.list(TaskType.PUSH);
 +        assertFalse(tasks.getResult().isEmpty());
 +        for (AbstractTaskTO task : tasks.getResult()) {
 +            if (!(task instanceof PushTaskTO)) {
 +                fail();
 +            }
 +        }
 +    }
 +
 +    @Test
 +    public void createPushTask() {
 +        PushTaskTO task = new PushTaskTO();
 +        task.setName("Test create Push");
 +        task.setResource(RESOURCE_NAME_WS2);
 +        task.setUserFilter(
 +                SyncopeClient.getUserSearchConditionBuilder().hasNotResources(RESOURCE_NAME_TESTDB2).query());
 +        task.setRoleFilter(
 +                SyncopeClient.getRoleSearchConditionBuilder().isNotNull("cool").query());
 +        task.setMatchingRule(MatchingRule.LINK);
 +
 +        final Response response = taskService.create(task);
 +        final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
 +        assertNotNull(actual);
 +
 +        task = taskService.read(actual.getKey());
 +        assertNotNull(task);
 +        assertEquals(task.getKey(), actual.getKey());
 +        assertEquals(task.getJobClassName(), actual.getJobClassName());
 +        assertEquals(task.getUserFilter(), actual.getUserFilter());
 +        assertEquals(task.getRoleFilter(), actual.getRoleFilter());
 +        assertEquals(UnmatchingRule.ASSIGN, actual.getUnmatchingRule());
 +        assertEquals(MatchingRule.LINK, actual.getMatchingRule());
 +    }
 +
 +    @Test
 +    public void pushMatchingUnmatchingRoles() {
 +        assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
 +
 +        execSyncTask(23L, 50, false);
 +
 +        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
 +        assertTrue(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
 +
 +        execSyncTask(23L, 50, false);
 +
 +        assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
 +        assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
 +    }
 +
 +    @Test
 +    public void pushUnmatchingUsers() throws Exception {
 +        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertTrue(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +
 +        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
 +
 +        // ------------------------------------------
 +        // Unmatching --> Assign --> dryRuyn
 +        // ------------------------------------------
 +        execSyncTask(13L, 50, true);
 +        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
 +        assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        // ------------------------------------------
 +
 +        final Set<Long> pushTaskIds = new HashSet<>();
 +        pushTaskIds.add(13L);
 +        pushTaskIds.add(14L);
 +        pushTaskIds.add(15L);
 +        pushTaskIds.add(16L);
 +        execSyncTasks(pushTaskIds, 50, false);
 +
 +        // ------------------------------------------
 +        // Unatching --> Ignore
 +        // ------------------------------------------
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
 +        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        // ------------------------------------------
 +
 +        // ------------------------------------------
 +        // Unmatching --> Assign
 +        // ------------------------------------------
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
 +        assertTrue(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        jdbcTemplate.execute("DELETE FROM test2 WHERE ID='vivaldi'");
 +        // ------------------------------------------
 +
 +        // ------------------------------------------
 +        // Unmatching --> Provision
 +        // ------------------------------------------
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='bellini'").size());
 +        assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        jdbcTemplate.execute("DELETE FROM test2 WHERE ID='bellini'");
 +        // ------------------------------------------
 +
 +        // ------------------------------------------
 +        // Unmatching --> Unlink
 +        // ------------------------------------------
 +        assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
 +        assertFalse(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        // ------------------------------------------
 +    }
 +
 +    @Test
 +    public void pushMatchingUser() throws Exception {
 +        assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +
 +        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
 +
 +        // ------------------------------------------
 +        // Matching --> Deprovision --> dryRuyn
 +        // ------------------------------------------
 +        execSyncTask(19L, 50, true);
 +        assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
 +        // ------------------------------------------
 +
 +        final Set<Long> pushTaskIds = new HashSet<>();
 +        pushTaskIds.add(18L);
 +        pushTaskIds.add(19L);
 +        pushTaskIds.add(16L);
 +
 +        execSyncTasks(pushTaskIds, 50, false);
 +
 +        // ------------------------------------------
 +        // Matching --> Deprovision && Ignore
 +        // ------------------------------------------
 +        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        // DELETE Capability not available ....
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
 +        // ------------------------------------------
 +
 +        // ------------------------------------------
 +        // Matching --> Unassign
 +        // ------------------------------------------
 +        assertFalse(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        // DELETE Capability not available ....
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
 +        // ------------------------------------------
 +
 +        // ------------------------------------------
 +        // Matching --> Link
 +        // ------------------------------------------
 +        execSyncTask(20L, 50, false);
 +        assertTrue(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
 +        // ------------------------------------------
 +
 +        pushTaskIds.clear();
 +        pushTaskIds.add(21L);
 +        pushTaskIds.add(22L);
 +
 +        execSyncTasks(pushTaskIds, 50, false);
 +
 +        // ------------------------------------------
 +        // Matching --> Unlink && Update
 +        // ------------------------------------------
 +        assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
 +        assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
 +        // ------------------------------------------
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE598() {
 +        // create a new role schema
 +        final PlainSchemaTO schemaTO = new PlainSchemaTO();
 +        schemaTO.setKey("LDAPGroupName" + getUUIDString());
 +        schemaTO.setType(AttrSchemaType.String);
 +        schemaTO.setMandatoryCondition("true");
 +
 +        final PlainSchemaTO newPlainSchemaTO = createSchema(AttributableType.ROLE, SchemaType.PLAIN, schemaTO);
 +        assertEquals(schemaTO, newPlainSchemaTO);
 +
 +        // create a new sample role
 +        RoleTO roleTO = new RoleTO();
 +        roleTO.setName("all" + getUUIDString());
 +        roleTO.setParent(8L);
 +
 +        roleTO.getRPlainAttrTemplates().add(newPlainSchemaTO.getKey());
 +        roleTO.getPlainAttrs().add(attrTO(newPlainSchemaTO.getKey(), "all"));
 +
 +        roleTO = createRole(roleTO);
 +        assertNotNull(roleTO);
 +
 +        String resourceName = "resource-ldap-roleonly";
 +        ResourceTO newResourceTO = null;
 +
 +        try {
 +            // Create resource ad-hoc
 +            ResourceTO resourceTO = new ResourceTO();
 +            resourceTO.setKey(resourceName);
 +            resourceTO.setConnectorId(105L);
 +
 +            final MappingTO umapping = new MappingTO();
 +            MappingItemTO item = new MappingItemTO();
 +            item.setIntMappingType(IntMappingType.Username);
 +            item.setExtAttrName("cn");
 +            item.setAccountid(true);
 +            item.setPurpose(MappingPurpose.PROPAGATION);
 +            item.setMandatoryCondition("true");
 +            umapping.setAccountIdItem(item);
 +
 +            item = new MappingItemTO();
 +            item.setIntMappingType(IntMappingType.UserPlainSchema);
 +            item.setExtAttrName("surname");
 +            item.setIntAttrName("sn");
 +            item.setPurpose(MappingPurpose.BOTH);
 +            umapping.addItem(item);
 +
 +            item = new MappingItemTO();
 +            item.setIntMappingType(IntMappingType.UserPlainSchema);
 +            item.setExtAttrName("email");
 +            item.setIntAttrName("mail");
 +            item.setPurpose(MappingPurpose.BOTH);
 +            umapping.addItem(item);
 +
 +            item = new MappingItemTO();
 +            item.setIntMappingType(IntMappingType.Password);
 +            item.setPassword(true);
 +            item.setPurpose(MappingPurpose.BOTH);
 +            item.setMandatoryCondition("true");
 +            umapping.addItem(item);
 +
 +            umapping.setAccountLink("'cn=' + username + ',ou=people,o=isp'");
 +
 +            final MappingTO rmapping = new MappingTO();
 +
 +            item = new MappingItemTO();
 +            item.setIntMappingType(IntMappingType.RolePlainSchema);
 +            item.setExtAttrName("cn");
 +            item.setIntAttrName(newPlainSchemaTO.getKey());
 +            item.setAccountid(true);
 +            item.setPurpose(MappingPurpose.BOTH);
 +            rmapping.setAccountIdItem(item);
 +
 +            rmapping.setAccountLink("'cn=' + " + newPlainSchemaTO.getKey() + " + ',ou=groups,o=isp'");
 +
 +            resourceTO.setRmapping(rmapping);
 +
 +            Response response = resourceService.create(resourceTO);
 +            newResourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +
 +            assertNotNull(newResourceTO);
 +            assertNull(newResourceTO.getUmapping());
 +            assertNotNull(newResourceTO.getRmapping());
 +
 +            // create push task ad-hoc
 +            final PushTaskTO task = new PushTaskTO();
 +            task.setName("issueSYNCOPE598");
 +            task.setResource(resourceName);
 +            task.setPerformCreate(true);
 +            task.setPerformDelete(true);
 +            task.setPerformUpdate(true);
 +            task.setUnmatchingRule(UnmatchingRule.ASSIGN);
 +            task.setMatchingRule(MatchingRule.UPDATE);
 +
 +            response = taskService.create(task);
 +            final PushTaskTO push = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
 +
 +            assertNotNull(push);
 +
 +            // execute the new task
 +            final TaskExecTO pushExec = execSyncTask(push.getKey(), 50, false);
 +            assertTrue(PropagationTaskExecStatus.valueOf(pushExec.getStatus()).isSuccessful());
 +        } finally {
 +            roleService.delete(roleTO.getKey());
 +            if (newResourceTO != null) {
 +                resourceService.delete(resourceName);
 +            }
 +        }
 +    }
++
++    @Test
++    public void issueSYNCOPE648() {
++        //1. Create Push Task
++        final PushTaskTO task = new PushTaskTO();
++        task.setName("Test create Push");
++        task.setResource(RESOURCE_NAME_LDAP);
++        task.setUserFilter(
++                SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("_NO_ONE_").query());
++        task.setRoleFilter(
++                SyncopeClient.getRoleSearchConditionBuilder().is("name").equalTo("citizen").query());
++        task.setMatchingRule(MatchingRule.IGNORE);
++        task.setUnmatchingRule(UnmatchingRule.IGNORE);
++
++        final Response response = taskService.create(task);
++        final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
++        assertNotNull(actual);
++
++        // 2. Create notification
++        NotificationTO notification = new NotificationTO();
++        notification.setTraceLevel(TraceLevel.FAILURES);
++        notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[matchingrule_ignore]:[SUCCESS]");
++        notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[unmatchingrule_ignore]:[SUCCESS]");
++
++        notification.getStaticRecipients().add("issueyncope648@syncope.apache.org");
++        notification.setSelfAsRecipient(false);
++        notification.setRecipientAttrName("email");
++        notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
++
++        notification.setSender("syncope648@syncope.apache.org");
++        String subject = "Test notification";
++        notification.setSubject(subject);
++        notification.setTemplate("optin");
++        notification.setActive(true);
++
++        Response responseNotification = notificationService.create(notification);
++        notification = getObject(responseNotification.getLocation(), NotificationService.class, NotificationTO.class);
++        assertNotNull(notification);
++
++        execSyncTask(actual.getKey(), 50, false);
++
++        NotificationTaskTO taskTO = findNotificationTaskBySender("syncope648@syncope.apache.org");
++        assertNotNull(taskTO);
++    }
 +}