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/02/12 10:14:21 UTC
[16/54] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Renaming
'server' after 'core',
to provide continuity with older releases (especially for archetype)
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java
new file mode 100644
index 0000000..a786b94
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java
@@ -0,0 +1,219 @@
+/*
+ * 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.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.mod.ReferenceMod;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.role.RMapping;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.core.persistence.api.entity.user.UMapping;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.sync.SyncActions;
+import org.apache.syncope.core.misc.security.UnauthorizedRoleException;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.provisioning.api.job.SyncJob;
+import org.apache.syncope.core.provisioning.api.sync.RoleSyncResultHandler;
+import org.apache.syncope.core.provisioning.api.sync.UserSyncResultHandler;
+import org.apache.syncope.core.workflow.api.RoleWorkflowAdapter;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+
+/**
+ * Job for executing synchronization (from external resource) tasks.
+ *
+ * @see AbstractProvisioningJob
+ * @see SyncTask
+ */
+public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions> implements SyncJob {
+
+ /**
+ * Role workflow adapter.
+ */
+ @Autowired
+ private RoleWorkflowAdapter rwfAdapter;
+
+ @Autowired
+ protected SyncUtilities syncUtilities;
+
+ protected void setRoleOwners(final RoleSyncResultHandler rhandler)
+ throws UnauthorizedRoleException, NotFoundException {
+
+ for (Map.Entry<Long, String> entry : rhandler.getRoleOwnerMap().entrySet()) {
+ RoleMod roleMod = new RoleMod();
+ roleMod.setKey(entry.getKey());
+
+ if (StringUtils.isBlank(entry.getValue())) {
+ roleMod.setRoleOwner(null);
+ roleMod.setUserOwner(null);
+ } else {
+ Long userId = syncUtilities.findMatchingAttributableKey(
+ ObjectClass.ACCOUNT,
+ entry.getValue(),
+ rhandler.getProfile().getTask().getResource(),
+ rhandler.getProfile().getConnector());
+
+ if (userId == null) {
+ Long roleId = syncUtilities.findMatchingAttributableKey(
+ ObjectClass.GROUP,
+ entry.getValue(),
+ rhandler.getProfile().getTask().getResource(),
+ rhandler.getProfile().getConnector());
+
+ if (roleId != null) {
+ roleMod.setRoleOwner(new ReferenceMod(roleId));
+ }
+ } else {
+ roleMod.setUserOwner(new ReferenceMod(userId));
+ }
+ }
+
+ rwfAdapter.update(roleMod);
+ }
+ }
+
+ @Override
+ protected String executeWithSecurityContext(
+ final SyncTask syncTask,
+ final Connector connector,
+ final UMapping uMapping,
+ final RMapping rMapping,
+ final boolean dryRun) throws JobExecutionException {
+
+ LOG.debug("Execute synchronization with token {}", syncTask.getResource().getUsyncToken());
+
+ final ProvisioningProfile<SyncTask, SyncActions> profile = new ProvisioningProfile<>(connector, syncTask);
+ if (actions != null) {
+ profile.getActions().addAll(actions);
+ }
+ profile.setDryRun(dryRun);
+ profile.setResAct(getSyncPolicySpec(syncTask).getConflictResolutionAction());
+
+ // Prepare handler for SyncDelta objects (users)
+ final UserSyncResultHandler uhandler =
+ (UserSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+ createBean(UserSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+ uhandler.setProfile(profile);
+
+ // Prepare handler for SyncDelta objects (roles/groups)
+ final RoleSyncResultHandler rhandler =
+ (RoleSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().
+ createBean(RoleSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+ rhandler.setProfile(profile);
+
+ if (actions != null && !profile.isDryRun()) {
+ for (SyncActions action : actions) {
+ action.beforeAll(profile);
+ }
+ }
+
+ try {
+ SyncToken latestUSyncToken = null;
+ if (uMapping != null && !syncTask.isFullReconciliation()) {
+ latestUSyncToken = connector.getLatestSyncToken(ObjectClass.ACCOUNT);
+ }
+ SyncToken latestRSyncToken = null;
+ if (rMapping != null && !syncTask.isFullReconciliation()) {
+ latestRSyncToken = connector.getLatestSyncToken(ObjectClass.GROUP);
+ }
+
+ if (syncTask.isFullReconciliation()) {
+ if (uMapping != null) {
+ connector.getAllObjects(ObjectClass.ACCOUNT, uhandler,
+ connector.getOperationOptions(uMapping.getItems()));
+ }
+ if (rMapping != null) {
+ connector.getAllObjects(ObjectClass.GROUP, rhandler,
+ connector.getOperationOptions(rMapping.getItems()));
+ }
+ } else {
+ if (uMapping != null) {
+ connector.sync(ObjectClass.ACCOUNT, syncTask.getResource().getUsyncToken(), uhandler,
+ connector.getOperationOptions(uMapping.getItems()));
+ }
+ if (rMapping != null) {
+ connector.sync(ObjectClass.GROUP, syncTask.getResource().getRsyncToken(), rhandler,
+ connector.getOperationOptions(rMapping.getItems()));
+ }
+ }
+
+ if (!dryRun && !syncTask.isFullReconciliation()) {
+ try {
+ ExternalResource resource = resourceDAO.find(syncTask.getResource().getKey());
+ if (uMapping != null) {
+ resource.setUsyncToken(latestUSyncToken);
+ }
+ if (rMapping != null) {
+ resource.setRsyncToken(latestRSyncToken);
+ }
+ resourceDAO.save(resource);
+ } catch (Exception e) {
+ throw new JobExecutionException("While updating SyncToken", e);
+ }
+ }
+ } catch (Throwable t) {
+ throw new JobExecutionException("While syncing on connector", t);
+ }
+
+ try {
+ setRoleOwners(rhandler);
+ } catch (Exception e) {
+ LOG.error("While setting role owners", e);
+ }
+
+ if (actions != null && !profile.isDryRun()) {
+ for (SyncActions action : actions) {
+ action.afterAll(profile);
+ }
+ }
+
+ final String result = createReport(profile.getResults(), syncTask.getResource().getSyncTraceLevel(), dryRun);
+
+ LOG.debug("Sync result: {}", result);
+
+ return result;
+ }
+
+ private SyncPolicySpec getSyncPolicySpec(final ProvisioningTask task) {
+ SyncPolicySpec syncPolicySpec;
+
+ if (task instanceof SyncTask) {
+ final SyncPolicy syncPolicy = task.getResource().getSyncPolicy() == null
+ ? policyDAO.getGlobalSyncPolicy()
+ : task.getResource().getSyncPolicy();
+
+ syncPolicySpec = syncPolicy == null ? null : syncPolicy.getSpecification(SyncPolicySpec.class);
+ } else {
+ syncPolicySpec = null;
+ }
+
+ // step required because the call <policy>.getSpecification() could return a null value
+ return syncPolicySpec == null ? new SyncPolicySpec() : syncPolicySpec;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java
new file mode 100644
index 0000000..90f8a13
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.core.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.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.dao.search.SubjectCond;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
+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.sync.SyncCorrelationRule;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SyncUtilities {
+
+ /**
+ * Logger.
+ */
+ protected static final Logger LOG = LoggerFactory.getLogger(SyncUtilities.class);
+
+ /**
+ * Policy DAO.
+ */
+ @Autowired
+ protected PolicyDAO policyDAO;
+
+ /**
+ * Entitlement DAO.
+ */
+ @Autowired
+ protected EntitlementDAO entitlementDAO;
+
+ /**
+ * Schema DAO.
+ */
+ @Autowired
+ protected PlainSchemaDAO plainSchemaDAO;
+
+ /**
+ * User DAO.
+ */
+ @Autowired
+ protected UserDAO userDAO;
+
+ /**
+ * Role DAO.
+ */
+ @Autowired
+ protected RoleDAO roleDAO;
+
+ /**
+ * Search DAO.
+ */
+ @Autowired
+ protected SubjectSearchDAO searchDAO;
+
+ @Autowired
+ protected AttributableUtilFactory attrUtilFactory;
+
+ public Long findMatchingAttributableKey(
+ final ObjectClass oclass,
+ final String name,
+ final ExternalResource resource,
+ final Connector connector) {
+
+ Long result = null;
+
+ final AttributableUtil attrUtil = attrUtilFactory.getInstance(oclass);
+
+ final List<ConnectorObject> found = connector.search(oclass,
+ new EqualsFilter(new Name(name)), connector.getOperationOptions(
+ attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)));
+
+ if (found.isEmpty()) {
+ LOG.debug("No {} found on {} with __NAME__ {}", oclass, resource, name);
+ } else {
+ if (found.size() > 1) {
+ LOG.warn("More than one {} found on {} with __NAME__ {} - taking first only", oclass, resource, name);
+ }
+
+ ConnectorObject connObj = found.iterator().next();
+ try {
+ List<Long> subjectKeys = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil);
+ if (subjectKeys.isEmpty()) {
+ LOG.debug("No matching {} found for {}, aborting", attrUtil.getType(), connObj);
+ } else {
+ if (subjectKeys.size() > 1) {
+ LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectKeys);
+ }
+
+ result = subjectKeys.iterator().next();
+ }
+ } catch (IllegalArgumentException e) {
+ LOG.warn(e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+ private List<Long> findByAccountIdItem(
+ final String uid, final ExternalResource resource, final AttributableUtil attrUtil) {
+ final List<Long> result = new ArrayList<>();
+
+ final MappingItem accountIdItem = attrUtil.getAccountIdItem(resource);
+ switch (accountIdItem.getIntMappingType()) {
+ case UserPlainSchema:
+ case RolePlainSchema:
+ final PlainAttrValue value = attrUtil.newPlainAttrValue();
+
+ PlainSchema schema = plainSchemaDAO.find(accountIdItem.getIntAttrName(), attrUtil.plainSchemaClass());
+ if (schema == null) {
+ value.setStringValue(uid);
+ } else {
+ try {
+ value.parseValue(schema, uid);
+ } catch (ParsingValidationException e) {
+ LOG.error("While parsing provided __UID__ {}", uid, e);
+ value.setStringValue(uid);
+ }
+ }
+
+ List<? extends Subject<UPlainAttr, UDerAttr, UVirAttr>> users =
+ userDAO.findByAttrValue(accountIdItem.getIntAttrName(), value, attrUtil);
+ for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) {
+ result.add(subject.getKey());
+ }
+ break;
+
+ case UserDerivedSchema:
+ case RoleDerivedSchema:
+ users = userDAO.findByDerAttrValue(accountIdItem.getIntAttrName(), uid, attrUtil);
+ for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) {
+ result.add(subject.getKey());
+ }
+ break;
+
+ case Username:
+ User user = userDAO.find(uid);
+ if (user != null) {
+ result.add(user.getKey());
+ }
+ break;
+
+ case UserId:
+ user = userDAO.find(Long.parseLong(uid));
+ if (user != null) {
+ result.add(user.getKey());
+ }
+ break;
+
+ case RoleName:
+ List<Role> roles = roleDAO.find(uid);
+ for (Role role : roles) {
+ result.add(role.getKey());
+ }
+ break;
+
+ case RoleId:
+ Role role = roleDAO.find(Long.parseLong(uid));
+ if (role != null) {
+ result.add(role.getKey());
+ }
+ break;
+
+ default:
+ LOG.error("Invalid accountId type '{}'", accountIdItem.getIntMappingType());
+ }
+
+ return result;
+ }
+
+ private List<Long> search(final SearchCond searchCond, final SubjectType type) {
+ final List<Long> result = new ArrayList<>();
+
+ List<Subject<?, ?, ?>> subjects = searchDAO.search(
+ RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
+ searchCond, Collections.<OrderByClause>emptyList(), type);
+ for (Subject<?, ?, ?> subject : subjects) {
+ result.add(subject.getKey());
+ }
+
+ return result;
+ }
+
+ private List<Long> findByCorrelationRule(
+ final ConnectorObject connObj, final SyncCorrelationRule rule, final SubjectType type) {
+
+ return search(rule.getSearchCond(connObj), type);
+ }
+
+ private List<Long> findByAttributableSearch(
+ final ConnectorObject connObj,
+ final List<String> altSearchSchemas,
+ final ExternalResource resource,
+ final AttributableUtil attrUtil) {
+
+ // search for external attribute's name/value of each specified name
+ final Map<String, Attribute> extValues = new HashMap<>();
+
+ for (MappingItem item : attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)) {
+ extValues.put(item.getIntAttrName(), connObj.getAttributeByName(item.getExtAttrName()));
+ }
+
+ // search for user/role 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
+ // roles: just id or name can be selected to be used
+ if ("key".equalsIgnoreCase(schema)
+ || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
+
+ SubjectCond cond = new SubjectCond();
+ 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, SubjectType.valueOf(attrUtil.getType().name()));
+ }
+
+ private SyncCorrelationRule getCorrelationRule(final AttributableType type, final SyncPolicySpec policySpec) {
+ String clazz;
+
+ switch (type) {
+ case USER:
+ clazz = policySpec.getUserJavaRule();
+ break;
+ case ROLE:
+ clazz = policySpec.getRoleJavaRule();
+ break;
+ case MEMBERSHIP:
+ case CONFIGURATION:
+ default:
+ clazz = null;
+ }
+
+ SyncCorrelationRule res = null;
+
+ if (StringUtils.isNotBlank(clazz)) {
+ try {
+ res = (SyncCorrelationRule) Class.forName(clazz).newInstance();
+ } catch (Exception e) {
+ LOG.error("Failure instantiating correlation rule class '{}'", clazz, e);
+ }
+ }
+
+ return res;
+ }
+
+ private List<String> getAltSearchSchemas(final AttributableType type, final SyncPolicySpec policySpec) {
+ List<String> result = Collections.emptyList();
+
+ switch (type) {
+ case USER:
+ result = policySpec.getuAltSearchSchemas();
+ break;
+ case ROLE:
+ result = policySpec.getrAltSearchSchemas();
+ break;
+ case MEMBERSHIP:
+ case CONFIGURATION:
+ default:
+ }
+
+ return result;
+ }
+
+ /**
+ * Find users / roles based on mapped uid value (or previous uid value, if updated).
+ *
+ * @param uid for finding by account id
+ * @param connObj for finding by attribute value
+ * @param resource external resource
+ * @param attrUtil attributable util
+ * @return list of matching users / roles
+ */
+ public List<Long> findExisting(
+ final String uid,
+ final ConnectorObject connObj,
+ final ExternalResource resource,
+ final AttributableUtil attrUtil) {
+
+ SyncPolicySpec syncPolicySpec = null;
+ if (resource.getSyncPolicy() == null) {
+ SyncPolicy globalSP = policyDAO.getGlobalSyncPolicy();
+ if (globalSP != null) {
+ syncPolicySpec = globalSP.getSpecification(SyncPolicySpec.class);
+ }
+ } else {
+ syncPolicySpec = resource.getSyncPolicy().getSpecification(SyncPolicySpec.class);
+ }
+
+ SyncCorrelationRule syncRule = null;
+ List<String> altSearchSchemas = null;
+
+ if (syncPolicySpec != null) {
+ syncRule = getCorrelationRule(attrUtil.getType(), syncPolicySpec);
+ altSearchSchemas = getAltSearchSchemas(attrUtil.getType(), syncPolicySpec);
+ }
+
+ return syncRule == null ? altSearchSchemas == null || altSearchSchemas.isEmpty()
+ ? findByAccountIdItem(uid, resource, attrUtil)
+ : findByAttributableSearch(connObj, altSearchSchemas, resource, attrUtil)
+ : findByCorrelationRule(connObj, syncRule, SubjectType.valueOf(attrUtil.getType().name()));
+ }
+
+ public Boolean readEnabled(final ConnectorObject connectorObject, final ProvisioningTask task) {
+ Boolean enabled = null;
+ if (task.isSyncStatus()) {
+ Attribute status = AttributeUtil.find(OperationalAttributes.ENABLE_NAME, connectorObject.getAttributes());
+ if (status != null && status.getValue() != null && !status.getValue().isEmpty()) {
+ enabled = (Boolean) status.getValue().get(0);
+ }
+ }
+
+ return enabled;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
new file mode 100644
index 0000000..0c1ad8b
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Mapping;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler {
+
+ @Override
+ protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+ final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
+ Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
+
+ return userDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUserCreateTaskIds(
+ before.getKey(),
+ enabled,
+ propByRes,
+ null,
+ Collections.unmodifiableCollection(before.getVirAttrs()),
+ Collections.unmodifiableCollection(before.getMemberships()),
+ noPropResources));
+
+ return userDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+
+ if (unlink) {
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ } else {
+ userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ }
+
+ uwfAdapter.update(userMod);
+
+ return userDAO.authFetch(userMod.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ uwfAdapter.update(userMod);
+ return deprovision(sbj);
+ }
+
+ @Override
+ protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+ userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ uwfAdapter.update(userMod);
+ return provision(sbj, enabled);
+ }
+
+ @Override
+ protected String getName(final Subject<?, ?, ?> subject) {
+ return User.class.cast(subject).getUsername();
+ }
+
+ @Override
+ protected AbstractSubjectTO getSubjectTO(final long key) {
+ try {
+ return userTransfer.getUserTO(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected Subject<?, ?, ?> getSubject(final long key) {
+ try {
+ return userDAO.authFetch(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected ConnectorObject getRemoteObject(final String accountId) {
+ ConnectorObject obj = null;
+
+ try {
+ final Uid uid = new Uid(accountId);
+
+ obj = profile.getConnector().getObject(
+ ObjectClass.ACCOUNT,
+ uid,
+ profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
+ } catch (RuntimeException ignore) {
+ LOG.debug("While resolving {}", accountId, ignore);
+ }
+ return obj;
+ }
+
+ @Override
+ protected Mapping<?> getMapping() {
+ return profile.getTask().getResource().getUmapping();
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
new file mode 100644
index 0000000..0f0d785
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
@@ -0,0 +1,148 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.core.provisioning.api.sync.UserSyncResultHandler;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+
+public class UserSyncResultHandlerImpl extends AbstractSyncResultHandler implements UserSyncResultHandler {
+
+ @Override
+ protected AttributableUtil getAttributableUtil() {
+ return attrUtilFactory.getInstance(AttributableType.USER);
+ }
+
+ @Override
+ protected String getName(final AbstractSubjectTO subjectTO) {
+ return UserTO.class.cast(subjectTO).getUsername();
+ }
+
+ @Override
+ protected AbstractSubjectTO getSubjectTO(final long key) {
+ try {
+ return userTransfer.getUserTO(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected AbstractSubjectMod getSubjectMod(
+ final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+ return connObjectUtil.getAttributableMod(
+ subjectTO.getKey(),
+ delta.getObject(),
+ subjectTO,
+ profile.getTask(),
+ getAttributableUtil());
+ }
+
+ @Override
+ protected AbstractSubjectTO create(
+ final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) {
+
+ UserTO userTO = UserTO.class.cast(subjectTO);
+
+ Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+ Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled,
+ Collections.singleton(profile.getTask().getResource().getKey()));
+
+ userTO = userTransfer.getUserTO(created.getKey());
+
+ result.setId(created.getKey());
+
+ return userTO;
+ }
+
+ @Override
+ protected AbstractSubjectTO link(
+ final AbstractSubjectTO before,
+ final ProvisioningResult result,
+ final boolean unlink) {
+
+ final UserMod userMod = new UserMod();
+ userMod.setKey(before.getKey());
+
+ if (unlink) {
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ } else {
+ userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ }
+
+ return userTransfer.getUserTO(uwfAdapter.update(userMod).getResult().getKey().getKey());
+ }
+
+ @Override
+ protected AbstractSubjectTO update(
+ final AbstractSubjectTO before,
+ final AbstractSubjectMod subjectMod,
+ final SyncDelta delta,
+ final ProvisioningResult result) {
+
+ final UserMod userMod = UserMod.class.cast(subjectMod);
+ final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+
+ Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(),
+ result, enabled, Collections.singleton(profile.getTask().getResource().getKey()));
+
+ return userTransfer.getUserTO(updated.getKey());
+ }
+
+ @Override
+ protected void deprovision(
+ final Long key,
+ final boolean unlink) {
+
+ taskExecutor.execute(
+ propagationManager.getUserDeleteTaskIds(key, profile.getTask().getResource().getKey()));
+
+ if (unlink) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(key);
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ }
+ }
+
+ @Override
+ protected void delete(final Long key) {
+ try {
+ userProvisioningManager.
+ delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey()));
+ } catch (Exception e) {
+ // A propagation failure doesn't imply a synchronization failure.
+ // The propagation exception status will be reported into the propagation task execution.
+ LOG.error("Could not propagate user " + key, e);
+ }
+
+ uwfAdapter.delete(key);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/connid.properties
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/connid.properties b/syncope620/core/provisioning-java/src/main/resources/connid.properties
new file mode 100644
index 0000000..24d5c93
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/connid.properties
@@ -0,0 +1,17 @@
+# 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.
+connid.locations=${connid.location}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mail.properties
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mail.properties b/syncope620/core/provisioning-java/src/main/resources/mail.properties
new file mode 100644
index 0000000..12f04e7
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mail.properties
@@ -0,0 +1,25 @@
+# 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.
+templates.directory=${conf.directory}
+smtpHost=none.syncope.apache.org
+smtpPort=25
+smtpUser=
+smtpPassword=
+smtpProtocol=smtp
+smtpEncoding=UTF-8
+smtpConnectionTimeout=3000
+mailDebug=false
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
new file mode 100644
index 0000000..90630ac
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+<html>
+<body>
+<p>Hi,</br>
+we are happy to inform you that the password request was execute successfully for your account.</p>
+
+<p>Best regards.</p>
+</body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
new file mode 100644
index 0000000..33f75dc
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
@@ -0,0 +1,20 @@
+# 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.
+Hi,
+we are happy to inform you that the password request was execute successfully for your account.
+
+Best regards.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
new file mode 100644
index 0000000..8240c7b
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
@@ -0,0 +1,72 @@
+<!--
+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.
+-->
+<html>
+<body>
+<h3>Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3>
+
+<p>
+ Your username is $user.getUsername().<br/>
+ Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
+ Your email address inside a <a href="http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0))">link</a>.
+</p>
+
+<p>
+ This message was sent to the following recipients:
+<ul>
+#foreach($recipient in $recipients)
+ <li>$recipient.getPlainAttrMap().get("email").getValues().get(0)</li>
+#end
+</ul>
+
+because one of the following events occurred:
+<ul>
+#foreach($event in $events)
+ <li>$event</i>
+#end
+</ul>
+</p>
+
+#if(!$user.getMemberships().isEmpty())
+You have been provided with the following roles:
+<ul>
+#foreach($membership in $user.getMemberships())
+ <li>$membership.roleName</i>
+#end
+</ul>
+#end
+
+#if(${output.class.simpleName} == "TaskExec")
+Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId().
+Task Details:
+<ul>
+<li>
+START DATE: $output.getStartDate()
+</li>
+<li>
+MESSAGE:<br/>
+$output.getMessage()
+</li>
+<li>
+END DATE: $output.getEndDate()
+</li>
+</ul>
+#end
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
new file mode 100644
index 0000000..fc8e398
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
@@ -0,0 +1,51 @@
+# 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.
+Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!
+
+Your username is $user.getUsername().
+Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
+Your email address inside a link: http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0)) .
+
+This message was sent to the following recipients:
+#foreach($recipient in $recipients)
+ * $recipient.getPlainAttrMap().get("surname").getValues().get(0)
+#end
+
+because one of the following events occurred:
+#foreach($event in $events)
+ * $event
+#end
+
+#if(!$user.getMemberships().isEmpty())
+You have been provided with the following roles:
+#foreach($membership in $user.getMemberships())
+ * $membership.roleName
+#end
+#end
+
+#if(${output.class.simpleName} == "TaskExec")
+Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId()
+
+Task Details:
+
+ * START DATE: $output.getStartDate()
+
+ * MESSAGE:
+$output.getMessage()
+
+ * END DATE: $output.getEndDate()
+#end
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
new file mode 100644
index 0000000..6594c3f
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
@@ -0,0 +1,31 @@
+<!--
+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.
+-->
+<html>
+<body>
+<p>Hi,
+a password reset was request for $user.getUsername().</p>
+
+<p>In order to complete this request, you need to visit this
+<a href="http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0)">link</a></p>.
+
+<p>If you did not request this reset, just ignore the present e-mail.</p>
+
+<p>Best regards.</p>
+</body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
new file mode 100644
index 0000000..5ac028a
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
@@ -0,0 +1,26 @@
+# 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.
+Hi,
+a password reset was request for $user.getUsername().
+
+In order to complete this request, you need to visit this link:
+
+http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0)
+
+If you did not request this reset, just ignore the present e-mail.
+
+Best regards.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/provisioning.properties
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/provisioning.properties b/syncope620/core/provisioning-java/src/main/resources/provisioning.properties
new file mode 100644
index 0000000..af5deee
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/provisioning.properties
@@ -0,0 +1,18 @@
+# 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.
+userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
+roleProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultRoleProvisioningManager
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml b/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml
new file mode 100644
index 0000000..fa31a73
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:task="http://www.springframework.org/schema/task"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/task
+ http://www.springframework.org/schema/task/spring-task.xsd">
+
+ <task:annotation-driven executor="connectorExecutor"/>
+ <task:executor id="connectorExecutor" pool-size="10"/>
+
+ <bean class="${userProvisioningManager}"/>
+ <bean class="${roleProvisioningManager}"/>
+
+ <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
+ lazy-init="false" depends-on="nonJPAdbInitializer">
+ <property name="autoStartup" value="true"/>
+ <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
+ <property name="waitForJobsToCompleteOnShutdown" value="true"/>
+ <property name="overwriteExistingJobs" value="true"/>
+ <property name="dataSource" ref="dataSource"/>
+ <property name="transactionManager" ref="transactionManager"/>
+ <property name="jobFactory">
+ <bean class="org.apache.syncope.core.provisioning.java.job.SpringBeanJobFactory"/>
+ </property>
+ <property name="quartzProperties">
+ <props>
+ <prop key="org.quartz.scheduler.idleWaitTime">${quartz.scheduler.idleWaitTime:30000}</prop>
+
+ <prop key="org.quartz.jobStore.misfireThreshold">6000000</prop>
+ <prop key="org.quartz.jobStore.driverDelegateClass">${quartz.jobstore}</prop>
+
+ <prop key="org.quartz.jobStore.isClustered">true</prop>
+ <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
+
+ <prop key="org.quartz.scheduler.instanceName">ClusteredScheduler</prop>
+ <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
+ <prop key="org.quartz.scheduler.jmx.export">true</prop>
+ </props>
+ </property>
+ </bean>
+
+ <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+ <property name="defaultEncoding" value="${smtpEncoding}"/>
+ <property name="host" value="${smtpHost}"/>
+ <property name="port" value="${smtpPort}"/>
+ <property name="username" value="${smtpUser}"/>
+ <property name="password" value="${smtpPassword}"/>
+ <property name="protocol" value="${smtpProtocol}"/>
+
+ <property name="javaMailProperties">
+ <props>
+ <prop key="mail.smtp.connectiontimeout">${smtpConnectionTimeout}</prop>
+ <prop key="mail.debug">${mailDebug}</prop>
+ </props>
+ </property>
+ </bean>
+
+ <bean class="org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl"/>
+ <bean class="org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter" scope="prototype"/>
+
+ <context:component-scan base-package="org.apache.syncope.core.misc"/>
+ <context:component-scan base-package="org.apache.syncope.core.provisioning.java"/>
+
+ <bean id="virAttrCache" class="org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache" scope="singleton">
+ <constructor-arg value="60"/>
+ <constructor-arg value="5000"/>
+ </bean>
+
+ <bean id="velocityResourceLoader" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader">
+ <property name="primary" value="file:${templates.directory}/"/>
+ <property name="fallback" value="classpath:"/>
+ </bean>
+ <bean id="velocityEngine" class="org.apache.syncope.core.provisioning.java.notification.VelocityEngineFactoryBean">
+ <property name="resourceLoader" ref="velocityResourceLoader"/>
+ </bean>
+ <bean id="velocityToolManager" class="org.apache.velocity.tools.ToolManager">
+ <!-- autoConfigure -->
+ <constructor-arg index="0" value="true"/>
+ <!-- include default velocity tools -->
+ <constructor-arg index="1" value="true"/>
+ </bean>
+
+ <bean id="connIdBundleManager" class="org.apache.syncope.core.provisioning.java.ConnIdBundleManagerImpl" scope="singleton">
+ <property name="stringLocations" value="${connid.locations}"/>
+ </bean>
+
+</beans>
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
new file mode 100644
index 0000000..e26f238
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java;
+
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {
+ "classpath:persistenceTest.xml",
+ "classpath:provisioningContext.xml",
+ "classpath:workflowContext.xml",
+ "classpath:provisioningTest.xml"
+})
+public abstract class AbstractTest {
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java
new file mode 100644
index 0000000..9ddc91d
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import org.apache.syncope.core.provisioning.java.ConnectorManager;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+public class ConnectorManagerTest extends AbstractTest {
+
+ private ConnectorManager connManager;
+
+ @Autowired
+ private ConnIdBundleManager connIdBundleManager;
+
+ @Autowired
+ private ExternalResourceDAO resourceDAO;
+
+ @Before
+ public void before() {
+ connManager = new ConnectorManager();
+ ReflectionTestUtils.setField(connManager, "connIdBundleManager", connIdBundleManager);
+ ReflectionTestUtils.setField(connManager, "resourceDAO", resourceDAO);
+
+ // Remove any other connector instance bean set up by standard ConnectorManager.load()
+ connManager.unload();
+ }
+
+ @Test
+ public void load() {
+ connManager.load();
+
+ // only consider local connector bundles
+ int expected = 0;
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ if (resource.getConnector().getLocation().startsWith("file")) {
+ expected++;
+ }
+ }
+
+ assertEquals(expected,
+ ApplicationContextProvider.getApplicationContext().
+ getBeanNamesForType(Connector.class, false, true).length);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
new file mode 100644
index 0000000..40764ab
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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 org.apache.syncope.core.provisioning.java.AbstractTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationMode;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema;
+import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+public class ResourceDataBinderTest extends AbstractTest {
+
+ @Autowired
+ private ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ private ResourceDataBinder resourceDataBinder;
+
+ @Autowired
+ private PlainSchemaDAO plainSchemaDAO;
+
+ @Test
+ public void databinding() throws IOException {
+ ExternalResource resource = resourceDAO.find("ws-target-resource-2");
+ assertNotNull(resource);
+
+ ResourceTO resourceTO = resourceDataBinder.getResourceTO(resource);
+ assertNotNull(resourceTO);
+
+ ExternalResource fromto = resourceDataBinder.update(resource, resourceTO);
+ assertNotNull(fromto);
+ assertEquals(resource, fromto);
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ StringWriter writer = new StringWriter();
+ mapper.writeValue(writer, resourceTO);
+
+ assertEquals(resourceTO, mapper.readValue(writer.toString(), ResourceTO.class));
+
+ List<ResourceTO> resourceTOs = resourceDataBinder.getResourceTOs(resourceDAO.findAll());
+ assertNotNull(resourceTOs);
+ assertFalse(resourceTOs.isEmpty());
+
+ writer = new StringWriter();
+ mapper.writeValue(writer, resourceTOs);
+
+ ResourceTO[] actual = mapper.readValue(writer.toString(), ResourceTO[].class);
+ assertEquals(resourceTOs, Arrays.asList(actual));
+ }
+
+ @Test
+ public void issue42() {
+ UPlainSchema userId = plainSchemaDAO.find("userId", UPlainSchema.class);
+
+ Set<MappingItem> beforeUserIdMappings = new HashSet<>();
+ for (ExternalResource res : resourceDAO.findAll()) {
+ if (res.getUmapping() != null) {
+ for (MappingItem mapItem : res.getUmapping().getItems()) {
+ if (userId.getKey().equals(mapItem.getIntAttrName())) {
+ beforeUserIdMappings.add(mapItem);
+ }
+ }
+ }
+ }
+
+ ResourceTO resourceTO = new ResourceTO();
+ resourceTO.setKey("resource-issue42");
+ resourceTO.setConnectorId(100L);
+ resourceTO.setPropagationMode(PropagationMode.ONE_PHASE);
+ resourceTO.setEnforceMandatoryCondition(true);
+
+ MappingTO mapping = new MappingTO();
+ resourceTO.setUmapping(mapping);
+
+ MappingItemTO item = new MappingItemTO();
+ item.setIntAttrName("userId");
+ item.setIntMappingType(IntMappingType.UserPlainSchema);
+ item.setExtAttrName("campo1");
+ item.setAccountid(true);
+ item.setMandatoryCondition("false");
+ item.setPurpose(MappingPurpose.BOTH);
+ mapping.setAccountIdItem(item);
+
+ ExternalResource resource = resourceDataBinder.create(resourceTO);
+ resource = resourceDAO.save(resource);
+ assertNotNull(resource);
+ assertNotNull(resource.getUmapping());
+ assertEquals(1, resource.getUmapping().getItems().size());
+
+ resourceDAO.flush();
+
+ ExternalResource actual = resourceDAO.find("resource-issue42");
+ assertNotNull(actual);
+ assertEquals(resource, actual);
+
+ userId = plainSchemaDAO.find("userId", UPlainSchema.class);
+
+ Set<MappingItem> afterUserIdMappings = new HashSet<>();
+ for (ExternalResource res : resourceDAO.findAll()) {
+ if (res.getUmapping() != null) {
+ for (MappingItem mapItem : res.getUmapping().getItems()) {
+ if (userId.getKey().equals(mapItem.getIntAttrName())) {
+ afterUserIdMappings.add(mapItem);
+ }
+ }
+ }
+ }
+
+ assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml b/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml
new file mode 100644
index 0000000..b6621c3
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="locations">
+ <list>
+ <value>classpath:persistence.properties</value>
+ <value>classpath:security.properties</value>
+ <value>classpath:connid.properties</value>
+ <value>classpath:mail.properties</value>
+ <value>classpath:workflow.properties</value>
+ <value>classpath:provisioning.properties</value>
+ </list>
+ </property>
+ <property name="ignoreResourceNotFound" value="true"/>
+ <property name="ignoreUnresolvablePlaceholders" value="true"/>
+ </bean>
+
+ <bean id="contentXML" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader">
+ <property name="primary" value="file:${conf.directory}/content.xml"/>
+ <property name="fallback" value="classpath:content.xml"/>
+ </bean>
+ <bean class="org.apache.syncope.core.persistence.jpa.content.XMLContentLoader" init-method="load"/>
+
+</beans>
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/rest-cxf/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/core/rest-cxf/pom.xml b/syncope620/core/rest-cxf/pom.xml
new file mode 100644
index 0000000..d9d06e0
--- /dev/null
+++ b/syncope620/core/rest-cxf/pom.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.syncope</groupId>
+ <artifactId>syncope-core</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <name>Apache Syncope Core REST CXF</name>
+ <description>Apache Syncope Core REST CXF</description>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-rest-cxf</artifactId>
+ <packaging>jar</packaging>
+
+ <properties>
+ <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jpa_2.0_spec</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-orm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-afterburner</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-extension-providers</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-extension-search</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxws</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-service-description</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-client</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope.common</groupId>
+ <artifactId>syncope-common-rest-api</artifactId>
+ <version>${project.version}</version>
+ <classifier>javadoc</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-logic</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.common</groupId>
+ <artifactId>syncope-common-rest-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java
----------------------------------------------------------------------
diff --git a/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java b/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java
new file mode 100644
index 0000000..a45e275
--- /dev/null
+++ b/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.jaxrs.ext.ResourceComparator;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator;
+import org.apache.cxf.jaxrs.model.Parameter;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+
+public class QueryResourceInfoComparator extends OperationResourceInfoComparator implements ResourceComparator {
+
+ public QueryResourceInfoComparator() {
+ super(null, null);
+ }
+
+ @Override
+ public int compare(final ClassResourceInfo cri1, final ClassResourceInfo cri2, final Message message) {
+ // Leave Class selection to CXF
+ return 0;
+ }
+
+ @Override
+ public int compare(final OperationResourceInfo oper1, final OperationResourceInfo oper2, final Message message) {
+ // Check if CXF can make a decision
+ int cxfResult = super.compare(oper1, oper2);
+ if (cxfResult != 0) {
+ return cxfResult;
+ }
+
+ int op1Counter = getMatchingRate(oper1, message);
+ int op2Counter = getMatchingRate(oper2, message);
+
+ return op1Counter == op2Counter
+ ? 0
+ : op1Counter < op2Counter
+ ? 1
+ : -1;
+ }
+
+ /**
+ * This method calculates a number indicating a good or bad match between values provided within the request and
+ * expected method parameters. A higher number means a better match.
+ *
+ * @param operation The operation to be rated, based on contained parameterInfo values.
+ * @param message A message containing query and header values from user request
+ * @return A positive or negative number, indicating a good match between query and method
+ */
+ protected int getMatchingRate(final OperationResourceInfo operation, final Message message) {
+ List<Parameter> params = operation.getParameters();
+ if (params == null || params.isEmpty()) {
+ return 0;
+ }
+
+ // Get Request QueryParams
+ String query = (String) message.get(Message.QUERY_STRING);
+ String path = (String) message.get(Message.REQUEST_URI);
+ Map<String, List<String>> qParams = JAXRSUtils.getStructuredParams(query, "&", true, false);
+ Map<String, List<String>> mParams = JAXRSUtils.getMatrixParams(path, true);
+ // Get Request Headers
+ Map<?, ?> qHeader = (java.util.Map<?, ?>) message.get(Message.PROTOCOL_HEADERS);
+
+ int rate = 0;
+ for (Parameter p : params) {
+ switch (p.getType()) {
+ case QUERY:
+ if (qParams.containsKey(p.getName())) {
+ rate += 2;
+ } else if (p.getDefaultValue() == null) {
+ rate -= 1;
+ }
+ break;
+ case MATRIX:
+ if (mParams.containsKey(p.getName())) {
+ rate += 2;
+ } else if (p.getDefaultValue() == null) {
+ rate -= 1;
+ }
+ break;
+ case HEADER:
+ if (qHeader.containsKey(p.getName())) {
+ rate += 2;
+ } else if (p.getDefaultValue() == null) {
+ rate -= 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return rate;
+ }
+}