Repository: syncope
Updated Branches:
  refs/heads/master 5537d29e9 -> 72e6ceb06


http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index 2568f45..4a203f9 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -156,6 +156,9 @@ abstract class AbstractAnyDataBinder {
     @Autowired
     protected ConnObjectUtils connObjectUtils;
 
+    @Autowired
+    protected MappingUtils mappingUtils;
+
     protected void setRealm(final Any<?, ?, ?> any, final AnyPatch anyPatch) {
         if (anyPatch.getRealm() != null && 
StringUtils.isNotBlank(anyPatch.getRealm().getValue())) {
             Realm newRealm = realmDAO.find(anyPatch.getRealm().getValue());
@@ -231,11 +234,9 @@ abstract class AbstractAnyDataBinder {
                         && (item.getPurpose() == MappingPurpose.PROPAGATION
                         || item.getPurpose() == MappingPurpose.BOTH)) {
 
-                    List<PlainAttrValue> values = MappingUtils.getIntValues(
+                    List<PlainAttrValue> values = mappingUtils.getIntValues(
                             provision, item, Collections.<Any<?, ?, 
?>>singletonList(any), null);
-                    if ((values == null || values.isEmpty())
-                            && 
JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
-
+                    if (values.isEmpty() && 
JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
                         missingAttrNames.add(item.getIntAttrName());
                     }
                 }
@@ -360,8 +361,8 @@ abstract class AbstractAnyDataBinder {
                 plainAttrDAO.delete(attr.getKey(), anyUtils.plainAttrClass());
 
                 for (ExternalResource resource : resources) {
-                    for (MappingItem mapItem : MappingUtils.getMappingItems(
-                            resource.getProvision(any.getType()), 
MappingPurpose.PROPAGATION)) {
+                    for (MappingItem mapItem
+                            : 
MappingUtils.getPropagationMappingItems(resource.getProvision(any.getType()))) {
 
                         if (schema.getKey().equals(mapItem.getIntAttrName())
                                 && mapItem.getIntMappingType() == 
anyUtils.plainIntMappingType()) {
@@ -410,8 +411,8 @@ abstract class AbstractAnyDataBinder {
                 derAttrDAO.delete(attr);
 
                 for (ExternalResource resource : resources) {
-                    for (MappingItem mapItem : MappingUtils.getMappingItems(
-                            resource.getProvision(any.getType()), 
MappingPurpose.PROPAGATION)) {
+                    for (MappingItem mapItem
+                            : 
MappingUtils.getPropagationMappingItems(resource.getProvision(any.getType()))) {
 
                         if (schema.getKey().equals(mapItem.getIntAttrName())
                                 && mapItem.getIntMappingType() == 
anyUtils.derIntMappingType()) {
@@ -704,7 +705,7 @@ abstract class AbstractAnyDataBinder {
                             + " on resource '" + resource.getKey() + "'");
                 }
 
-                connObjectKeys.put(resource.getKey(), 
MappingUtils.getConnObjectKeyValue(any, provision));
+                connObjectKeys.put(resource.getKey(), 
mappingUtils.getConnObjectKeyValue(any, provision));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DefaultMappingItemTransformer.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DefaultMappingItemTransformer.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DefaultMappingItemTransformer.java
new file mode 100644
index 0000000..3810a87
--- /dev/null
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/DefaultMappingItemTransformer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.data;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer;
+
+/**
+ * Default (empty) implementation of {@link MappingItemTransformer}.
+ */
+public class DefaultMappingItemTransformer implements MappingItemTransformer {
+
+    @Override
+    public List<PlainAttrValue> beforePropagation(final List<PlainAttrValue> 
values) {
+        return values;
+    }
+
+    @Override
+    public List<Object> beforeSync(final List<Object> values) {
+        return values;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 4ccc0c4..69a2e9d 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -22,13 +22,13 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -148,6 +148,27 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
         return result;
     }
 
+    /**
+     * Transform a
+     * <code>Collection</code> of {@link Attribute} instances into a {@link 
Map}. The key to each element in the map is
+     * the <i>name</i> of an
+     * <code>Attribute</code>. The value of each element in the map is the
+     * <code>Attribute</code> instance with that name. <br/> Different from 
the original because: <ul> <li>map keys are
+     * transformed toUpperCase()</li> <li>returned map is mutable</li> </ul>
+     *
+     * @param attributes set of attribute to transform to a map.
+     * @return a map of string and attribute.
+     *
+     * @see 
org.identityconnectors.framework.common.objects.AttributeUtil#toMap(java.util.Collection)
+     */
+    private Map<String, Attribute> toMap(final Collection<? extends Attribute> 
attributes) {
+        Map<String, Attribute> map = new HashMap<>();
+        for (Attribute attr : attributes) {
+            map.put(attr.getName().toUpperCase(), attr);
+        }
+        return map;
+    }
+
     protected void createOrUpdate(
             final PropagationTask task,
             final ConnectorObject beforeObj,
@@ -155,10 +176,10 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
             final Set<String> propagationAttempted) {
 
         // set of attributes to be propagated
-        final Set<Attribute> attributes = new HashSet<>(task.getAttributes());
+        Set<Attribute> attributes = new HashSet<>(task.getAttributes());
 
         // check if there is any missing or null / empty mandatory attribute
-        List<Object> mandatoryAttrNames = new ArrayList<>();
+        Set<Object> mandatoryAttrNames = new HashSet<>();
         Attribute mandatoryMissing = 
AttributeUtil.find(MANDATORY_MISSING_ATTR_NAME, task.getAttributes());
         if (mandatoryMissing != null) {
             attributes.remove(mandatoryMissing);
@@ -200,8 +221,8 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
 
             // 2. check wether anything is actually needing to be propagated, 
i.e. if there is attribute
             // difference between beforeObj - just read above from the 
connector - and the values to be propagated
-            Map<String, Attribute> originalAttrMap = 
connObjectUtils.toMap(beforeObj.getAttributes());
-            Map<String, Attribute> updateAttrMap = 
connObjectUtils.toMap(attributes);
+            Map<String, Attribute> originalAttrMap = 
toMap(beforeObj.getAttributes());
+            Map<String, Attribute> updateAttrMap = toMap(attributes);
 
             // Only compare attribute from beforeObj that are also being 
updated
             Set<String> skipAttrNames = originalAttrMap.keySet();
@@ -527,7 +548,7 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
                     task.getOperation(),
                     new ObjectClass(task.getObjectClassName()),
                     new Uid(connObjectKey),
-                    
connector.getOperationOptions(MappingUtils.getMappingItems(provision, 
MappingPurpose.PROPAGATION)));
+                    
connector.getOperationOptions(MappingUtils.getPropagationMappingItems(provision)));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
             throw toe;

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationActions.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationActions.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationActions.java
index 23726ed..157fdc1 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationActions.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationActions.java
@@ -24,7 +24,7 @@ import 
org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 /**
- * Default (empty) implementation of PropagationActions.
+ * Default (empty) implementation of {@link PropagationActions}.
  */
 public abstract class DefaultPropagationActions implements PropagationActions {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
index 7437241..22feb44 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
@@ -77,7 +77,7 @@ public class DefaultPropagationReporter implements 
PropagationReporter {
 
         for (PropagationTask propagationTask : tasks) {
             if 
(!containsPropagationStatusTO(propagationTask.getResource().getKey())) {
-                final PropagationStatus propagationStatusTO = new 
PropagationStatus();
+                PropagationStatus propagationStatusTO = new 
PropagationStatus();
                 
propagationStatusTO.setResource(propagationTask.getResource().getKey());
                 
propagationStatusTO.setStatus(PropagationTaskExecStatus.FAILURE);
                 propagationStatusTO.setFailureReason(

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index b8bc838..ebf9c99 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -34,7 +34,6 @@ import org.apache.syncope.common.lib.patch.StringPatchItem;
 import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
@@ -106,6 +105,9 @@ public class PropagationManagerImpl implements 
PropagationManager {
     @Autowired
     protected VirAttrHandler virAttrHandler;
 
+    @Autowired
+    protected MappingUtils mappingUtils;
+
     protected Any<?, ?, ?> find(final AnyTypeKind kind, final Long key) {
         AnyDAO<? extends Any<?, ?, ?>> dao;
         switch (kind) {
@@ -367,7 +369,7 @@ public class PropagationManagerImpl implements 
PropagationManager {
                 } else if (provision == null) {
                     LOG.error("No provision specified on resource {} for type 
{}, ignoring...",
                             resource, any.getType());
-                } else if (MappingUtils.getMappingItems(provision, 
MappingPurpose.PROPAGATION).isEmpty()) {
+                } else if 
(MappingUtils.getPropagationMappingItems(provision).isEmpty()) {
                     LOG.warn("Requesting propagation for {} but no propagation 
mapping provided for {}",
                             any.getType(), resource);
                 } else {
@@ -382,7 +384,7 @@ public class PropagationManagerImpl implements 
PropagationManager {
                     task.setOperation(operation);
                     
task.setOldConnObjectKey(propByRes.getOldConnObjectKey(resource.getKey()));
 
-                    Pair<String, Set<Attribute>> preparedAttrs = 
MappingUtils.prepareAttrs(
+                    Pair<String, Set<Attribute>> preparedAttrs = 
mappingUtils.prepareAttrs(
                             any, password, changePwd, vAttrs, enable, 
provision);
                     task.setConnObjectKey(preparedAttrs.getKey());
 
@@ -390,7 +392,7 @@ public class PropagationManagerImpl implements 
PropagationManager {
                     // if so, add special attributes that will be evaluated by 
PropagationTaskExecutor
                     List<String> mandatoryMissing = new ArrayList<>();
                     List<String> mandatoryNullOrEmpty = new ArrayList<>();
-                    for (MappingItem item : 
MappingUtils.getMappingItems(provision, MappingPurpose.PROPAGATION)) {
+                    for (MappingItem item : 
MappingUtils.getPropagationMappingItems(provision)) {
                         if (!item.isConnObjectKey()
                                 && 
JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
index 8aa37d3..94936eb 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
@@ -58,12 +58,16 @@ import 
org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 public abstract class AbstractPushResultHandler extends 
AbstractSyncopeResultHandler<PushTask, PushActions>
         implements SyncopePushResultHandler {
 
+    @Autowired
+    protected MappingUtils mappingUtils;
+
     protected abstract String getName(Any<?, ?, ?> any);
 
     protected void deprovision(final Any<?, ?, ?> any) {
@@ -195,7 +199,7 @@ public abstract class AbstractPushResultHandler extends 
AbstractSyncopeResultHan
 
         // Try to read remote object BEFORE any actual operation
         Provision provision = 
profile.getTask().getResource().getProvision(any.getType());
-        String connObjecKey = MappingUtils.getConnObjectKeyValue(any, 
provision);
+        String connObjecKey = mappingUtils.getConnObjectKeyValue(any, 
provision);
 
         ConnectorObject beforeObj = getRemoteObject(connObjecKey, 
provision.getObjectClass());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
index 30d2879..2ca7eaa 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
@@ -201,7 +201,7 @@ public abstract class AbstractSyncResultHandler extends 
AbstractSyncopeResultHan
             create(anyTO, actionedDelta, 
UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result);
         }
 
-        return Collections.<ProvisioningResult>singletonList(result);
+        return Collections.singletonList(result);
     }
 
     private void create(

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
index 1af195e..1ed0d2c 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
@@ -18,17 +18,19 @@
  */
 package org.apache.syncope.core.provisioning.java.sync;
 
+import static 
org.apache.syncope.core.misc.MappingUtils.getMappingItemTransformers;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.misc.MappingUtils;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer;
 import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
@@ -46,41 +48,44 @@ public class PlainAttrsSyncCorrelationRule implements 
SyncCorrelationRule {
 
     @Override
     public SearchCond getSearchCond(final ConnectorObject connObj) {
-        // search for external attribute's name/value of each specified name
-        Map<String, Attribute> extValues = new HashMap<>();
-
-        for (MappingItem item : MappingUtils.getMappingItems(provision, 
MappingPurpose.SYNCHRONIZATION)) {
-            extValues.put(item.getIntAttrName(), 
connObj.getAttributeByName(item.getExtAttrName()));
+        Map<String, MappingItem> mappingItems = new HashMap<>();
+        for (MappingItem item : MappingUtils.getSyncMappingItems(provision)) {
+            mappingItems.put(item.getIntAttrName(), item);
         }
 
-        // search for user/group by attribute(s) specified in the policy
+        // search for anys by attribute(s) specified in the policy
         SearchCond searchCond = null;
 
         for (String schema : plainSchemaNames) {
-            Attribute value = extValues.get(schema);
-
-            if (value == null) {
+            Attribute attr = mappingItems.get(schema) == null
+                    ? null
+                    : 
connObj.getAttributeByName(mappingItems.get(schema).getExtAttrName());
+            if (attr == null) {
                 throw new IllegalArgumentException(
                         "Connector object does not contains the attributes to 
perform the search: " + schema);
             }
 
+            List<Object> values = attr.getValue();
+            for (MappingItemTransformer transformer : 
getMappingItemTransformers(mappingItems.get(schema))) {
+                values = transformer.beforeSync(values);
+            }
+
             AttributeCond.Type type;
             String expression = null;
 
-            if (value.getValue() == null || value.getValue().isEmpty()
-                    || (value.getValue().size() == 1 && 
value.getValue().get(0) == null)) {
-
+            if (values == null || values.isEmpty() || (values.size() == 1 && 
values.get(0) == null)) {
                 type = AttributeCond.Type.ISNULL;
             } else {
                 type = AttributeCond.Type.EQ;
-                expression = value.getValue().size() > 1
-                        ? value.getValue().toString()
-                        : value.getValue().get(0).toString();
+                expression = values.size() > 1
+                        ? values.toString()
+                        : values.get(0).toString();
             }
 
             SearchCond nodeCond;
-            // users: just id or username can be selected to be used
-            // groups: just id or name can be selected to be used
+            // users: just key or username can be selected
+            // groups: just key or name can be selected
+            // any objects: just key can be selected
             if ("key".equalsIgnoreCase(schema)
                     || "username".equalsIgnoreCase(schema) || 
"name".equalsIgnoreCase(schema)) {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
----------------------------------------------------------------------
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
index 6fe699b..0c01254 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
@@ -24,7 +24,6 @@ import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.policy.SyncPolicySpec;
 import org.apache.syncope.core.misc.MappingUtils;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
@@ -49,6 +48,7 @@ import 
org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer;
 import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeUtil;
@@ -128,7 +128,7 @@ public class SyncUtils {
                         return found.add(obj);
                     }
                 },
-                
connector.getOperationOptions(MappingUtils.getMappingItems(provision, 
MappingPurpose.SYNCHRONIZATION)));
+                
connector.getOperationOptions(MappingUtils.getSyncMappingItems(provision)));
 
         if (found.isEmpty()) {
             LOG.debug("No {} found on {} with __NAME__ {}", 
provision.getObjectClass(), resource, name);
@@ -172,6 +172,15 @@ public class SyncUtils {
         List<Long> result = new ArrayList<>();
 
         MappingItem connObjectKeyItem = 
MappingUtils.getConnObjectKeyItem(provision);
+
+        String transfUid = uid;
+        for (MappingItemTransformer transformer : 
MappingUtils.getMappingItemTransformers(connObjectKeyItem)) {
+            List<Object> output = 
transformer.beforeSync(Collections.<Object>singletonList(transfUid));
+            if (output != null && !output.isEmpty()) {
+                transfUid = output.get(0).toString();
+            }
+        }
+
         switch (connObjectKeyItem.getIntMappingType()) {
             case UserPlainSchema:
             case GroupPlainSchema:
@@ -180,13 +189,13 @@ public class SyncUtils {
 
                 PlainSchema schema = 
plainSchemaDAO.find(connObjectKeyItem.getIntAttrName());
                 if (schema == null) {
-                    value.setStringValue(uid);
+                    value.setStringValue(transfUid);
                 } else {
                     try {
-                        value.parseValue(schema, uid);
+                        value.parseValue(schema, transfUid);
                     } catch (ParsingValidationException e) {
-                        LOG.error("While parsing provided __UID__ {}", uid, e);
-                        value.setStringValue(uid);
+                        LOG.error("While parsing provided __UID__ {}", 
transfUid, e);
+                        value.setStringValue(transfUid);
                     }
                 }
 
@@ -200,7 +209,7 @@ public class SyncUtils {
             case UserDerivedSchema:
             case GroupDerivedSchema:
             case AnyObjectDerivedSchema:
-                anys = 
getAnyDAO(connObjectKeyItem).findByDerAttrValue(connObjectKeyItem.getIntAttrName(),
 uid);
+                anys = 
getAnyDAO(connObjectKeyItem).findByDerAttrValue(connObjectKeyItem.getIntAttrName(),
 transfUid);
                 for (Any<?, ?, ?> any : anys) {
                     result.add(any.getKey());
                 }
@@ -209,21 +218,21 @@ public class SyncUtils {
             case UserKey:
             case GroupKey:
             case AnyObjectKey:
-                Any<?, ?, ?> any = 
getAnyDAO(connObjectKeyItem).find(Long.parseLong(uid));
+                Any<?, ?, ?> any = 
getAnyDAO(connObjectKeyItem).find(Long.parseLong(transfUid));
                 if (any != null) {
                     result.add(any.getKey());
                 }
                 break;
 
             case Username:
-                User user = userDAO.find(uid);
+                User user = userDAO.find(transfUid);
                 if (user != null) {
                     result.add(user.getKey());
                 }
                 break;
 
             case GroupName:
-                Group group = groupDAO.find(uid);
+                Group group = groupDAO.find(transfUid);
                 if (group != null) {
                     result.add(group.getKey());
                 }
@@ -240,12 +249,12 @@ public class SyncUtils {
             final ConnectorObject connObj, final SyncCorrelationRule rule, 
final AnyTypeKind type) {
 
         List<Long> result = new ArrayList<>();
-
-        List<Any<?, ?, ?>> anys = searchDAO.search(
+        for (Any<?, ?, ?> any : searchDAO.search(
                 SyncopeConstants.FULL_ADMIN_REALMS,
                 rule.getSearchCond(connObj),
-                Collections.<OrderByClause>emptyList(), type);
-        for (Any<?, ?, ?> any : anys) {
+                Collections.<OrderByClause>emptyList(),
+                type)) {
+
             result.add(any.getKey());
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java
----------------------------------------------------------------------
diff --git 
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java
 
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java
new file mode 100644
index 0000000..c620541
--- /dev/null
+++ 
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/PrefixMappingItemTransformer.java
@@ -0,0 +1,55 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import 
org.apache.syncope.core.provisioning.java.data.DefaultMappingItemTransformer;
+
+public class PrefixMappingItemTransformer extends 
DefaultMappingItemTransformer {
+
+    public static final String PREFIX = "PREFIX_";
+
+    @Override
+    public List<PlainAttrValue> beforePropagation(final List<PlainAttrValue> 
values) {
+        if (values == null || values.isEmpty() || 
values.get(0).getStringValue() == null) {
+            return super.beforePropagation(values);
+        } else {
+            String value = values.get(0).getStringValue();
+            values.get(0).setStringValue(PREFIX + value);
+
+            return values;
+        }
+    }
+
+    @Override
+    public List<Object> beforeSync(final List<Object> values) {
+        if (values == null || values.isEmpty() || values.get(0) == null) {
+            return super.beforeSync(values);
+        } else {
+            List<Object> newValues = new ArrayList<>(values);
+            newValues.set(0, 
StringUtils.substringAfter(values.get(0).toString(), PREFIX));
+
+            return newValues;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/72e6ceb0/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
index eaff578..75049da 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
@@ -32,6 +32,7 @@ import java.util.Set;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
@@ -49,6 +50,7 @@ import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
 import org.apache.syncope.common.lib.policy.SyncPolicyTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
 import org.apache.syncope.common.lib.to.SyncTaskTO;
 import org.apache.syncope.common.lib.to.TaskExecTO;
 import org.apache.syncope.common.lib.to.UserTO;
@@ -356,47 +358,78 @@ public class SyncTaskITCase extends AbstractTaskITCase {
 
     @Test
     public void reconcileFromScriptedSQL() {
-        // 0. reset sync token
+        // 0. reset sync token and set MappingItemTransformer
         ResourceTO resource = resourceService.read(RESOURCE_NAME_DBSCRIPTED);
+        ResourceTO originalResource = SerializationUtils.clone(resource);
         ProvisionTO provision = resource.getProvision("PRINTER");
         assertNotNull(provision);
 
-        provision.setSyncToken(null);
-        resourceService.update(resource);
-
-        // 1. create printer on external resource
-        AnyObjectTO anyObjectTO = AnyObjectITCase.getSampleTO("sync");
-        anyObjectTO = createAnyObject(anyObjectTO);
-        assertNotNull(anyObjectTO);
-
-        // 2. unlink any existing printer and delete from Syncope (printer is 
now only on external resource)
-        PagedResult<AnyObjectTO> matchingPrinters = anyObjectService.search(
-                
SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
-                
fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
-                        is("location").equalTo("sync*").query()).build());
-        assertTrue(matchingPrinters.getSize() > 0);
-        for (AnyObjectTO printer : matchingPrinters.getResult()) {
-            DeassociationPatch deassociationPatch = new DeassociationPatch();
-            deassociationPatch.setKey(printer.getKey());
-            deassociationPatch.setAction(ResourceDeassociationAction.UNLINK);
-            deassociationPatch.getResources().add(RESOURCE_NAME_DBSCRIPTED);
-            anyObjectService.deassociate(deassociationPatch);
-            anyObjectService.delete(printer.getKey());
-        }
+        try {
+            provision.setSyncToken(null);
+
+            MappingItemTO mappingItem = CollectionUtils.find(
+                    provision.getMapping().getItems(), new 
Predicate<MappingItemTO>() {
+
+                        @Override
+                        public boolean evaluate(final MappingItemTO object) {
+                            return "location".equals(object.getIntAttrName());
+                        }
+                    });
+            assertNotNull(mappingItem);
+            mappingItem.getMappingItemTransformerClassNames().clear();
+            
mappingItem.getMappingItemTransformerClassNames().add(PrefixMappingItemTransformer.class.getName());
+
+            resourceService.update(resource);
+
+            // 1. create printer on external resource
+            AnyObjectTO anyObjectTO = AnyObjectITCase.getSampleTO("sync");
+            String originalLocation = 
anyObjectTO.getPlainAttrMap().get("location").getValues().get(0);
+            
assertFalse(originalLocation.startsWith(PrefixMappingItemTransformer.PREFIX));
+
+            anyObjectTO = createAnyObject(anyObjectTO);
+            assertNotNull(anyObjectTO);
+
+            // 2. verify that PrefixMappingItemTransformer was applied during 
propagation
+            // (location starts with given prefix on external resource)
+            ConnObjectTO connObjectTO = resourceService.
+                    readConnObject(RESOURCE_NAME_DBSCRIPTED, 
anyObjectTO.getType(), anyObjectTO.getKey());
+            
assertFalse(anyObjectTO.getPlainAttrMap().get("location").getValues().get(0).
+                    startsWith(PrefixMappingItemTransformer.PREFIX));
+            
assertTrue(connObjectTO.getPlainAttrMap().get("location").getValues().get(0).
+                    startsWith(PrefixMappingItemTransformer.PREFIX));
+
+            // 3. unlink any existing printer and delete from Syncope (printer 
is now only on external resource)
+            PagedResult<AnyObjectTO> matchingPrinters = 
anyObjectService.search(
+                    
SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
+                    
fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
+                            is("location").equalTo("sync*").query()).build());
+            assertTrue(matchingPrinters.getSize() > 0);
+            for (AnyObjectTO printer : matchingPrinters.getResult()) {
+                DeassociationPatch deassociationPatch = new 
DeassociationPatch();
+                deassociationPatch.setKey(printer.getKey());
+                
deassociationPatch.setAction(ResourceDeassociationAction.UNLINK);
+                
deassociationPatch.getResources().add(RESOURCE_NAME_DBSCRIPTED);
+                anyObjectService.deassociate(deassociationPatch);
+                anyObjectService.delete(printer.getKey());
+            }
 
-        // 3. synchronize
-        execProvisioningTask(taskService, 28L, 50, false);
+            // 4. synchronize
+            execProvisioningTask(taskService, 28L, 50, false);
 
-        // 4. verify that printer was re-created in Syncope
-        matchingPrinters = anyObjectService.search(
-                
SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
-                
fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
-                        is("location").equalTo("sync*").query()).build());
-        assertTrue(matchingPrinters.getSize() > 0);
+            // 5. verify that printer was re-created in Syncope (implies that 
location does not start with given prefix,
+            // hence PrefixMappingItemTransformer was applied during sync)
+            matchingPrinters = anyObjectService.search(
+                    
SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
+                    
fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
+                            is("location").equalTo("sync*").query()).build());
+            assertTrue(matchingPrinters.getSize() > 0);
 
-        // 5. verify that synctoken was updated
-        assertNotNull(
-                
resourceService.read(RESOURCE_NAME_DBSCRIPTED).getProvision(anyObjectTO.getType()).getSyncToken());
+            // 6. verify that synctoken was updated
+            assertNotNull(
+                    
resourceService.read(RESOURCE_NAME_DBSCRIPTED).getProvision(anyObjectTO.getType()).getSyncToken());
+        } finally {
+            resourceService.update(originalResource);
+        }
     }
 
     @Test

Reply via email to