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 2013/10/14 16:45:21 UTC

svn commit: r1531921 [1/2] - in /syncope/branches/1_1_X: common/src/main/java/org/apache/syncope/common/mod/ common/src/main/java/org/apache/syncope/common/to/ core/ core/src/main/java/org/apache/syncope/core/propagation/ core/src/main/java/org/apache/...

Author: ilgrosso
Date: Mon Oct 14 14:45:20 2013
New Revision: 1531921

URL: http://svn.apache.org/r1531921
Log:
[SYNCOPE-420] Implementation provided for users and roles, either when acting via REST or via SyncJob

Added:
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationReporter.java
      - copied, changed from r1531834, syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationHandler.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationReporter.java
      - copied, changed from r1531834, syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationHandler.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java   (with props)
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java   (with props)
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java   (with props)
Removed:
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationHandler.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationHandler.java
Modified:
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/mod/AbstractAttributableMod.java
    syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/AbstractAttributableTO.java
    syncope/branches/1_1_X/core/pom.xml
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationTaskExecutor.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
    syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
    syncope/branches/1_1_X/core/src/main/resources/restContext.xml
    syncope/branches/1_1_X/core/src/main/resources/syncopeContext.xml
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/persistence/dao/SchemaTest.java
    syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
    syncope/branches/1_1_X/core/src/test/resources/content.xml
    syncope/branches/1_1_X/core/src/test/resources/persistenceTestEnv.xml

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/mod/AbstractAttributableMod.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/mod/AbstractAttributableMod.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/mod/AbstractAttributableMod.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/mod/AbstractAttributableMod.java Mon Oct 14 14:45:20 2013
@@ -36,14 +36,23 @@ import org.apache.syncope.common.Abstrac
 public abstract class AbstractAttributableMod extends AbstractBaseBean {
 
     private static final long serialVersionUID = 3241118574016303198L;
+
     protected long id;
+
     protected Set<AttributeMod> attributesToBeUpdated;
+
     protected Set<String> attributesToBeRemoved;
+
     protected Set<String> derivedAttributesToBeAdded;
+
     protected Set<String> derivedAttributesToBeRemoved;
+
     protected Set<AttributeMod> virtualAttributesToBeUpdated;
+
     protected Set<String> virtualAttributesToBeRemoved;
+
     protected Set<String> resourcesToBeAdded;
+
     protected Set<String> resourcesToBeRemoved;
 
     /**
@@ -71,9 +80,9 @@ public abstract class AbstractAttributab
     }
 
     /**
-     * Convenience method for removing entire attribute instead removing each value in an AttributeMod object
+     * Convenience method for removing entire attribute instead removing each value in an AttributeMod object.
      *
-     * @param name (schema) of attribute to be removed.
+     * @param attribute (schema) of attribute to be removed.
      * @return true on success.
      */
     public boolean addAttributeToBeRemoved(String attribute) {
@@ -81,9 +90,9 @@ public abstract class AbstractAttributab
     }
 
     /**
-     * Convenience method for removing entire attribute instead removing each value in an AttributeMod object
+     * Convenience method for removing entire attribute instead removing each value in an AttributeMod object.
      *
-     * @param name (schema) of attribute to be removed.
+     * @param attribute (schema) of attribute to be removed.
      * @return true on success.
      */
     public boolean removeAttributeToBeRemoved(String attribute) {
@@ -160,7 +169,6 @@ public abstract class AbstractAttributab
     }
 
     public void setDerivedAttributesToBeAdded(Set<String> derivedAttributesToBeAdded) {
-
         this.derivedAttributesToBeAdded = derivedAttributesToBeAdded;
     }
 

Modified: syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/AbstractAttributableTO.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/AbstractAttributableTO.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/AbstractAttributableTO.java (original)
+++ syncope/branches/1_1_X/common/src/main/java/org/apache/syncope/common/to/AbstractAttributableTO.java Mon Oct 14 14:45:20 2013
@@ -111,8 +111,10 @@ public abstract class AbstractAttributab
     }
 
     public void setDerivedAttributes(final List<AttributeTO> derivedAttributes) {
-
-        this.derivedAttributes = derivedAttributes;
+        this.derivedAttributes.clear();
+        if (derivedAttributes != null && !derivedAttributes.isEmpty()) {
+            this.derivedAttributes.addAll(derivedAttributes);
+        }
     }
 
     public boolean addVirtualAttribute(final AttributeTO virtualAttribute) {
@@ -130,7 +132,10 @@ public abstract class AbstractAttributab
     }
 
     public void setVirtualAttributes(final List<AttributeTO> virtualAttributes) {
-        this.virtualAttributes = virtualAttributes;
+        this.virtualAttributes.clear();
+        if (virtualAttributes != null && !virtualAttributes.isEmpty()) {
+            this.virtualAttributes.addAll(virtualAttributes);
+        }
     }
 
     public boolean addResource(final String resource) {

Modified: syncope/branches/1_1_X/core/pom.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/pom.xml?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/pom.xml (original)
+++ syncope/branches/1_1_X/core/pom.xml Mon Oct 14 14:45:20 2013
@@ -425,8 +425,13 @@ under the License.
             <configuration>
               <target>
                 <unzip src="${project.build.directory}/${project.build.finalName}.war" dest="${cargo.run.dir}" />
-                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncActions.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" />
-                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncRule.class" todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" />
+                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncActions.class" 
+                      todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" />
+                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncRule.class" 
+                      todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync" />
+                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.class" 
+                      todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/rest/data" 
+                      overwrite="true"/>
                 <copy file="${project.build.directory}/test-classes/db.jsp" todir="${cargo.run.dir}" />
                 <copy todir="${cargo.run.dir}/WEB-INF/classes" includeEmptyDirs="false">
                   <fileset dir="${project.build.directory}/test-classes">

Copied: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationReporter.java (from r1531834, syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationHandler.java)
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationReporter.java?p2=syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationReporter.java&p1=syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationHandler.java&r1=1531834&r2=1531921&rev=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationHandler.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationReporter.java Mon Oct 14 14:45:20 2013
@@ -18,17 +18,20 @@
  */
 package org.apache.syncope.core.propagation;
 
+import java.util.List;
+import org.apache.syncope.common.to.PropagationStatusTO;
 import org.apache.syncope.common.types.PropagationTaskExecStatus;
+import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 /**
- * Handle propagation executions.
+ * Report propagation status after executions.
  */
-public interface PropagationHandler {
+public interface PropagationReporter {
 
     /**
-     *
-     * Handle propagation executions.
+     * Report propagation status after executions in case of success or non-blocking failure
+     * (e.g. on secondary resources).
      *
      * @param resourceName resource name.
      * @param execStatus propagation execution status.
@@ -36,6 +39,20 @@ public interface PropagationHandler {
      * @param beforeObj retrieved connector object before operation execution.
      * @param afterObj retrieved connector object after operation execution.
      */
-    void handle(String resourceName, PropagationTaskExecStatus execStatus,
+    void onSuccessOrSecondaryResourceFailures(String resourceName, PropagationTaskExecStatus execStatus,
             String failureReason, ConnectorObject beforeObj, ConnectorObject afterObj);
+
+    /**
+     * Report propagation status after executions in case blocking failure (e.g. on primary resources).
+     * 
+     * @param tasks propagation tasks performed before failure
+     */
+    void onPrimaryResourceFailure(List<PropagationTask> tasks);
+
+    /**
+     * Returns the list of propagation statuses.
+     *
+     * @return the list of propagation statuses
+     */
+    List<PropagationStatusTO> getStatuses();
 }

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationTaskExecutor.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationTaskExecutor.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationTaskExecutor.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationTaskExecutor.java Mon Oct 14 14:45:20 2013
@@ -42,10 +42,10 @@ public interface PropagationTaskExecutor
      * Execute the given PropagationTask, invoke the given handler and returns the generated TaskExec.
      *
      * @param task to be executed
-     * @param handler to be invoked
+     * @param reporter to report propagation execution status
      * @return the generated TaskExec
      */
-    TaskExec execute(PropagationTask task, PropagationHandler handler);
+    TaskExec execute(PropagationTask task, PropagationReporter reporter);
 
     /**
      * Execute a collection of PropagationTask objects.
@@ -60,7 +60,7 @@ public interface PropagationTaskExecutor
      * The process is interrupted as soon as the result of the communication with a primary resource is in error.
      *
      * @param tasks to be execute, in given order
-     * @param handler propagation handler
+     * @param reporter to report propagation execution status
      */
-    void execute(Collection<PropagationTask> tasks, PropagationHandler handler);
+    void execute(Collection<PropagationTask> tasks, PropagationReporter reporter);
 }

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java Mon Oct 14 14:45:20 2013
@@ -40,7 +40,7 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.propagation.ConnectorFactory;
 import org.apache.syncope.core.propagation.DefaultPropagationActions;
 import org.apache.syncope.core.propagation.PropagationActions;
-import org.apache.syncope.core.propagation.PropagationHandler;
+import org.apache.syncope.core.propagation.PropagationReporter;
 import org.apache.syncope.core.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.propagation.Connector;
 import org.apache.syncope.core.propagation.TimeoutException;
@@ -266,7 +266,7 @@ public abstract class AbstractPropagatio
     }
 
     @Override
-    public TaskExec execute(final PropagationTask task, final PropagationHandler handler) {
+    public TaskExec execute(final PropagationTask task, final PropagationReporter reporter) {
         final PropagationActions actions = getPropagationActions(task.getResource());
 
         final Date startDate = new Date();
@@ -321,10 +321,10 @@ public abstract class AbstractPropagatio
                 exceptionWriter.write(e.getMessage() + "\n\n");
                 e.printStackTrace(new PrintWriter(exceptionWriter));
                 taskExecutionMessage = exceptionWriter.toString();
-                if (e.getCause() != null) {
-                    failureReason = e.getMessage() + "\n\n Cause: " + e.getCause().getMessage().split("\n")[0];
-                } else {
+                if (e.getCause() == null) {
                     failureReason = e.getMessage();
+                } else {
+                    failureReason = e.getMessage() + "\n\n Cause: " + e.getCause().getMessage().split("\n")[0];
                 }
             }
 
@@ -370,8 +370,8 @@ public abstract class AbstractPropagatio
                 taskDAO.flush();
             }
 
-            if (handler != null) {
-                handler.handle(
+            if (reporter != null) {
+                reporter.onSuccessOrSecondaryResourceFailures(
                         task.getResource().getName(),
                         PropagationTaskExecStatus.valueOf(execution.getStatus()),
                         failureReason,
@@ -391,7 +391,7 @@ public abstract class AbstractPropagatio
     }
 
     @Override
-    public abstract void execute(Collection<PropagationTask> tasks, PropagationHandler handler);
+    public abstract void execute(Collection<PropagationTask> tasks, final PropagationReporter reporter);
 
     /**
      * Check whether an execution has to be stored, for a given task.

Copied: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationReporter.java (from r1531834, syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationHandler.java)
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationReporter.java?p2=syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationReporter.java&p1=syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationHandler.java&r1=1531834&r2=1531921&rev=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationHandler.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/DefaultPropagationReporter.java Mon Oct 14 14:45:20 2013
@@ -18,33 +18,30 @@
  */
 package org.apache.syncope.core.propagation.impl;
 
+import java.util.ArrayList;
 import java.util.List;
 import org.apache.syncope.common.to.PropagationStatusTO;
 import org.apache.syncope.common.types.PropagationTaskExecStatus;
 import org.apache.syncope.core.connid.ConnObjectUtil;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
-import org.apache.syncope.core.propagation.PropagationHandler;
+import org.apache.syncope.core.propagation.PropagationReporter;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 
-public class DefaultPropagationHandler implements PropagationHandler {
+public class DefaultPropagationReporter implements PropagationReporter {
 
-    protected static final Logger LOG = LoggerFactory.getLogger(DefaultPropagationHandler.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(DefaultPropagationReporter.class);
 
-    private final ConnObjectUtil connObjectUtil;
+    @Autowired
+    protected ConnObjectUtil connObjectUtil;
 
-    private final List<PropagationStatusTO> propagations;
-
-    public DefaultPropagationHandler(final ConnObjectUtil connObjectUtil,
-            final List<PropagationStatusTO> propagations) {
-
-        this.connObjectUtil = connObjectUtil;
-        this.propagations = propagations;
-    }
+    protected final List<PropagationStatusTO> statuses = new ArrayList<PropagationStatusTO>();
 
     @Override
-    public void handle(final String resource, final PropagationTaskExecStatus executionStatus,
+    public void onSuccessOrSecondaryResourceFailures(final String resource,
+            final PropagationTaskExecStatus executionStatus,
             final String failureReason, final ConnectorObject beforeObj, final ConnectorObject afterObj) {
 
         final PropagationStatusTO propagation = new PropagationStatusTO();
@@ -60,34 +57,38 @@ public class DefaultPropagationHandler i
             propagation.setAfterObj(connObjectUtil.getConnObjectTO(afterObj));
         }
 
-        propagations.add(propagation);
+        statuses.add(propagation);
     }
 
-    public void completeWhenPrimaryResourceErrored(
-            final List<PropagationStatusTO> propagations, final List<PropagationTask> tasks) {
+    private boolean containsPropagationStatusTO(final String resourceName) {
+        for (PropagationStatusTO status : statuses) {
+            if (resourceName.equals(status.getResource())) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-        final String failedResource = propagations.get(propagations.size() - 1).getResource();
+    @Override
+    public void onPrimaryResourceFailure(final List<PropagationTask> tasks) {
+        final String failedResource = statuses.get(statuses.size() - 1).getResource();
 
         LOG.debug("Propagation error: {} primary resource failed to propagate", failedResource);
 
         for (PropagationTask propagationTask : tasks) {
-            if (!containsPropagationStatusTO(propagationTask.getResource().getName(), propagations)) {
+            if (!containsPropagationStatusTO(propagationTask.getResource().getName())) {
                 final PropagationStatusTO propagationStatusTO = new PropagationStatusTO();
                 propagationStatusTO.setResource(propagationTask.getResource().getName());
                 propagationStatusTO.setStatus(PropagationTaskExecStatus.FAILURE);
                 propagationStatusTO.setFailureReason(
                         "Propagation error: " + failedResource + " primary resource failed to propagate.");
-                propagations.add(propagationStatusTO);
+                statuses.add(propagationStatusTO);
             }
         }
     }
 
-    private boolean containsPropagationStatusTO(final String resource, final List<PropagationStatusTO> propagations) {
-        for (PropagationStatusTO status : propagations) {
-            if (resource.equals(status.getResource())) {
-                return true;
-            }
-        }
-        return false;
+    @Override
+    public List<PropagationStatusTO> getStatuses() {
+        return statuses;
     }
 }

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java Mon Oct 14 14:45:20 2013
@@ -28,7 +28,7 @@ import org.apache.syncope.common.types.P
 import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.TaskExec;
 import org.apache.syncope.core.propagation.PropagationException;
-import org.apache.syncope.core.propagation.PropagationHandler;
+import org.apache.syncope.core.propagation.PropagationReporter;
 
 public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExecutor {
 
@@ -37,14 +37,14 @@ public class PriorityPropagationTaskExec
      * {@inheritDoc}
      */
     @Override
-    public void execute(final Collection<PropagationTask> tasks, final PropagationHandler handler) {
+    public void execute(final Collection<PropagationTask> tasks, final PropagationReporter reporter) {
         final List<PropagationTask> prioritizedTasks = new ArrayList<PropagationTask>(tasks);
         Collections.sort(prioritizedTasks, new PriorityComparator());
 
         for (PropagationTask task : prioritizedTasks) {
             LOG.debug("Execution started for {}", task);
 
-            TaskExec execution = execute(task, handler);
+            TaskExec execution = execute(task, reporter);
 
             LOG.debug("Execution finished for {}, {}", task, execution);
 

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java Mon Oct 14 14:45:20 2013
@@ -20,6 +20,7 @@ package org.apache.syncope.core.propagat
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -133,7 +134,7 @@ public class PropagationManager {
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
      */
     public List<PropagationTask> getUserCreateTaskIds(final WorkflowResult<Map.Entry<Long, Boolean>> wfResult,
-            final String password, final List<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
+            final String password, final Collection<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeUser user = userDataBinder.getUserFromId(wfResult.getResult().getKey());
@@ -171,7 +172,7 @@ public class PropagationManager {
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
      */
     public List<PropagationTask> getRoleCreateTaskIds(final WorkflowResult<Long> wfResult,
-            final List<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
+            final Collection<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeRole role = roleDataBinder.getRoleFromId(wfResult.getResult());
@@ -182,7 +183,7 @@ public class PropagationManager {
     }
 
     protected List<PropagationTask> getCreateTaskIds(final AbstractAttributable attributable,
-            final String password, final List<AttributeTO> vAttrs, final Boolean enable,
+            final String password, final Collection<AttributeTO> vAttrs, final Boolean enable,
             final PropagationByResource propByRes, final Set<String> noPropResourceNames) {
 
         if (propByRes == null || propByRes.isEmpty()) {

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/RoleController.java Mon Oct 14 14:45:20 2013
@@ -26,7 +26,6 @@ import javax.servlet.http.HttpServletRes
 import org.apache.syncope.common.mod.RoleMod;
 import org.apache.syncope.common.search.NodeCond;
 import org.apache.syncope.common.services.InvalidSearchConditionException;
-import org.apache.syncope.common.to.PropagationStatusTO;
 import org.apache.syncope.common.to.RoleTO;
 import org.apache.syncope.common.types.AttributableType;
 import org.apache.syncope.common.types.AuditElements;
@@ -34,7 +33,6 @@ import org.apache.syncope.common.types.A
 import org.apache.syncope.common.types.AuditElements.Result;
 import org.apache.syncope.common.types.AuditElements.RoleSubCategory;
 import org.apache.syncope.core.audit.AuditManager;
-import org.apache.syncope.core.connid.ConnObjectUtil;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
 import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
@@ -43,10 +41,12 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.persistence.dao.RoleDAO;
 import org.apache.syncope.core.persistence.dao.UserDAO;
 import org.apache.syncope.core.propagation.PropagationException;
+import org.apache.syncope.core.propagation.PropagationReporter;
 import org.apache.syncope.core.propagation.PropagationTaskExecutor;
-import org.apache.syncope.core.propagation.impl.DefaultPropagationHandler;
 import org.apache.syncope.core.propagation.impl.PropagationManager;
+import org.apache.syncope.core.rest.data.AttributableTransformer;
 import org.apache.syncope.core.rest.data.RoleDataBinder;
+import org.apache.syncope.core.util.ApplicationContextProvider;
 import org.apache.syncope.core.util.AttributableUtil;
 import org.apache.syncope.core.util.EntitlementUtil;
 import org.apache.syncope.core.workflow.WorkflowResult;
@@ -74,34 +74,31 @@ public class RoleController {
     protected static final Logger LOG = LoggerFactory.getLogger(RoleController.class);
 
     @Autowired
-    private AuditManager auditManager;
+    protected AuditManager auditManager;
 
     @Autowired
-    private RoleDAO roleDAO;
+    protected RoleDAO roleDAO;
 
     @Autowired
-    private UserDAO userDAO;
+    protected UserDAO userDAO;
 
     @Autowired
-    private AttributableSearchDAO searchDAO;
+    protected AttributableSearchDAO searchDAO;
 
     @Autowired
-    private RoleDataBinder binder;
+    protected RoleDataBinder binder;
 
     @Autowired
-    private RoleWorkflowAdapter rwfAdapter;
+    protected RoleWorkflowAdapter rwfAdapter;
 
     @Autowired
-    private PropagationManager propagationManager;
+    protected PropagationManager propagationManager;
 
     @Autowired
-    private PropagationTaskExecutor taskExecutor;
+    protected PropagationTaskExecutor taskExecutor;
 
-    /**
-     * ConnectorObject util.
-     */
     @Autowired
-    private ConnObjectUtil connObjectUtil;
+    protected AttributableTransformer attrTransformer;
 
     @PreAuthorize("hasRole('ROLE_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/read/{roleId}")
@@ -270,28 +267,36 @@ public class RoleController {
     public RoleTO create(final HttpServletResponse response, @RequestBody final RoleTO roleTO) {
         LOG.debug("Role create called with parameters {}", roleTO);
 
+        // Check that this operation is allowed to be performed by caller
         Set<Long> allowedRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames());
         if (roleTO.getParent() != 0 && !allowedRoleIds.contains(roleTO.getParent())) {
             throw new UnauthorizedRoleException(roleTO.getParent());
         }
 
-        WorkflowResult<Long> created = rwfAdapter.create(roleTO);
+        // Attributable transformation (if configured)
+        RoleTO actual = attrTransformer.transform(roleTO);
+        LOG.debug("Transformed: {}", actual);
+
+        /*
+         * Actual operations: workflow, propagation
+         */
 
-        EntitlementUtil.extendAuthContext(created.getResult());
+        WorkflowResult<Long> created = rwfAdapter.create(actual);
 
-        List<PropagationTask> tasks = propagationManager.getRoleCreateTaskIds(created, roleTO.getVirtualAttributes());
+        EntitlementUtil.extendAuthContext(created.getResult());
 
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+        List<PropagationTask> tasks = propagationManager.getRoleCreateTaskIds(created, actual.getVirtualAttributes());
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, new DefaultPropagationHandler(connObjectUtil, propagations));
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
         final RoleTO savedTO = binder.getRoleTO(created.getResult());
-        savedTO.setPropagationStatusTOs(propagations);
+        savedTO.setPropagationStatusTOs(propagationReporter.getStatuses());
 
         LOG.debug("About to return created role\n{}", savedTO);
 
@@ -307,23 +312,31 @@ public class RoleController {
     public RoleTO update(@RequestBody final RoleMod roleMod) {
         LOG.debug("Role update called with {}", roleMod);
 
+        // Check that this operation is allowed to be performed by caller
         SyncopeRole role = binder.getRoleFromId(roleMod.getId());
 
-        WorkflowResult<Long> updated = rwfAdapter.update(roleMod);
+        // Attribute value transformation (if configured)
+        RoleMod actual = attrTransformer.transform(roleMod);
+        LOG.debug("Transformed: {}", actual);
+
+        /*
+         * Actual operations: workflow, propagation
+         */
 
-        List<PropagationTask> tasks = propagationManager.getRoleUpdateTaskIds(updated,
-                roleMod.getVirtualAttributesToBeRemoved(), roleMod.getVirtualAttributesToBeUpdated());
+        WorkflowResult<Long> updated = rwfAdapter.update(actual);
 
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+        List<PropagationTask> tasks = propagationManager.getRoleUpdateTaskIds(updated,
+                actual.getVirtualAttributesToBeRemoved(), actual.getVirtualAttributesToBeUpdated());
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, new DefaultPropagationHandler(connObjectUtil, propagations));
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
         final RoleTO updatedTO = binder.getRoleTO(updated.getResult());
-        updatedTO.setPropagationStatusTOs(propagations);
+        updatedTO.setPropagationStatusTOs(propagationReporter.getStatuses());
 
         auditManager.audit(Category.role, RoleSubCategory.update, Result.success,
                 "Successfully updated role: " + role.getId());
@@ -351,15 +364,15 @@ public class RoleController {
         RoleTO roleTO = new RoleTO();
         roleTO.setId(roleId);
 
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, new DefaultPropagationHandler(connObjectUtil, propagations));
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
-        roleTO.setPropagationStatusTOs(propagations);
+        roleTO.setPropagationStatusTOs(propagationReporter.getStatuses());
 
         rwfAdapter.delete(roleId);
 

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java Mon Oct 14 14:45:20 2013
@@ -34,7 +34,6 @@ import org.apache.syncope.common.to.Bulk
 import org.apache.syncope.common.to.BulkActionRes.Status;
 import org.apache.syncope.common.to.MembershipTO;
 import org.apache.syncope.common.to.PropagationRequestTO;
-import org.apache.syncope.common.to.PropagationStatusTO;
 import org.apache.syncope.common.to.UserTO;
 import org.apache.syncope.common.to.WorkflowFormTO;
 import org.apache.syncope.common.types.AttributableType;
@@ -46,7 +45,6 @@ import org.apache.syncope.common.types.S
 import org.apache.syncope.common.validation.SyncopeClientCompositeErrorException;
 import org.apache.syncope.common.validation.SyncopeClientException;
 import org.apache.syncope.core.audit.AuditManager;
-import org.apache.syncope.core.connid.ConnObjectUtil;
 import org.apache.syncope.core.notification.NotificationManager;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
@@ -56,10 +54,12 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.persistence.dao.UserDAO;
 import org.apache.syncope.core.propagation.PropagationByResource;
 import org.apache.syncope.core.propagation.PropagationException;
+import org.apache.syncope.core.propagation.PropagationReporter;
 import org.apache.syncope.core.propagation.PropagationTaskExecutor;
-import org.apache.syncope.core.propagation.impl.DefaultPropagationHandler;
 import org.apache.syncope.core.propagation.impl.PropagationManager;
+import org.apache.syncope.core.rest.data.AttributableTransformer;
 import org.apache.syncope.core.rest.data.UserDataBinder;
+import org.apache.syncope.core.util.ApplicationContextProvider;
 import org.apache.syncope.core.util.AttributableUtil;
 import org.apache.syncope.core.util.EntitlementUtil;
 import org.apache.syncope.core.workflow.WorkflowResult;
@@ -93,47 +93,44 @@ public class UserController {
      * Logger.
      */
     protected static final Logger LOG = LoggerFactory.getLogger(UserController.class);
-    
+
     @Autowired
     protected AuditManager auditManager;
-    
+
     @Autowired
     protected UserDAO userDAO;
-    
+
     @Autowired
     protected RoleDAO roleDAO;
-    
+
     @Autowired
     protected AttributableSearchDAO searchDAO;
-    
+
     @Autowired
     protected UserDataBinder binder;
-    
+
     @Autowired
     protected UserWorkflowAdapter uwfAdapter;
-    
+
     @Autowired
     protected PropagationManager propagationManager;
-    
+
     @Autowired
     protected PropagationTaskExecutor taskExecutor;
-    
+
     @Autowired
     protected NotificationManager notificationManager;
 
-    /**
-     * ConnectorObject util.
-     */
     @Autowired
-    protected ConnObjectUtil connObjectUtil;
-    
+    protected AttributableTransformer attrTransformer;
+
     @RequestMapping(method = RequestMethod.GET, value = "/verifyPassword/{username}")
     public ModelAndView verifyPassword(@PathVariable("username") String username,
             @RequestParam("password") final String password) {
-        
+
         return new ModelAndView().addObject(verifyPasswordInternal(username, password));
     }
-    
+
     @PreAuthorize("hasRole('USER_READ')")
     @Transactional(readOnly = true)
     public Boolean verifyPasswordInternal(final String username, final String password) {
@@ -141,25 +138,25 @@ public class UserController {
                 "Verified password for: " + username);
         return binder.verifyPassword(username, password);
     }
-    
+
     @RequestMapping(method = RequestMethod.GET, value = "/count")
     public ModelAndView count() {
         return new ModelAndView().addObject(countInternal());
     }
-    
+
     @PreAuthorize("hasRole('USER_LIST')")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public int countInternal() {
         return userDAO.count(EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()));
     }
-    
+
     @RequestMapping(method = RequestMethod.POST, value = "/search/count")
     public ModelAndView searchCount(@RequestBody final NodeCond searchCondition)
             throws InvalidSearchConditionException {
-        
+
         return new ModelAndView().addObject(searchCountInternal(searchCondition));
     }
-    
+
     @PreAuthorize("hasRole('USER_READ')")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public int searchCountInternal(final NodeCond searchCondition) throws InvalidSearchConditionException {
@@ -167,239 +164,250 @@ public class UserController {
             LOG.error("Invalid search condition: {}", searchCondition);
             throw new InvalidSearchConditionException();
         }
-        
+
         return searchDAO.count(EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()),
                 searchCondition, AttributableUtil.getInstance(AttributableType.USER));
     }
-    
+
     @PreAuthorize("hasRole('USER_LIST')")
     @RequestMapping(method = RequestMethod.GET, value = "/list")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public List<UserTO> list() {
         List<SyncopeUser> users =
                 userDAO.findAll(EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()));
-        
+
         List<UserTO> userTOs = new ArrayList<UserTO>(users.size());
         for (SyncopeUser user : users) {
             userTOs.add(binder.getUserTO(user));
         }
-        
+
         auditManager.audit(Category.user, UserSubCategory.list, Result.success,
                 "Successfully listed all users: " + userTOs.size());
-        
+
         return userTOs;
     }
-    
+
     @PreAuthorize("hasRole('USER_LIST')")
     @RequestMapping(method = RequestMethod.GET, value = "/list/{page}/{size}")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public List<UserTO> list(@PathVariable("page") final int page, @PathVariable("size") final int size) {
         Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames());
-        
+
         List<SyncopeUser> users = userDAO.findAll(adminRoleIds, page, size);
         List<UserTO> userTOs = new ArrayList<UserTO>(users.size());
         for (SyncopeUser user : users) {
             userTOs.add(binder.getUserTO(user));
         }
-        
+
         auditManager.audit(Category.user, UserSubCategory.list, Result.success,
                 "Successfully listed all users (page=" + page + ", size=" + size + "): " + userTOs.size());
-        
+
         return userTOs;
     }
-    
+
     @PreAuthorize("hasRole('USER_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/read/{userId}")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public UserTO read(@PathVariable("userId") final Long userId) {
         UserTO result = binder.getUserTO(userId);
-        
+
         auditManager.audit(Category.user, UserSubCategory.read, Result.success,
                 "Successfully read user: " + userId);
-        
+
         return result;
     }
-    
+
     @PreAuthorize("#username == authentication.name or hasRole('USER_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/readByUsername/{username}")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public UserTO read(@PathVariable final String username) {
         UserTO result = binder.getUserTO(username);
-        
+
         auditManager.audit(Category.user, UserSubCategory.read, Result.success,
                 "Successfully read user: " + username);
-        
+
         return result;
     }
-    
+
     @PreAuthorize("isAuthenticated()")
     @RequestMapping(method = RequestMethod.GET, value = "/read/self")
     @Transactional(readOnly = true)
     public UserTO read() {
         UserTO userTO = binder.getAuthenticatedUserTO();
-        
+
         auditManager.audit(Category.user, UserSubCategory.read, Result.success,
                 "Successfully read own data: " + userTO.getUsername());
-        
+
         return userTO;
     }
-    
+
     @PreAuthorize("hasRole('USER_READ')")
     @RequestMapping(method = RequestMethod.POST, value = "/search")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public List<UserTO> search(@RequestBody final NodeCond searchCondition)
             throws InvalidSearchConditionException {
-        
+
         return search(searchCondition, -1, -1);
     }
-    
+
     @PreAuthorize("hasRole('USER_READ')")
     @RequestMapping(method = RequestMethod.POST, value = "/search/{page}/{size}")
     @Transactional(readOnly = true, rollbackFor = {Throwable.class})
     public List<UserTO> search(@RequestBody final NodeCond searchCondition, @PathVariable("page") final int page,
             @PathVariable("size") final int size)
             throws InvalidSearchConditionException {
-        
+
         LOG.debug("User search called with condition {}", searchCondition);
-        
+
         if (!searchCondition.isValid()) {
             LOG.error("Invalid search condition: {}", searchCondition);
             throw new InvalidSearchConditionException();
         }
-        
+
         final List<SyncopeUser> matchingUsers = searchDAO.search(EntitlementUtil.getRoleIds(EntitlementUtil.
                 getOwnedEntitlementNames()), searchCondition, page, size,
                 AttributableUtil.getInstance(AttributableType.USER));
-        
+
         final List<UserTO> result = new ArrayList<UserTO>(matchingUsers.size());
         for (SyncopeUser user : matchingUsers) {
             result.add(binder.getUserTO(user));
         }
-        
+
         auditManager.audit(Category.user, UserSubCategory.read, Result.success,
                 "Successfully searched for users (page=" + page + ", size=" + size + "): " + result.size());
-        
+
         return result;
     }
-    
+
     @RequestMapping(method = RequestMethod.POST, value = "/create")
     public UserTO create(final HttpServletResponse response, @RequestBody final UserTO userTO) {
         UserTO savedTO = createInternal(userTO);
         response.setStatus(HttpServletResponse.SC_CREATED);
         return savedTO;
     }
-    
+
     @PreAuthorize("hasRole('USER_CREATE')")
     public UserTO createInternal(final UserTO userTO) {
         LOG.debug("User create called with {}", userTO);
-        
+
+        // Check that this operation is allowed to be performed by caller
         Set<Long> requestRoleIds = new HashSet<Long>(userTO.getMemberships().size());
         for (MembershipTO membership : userTO.getMemberships()) {
             requestRoleIds.add(membership.getRoleId());
         }
-        
         Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames());
         requestRoleIds.removeAll(adminRoleIds);
         if (!requestRoleIds.isEmpty()) {
             throw new UnauthorizedRoleException(requestRoleIds);
         }
-        
-        WorkflowResult<Map.Entry<Long, Boolean>> created = uwfAdapter.create(userTO);
-        
+
+        // Attributable transformation (if configured)
+        UserTO actual = attrTransformer.transform(userTO);
+        LOG.debug("Transformed: {}", actual);
+
+        /*
+         * Actual operations: workflow, propagation, notification
+         */
+
+        WorkflowResult<Map.Entry<Long, Boolean>> created = uwfAdapter.create(actual);
+
         List<PropagationTask> tasks = propagationManager.getUserCreateTaskIds(
-                created, userTO.getPassword(), userTO.getVirtualAttributes());
-        
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+                created, actual.getPassword(), actual.getVirtualAttributes());
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, propHanlder);
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
-        
+
         notificationManager.createTasks(created.getResult().getKey(), created.getPerformedTasks());
-        
+
         final UserTO savedTO = binder.getUserTO(created.getResult().getKey());
-        savedTO.setPropagationStatusTOs(propagations);
-        
+        savedTO.setPropagationStatusTOs(propagationReporter.getStatuses());
+
         LOG.debug("About to return created user\n{}", savedTO);
-        
+
         auditManager.audit(Category.user, UserSubCategory.create, Result.success,
                 "Successfully created user: " + savedTO.getUsername());
-        
+
         return savedTO;
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/update")
     public UserTO update(@RequestBody final UserMod userMod) {
         LOG.debug("User update called with {}", userMod);
-        
-        final String changedPwd = userMod.getPassword();
+
+        // AttributableMod transformation (if configured)
+        UserMod actual = attrTransformer.transform(userMod);
+        LOG.debug("Transformed: {}", actual);
+
+        final String changedPwd = actual.getPassword();
 
         // 1. update password internally only if required
-        if (userMod.getPwdPropRequest() != null && !userMod.getPwdPropRequest().isOnSyncope()) {
-            userMod.setPassword(null);
+        if (actual.getPwdPropRequest() != null && !actual.getPwdPropRequest().isOnSyncope()) {
+            actual.setPassword(null);
         }
-        WorkflowResult<Map.Entry<Long, Boolean>> updated = uwfAdapter.update(userMod);
+        WorkflowResult<Map.Entry<Long, Boolean>> updated = uwfAdapter.update(actual);
 
         // 2. propagate password update only to requested resources
         List<PropagationTask> tasks = new ArrayList<PropagationTask>();
-        if (userMod.getPwdPropRequest() == null) {
+        if (actual.getPwdPropRequest() == null) {
             // 2a. no specific password propagation request: generate propagation tasks for any resource associated
             tasks = propagationManager.getUserUpdateTaskIds(updated, changedPwd,
-                    userMod.getVirtualAttributesToBeRemoved(), userMod.getVirtualAttributesToBeUpdated());
+                    actual.getVirtualAttributesToBeRemoved(), actual.getVirtualAttributesToBeUpdated());
         } else {
             // 2b. generate the propagation task list in two phases: first the ones containing password,
             // the the rest (with no password)
             final PropagationByResource origPropByRes = new PropagationByResource();
             origPropByRes.merge(updated.getPropByRes());
-            
-            Set<String> pwdResourceNames = userMod.getPwdPropRequest().getResources();
+
+            Set<String> pwdResourceNames = actual.getPwdPropRequest().getResources();
             SyncopeUser user = binder.getUserFromId(updated.getResult().getKey());
             pwdResourceNames.retainAll(user.getResourceNames());
             final PropagationByResource pwdPropByRes = new PropagationByResource();
             pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);
             updated.setPropByRes(pwdPropByRes);
-            
+
             if (!pwdPropByRes.isEmpty()) {
                 Set<String> toBeExcluded = new HashSet<String>(user.getResourceNames());
-                toBeExcluded.addAll(userMod.getResourcesToBeAdded());
+                toBeExcluded.addAll(actual.getResourcesToBeAdded());
                 toBeExcluded.removeAll(pwdResourceNames);
                 tasks.addAll(propagationManager.getUserUpdateTaskIds(
                         updated,
                         changedPwd,
-                        userMod.getVirtualAttributesToBeRemoved(),
-                        userMod.getVirtualAttributesToBeUpdated(),
+                        actual.getVirtualAttributesToBeRemoved(),
+                        actual.getVirtualAttributesToBeUpdated(),
                         toBeExcluded));
             }
-            
+
             final PropagationByResource nonPwdPropByRes = new PropagationByResource();
             nonPwdPropByRes.merge(origPropByRes);
             nonPwdPropByRes.removeAll(pwdResourceNames);
             nonPwdPropByRes.purge();
             updated.setPropByRes(nonPwdPropByRes);
-            
+
             if (!nonPwdPropByRes.isEmpty()) {
                 tasks.addAll(propagationManager.getUserUpdateTaskIds(
                         updated,
                         null,
-                        userMod.getVirtualAttributesToBeRemoved(),
-                        userMod.getVirtualAttributesToBeUpdated(),
+                        actual.getVirtualAttributesToBeRemoved(),
+                        actual.getVirtualAttributesToBeUpdated(),
                         pwdResourceNames));
             }
-            
+
             updated.setPropByRes(origPropByRes);
         }
-        
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, propHanlder);
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
 
         // 3. create notification tasks
@@ -407,232 +415,232 @@ public class UserController {
 
         // 4. prepare result, including propagation status on external resources
         final UserTO updatedTO = binder.getUserTO(updated.getResult().getKey());
-        updatedTO.setPropagationStatusTOs(propagations);
-        
+        updatedTO.setPropagationStatusTOs(propagationReporter.getStatuses());
+
         auditManager.audit(Category.user, UserSubCategory.update, Result.success,
                 "Successfully updated user: " + updatedTO.getUsername());
-        
+
         LOG.debug("About to return updated user\n{}", updatedTO);
-        
+
         return updatedTO;
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.GET, value = "/activate/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO activate(@PathVariable("userId") final Long userId,
             @RequestParam(required = true) final String token) {
-        
+
         return activate(userId, token, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/activate/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO activate(@PathVariable("userId") final Long userId,
             @RequestParam(required = true) final String token,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to activate " + userId);
-        
+
         SyncopeUser user = binder.getUserFromId(userId);
-        
+
         return setStatus(user, token, propagationRequestTO, true, "activate");
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.GET, value = "/activateByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO activate(@PathVariable("username") final String username,
             @RequestParam(required = true) final String token) {
-        
+
         return activate(username, token, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/activateByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO activate(@PathVariable("username") final String username,
             @RequestParam(required = true) final String token,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to activate " + username);
-        
+
         SyncopeUser user = binder.getUserFromUsername(username);
-        
+
         return setStatus(user, token, propagationRequestTO, true, "activate");
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.GET, value = "/suspend/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO suspend(@PathVariable("userId") final Long userId) {
-        
+
         return suspend(userId, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/suspend/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO suspend(@PathVariable("userId") final Long userId,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to suspend " + userId);
-        
+
         SyncopeUser user = binder.getUserFromId(userId);
-        
+
         return setStatus(user, null, propagationRequestTO, false, "suspend");
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.GET, value = "/suspendByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO suspend(@PathVariable("username") final String username) {
-        
+
         return suspend(username, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/suspendByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO suspend(@PathVariable("username") final String username,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to suspend " + username);
-        
+
         SyncopeUser user = binder.getUserFromUsername(username);
-        
+
         return setStatus(user, null, propagationRequestTO, false, "suspend");
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.GET, value = "/reactivate/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO reactivate(@PathVariable("userId") final Long userId) {
-        
+
         return reactivate(userId, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/reactivate/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO reactivate(@PathVariable("userId") final Long userId,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to reactivate " + userId);
-        
+
         SyncopeUser user = binder.getUserFromId(userId);
-        
+
         return setStatus(user, null, propagationRequestTO, true, "reactivate");
     }
-    
+
     @RequestMapping(method = RequestMethod.GET, value = "/reactivateByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO reactivate(@PathVariable("username") final String username) {
         return reactivate(username, null);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/reactivateByUsername/{username}")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO reactivate(@PathVariable("username") final String username,
             @RequestBody final PropagationRequestTO propagationRequestTO) {
-        
+
         LOG.debug("About to reactivate " + username);
-        
+
         SyncopeUser user = binder.getUserFromUsername(username);
-        
+
         return setStatus(user, null, propagationRequestTO, true, "reactivate");
     }
-    
+
     @PreAuthorize("hasRole('USER_DELETE')")
     @RequestMapping(method = RequestMethod.GET, value = "/delete/{userId}")
     public UserTO delete(@PathVariable("userId") final Long userId) {
         LOG.debug("User delete called with {}", userId);
-        
+
         return doDelete(userId);
     }
-    
+
     @PreAuthorize("hasRole('USER_DELETE')")
     @RequestMapping(method = RequestMethod.GET, value = "/deleteByUsername/{username}")
     public UserTO delete(@PathVariable final String username) {
         LOG.debug("User delete called with {}", username);
-        
+
         UserTO result = binder.getUserTO(username);
         long userId = result.getId();
-        
+
         return doDelete(userId);
     }
-    
+
     @PreAuthorize("hasRole('USER_UPDATE')")
     @RequestMapping(method = RequestMethod.POST, value = "/execute/workflow/{taskId}")
     public UserTO executeWorkflow(@RequestBody final UserTO userTO, @PathVariable("taskId") final String taskId) {
         LOG.debug("About to execute {} on {}", taskId, userTO.getId());
-        
+
         WorkflowResult<Long> updated = uwfAdapter.execute(userTO, taskId);
-        
+
         List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
                 new WorkflowResult<Map.Entry<Long, Boolean>>(new SimpleEntry<Long, Boolean>(updated.getResult(), null),
                 updated.getPropByRes(), updated.getPerformedTasks()));
-        
+
         taskExecutor.execute(tasks);
-        
+
         notificationManager.createTasks(updated.getResult(), updated.getPerformedTasks());
-        
+
         final UserTO savedTO = binder.getUserTO(updated.getResult());
-        
+
         LOG.debug("About to return updated user\n{}", savedTO);
-        
+
         auditManager.audit(Category.user, UserSubCategory.executeWorkflow, Result.success,
                 "Successfully executed workflow action " + taskId + " on user: " + userTO.getUsername());
-        
+
         return savedTO;
     }
-    
+
     @PreAuthorize("hasRole('WORKFLOW_FORM_LIST')")
     @RequestMapping(method = RequestMethod.GET, value = "/workflow/form/list")
     @Transactional(rollbackFor = {Throwable.class})
     public List<WorkflowFormTO> getForms() {
         List<WorkflowFormTO> forms = uwfAdapter.getForms();
-        
+
         auditManager.audit(Category.user, UserSubCategory.getForms, Result.success,
                 "Successfully list workflow forms: " + forms.size());
-        
+
         return forms;
     }
-    
+
     @PreAuthorize("hasRole('WORKFLOW_FORM_READ') and hasRole('USER_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/workflow/form/{userId}")
     @Transactional(rollbackFor = {Throwable.class})
     public WorkflowFormTO getFormForUser(@PathVariable("userId") final Long userId) {
         SyncopeUser user = binder.getUserFromId(userId);
         WorkflowFormTO result = uwfAdapter.getForm(user.getWorkflowId());
-        
+
         auditManager.audit(Category.user, UserSubCategory.getFormForUser, Result.success,
                 "Successfully read workflow form for user: " + user.getUsername());
-        
+
         return result;
     }
-    
+
     @PreAuthorize("hasRole('WORKFLOW_FORM_CLAIM')")
     @RequestMapping(method = RequestMethod.GET, value = "/workflow/form/claim/{taskId}")
     @Transactional(rollbackFor = {Throwable.class})
     public WorkflowFormTO claimForm(@PathVariable("taskId") final String taskId) {
         WorkflowFormTO result = uwfAdapter.claimForm(taskId,
                 SecurityContextHolder.getContext().getAuthentication().getName());
-        
+
         auditManager.audit(Category.user, UserSubCategory.claimForm, Result.success,
                 "Successfully claimed workflow form: " + taskId);
-        
+
         return result;
     }
-    
+
     @PreAuthorize("hasRole('WORKFLOW_FORM_SUBMIT')")
     @RequestMapping(method = RequestMethod.POST, value = "/workflow/form/submit")
     @Transactional(rollbackFor = {Throwable.class})
     public UserTO submitForm(@RequestBody final WorkflowFormTO form) {
         LOG.debug("About to process form {}", form);
-        
+
         WorkflowResult<Map.Entry<Long, String>> updated = uwfAdapter.submitForm(form,
                 SecurityContextHolder.getContext().getAuthentication().getName());
 
@@ -650,22 +658,22 @@ public class UserController {
                     null);
             taskExecutor.execute(tasks);
         }
-        
+
         final UserTO savedTO = binder.getUserTO(updated.getResult().getKey());
-        
+
         auditManager.audit(Category.user, UserSubCategory.submitForm, Result.success,
                 "Successfully submitted workflow form for user: " + savedTO.getUsername());
-        
+
         LOG.debug("About to return user after form processing\n{}", savedTO);
-        
+
         return savedTO;
     }
-    
+
     protected UserTO setStatus(final SyncopeUser user, final String token,
             final PropagationRequestTO propagationRequestTO, final boolean status, final String task) {
-        
+
         LOG.debug("About to set status of {}" + user);
-        
+
         WorkflowResult<Long> updated;
         if (propagationRequestTO == null || propagationRequestTO.isOnSyncope()) {
             updated = setStatusOnWfAdapter(user, token, task);
@@ -678,22 +686,22 @@ public class UserController {
         if (propagationRequestTO != null) {
             resourcesToBeExcluded.removeAll(propagationRequestTO.getResources());
         }
-        
+
         List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(user, status, resourcesToBeExcluded);
         taskExecutor.execute(tasks);
-        
+
         notificationManager.createTasks(updated.getResult(), updated.getPerformedTasks());
-        
+
         final UserTO savedTO = binder.getUserTO(updated.getResult());
-        
+
         auditManager.audit(Category.user, UserSubCategory.setStatus, Result.success,
                 "Successfully changed status to " + savedTO.getStatus() + " for user: " + savedTO.getUsername());
-        
+
         LOG.debug("About to return updated user\n{}", savedTO);
-        
+
         return savedTO;
     }
-    
+
     protected WorkflowResult<Long> setStatusOnWfAdapter(final SyncopeUser user, final String token, final String task) {
         WorkflowResult<Long> updated;
         if ("suspend".equals(task)) {
@@ -705,7 +713,7 @@ public class UserController {
         }
         return updated;
     }
-    
+
     protected UserTO doDelete(final Long userId) {
         List<SyncopeRole> ownedRoles = roleDAO.findOwned(binder.getUserFromId(userId));
         if (!ownedRoles.isEmpty()) {
@@ -713,17 +721,17 @@ public class UserController {
             for (SyncopeRole role : ownedRoles) {
                 owned.add(role.getId() + " " + role.getName());
             }
-            
+
             auditManager.audit(Category.user, UserSubCategory.delete, Result.failure,
                     "Could not delete user: " + userId + " because of role(s) ownership " + owned);
-            
+
             SyncopeClientCompositeErrorException sccee =
                     new SyncopeClientCompositeErrorException(HttpStatus.BAD_REQUEST);
-            
+
             SyncopeClientException sce = new SyncopeClientException(SyncopeClientExceptionType.RoleOwnership);
             sce.setElements(owned);
             sccee.addException(sce);
-            
+
             throw sccee;
         }
 
@@ -733,33 +741,33 @@ public class UserController {
         // will also effectively remove user from db, thus making virtually
         // impossible by NotificationManager to fetch required user information
         notificationManager.createTasks(userId, Collections.singleton("delete"));
-        
+
         List<PropagationTask> tasks = propagationManager.getUserDeleteTaskIds(userId);
-        
+
         final UserTO userTO = new UserTO();
         userTO.setId(userId);
-        
-        final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
-        final DefaultPropagationHandler propHanlder = new DefaultPropagationHandler(connObjectUtil, propagations);
+
+        PropagationReporter propagationReporter =
+                ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
-            taskExecutor.execute(tasks, new DefaultPropagationHandler(connObjectUtil, propagations));
+            taskExecutor.execute(tasks, propagationReporter);
         } catch (PropagationException e) {
             LOG.error("Error propagation primary resource", e);
-            propHanlder.completeWhenPrimaryResourceErrored(propagations, tasks);
+            propagationReporter.onPrimaryResourceFailure(tasks);
         }
-        
-        userTO.setPropagationStatusTOs(propagations);
-        
+
+        userTO.setPropagationStatusTOs(propagationReporter.getStatuses());
+
         uwfAdapter.delete(userId);
-        
+
         auditManager.audit(Category.user, UserSubCategory.delete, Result.success,
                 "Successfully deleted user: " + userId);
-        
+
         LOG.debug("User successfully deleted: {}", userId);
-        
+
         return userTO;
     }
-    
+
     @PreAuthorize("(hasRole('USER_DELETE') and #bulkAction.operation == #bulkAction.operation.DELETE) or "
             + "(hasRole('USER_UPDATE') and "
             + "(#bulkAction.operation == #bulkAction.operation.REACTIVATE or "
@@ -767,9 +775,9 @@ public class UserController {
     @RequestMapping(method = RequestMethod.POST, value = "/bulk")
     public BulkActionRes bulkAction(@RequestBody final BulkAction bulkAction) {
         LOG.debug("Bulk action '{}' called on '{}'", bulkAction.getOperation(), bulkAction.getTargets());
-        
+
         BulkActionRes res = new BulkActionRes();
-        
+
         switch (bulkAction.getOperation()) {
             case DELETE:
                 for (String userId : bulkAction.getTargets()) {
@@ -803,7 +811,7 @@ public class UserController {
                 break;
             default:
         }
-        
+
         return res;
     }
 }

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java Mon Oct 14 14:45:20 2013
@@ -594,7 +594,7 @@ public abstract class AbstractAttributab
      * @param vAttrs virtual attributes to be added.
      * @param attrUtil attributable util.
      */
-    public void fillVirtual(final AbstractAttributable attributable, final List<AttributeTO> vAttrs,
+    public void fillVirtual(final AbstractAttributable attributable, final Collection<AttributeTO> vAttrs,
             final AttributableUtil attrUtil) {
 
         for (AttributeTO attributeTO : vAttrs) {

Added: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java?rev=1531921&view=auto
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java (added)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java Mon Oct 14 14:45:20 2013
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.data;
+
+import org.apache.syncope.common.mod.AbstractAttributableMod;
+import org.apache.syncope.common.to.AbstractAttributableTO;
+
+/**
+ * Provides logic for transforming user or role, received as input by RESTful methods, before any internal
+ * processing logic takes place.
+ */
+public interface AttributableTransformer {
+
+    <T extends AbstractAttributableTO> T transform(T input);
+
+    <T extends AbstractAttributableMod> T transform(T input);
+}

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/AttributableTransformer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java?rev=1531921&view=auto
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java (added)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java Mon Oct 14 14:45:20 2013
@@ -0,0 +1,38 @@
+/*
+ * 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.rest.data;
+
+import org.apache.syncope.common.mod.AbstractAttributableMod;
+import org.apache.syncope.common.to.AbstractAttributableTO;
+
+/**
+ * Default empty implementation returning received input as result.
+ */
+public class DefaultAttributableTransformer implements AttributableTransformer {
+
+    @Override
+    public <T extends AbstractAttributableTO> T transform(final T input) {
+        return input;
+    }
+
+    @Override
+    public <T extends AbstractAttributableMod> T transform(final T input) {
+        return input;
+    }
+}

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/rest/data/DefaultAttributableTransformer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java (original)
+++ syncope/branches/1_1_X/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java Mon Oct 14 14:45:20 2013
@@ -68,6 +68,7 @@ import org.apache.syncope.core.propagati
 import org.apache.syncope.core.propagation.Connector;
 import org.apache.syncope.core.propagation.impl.PropagationManager;
 import org.apache.syncope.core.rest.controller.UnauthorizedRoleException;
+import org.apache.syncope.core.rest.data.AttributableTransformer;
 import org.apache.syncope.core.rest.data.RoleDataBinder;
 import org.apache.syncope.core.rest.data.UserDataBinder;
 import org.apache.syncope.core.sync.SyncActions;
@@ -184,6 +185,9 @@ public class SyncopeSyncResultHandler im
     @Autowired
     protected NotificationManager notificationManager;
 
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
     /**
      * Syncing connector.
      */
@@ -518,37 +522,41 @@ public class SyncopeSyncResultHandler im
 
         delta = actions.beforeCreate(this, delta, subjectTO);
 
+        // Attributable transformation (if configured)
+        AbstractAttributableTO actual = attrTransformer.transform(subjectTO);
+        LOG.debug("Transformed: {}", actual);
+
         if (dryRun) {
             result.setId(0L);
-            if (subjectTO instanceof UserTO) {
-                result.setName(((UserTO) subjectTO).getUsername());
+            if (actual instanceof UserTO) {
+                result.setName(((UserTO) actual).getUsername());
             }
-            if (subjectTO instanceof RoleTO) {
-                result.setName(((RoleTO) subjectTO).getName());
+            if (actual instanceof RoleTO) {
+                result.setName(((RoleTO) actual).getName());
             }
         } else {
             try {
                 if (AttributableType.USER == attrUtil.getType()) {
                     Boolean enabled = readEnabled(delta.getObject());
                     WorkflowResult<Map.Entry<Long, Boolean>> created =
-                            uwfAdapter.create((UserTO) subjectTO, true, enabled);
+                            uwfAdapter.create((UserTO) actual, true, enabled);
 
                     List<PropagationTask> tasks = propagationManager.getUserCreateTaskIds(created,
-                            ((UserTO) subjectTO).getPassword(), subjectTO.getVirtualAttributes(),
+                            ((UserTO) actual).getPassword(), actual.getVirtualAttributes(),
                             Collections.singleton(syncTask.getResource().getName()));
 
                     taskExecutor.execute(tasks);
 
                     notificationManager.createTasks(created.getResult().getKey(), created.getPerformedTasks());
 
-                    subjectTO = userDataBinder.getUserTO(created.getResult().getKey());
+                    actual = userDataBinder.getUserTO(created.getResult().getKey());
 
                     result.setId(created.getResult().getKey());
-                    result.setName(((UserTO) subjectTO).getUsername());
+                    result.setName(((UserTO) actual).getUsername());
                 }
                 if (AttributableType.ROLE == attrUtil.getType()) {
-                    WorkflowResult<Long> created = rwfAdapter.create((RoleTO) subjectTO);
-                    AttributeTO roleOwner = subjectTO.getAttributeMap().get(StringUtils.EMPTY);
+                    WorkflowResult<Long> created = rwfAdapter.create((RoleTO) actual);
+                    AttributeTO roleOwner = actual.getAttributeMap().get(StringUtils.EMPTY);
                     if (roleOwner != null) {
                         roleOwnerMap.put(created.getResult(), roleOwner.getValues().iterator().next());
                     }
@@ -556,14 +564,14 @@ public class SyncopeSyncResultHandler im
                     EntitlementUtil.extendAuthContext(created.getResult());
 
                     List<PropagationTask> tasks = propagationManager.getRoleCreateTaskIds(created,
-                            subjectTO.getVirtualAttributes(), Collections.singleton(syncTask.getResource().getName()));
+                            actual.getVirtualAttributes(), Collections.singleton(syncTask.getResource().getName()));
 
                     taskExecutor.execute(tasks);
 
-                    subjectTO = roleDataBinder.getRoleTO(created.getResult());
+                    actual = roleDataBinder.getRoleTO(created.getResult());
 
                     result.setId(created.getResult());
-                    result.setName(((RoleTO) subjectTO).getName());
+                    result.setName(((RoleTO) actual).getName());
                 }
 
             } catch (PropagationException e) {
@@ -577,7 +585,7 @@ public class SyncopeSyncResultHandler im
             }
         }
 
-        actions.after(this, delta, subjectTO, result);
+        actions.after(this, delta, actual, result);
         return Collections.singletonList(result);
     }
 
@@ -594,9 +602,13 @@ public class SyncopeSyncResultHandler im
             return userTO;
         }
 
+        // Attribute value transformation (if configured)
+        UserMod actual = attrTransformer.transform(userMod);
+        LOG.debug("Transformed: {}", actual);
+
         WorkflowResult<Map.Entry<Long, Boolean>> updated;
         try {
-            updated = uwfAdapter.update(userMod);
+            updated = uwfAdapter.update(actual);
         } catch (Exception e) {
             LOG.error("Update of user {} failed, trying to sync its status anyway (if configured)", id, e);
 
@@ -631,9 +643,9 @@ public class SyncopeSyncResultHandler im
         }
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated,
-                userMod.getPassword(),
-                userMod.getVirtualAttributesToBeRemoved(),
-                userMod.getVirtualAttributesToBeUpdated(),
+                actual.getPassword(),
+                actual.getVirtualAttributesToBeRemoved(),
+                actual.getVirtualAttributesToBeUpdated(),
                 Collections.singleton(syncTask.getResource().getName()));
 
         taskExecutor.execute(tasks);
@@ -660,9 +672,13 @@ public class SyncopeSyncResultHandler im
             return roleTO;
         }
 
-        WorkflowResult<Long> updated = rwfAdapter.update(roleMod);
+        // Attribute value transformation (if configured)
+        RoleMod actual = attrTransformer.transform(roleMod);
+        LOG.debug("Transformed: {}", actual);
+
+        WorkflowResult<Long> updated = rwfAdapter.update(actual);
         String roleOwner = null;
-        for (AttributeMod attrMod : roleMod.getAttributesToBeUpdated()) {
+        for (AttributeMod attrMod : actual.getAttributesToBeUpdated()) {
             if (attrMod.getSchema().isEmpty()) {
                 roleOwner = attrMod.getValuesToBeAdded().iterator().next();
             }
@@ -672,8 +688,8 @@ public class SyncopeSyncResultHandler im
         }
 
         List<PropagationTask> tasks = propagationManager.getRoleUpdateTaskIds(updated,
-                roleMod.getVirtualAttributesToBeRemoved(),
-                roleMod.getVirtualAttributesToBeUpdated(),
+                actual.getVirtualAttributesToBeRemoved(),
+                actual.getVirtualAttributesToBeUpdated(),
                 Collections.singleton(syncTask.getResource().getName()));
 
         taskExecutor.execute(tasks);

Modified: syncope/branches/1_1_X/core/src/main/resources/restContext.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/resources/restContext.xml?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/resources/restContext.xml (original)
+++ syncope/branches/1_1_X/core/src/main/resources/restContext.xml Mon Oct 14 14:45:20 2013
@@ -55,7 +55,9 @@ under the License.
     <property name="collectionWrapperMap">
       <map>
         <entry>
-          <key><value>org.apache.syncope.common.to.PolicyTO</value></key>
+          <key>
+            <value>org.apache.syncope.common.to.PolicyTO</value>
+          </key>
           <value>policies</value>
         </entry>
       </map>
@@ -161,4 +163,7 @@ under the License.
 
     <property name="ignoreAcceptHeader" value="false"/>
   </bean>
+  
+  <!-- Attribute transformer for internal storage -->
+  <bean id="attrTransformer" class="org.apache.syncope.core.rest.data.DefaultAttributableTransformer"/>
 </beans>

Modified: syncope/branches/1_1_X/core/src/main/resources/syncopeContext.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/main/resources/syncopeContext.xml?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/main/resources/syncopeContext.xml (original)
+++ syncope/branches/1_1_X/core/src/main/resources/syncopeContext.xml Mon Oct 14 14:45:20 2013
@@ -73,6 +73,7 @@ under the License.
 
   <bean id="propagationManager" class="org.apache.syncope.core.propagation.impl.PropagationManager"/>
   <bean id="propagationTaskExecutor" class="org.apache.syncope.core.propagation.impl.PriorityPropagationTaskExecutor"/>
+  <bean id="propagationReporter" class="org.apache.syncope.core.propagation.impl.DefaultPropagationReporter" scope="prototype"/>
 
   <bean id="notificationManager" class="org.apache.syncope.core.notification.NotificationManager"/>
 

Modified: syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/persistence/dao/SchemaTest.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/persistence/dao/SchemaTest.java?rev=1531921&r1=1531920&r2=1531921&view=diff
==============================================================================
--- syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/persistence/dao/SchemaTest.java (original)
+++ syncope/branches/1_1_X/core/src/test/java/org/apache/syncope/core/persistence/dao/SchemaTest.java Mon Oct 14 14:45:20 2013
@@ -49,7 +49,7 @@ public class SchemaTest extends Abstract
     @Test
     public void findAll() {
         List<USchema> userList = schemaDAO.findAll(USchema.class);
-        assertEquals(12, userList.size());
+        assertEquals(13, userList.size());
 
         List<RSchema> roleList = schemaDAO.findAll(RSchema.class);
         assertEquals(5, roleList.size());