You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/08/17 16:54:26 UTC

syncope git commit: [SYNCOPE-685] Pre: re-organize SyncPoliySpec

Repository: syncope
Updated Branches:
  refs/heads/master dbeb62082 -> 8fe3c7c20


[SYNCOPE-685] Pre: re-organize SyncPoliySpec


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/8fe3c7c2
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/8fe3c7c2
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/8fe3c7c2

Branch: refs/heads/master
Commit: 8fe3c7c20105df6112a1ee7b0206cb00e9c1a054
Parents: dbeb620
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 17 16:54:18 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 17 16:54:18 2015 +0200

----------------------------------------------------------------------
 .../console/panels/BeanReflectionModal.java     |  36 --
 .../console/panels/BeanReflectionPanel.java     | 339 -------------------
 .../common/lib/annotation/ClassList.java        |  26 --
 .../common/lib/annotation/SchemaList.java       |  28 --
 .../common/lib/types/AbstractPolicySpec.java    |   2 -
 .../common/lib/types/SyncPolicySpec.java        |  42 +--
 .../common/lib/types/SyncPolicySpecItem.java    |  67 ----
 .../apache/syncope/core/logic/SyncopeLogic.java |  26 +-
 .../init/ImplementationClassNamesLoader.java    |   7 +-
 .../core/persistence/jpa/inner/PolicyTest.java  |  39 ++-
 .../test/resources/domains/MasterContent.xml    |   8 +-
 .../java/data/PolicyDataBinderImpl.java         |   3 -
 .../sync/PlainAttrsSyncCorrelationRule.java     | 110 ++++++
 .../core/provisioning/java/sync/SyncUtils.java  | 126 ++-----
 .../fit/core/reference/PolicyITCase.java        |  11 +-
 .../fit/core/reference/SyncTaskITCase.java      |  12 +-
 16 files changed, 193 insertions(+), 689 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
deleted file mode 100644
index a6859e8..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.console.panels;
-
-import java.io.Serializable;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
-
-/**
- * Modal window with Resource form.
- */
-public abstract class BeanReflectionModal extends ModalContent {
-
-    private static final long serialVersionUID = 1734415311027284222L;
-
-    public BeanReflectionModal(final Serializable bean, final ModalWindow window, final PageReference pageRef) {
-        super(window, pageRef);
-        add(new BeanReflectionPanel("bean", bean));
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
deleted file mode 100644
index 4f3f1d5..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.console.panels;
-
-import java.beans.PropertyDescriptor;
-import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.apache.syncope.client.console.commons.Constants;
-import org.apache.syncope.client.console.rest.PolicyRestClient;
-import org.apache.syncope.client.console.rest.SchemaRestClient;
-import org.apache.syncope.client.console.wicket.markup.html.form.AbstractFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.list.AltListView;
-import org.apache.syncope.common.lib.annotation.ClassList;
-import org.apache.syncope.common.lib.annotation.SchemaList;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LoadableDetachableModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.PropertyModel;
-import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.model.util.ListModel;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.ReflectionUtils.FieldCallback;
-import org.springframework.util.ReflectionUtils.FieldFilter;
-
-public class BeanReflectionPanel extends Panel {
-
-    private static final long serialVersionUID = -3035998190456928143L;
-
-    /**
-     * Logger.
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(BeanReflectionPanel.class);
-
-    @SpringBean
-    private SchemaRestClient schemaRestClient;
-
-    @SpringBean
-    private PolicyRestClient policyRestClient;
-
-    private final IModel<List<String>> userSchemas = new LoadableDetachableModel<List<String>>() {
-
-        private static final long serialVersionUID = -2012833443695917883L;
-
-        @Override
-        protected List<String> load() {
-            return schemaRestClient.getPlainSchemaNames();
-        }
-    };
-
-    private final IModel<List<String>> groupSchemas = new LoadableDetachableModel<List<String>>() {
-
-        private static final long serialVersionUID = 5275935387613157437L;
-
-        @Override
-        protected List<String> load() {
-            return schemaRestClient.getPlainSchemaNames();
-        }
-    };
-
-    private final IModel<List<String>> correlationRules = new LoadableDetachableModel<List<String>>() {
-
-        private static final long serialVersionUID = 5275935387613157437L;
-
-        @Override
-        protected List<String> load() {
-            return policyRestClient.getCorrelationRuleClasses();
-        }
-    };
-
-    public BeanReflectionPanel(final String id, final Serializable bean) {
-        super(id);
-
-        final List<FieldWrapper> items = new ArrayList<>();
-        ReflectionUtils.doWithFields(bean.getClass(),
-                new FieldCallback() {
-
-                    @Override
-                    public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
-                        FieldWrapper fieldWrapper = new FieldWrapper();
-                        fieldWrapper.setName(field.getName());
-                        fieldWrapper.setType(field.getType());
-
-                        final SchemaList schemaList = field.getAnnotation(SchemaList.class);
-                        fieldWrapper.setSchemaList(schemaList);
-
-                        final ClassList classList = field.getAnnotation(ClassList.class);
-                        fieldWrapper.setClassList(classList);
-
-                        items.add(fieldWrapper);
-                    }
-                },
-                new FieldFilter() {
-
-                    @Override
-                    public boolean matches(final Field field) {
-                        return !Modifier.isStatic(field.getModifiers()) && !"serialVersionUID".equals(field.getName());
-                    }
-                });
-
-        final ListView<FieldWrapper> policies = new AltListView<FieldWrapper>("fields", items) {
-
-            private static final long serialVersionUID = 9101744072914090143L;
-
-            @Override
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            protected void populateItem(final ListItem<FieldWrapper> item) {
-                final FieldWrapper field = item.getModelObject();
-
-                final PropertyDescriptor propDesc = BeanUtils.getPropertyDescriptor(bean.getClass(), field.getName());
-
-                item.add(new Label("label", new ResourceModel(field.getName())));
-
-                AbstractFieldPanel component;
-                try {
-                    if (field.getClassList() != null) {
-                        component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(bean,
-                                field.getName()));
-
-                        final List<String> rules = correlationRules.getObject();
-
-                        if (rules != null && !rules.isEmpty()) {
-                            ((AjaxDropDownChoicePanel) component).setChoices(correlationRules.getObject());
-                        }
-
-                        item.add(component);
-
-                        item.add(getActivationControl(
-                                component,
-                                propDesc.getReadMethod().invoke(bean, new Object[] {}) != null,
-                                null,
-                                null));
-
-                    } else if (field.getType().isEnum()) {
-                        component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(bean,
-                                field.getName()));
-
-                        final Serializable[] values = (Serializable[]) field.getType().getEnumConstants();
-
-                        if (values != null && values.length > 0) {
-                            ((AjaxDropDownChoicePanel) component).setChoices(Arrays.asList(values));
-                        }
-
-                        item.add(component);
-
-                        item.add(getActivationControl(
-                                component,
-                                (Enum<?>) propDesc.getReadMethod().invoke(bean, new Object[] {}) != null,
-                                values[0],
-                                values[0]));
-
-                    } else if (ClassUtils.isAssignable(Boolean.class, field.getType())) {
-                        item.add(new AjaxCheckBoxPanel("check", field.getName(),
-                                new PropertyModel<Boolean>(bean, field.getName())));
-
-                        item.add(new Label("field", new Model(null)));
-                    } else if (Collection.class.isAssignableFrom(field.getType())) {
-                        if (field.getSchemaList() != null) {
-                            final List<String> values = new ArrayList<>();
-                            if (field.getName().charAt(0) == 'r') {
-                                values.addAll(groupSchemas.getObject());
-
-                                if (field.getSchemaList().extended()) {
-                                    values.add("name");
-                                }
-                            } else {
-                                values.addAll(userSchemas.getObject());
-
-                                if (field.getSchemaList().extended()) {
-                                    values.add("key");
-                                    values.add("username");
-                                }
-                            }
-
-                            component = new AjaxPalettePanel("field", new PropertyModel(bean, field.getName()),
-                                    new ListModel<>(values));
-                            item.add(component);
-
-                            Collection<?> collection = (Collection) propDesc.getReadMethod().invoke(bean);
-                            item.add(getActivationControl(component,
-                                    !collection.isEmpty(), new ArrayList<String>(), new ArrayList<String>()));
-                        } else {
-                            final FieldPanel panel = new AjaxTextFieldPanel("panel", field.getName(),
-                                    new Model<String>(null));
-                            panel.setRequired(true);
-
-                            component = new MultiFieldPanel<String>("field",
-                                    new PropertyModel(bean, field.getName()), panel);
-
-                            item.add(component);
-
-                            final List<String> reinitializedValue = new ArrayList<String>();
-
-                            reinitializedValue.add("");
-
-                            item.add(getActivationControl(component,
-                                    !((Collection) propDesc.getReadMethod().invoke(bean, new Object[] {})).isEmpty(),
-                                    new ArrayList<String>(), (Serializable) reinitializedValue));
-                        }
-                    } else if (ClassUtils.isAssignable(Number.class, field.getType())) {
-                        component = new SpinnerFieldPanel<Number>("field", field.getName(),
-                                (Class<Number>) field.getType(), new PropertyModel<Number>(bean, field.getName()),
-                                null, null);
-                        item.add(component);
-
-                        item.add(getActivationControl(component,
-                                (Integer) propDesc.getReadMethod().invoke(bean, new Object[] {}) > 0, 0, 0));
-                    } else if (field.getType().equals(String.class)) {
-                        component = new AjaxTextFieldPanel("field", field.getName(),
-                                new PropertyModel(bean, field.getName()));
-
-                        item.add(component);
-
-                        item.add(getActivationControl(component,
-                                propDesc.getReadMethod().invoke(bean, new Object[] {}) != null, null, null));
-                    } else {
-                        item.add(new AjaxCheckBoxPanel("check", field.getName(), new Model()));
-                        item.add(new Label("field", new Model(null)));
-                    }
-                } catch (Exception e) {
-                    LOG.error("Error retrieving bean fields", e);
-                }
-            }
-        };
-
-        add(policies);
-    }
-
-    private <T extends Serializable> AjaxCheckBoxPanel getActivationControl(final AbstractFieldPanel<T> panel,
-            final Boolean checked, final T defaultModelObject, final T reinitializedValue) {
-
-        final AjaxCheckBoxPanel check = new AjaxCheckBoxPanel("check", "check", new Model<Boolean>(checked));
-
-        panel.setEnabled(checked);
-
-        check.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
-            private static final long serialVersionUID = -1107858522700306810L;
-
-            @Override
-            protected void onUpdate(final AjaxRequestTarget target) {
-                if (check.getModelObject()) {
-                    panel.setEnabled(true);
-                    panel.setModelObject(reinitializedValue);
-                } else {
-                    panel.setModelObject(defaultModelObject);
-                    panel.setEnabled(false);
-                }
-
-                target.add(panel);
-            }
-        });
-
-        return check;
-    }
-
-    private static class FieldWrapper implements Serializable {
-
-        private static final long serialVersionUID = -6770429509752964215L;
-
-        private Class<?> type;
-
-        private String name;
-
-        private transient SchemaList schemaList;
-
-        private transient ClassList classList;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(final String name) {
-            this.name = name;
-        }
-
-        public Class<?> getType() {
-            return type;
-        }
-
-        public void setType(final Class<?> type) {
-            this.type = type;
-        }
-
-        public SchemaList getSchemaList() {
-            return schemaList;
-        }
-
-        public void setSchemaList(final SchemaList schemaList) {
-            this.schemaList = schemaList;
-        }
-
-        public ClassList getClassList() {
-            return classList;
-        }
-
-        public void setClassList(final ClassList classList) {
-            this.classList = classList;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/ClassList.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/ClassList.java b/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/ClassList.java
deleted file mode 100644
index 07528ea..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/ClassList.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.lib.annotation;
-
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Retention(RUNTIME)
-public @interface ClassList {
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/SchemaList.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/SchemaList.java b/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/SchemaList.java
deleted file mode 100644
index 7469731..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/SchemaList.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.lib.annotation;
-
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Retention(RUNTIME)
-public @interface SchemaList {
-
-    boolean extended() default false;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
index e1e7333..33a0c62 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
@@ -28,7 +28,6 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.syncope.common.lib.annotation.SchemaList;
 
 @XmlType
 public abstract class AbstractPolicySpec implements PolicySpec {
@@ -43,7 +42,6 @@ public abstract class AbstractPolicySpec implements PolicySpec {
     /**
      * User attribute values not permitted.
      */
-    @SchemaList
     protected final List<String> schemasNotPermitted = new ArrayList<>();
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
index 34f95dd..5a3d309 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
@@ -18,26 +18,32 @@
  */
 package org.apache.syncope.common.lib.types;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
+import java.util.HashMap;
+import java.util.Map;
 import javax.xml.bind.annotation.XmlType;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Predicate;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
 
 @XmlType
 public class SyncPolicySpec implements PolicySpec {
 
     private static final long serialVersionUID = -3144027171719498127L;
 
-    private final List<SyncPolicySpecItem> items = new ArrayList<>();
+    private ConflictResolutionAction conflictResolutionAction;
 
     /**
-     * Conflict resolution action.
+     * Associates anyTypeKey to either:
+     * <ol>
+     * <li>Java class name, implementing {@code SyncCorrelationRule}</li>
+     * <li>JSON array containing plain schema names - this will be used to feed
+     * {@code PlainAttrsSyncCorrelationRule}</li>
+     * </ol>
      */
-    private ConflictResolutionAction conflictResolutionAction;
+    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
+    @JsonIgnore
+    private final Map<String, String> correlationRules = new HashMap<>();
 
     public ConflictResolutionAction getConflictResolutionAction() {
         return conflictResolutionAction == null
@@ -49,20 +55,8 @@ public class SyncPolicySpec implements PolicySpec {
         this.conflictResolutionAction = conflictResolutionAction;
     }
 
-    public SyncPolicySpecItem getItem(final String anyTypeKey) {
-        return CollectionUtils.find(items, new Predicate<SyncPolicySpecItem>() {
-
-            @Override
-            public boolean evaluate(final SyncPolicySpecItem item) {
-                return anyTypeKey != null && anyTypeKey.equals(item.getAnyTypeKey());
-            }
-        });
-    }
-
-    @XmlElementWrapper(name = "items")
-    @XmlElement(name = "item")
-    @JsonProperty("items")
-    public List<SyncPolicySpecItem> getItems() {
-        return items;
+    @JsonProperty
+    public Map<String, String> getCorrelationRules() {
+        return correlationRules;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpecItem.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpecItem.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpecItem.java
deleted file mode 100644
index 4acb61b..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpecItem.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.lib.types;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.AbstractBaseBean;
-import org.apache.syncope.common.lib.annotation.ClassList;
-import org.apache.syncope.common.lib.annotation.SchemaList;
-
-@XmlType
-public class SyncPolicySpecItem extends AbstractBaseBean {
-
-    private static final long serialVersionUID = 692466729711976485L;
-
-    private String anyTypeKey;
-
-    @SchemaList(extended = true)
-    private final List<String> altSearchSchemas = new ArrayList<>();
-
-    @ClassList
-    private String javaRule;
-
-    public String getAnyTypeKey() {
-        return anyTypeKey;
-    }
-
-    public void setAnyTypeKey(final String anyTypeKey) {
-        this.anyTypeKey = anyTypeKey;
-    }
-
-    public String getJavaRule() {
-        return javaRule;
-    }
-
-    public void setJavaRule(final String javaRule) {
-        this.javaRule = javaRule;
-    }
-
-    @XmlElementWrapper(name = "altSearchSchemas")
-    @XmlElement(name = "altSearchSchema")
-    @JsonProperty("altSearchSchemas")
-    public List<String> getAltSearchSchemas() {
-        return altSearchSchemas;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 8dd20a8..d6432e2 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.logic;
 
+import static org.apache.syncope.core.logic.init.ImplementationClassNamesLoader.Type;
+
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
@@ -128,22 +130,14 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.setGroupProvisioningManager(gProvisioningManager.getClass().getName());
         syncopeTO.setVirAttrCache(virAttrCache.getClass().getName());
 
-        syncopeTO.getReportlets().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.REPORTLET));
-        syncopeTO.getTaskJobs().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.TASKJOBDELEGATE));
-        syncopeTO.getPropagationActions().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PROPAGATION_ACTIONS));
-        syncopeTO.getSyncActions().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.SYNC_ACTIONS));
-        syncopeTO.getPushActions().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PUSH_ACTIONS));
-        syncopeTO.getSyncCorrelationRules().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.SYNC_CORRELATION_RULE));
-        syncopeTO.getPushCorrelationRules().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PUSH_CORRELATION_RULE));
-        syncopeTO.getValidators().addAll(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.VALIDATOR));
+        syncopeTO.getReportlets().addAll(classNamesLoader.getClassNames(Type.REPORTLET));
+        syncopeTO.getTaskJobs().addAll(classNamesLoader.getClassNames(Type.TASKJOBDELEGATE));
+        syncopeTO.getPropagationActions().addAll(classNamesLoader.getClassNames(Type.PROPAGATION_ACTIONS));
+        syncopeTO.getSyncActions().addAll(classNamesLoader.getClassNames(Type.SYNC_ACTIONS));
+        syncopeTO.getPushActions().addAll(classNamesLoader.getClassNames(Type.PUSH_ACTIONS));
+        syncopeTO.getSyncCorrelationRules().addAll(classNamesLoader.getClassNames(Type.SYNC_CORRELATION_RULE));
+        syncopeTO.getPushCorrelationRules().addAll(classNamesLoader.getClassNames(Type.PUSH_CORRELATION_RULE));
+        syncopeTO.getValidators().addAll(classNamesLoader.getClassNames(Type.VALIDATOR));
 
         Set<String> htmlTemplates = new HashSet<>();
         Set<String> textTemplates = new HashSet<>();

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
index 2bcd78f..1b53e5c 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
@@ -62,9 +62,6 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
 
     }
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(ImplementationClassNamesLoader.class);
 
     private Map<Type, Set<String>> classNames;
@@ -87,7 +84,7 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
         scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
-        // Remove once SYNCOPE-631 is done
+        // Remove once SYNCOPE-470 is done
         //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
@@ -121,7 +118,7 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
                     classNames.get(Type.SYNC_CORRELATION_RULE).add(bd.getBeanClassName());
                 }
 
-                // Uncomment when SYNCOPE-631 is done
+                // Uncomment when SYNCOPE-470 is done
                 /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
                  * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
                  * } */

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index c75a148..603cd01 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -22,12 +22,15 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.SyncPolicySpec;
-import org.apache.syncope.common.lib.types.SyncPolicySpecItem;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
@@ -55,9 +58,20 @@ public class PolicyTest extends AbstractTest {
     }
 
     @Test
-    public void findById() {
-        Policy policy = policyDAO.find(1L);
+    public void findByKey() {
+        SyncPolicy policy = policyDAO.find(3L);
         assertNotNull("findById did not work", policy);
+
+        SyncPolicySpec spec = policy.getSpecification(SyncPolicySpec.class);
+        assertNotNull(spec);
+
+        String rule = spec.getCorrelationRules().get(AnyTypeKind.USER.name());
+        assertNotNull(rule);
+        String[] plainSchemas = POJOHelper.deserialize(rule, String[].class);
+        assertNotNull(plainSchemas);
+        assertEquals(2, plainSchemas.length);
+        assertTrue(ArrayUtils.contains(plainSchemas, "username"));
+        assertTrue(ArrayUtils.contains(plainSchemas, "firstname"));
     }
 
     @Test
@@ -89,15 +103,8 @@ public class PolicyTest extends AbstractTest {
 
         SyncPolicySpec syncPolicySpec = new SyncPolicySpec();
 
-        SyncPolicySpecItem item = new SyncPolicySpecItem();
-        item.setAnyTypeKey(anyTypeDAO.findUser().getKey());
-        item.setJavaRule(syncURuleName);
-        syncPolicySpec.getItems().add(item);
-
-        item = new SyncPolicySpecItem();
-        item.setAnyTypeKey(anyTypeDAO.findGroup().getKey());
-        item.setJavaRule(syncGRuleName);
-        syncPolicySpec.getItems().add(item);
+        syncPolicySpec.getCorrelationRules().put(anyTypeDAO.findUser().getKey(), syncURuleName);
+        syncPolicySpec.getCorrelationRules().put(anyTypeDAO.findGroup().getKey(), syncGRuleName);
 
         policy.setSpecification(syncPolicySpec);
         policy.setDescription("Sync policy");
@@ -106,10 +113,10 @@ public class PolicyTest extends AbstractTest {
 
         assertNotNull(policy);
         assertEquals(PolicyType.SYNC, policy.getType());
-        assertEquals(syncURuleName,
-                (policy.getSpecification(SyncPolicySpec.class)).getItem(anyTypeDAO.findUser().getKey()).getJavaRule());
-        assertEquals(syncGRuleName,
-                (policy.getSpecification(SyncPolicySpec.class)).getItem(anyTypeDAO.findGroup().getKey()).getJavaRule());
+        assertEquals(syncURuleName, (policy.getSpecification(SyncPolicySpec.class)).
+                getCorrelationRules().get(anyTypeDAO.findUser().getKey()));
+        assertEquals(syncGRuleName, (policy.getSpecification(SyncPolicySpec.class)).
+                getCorrelationRules().get(anyTypeDAO.findGroup().getKey()));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 37ff60f..5977c9a 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -101,11 +101,11 @@ under the License.
   
   <!-- sample policies -->
   <Policy DTYPE="SyncPolicy" id="1" description="a sync policy" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE","items":[]}'/>
+          specification='{"conflictResolutionAction":"IGNORE"'/>
   <Policy DTYPE="PasswordPolicy" id="2" description="a password policy" type="PASSWORD" 
           specification='{"historyLength":1,"maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[],"allowNullPassword":true}'/>
   <Policy DTYPE="SyncPolicy" id="3" description="sync policy 2" type="SYNC" 
-          specification='{"conflictResolutionAction":"ALL","items":[{"anyTypeKey":"USER","javaRule":null,"altSearchSchemas":["username","firstname"]}]}'/>
+          specification='{"conflictResolutionAction":"ALL","correlationRules":{"USER":"[\"username\",\"firstname\"]"}}'/>
   <Policy DTYPE="PasswordPolicy" id="4" description="sample password policy" type="PASSWORD" 
           specification='{"historyLength":0,"maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[], "allowNullPassword":true}'/>
   <Policy DTYPE="AccountPolicy" id="5" description="an account policy" type="ACCOUNT" 
@@ -113,11 +113,11 @@ under the License.
   <Policy DTYPE="AccountPolicy" id="6" description="sample account policy" type="ACCOUNT" 
           specification='{"maxLength":0,"minLength":4,"pattern":null,"allUpperCase":false,"allLowerCase":false,"propagateSuspension":false,"maxAuthenticationAttempts":3,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
   <Policy DTYPE="SyncPolicy" id="7" description="sync policy 1" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE","items":[]}'/>
+          specification='{"conflictResolutionAction":"IGNORE"}'/>
   <Policy DTYPE="PasswordPolicy" id="8" description="sample password policy" type="PASSWORD" 
           specification='{"historyLength":0,"maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[],"allowNullPassword":false}'/>
   <Policy DTYPE="SyncPolicy" id="9" description="sync policy for java rule" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE","items":[]}'/>
+          specification='{"conflictResolutionAction":"IGNORE"}'/>
 
   <RelationshipType name="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType name="neighborhood"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
index fde025d..fa53bbd 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
@@ -45,9 +45,6 @@ import org.springframework.stereotype.Component;
 @Component
 public class PolicyDataBinderImpl implements PolicyDataBinder {
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(PolicyDataBinder.class);
 
     @Autowired

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/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
new file mode 100644
index 0000000..1af195e
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/PlainAttrsSyncCorrelationRule.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.sync;
+
+import java.util.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.sync.SyncCorrelationRule;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+
+public class PlainAttrsSyncCorrelationRule implements SyncCorrelationRule {
+
+    private final List<String> plainSchemaNames;
+
+    private final Provision provision;
+
+    public PlainAttrsSyncCorrelationRule(final String[] plainSchemaNames, final Provision provision) {
+        this.plainSchemaNames = Arrays.asList(plainSchemaNames);
+        this.provision = provision;
+    }
+
+    @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()));
+        }
+
+        // search for user/group by attribute(s) specified in the policy
+        SearchCond searchCond = null;
+
+        for (String schema : plainSchemaNames) {
+            Attribute value = extValues.get(schema);
+
+            if (value == null) {
+                throw new IllegalArgumentException(
+                        "Connector object does not contains the attributes to perform the search: " + schema);
+            }
+
+            AttributeCond.Type type;
+            String expression = null;
+
+            if (value.getValue() == null || value.getValue().isEmpty()
+                    || (value.getValue().size() == 1 && value.getValue().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();
+            }
+
+            SearchCond nodeCond;
+            // users: just id or username can be selected to be used
+            // groups: just id or name can be selected to be used
+            if ("key".equalsIgnoreCase(schema)
+                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
+
+                AnyCond cond = new AnyCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            } else {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            }
+
+            searchCond = searchCond == null
+                    ? nodeCond
+                    : SearchCond.getAndCond(searchCond, nodeCond);
+        }
+
+        return searchCond;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/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 dc5cdf0..7fd244b 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
@@ -20,15 +20,14 @@ package org.apache.syncope.core.provisioning.java.sync;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.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.types.SyncPolicySpec;
 import org.apache.syncope.core.misc.MappingUtils;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
@@ -36,10 +35,7 @@ import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-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.OrderByClause;
-import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -230,11 +226,15 @@ public class SyncUtils {
         return result;
     }
 
-    private List<Long> search(final SearchCond searchCond, final AnyTypeKind type) {
-        final List<Long> result = new ArrayList<>();
+    private List<Long> findByCorrelationRule(
+            final ConnectorObject connObj, final SyncCorrelationRule rule, final AnyTypeKind type) {
+
+        List<Long> result = new ArrayList<>();
 
         List<Any<?, ?, ?>> anys = searchDAO.search(
-                SyncopeConstants.FULL_ADMIN_REALMS, searchCond, Collections.<OrderByClause>emptyList(), type);
+                SyncopeConstants.FULL_ADMIN_REALMS,
+                rule.getSearchCond(connObj),
+                Collections.<OrderByClause>emptyList(), type);
         for (Any<?, ?, ?> any : anys) {
             result.add(any.getKey());
         }
@@ -242,101 +242,24 @@ public class SyncUtils {
         return result;
     }
 
-    private List<Long> findByCorrelationRule(
-            final ConnectorObject connObj, final SyncCorrelationRule rule, final AnyTypeKind type) {
-
-        return search(rule.getSearchCond(connObj), type);
-    }
-
-    private List<Long> findByAnySearch(
-            final ConnectorObject connObj,
-            final List<String> altSearchSchemas,
-            final Provision provision,
-            final AnyTypeKind anyTypeKind) {
-
-        // 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()));
-        }
-
-        // search for user/group by attribute(s) specified in the policy
-        SearchCond searchCond = null;
-
-        for (String schema : altSearchSchemas) {
-            Attribute value = extValues.get(schema);
-
-            if (value == null) {
-                throw new IllegalArgumentException(
-                        "Connector object does not contains the attributes to perform the search: " + schema);
-            }
-
-            AttributeCond.Type type;
-            String expression = null;
-
-            if (value.getValue() == null || value.getValue().isEmpty()
-                    || (value.getValue().size() == 1 && value.getValue().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();
-            }
-
-            SearchCond nodeCond;
-            // users: just id or username can be selected to be used
-            // groups: just id or name can be selected to be used
-            if ("key".equalsIgnoreCase(schema)
-                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
-
-                AnyCond cond = new AnyCond();
-                cond.setSchema(schema);
-                cond.setType(type);
-                cond.setExpression(expression);
-
-                nodeCond = SearchCond.getLeafCond(cond);
-            } else {
-                AttributeCond cond = new AttributeCond();
-                cond.setSchema(schema);
-                cond.setType(type);
-                cond.setExpression(expression);
-
-                nodeCond = SearchCond.getLeafCond(cond);
-            }
-
-            searchCond = searchCond == null
-                    ? nodeCond
-                    : SearchCond.getAndCond(searchCond, nodeCond);
-        }
-
-        return search(searchCond, anyTypeKind);
-    }
-
     private SyncCorrelationRule getCorrelationRule(final Provision provision, final SyncPolicySpec policySpec) {
-        String clazz = policySpec.getItem(provision.getAnyType().getKey()) == null
-                ? null
-                : policySpec.getItem(provision.getAnyType().getKey()).getJavaRule();
-
-        SyncCorrelationRule res = null;
+        SyncCorrelationRule result = null;
 
-        if (StringUtils.isNotBlank(clazz)) {
-            try {
-                res = (SyncCorrelationRule) Class.forName(clazz).newInstance();
-            } catch (Exception e) {
-                LOG.error("Failure instantiating correlation rule class '{}'", clazz, e);
+        String syncCorrelationRule = policySpec.getCorrelationRules().get(provision.getAnyType().getKey());
+        if (StringUtils.isNotBlank(syncCorrelationRule)) {
+            if (syncCorrelationRule.charAt(0) == '[') {
+                result = new PlainAttrsSyncCorrelationRule(
+                        POJOHelper.deserialize(syncCorrelationRule, String[].class), provision);
+            } else {
+                try {
+                    result = (SyncCorrelationRule) Class.forName(syncCorrelationRule).newInstance();
+                } catch (Exception e) {
+                    LOG.error("Failure instantiating correlation rule class '{}'", syncCorrelationRule, e);
+                }
             }
         }
 
-        return res;
-    }
-
-    private List<String> getAltSearchSchemas(final Provision provision, final SyncPolicySpec policySpec) {
-        return policySpec.getItem(provision.getAnyType().getKey()) == null
-                ? Collections.<String>emptyList()
-                : policySpec.getItem(provision.getAnyType().getKey()).getAltSearchSchemas();
+        return result;
     }
 
     /**
@@ -360,17 +283,12 @@ public class SyncUtils {
         }
 
         SyncCorrelationRule syncRule = null;
-        List<String> altSearchSchemas = null;
-
         if (syncPolicySpec != null) {
             syncRule = getCorrelationRule(provision, syncPolicySpec);
-            altSearchSchemas = getAltSearchSchemas(provision, syncPolicySpec);
         }
 
         return syncRule == null
-                ? altSearchSchemas == null || altSearchSchemas.isEmpty()
-                        ? findByConnObjectKeyItem(uid, provision, anyUtils)
-                        : findByAnySearch(connObj, altSearchSchemas, provision, anyUtils.getAnyTypeKind())
+                ? findByConnObjectKeyItem(uid, provision, anyUtils)
                 : findByCorrelationRule(connObj, syncRule, anyUtils.getAnyTypeKind());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
index 0098821..95701ae 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
@@ -36,7 +36,6 @@ import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.SyncPolicySpec;
-import org.apache.syncope.common.lib.types.SyncPolicySpecItem;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -47,12 +46,8 @@ public class PolicyITCase extends AbstractITCase {
     private SyncPolicyTO buildSyncPolicyTO() {
         SyncPolicyTO policy = new SyncPolicyTO();
 
-        SyncPolicySpecItem item = new SyncPolicySpecItem();
-        item.setAnyTypeKey(AnyTypeKind.USER.name());
-        item.setJavaRule(TestSyncRule.class.getName());
-
         SyncPolicySpec spec = new SyncPolicySpec();
-        spec.getItems().add(item);
+        spec.getCorrelationRules().put(AnyTypeKind.USER.name(), TestSyncRule.class.getName());
 
         policy.setSpecification(spec);
         policy.setDescription("Sync policy");
@@ -116,7 +111,7 @@ public class PolicyITCase extends AbstractITCase {
         assertNotNull(policyTO);
         assertEquals(PolicyType.SYNC, policyTO.getType());
         assertEquals(TestSyncRule.class.getName(),
-                policyTO.getSpecification().getItem(AnyTypeKind.USER.name()).getJavaRule());
+                policyTO.getSpecification().getCorrelationRules().get(AnyTypeKind.USER.name()));
     }
 
     @Test
@@ -169,7 +164,7 @@ public class PolicyITCase extends AbstractITCase {
 
     @Test
     public void getCorrelationRules() {
-        assertEquals(1, syncopeService.info().getSyncCorrelationRules().size());
+        assertEquals(2, syncopeService.info().getSyncCorrelationRules().size());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/8fe3c7c2/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 4313c00..d97eaed 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
@@ -55,7 +55,6 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
-import org.apache.syncope.common.lib.types.SyncPolicySpecItem;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.lib.wrap.ResourceKey;
 import org.apache.syncope.common.rest.api.CollectionWrapper;
@@ -505,16 +504,7 @@ public class SyncTaskITCase extends AbstractTaskITCase {
         // Add a custom correlation rule
         // -----------------------------
         SyncPolicyTO policyTO = policyService.read(9L);
-
-        SyncPolicySpecItem item = policyTO.getSpecification().getItem(AnyTypeKind.USER.name());
-        if (item == null) {
-            item = new SyncPolicySpecItem();
-            item.setAnyTypeKey(AnyTypeKind.USER.name());
-
-            policyTO.getSpecification().getItems().add(item);
-        }
-        item.setJavaRule(TestSyncRule.class.getName());
-
+        policyTO.getSpecification().getCorrelationRules().put(AnyTypeKind.USER.name(), TestSyncRule.class.getName());
         policyService.update(policyTO);
         // -----------------------------