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:26 UTC
[21/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/VirAttrHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
new file mode 100644
index 0000000..6f0e38e
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandler.java
@@ -0,0 +1,308 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.MembershipDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.VirAttrDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+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.Subject;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.role.RVirAttr;
+import org.apache.syncope.core.persistence.api.entity.role.RVirAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UVirSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@Transactional(rollbackFor = { Throwable.class })
+public class VirAttrHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(VirAttrHandler.class);
+
+ @Autowired
+ private ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ private VirSchemaDAO virSchemaDAO;
+
+ @Autowired
+ private VirAttrDAO virAttrDAO;
+
+ @Autowired
+ private UserDAO userDAO;
+
+ @Autowired
+ private MembershipDAO membershipDAO;
+
+ @Autowired
+ private AttributableUtilFactory attrUtilFactory;
+
+ public <T extends VirSchema> T getVirSchema(final String virSchemaName, final Class<T> reference) {
+ T virtualSchema = null;
+ if (StringUtils.isNotBlank(virSchemaName)) {
+ virtualSchema = virSchemaDAO.find(virSchemaName, reference);
+
+ if (virtualSchema == null) {
+ LOG.debug("Ignoring invalid virtual schema {}", virSchemaName);
+ }
+ }
+
+ return virtualSchema;
+ }
+
+ public void setVirAttrSchema(final Attributable<?, ?, ?> attributable,
+ final VirAttr virAttr, final VirSchema virSchema) {
+
+ if (virAttr instanceof UVirAttr) {
+ ((UVirAttr) virAttr).setSchema((UVirSchema) virSchema);
+ } else if (virAttr instanceof RVirAttr) {
+ RVirAttrTemplate template = ((Role) attributable).
+ getAttrTemplate(RVirAttrTemplate.class, virSchema.getKey());
+ if (template != null) {
+ ((RVirAttr) virAttr).setTemplate(template);
+ }
+ } else if (virAttr instanceof MVirAttr) {
+ MVirAttrTemplate template =
+ ((Membership) attributable).getRole().
+ getAttrTemplate(MVirAttrTemplate.class, virSchema.getKey());
+ if (template != null) {
+ ((MVirAttr) virAttr).setTemplate(template);
+ }
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public PropagationByResource fillVirtual(final Attributable attributable,
+ final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
+ final AttributableUtil attrUtil) {
+
+ PropagationByResource propByRes = new PropagationByResource();
+
+ final Set<ExternalResource> externalResources = new HashSet<>();
+ if (attributable instanceof Subject) {
+ externalResources.addAll(((Subject<?, ?, ?>) attributable).getResources());
+ }
+
+ if (attributable instanceof Membership) {
+ externalResources.clear();
+ externalResources.addAll(((Membership) attributable).getUser().getResources());
+ }
+
+ // 1. virtual attributes to be removed
+ for (String vAttrToBeRemoved : vAttrsToBeRemoved) {
+ VirSchema virSchema = getVirSchema(vAttrToBeRemoved, attrUtil.virSchemaClass());
+ if (virSchema != null) {
+ VirAttr virAttr = attributable.getVirAttr(virSchema.getKey());
+ if (virAttr == null) {
+ LOG.debug("No virtual attribute found for schema {}", virSchema.getKey());
+ } else {
+ attributable.removeVirAttr(virAttr);
+ virAttrDAO.delete(virAttr);
+ }
+
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (virSchema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.virIntMappingType()
+ && externalResources.contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+
+ // Using virtual attribute as AccountId must be avoided
+ if (mapItem.isAccountid() && virAttr != null && !virAttr.getValues().isEmpty()) {
+ propByRes.addOldAccountId(resource.getKey(), virAttr.getValues().get(0));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ LOG.debug("Virtual attributes to be removed:\n{}", propByRes);
+
+ // 2. virtual attributes to be updated
+ for (AttrMod vAttrToBeUpdated : vAttrsToBeUpdated) {
+ VirSchema virSchema = getVirSchema(vAttrToBeUpdated.getSchema(), attrUtil.virSchemaClass());
+ VirAttr virAttr = null;
+ if (virSchema != null) {
+ virAttr = attributable.getVirAttr(virSchema.getKey());
+ if (virAttr == null) {
+ virAttr = attrUtil.newVirAttr();
+ setVirAttrSchema(attributable, virAttr, virSchema);
+ if (virAttr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", vAttrToBeUpdated);
+ } else {
+ attributable.addVirAttr(virAttr);
+ }
+ }
+ }
+
+ if (virSchema != null && virAttr != null && virAttr.getSchema() != null) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (virSchema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.virIntMappingType()
+ && externalResources.contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+ }
+ }
+ }
+
+ final List<String> values = new ArrayList<>(virAttr.getValues());
+ values.removeAll(vAttrToBeUpdated.getValuesToBeRemoved());
+ values.addAll(vAttrToBeUpdated.getValuesToBeAdded());
+
+ virAttr.getValues().clear();
+ virAttr.getValues().addAll(values);
+
+ // Owner cannot be specified before otherwise a virtual attribute remove will be invalidated.
+ virAttr.setOwner(attributable);
+ }
+ }
+
+ LOG.debug("Virtual attributes to be added:\n{}", propByRes);
+
+ return propByRes;
+ }
+
+ /**
+ * Add virtual attributes and specify values to be propagated.
+ *
+ * @param attributable attributable.
+ * @param vAttrs virtual attributes to be added.
+ * @param attrUtil attributable util.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void fillVirtual(final Attributable attributable, final Collection<AttrTO> vAttrs,
+ final AttributableUtil attrUtil) {
+
+ for (AttrTO attributeTO : vAttrs) {
+ VirAttr virAttr = attributable.getVirAttr(attributeTO.getSchema());
+ if (virAttr == null) {
+ VirSchema virSchema = getVirSchema(attributeTO.getSchema(), attrUtil.virSchemaClass());
+ if (virSchema != null) {
+ virAttr = attrUtil.newVirAttr();
+ setVirAttrSchema(attributable, virAttr, virSchema);
+ if (virAttr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO);
+ } else {
+ virAttr.setOwner(attributable);
+ attributable.addVirAttr(virAttr);
+ virAttr.getValues().clear();
+ virAttr.getValues().addAll(attributeTO.getValues());
+ }
+ }
+ } else {
+ virAttr.getValues().clear();
+ virAttr.getValues().addAll(attributeTO.getValues());
+ }
+ }
+ }
+
+ /**
+ * SYNCOPE-459: build virtual attribute changes in case no other changes were made.
+ *
+ * @param key user id
+ * @param vAttrsToBeRemoved virtual attributes to be removed.
+ * @param vAttrsToBeUpdated virtual attributes to be updated.
+ * @return operations to be performed on external resources for virtual attributes changes
+ */
+ public PropagationByResource fillVirtual(
+ final Long key, final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated) {
+
+ return fillVirtual(
+ userDAO.authFetch(key),
+ vAttrsToBeRemoved,
+ vAttrsToBeUpdated,
+ attrUtilFactory.getInstance(AttributableType.USER));
+ }
+
+ /**
+ * SYNCOPE-501: build membership virtual attribute changes in case no other changes were made.
+ *
+ * @param key user key
+ * @param roleKey role key
+ * @param membershipKey membership key
+ * @param vAttrsToBeRemoved virtual attributes to be removed.
+ * @param vAttrsToBeUpdated virtual attributes to be updated.
+ * @param isRemoval flag to check if fill is on removed or added membership
+ * @return operations to be performed on external resources for membership virtual attributes changes
+ */
+ public PropagationByResource fillMembershipVirtual(
+ final Long key, final Long roleKey, final Long membershipKey, final Set<String> vAttrsToBeRemoved,
+ final Set<AttrMod> vAttrsToBeUpdated, final boolean isRemoval) {
+
+ final Membership membership = membershipKey == null
+ ? userDAO.authFetch(key).getMembership(roleKey)
+ : membershipDAO.authFetch(membershipKey);
+
+ return membership == null ? new PropagationByResource() : isRemoval
+ ? fillVirtual(
+ membership,
+ membership.getVirAttrs() == null
+ ? Collections.<String>emptySet()
+ : getAttrNames(membership.getVirAttrs()),
+ vAttrsToBeUpdated,
+ attrUtilFactory.getInstance(AttributableType.MEMBERSHIP))
+ : fillVirtual(
+ membership,
+ vAttrsToBeRemoved,
+ vAttrsToBeUpdated,
+ attrUtilFactory.getInstance(AttributableType.MEMBERSHIP));
+ }
+
+ private Set<String> getAttrNames(final List<? extends VirAttr> virAttrs) {
+ final Set<String> virAttrNames = new HashSet<>();
+ for (VirAttr attr : virAttrs) {
+ virAttrNames.add(attr.getSchema().getKey());
+ }
+ return virAttrNames;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java
new file mode 100644
index 0000000..d1e0dda
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/DisabledVirAttrCache.java
@@ -0,0 +1,52 @@
+/*
+ * 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.cache;
+
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
+
+/**
+ * Empty virtual attribute value cache implementation.
+ */
+public class DisabledVirAttrCache implements VirAttrCache {
+
+ @Override
+ public void expire(final AttributableType type, final Long id, final String schemaName) {
+ // nothing to do
+ }
+
+ @Override
+ public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) {
+ return null;
+ }
+
+ @Override
+ public boolean isValidEntry(VirAttrCacheValue value) {
+ return false;
+ }
+
+ @Override
+ public void put(
+ final AttributableType type, final Long id, final String schemaName, final VirAttrCacheValue value) {
+
+ // nothing to do
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java
new file mode 100644
index 0000000..d746ae4
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/cache/MemoryVirAttrCache.java
@@ -0,0 +1,151 @@
+/*
+ * 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.cache;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
+
+/**
+ * In-memory (HashMap) virtual attribute value cache implementation.
+ */
+public class MemoryVirAttrCache implements VirAttrCache {
+
+ /**
+ * Elapsed time in seconds.
+ */
+ protected int ttl;
+
+ /**
+ * Max cache size.
+ */
+ protected int maxCacheSize;
+
+ /**
+ * Cache entries.
+ */
+ protected final Map<VirAttrCacheKey, VirAttrCacheValue> cache = new HashMap<VirAttrCacheKey, VirAttrCacheValue>();
+
+ public MemoryVirAttrCache(final int ttl, final int maxCacheSize) {
+ this.ttl = ttl;
+ this.maxCacheSize = maxCacheSize;
+ }
+
+ /**
+ * Cache virtual attribute values.
+ *
+ * @param type user or role
+ * @param key user or role id
+ * @param schemaName virtual attribute name
+ * @param value virtual attribute values
+ */
+ @Override
+ public void put(
+ final AttributableType type,
+ final Long key,
+ final String schemaName,
+ final VirAttrCacheValue value) {
+
+ synchronized (cache) {
+ // this operations (retrieve cache space and put entry on) have to be thread safe.
+ if (this.cache.size() >= this.maxCacheSize) {
+ free();
+ }
+
+ cache.put(new VirAttrCacheKey(type, key, schemaName), value);
+ }
+ }
+
+ /**
+ * Retrieve cached value. Return null in case of virtual attribute not cached.
+ *
+ * @param type user or role
+ * @param id user or role id
+ * @param schemaName virtual attribute schema name.
+ * @return cached values or null if virtual attribute is not cached.
+ */
+ @Override
+ public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) {
+ return cache.get(new VirAttrCacheKey(type, id, schemaName));
+ }
+
+ /**
+ * Force entry expiring.
+ *
+ * @param type user or role
+ * @param id user or role id
+ * @param schemaName virtual attribute schema name
+ */
+ @Override
+ public void expire(final AttributableType type, final Long id, final String schemaName) {
+ final VirAttrCacheValue value = cache.get(new VirAttrCacheKey(type, id, schemaName));
+ if (isValidEntry(value)) {
+ synchronized (cache) {
+ value.forceExpiring();
+ }
+ }
+ }
+
+ /**
+ * Remove expired entries if exist. If required, one entry at least (the latest recently used) will be taken off.
+ * This method is not thread safe: the caller have to take care to synchronize the call.
+ */
+ private void free() {
+ final Set<VirAttrCacheKey> toBeRemoved = new HashSet<VirAttrCacheKey>();
+
+ Map.Entry<VirAttrCacheKey, VirAttrCacheValue> latest = null;
+
+ for (Map.Entry<VirAttrCacheKey, VirAttrCacheValue> entry : cache.entrySet()) {
+ if (isValidEntry(entry.getValue())) {
+ final Date date = entry.getValue().getLastAccessDate();
+ if (latest == null || latest.getValue().getLastAccessDate().after(date)) {
+ latest = entry;
+ }
+ } else {
+ toBeRemoved.add(entry.getKey());
+ }
+ }
+
+ if (toBeRemoved.isEmpty() && latest != null) {
+ // remove the oldest entry
+ cache.remove(latest.getKey());
+ } else {
+ // remove expired entries
+ cache.keySet().removeAll(toBeRemoved);
+ }
+ }
+
+ /**
+ * Cache entry is valid if and only if value exist and it is not expired.
+ *
+ * @param value cache entry value.
+ * @return TRUE if the value is valid; FALSE otherwise.
+ */
+ @Override
+ public boolean isValidEntry(final VirAttrCacheValue value) {
+ final Date expiringDate = new Date(value == null ? 0 : value.getCreationDate().getTime() + ttl * 1000);
+ return expiringDate.after(new Date());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
new file mode 100644
index 0000000..fb8d8d4
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAttributableDataBinder.java
@@ -0,0 +1,779 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientCompositeException;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.AbstractAttributableMod;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
+import org.apache.syncope.core.persistence.api.dao.DerAttrDAO;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.MembershipDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
+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.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.VirAttrDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+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.DerAttr;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+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.PlainAttr;
+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.Schema;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MDerAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.MDerAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.MPlainAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.role.RDerAttr;
+import org.apache.syncope.core.persistence.api.entity.role.RDerAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.role.RPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.role.RPlainAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.role.RVirAttrTemplate;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UDerSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.core.provisioning.java.VirAttrHandler;
+import org.apache.syncope.core.misc.MappingUtil;
+import org.apache.syncope.core.misc.jexl.JexlUtil;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+abstract class AbstractAttributableDataBinder {
+
+ /**
+ * Logger.
+ */
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractAttributableDataBinder.class);
+
+ @Autowired
+ protected RoleDAO roleDAO;
+
+ @Autowired
+ protected PlainSchemaDAO plainSchemaDAO;
+
+ @Autowired
+ protected DerSchemaDAO derSchemaDAO;
+
+ @Autowired
+ protected VirSchemaDAO virSchemaDAO;
+
+ @Autowired
+ protected PlainAttrDAO plainAttrDAO;
+
+ @Autowired
+ protected DerAttrDAO derAttrDAO;
+
+ @Autowired
+ protected VirAttrDAO virAttrDAO;
+
+ @Autowired
+ protected PlainAttrValueDAO plainAttrValueDAO;
+
+ @Autowired
+ protected UserDAO userDAO;
+
+ @Autowired
+ protected ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ protected MembershipDAO membershipDAO;
+
+ @Autowired
+ protected PolicyDAO policyDAO;
+
+ @Autowired
+ protected EntityFactory entityFactory;
+
+ @Autowired
+ protected AttributableUtilFactory attrUtilFactory;
+
+ @Autowired
+ protected VirAttrHandler virtAttrHander;
+
+ @SuppressWarnings("unchecked")
+ protected <T extends Schema> T getSchema(final String schemaName, final Class<T> reference) {
+ T result = null;
+
+ if (PlainSchema.class.isAssignableFrom(reference)) {
+ result = (T) getPlainSchema(schemaName, (Class<? extends PlainSchema>) reference);
+ } else if (DerSchema.class.isAssignableFrom(reference)) {
+ result = (T) getDerSchema(schemaName, (Class<? extends DerSchema>) reference);
+ } else if (VirSchema.class.isAssignableFrom(reference)) {
+ result = (T) virtAttrHander.getVirSchema(schemaName, (Class<? extends VirSchema>) reference);
+ }
+
+ return result;
+ }
+
+ protected <T extends PlainSchema> T getPlainSchema(final String schemaName, final Class<T> reference) {
+ T schema = null;
+ if (StringUtils.isNotBlank(schemaName)) {
+ schema = plainSchemaDAO.find(schemaName, reference);
+
+ // safely ignore invalid schemas from AttrTO
+ // see http://code.google.com/p/syncope/issues/detail?id=17
+ if (schema == null) {
+ LOG.debug("Ignoring invalid schema {}", schemaName);
+ } else if (schema.isReadonly()) {
+ schema = null;
+
+ LOG.debug("Ignoring readonly schema {}", schemaName);
+ }
+ }
+
+ return schema;
+ }
+
+ private <T extends DerSchema> T getDerSchema(final String derSchemaName, final Class<T> reference) {
+ T derivedSchema = null;
+ if (StringUtils.isNotBlank(derSchemaName)) {
+ derivedSchema = derSchemaDAO.find(derSchemaName, reference);
+ if (derivedSchema == null) {
+ LOG.debug("Ignoring invalid derived schema {}", derSchemaName);
+ }
+ }
+
+ return derivedSchema;
+ }
+
+ protected void fillAttribute(final List<String> values, final AttributableUtil attributableUtil,
+ final PlainSchema schema, final PlainAttr attr, final SyncopeClientException invalidValues) {
+
+ // if schema is multivalue, all values are considered for addition;
+ // otherwise only the fist one - if provided - is considered
+ List<String> valuesProvided = schema.isMultivalue()
+ ? values
+ : (values.isEmpty()
+ ? Collections.<String>emptyList()
+ : Collections.singletonList(values.iterator().next()));
+
+ for (String value : valuesProvided) {
+ if (value == null || value.isEmpty()) {
+ LOG.debug("Null value for {}, ignoring", schema.getKey());
+ } else {
+ try {
+ attr.addValue(value, attributableUtil);
+ } catch (InvalidPlainAttrValueException e) {
+ LOG.warn("Invalid value for attribute " + schema.getKey() + ": " + value, e);
+
+ invalidValues.getElements().add(schema.getKey() + ": " + value + " - " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil, final ExternalResource resource,
+ final Attributable<?, ?, ?> attributable, final String intAttrName, final IntMappingType intMappingType) {
+
+ boolean result = false;
+
+ final List<MappingItem> mappings = MappingUtil.getMatchingMappingItems(
+ attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION), intAttrName, intMappingType);
+ for (Iterator<MappingItem> itor = mappings.iterator(); itor.hasNext() && !result;) {
+ final MappingItem mapping = itor.next();
+ result |= JexlUtil.evaluateMandatoryCondition(mapping.getMandatoryCondition(), attributable);
+ }
+
+ return result;
+ }
+
+ private boolean evaluateMandatoryCondition(final AttributableUtil attrUtil,
+ final Attributable<?, ?, ?> attributable, final String intAttrName, final IntMappingType intMappingType) {
+
+ boolean result = false;
+
+ if (attributable instanceof Subject) {
+ for (Iterator<? extends ExternalResource> itor = ((Subject<?, ?, ?>) attributable).getResources().iterator();
+ itor.hasNext() && !result;) {
+
+ final ExternalResource resource = itor.next();
+ if (resource.isEnforceMandatoryCondition()) {
+ result |= evaluateMandatoryCondition(attrUtil, resource, attributable, intAttrName, intMappingType);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private SyncopeClientException checkMandatory(final AttributableUtil attrUtil,
+ final Attributable<?, ?, ?> attributable) {
+
+ SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+
+ // Check if there is some mandatory schema defined for which no value has been provided
+ List<? extends PlainSchema> plainSchemas;
+ switch (attrUtil.getType()) {
+ case ROLE:
+ plainSchemas = ((Role) attributable).getAttrTemplateSchemas(RPlainAttrTemplate.class);
+ break;
+
+ case MEMBERSHIP:
+ plainSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MPlainAttrTemplate.class);
+ break;
+
+ case USER:
+ default:
+ plainSchemas = plainSchemaDAO.findAll(attrUtil.plainSchemaClass());
+ }
+ for (PlainSchema schema : plainSchemas) {
+ if (attributable.getPlainAttr(schema.getKey()) == null
+ && !schema.isReadonly()
+ && (JexlUtil.evaluateMandatoryCondition(schema.getMandatoryCondition(), attributable)
+ || evaluateMandatoryCondition(attrUtil, attributable, schema.getKey(),
+ attrUtil.intMappingType()))) {
+
+ LOG.error("Mandatory schema " + schema.getKey() + " not provided with values");
+
+ reqValMissing.getElements().add(schema.getKey());
+ }
+ }
+
+ List<? extends DerSchema> derSchemas;
+ switch (attrUtil.getType()) {
+ case ROLE:
+ derSchemas = ((Role) attributable).getAttrTemplateSchemas(RDerAttrTemplate.class);
+ break;
+
+ case MEMBERSHIP:
+ derSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MDerAttrTemplate.class);
+ break;
+
+ case USER:
+ default:
+ derSchemas = derSchemaDAO.findAll(attrUtil.derSchemaClass());
+ }
+ for (DerSchema derSchema : derSchemas) {
+ if (attributable.getDerAttr(derSchema.getKey()) == null
+ && evaluateMandatoryCondition(attrUtil, attributable, derSchema.getKey(),
+ attrUtil.derIntMappingType())) {
+
+ LOG.error("Mandatory derived schema " + derSchema.getKey() + " does not evaluate to any value");
+
+ reqValMissing.getElements().add(derSchema.getKey());
+ }
+ }
+
+ List<? extends VirSchema> virSchemas;
+ switch (attrUtil.getType()) {
+ case ROLE:
+ virSchemas = ((Role) attributable).getAttrTemplateSchemas(RVirAttrTemplate.class);
+ break;
+
+ case MEMBERSHIP:
+ virSchemas = ((Membership) attributable).getRole().getAttrTemplateSchemas(MVirAttrTemplate.class);
+ break;
+
+ case USER:
+ default:
+ virSchemas = virSchemaDAO.findAll(attrUtil.virSchemaClass());
+ }
+ for (VirSchema virSchema : virSchemas) {
+ if (attributable.getVirAttr(virSchema.getKey()) == null
+ && !virSchema.isReadonly()
+ && evaluateMandatoryCondition(attrUtil, attributable, virSchema.getKey(),
+ attrUtil.virIntMappingType())) {
+
+ LOG.error("Mandatory virtual schema " + virSchema.getKey() + " not provided with values");
+
+ reqValMissing.getElements().add(virSchema.getKey());
+ }
+ }
+
+ return reqValMissing;
+ }
+
+ private void setPlainAttrSchema(final Attributable<?, ?, ?> attributable,
+ final PlainAttr attr, final PlainSchema schema) {
+
+ if (attr instanceof UPlainAttr) {
+ ((UPlainAttr) attr).setSchema((UPlainSchema) schema);
+ } else if (attr instanceof RPlainAttr) {
+ RPlainAttrTemplate template =
+ ((Role) attributable).getAttrTemplate(RPlainAttrTemplate.class, schema.getKey());
+ if (template != null) {
+ ((RPlainAttr) attr).setTemplate(template);
+ }
+ } else if (attr instanceof MPlainAttr) {
+ MPlainAttrTemplate template = ((Membership) attributable).getRole().
+ getAttrTemplate(MPlainAttrTemplate.class, schema.getKey());
+ if (template != null) {
+ ((MPlainAttr) attr).setTemplate(template);
+ }
+ }
+ }
+
+ private void setDerAttrSchema(final Attributable<?, ?, ?> attributable,
+ final DerAttr derAttr, final DerSchema derSchema) {
+
+ if (derAttr instanceof UDerAttr) {
+ ((UDerAttr) derAttr).setSchema((UDerSchema) derSchema);
+ } else if (derAttr instanceof RDerAttr) {
+ RDerAttrTemplate template = ((Role) attributable).
+ getAttrTemplate(RDerAttrTemplate.class, derSchema.getKey());
+ if (template != null) {
+ ((RDerAttr) derAttr).setTemplate(template);
+ }
+ } else if (derAttr instanceof MDerAttr) {
+ MDerAttrTemplate template = ((Membership) attributable).getRole().
+ getAttrTemplate(MDerAttrTemplate.class, derSchema.getKey());
+ if (template != null) {
+ ((MDerAttr) derAttr).setTemplate(template);
+ }
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected PropagationByResource fill(final Attributable attributable,
+ final AbstractAttributableMod attributableMod, final AttributableUtil attrUtil,
+ final SyncopeClientCompositeException scce) {
+
+ PropagationByResource propByRes = new PropagationByResource();
+
+ SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
+
+ if (attributable instanceof Subject && attributableMod instanceof AbstractSubjectMod) {
+ // 1. resources to be removed
+ for (String resourceToBeRemoved : ((AbstractSubjectMod) attributableMod).getResourcesToRemove()) {
+ ExternalResource resource = resourceDAO.find(resourceToBeRemoved);
+ if (resource != null) {
+ propByRes.add(ResourceOperation.DELETE, resource.getKey());
+ ((Subject<?, ?, ?>) attributable).removeResource(resource);
+ }
+ }
+
+ LOG.debug("Resources to be removed:\n{}", propByRes);
+
+ // 2. resources to be added
+ for (String resourceToBeAdded : ((AbstractSubjectMod) attributableMod).getResourcesToAdd()) {
+ ExternalResource resource = resourceDAO.find(resourceToBeAdded);
+ if (resource != null) {
+ propByRes.add(ResourceOperation.CREATE, resource.getKey());
+ ((Subject<?, ?, ?>) attributable).addResource(resource);
+ }
+ }
+
+ LOG.debug("Resources to be added:\n{}", propByRes);
+ }
+
+ // 3. attributes to be removed
+ for (String attributeToBeRemoved : attributableMod.getPlainAttrsToRemove()) {
+ PlainSchema schema = getPlainSchema(attributeToBeRemoved, attrUtil.plainSchemaClass());
+ if (schema != null) {
+ PlainAttr attr = attributable.getPlainAttr(schema.getKey());
+ if (attr == null) {
+ LOG.debug("No attribute found for schema {}", schema);
+ } else {
+ String newValue = null;
+ for (AttrMod mod : attributableMod.getPlainAttrsToUpdate()) {
+ if (schema.getKey().equals(mod.getSchema())) {
+ newValue = mod.getValuesToBeAdded().get(0);
+ }
+ }
+
+ if (!schema.isUniqueConstraint()
+ || (!attr.getUniqueValue().getStringValue().equals(newValue))) {
+
+ attributable.removePlainAttr(attr);
+ plainAttrDAO.delete(attr.getKey(), attrUtil.plainAttrClass());
+ }
+ }
+
+ if (attributable instanceof Subject) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (schema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.intMappingType()
+ && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+
+ if (mapItem.isAccountid() && attr != null
+ && !attr.getValuesAsStrings().isEmpty()) {
+
+ propByRes.addOldAccountId(resource.getKey(),
+ attr.getValuesAsStrings().iterator().next());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ LOG.debug("Attributes to be removed:\n{}", propByRes);
+
+ // 4. attributes to be updated
+ for (AttrMod attributeMod : attributableMod.getPlainAttrsToUpdate()) {
+ PlainSchema schema = getPlainSchema(attributeMod.getSchema(), attrUtil.plainSchemaClass());
+ PlainAttr attr = null;
+ if (schema != null) {
+ attr = attributable.getPlainAttr(schema.getKey());
+ if (attr == null) {
+ attr = attrUtil.newPlainAttr();
+ setPlainAttrSchema(attributable, attr, schema);
+ if (attr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", attributeMod);
+ } else {
+ attr.setOwner(attributable);
+ attributable.addPlainAttr(attr);
+ }
+ }
+ }
+
+ if (schema != null && attr != null && attr.getSchema() != null) {
+ if (attributable instanceof Subject) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (schema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.intMappingType()
+ && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+ }
+ }
+ }
+ }
+
+ // 1.1 remove values
+ Set<Long> valuesToBeRemoved = new HashSet<>();
+ for (String valueToBeRemoved : attributeMod.getValuesToBeRemoved()) {
+ if (attr.getSchema().isUniqueConstraint()) {
+ if (attr.getUniqueValue() != null
+ && valueToBeRemoved.equals(attr.getUniqueValue().getValueAsString())) {
+
+ valuesToBeRemoved.add(attr.getUniqueValue().getKey());
+ }
+ } else {
+ for (PlainAttrValue mav : attr.getValues()) {
+ if (valueToBeRemoved.equals(mav.getValueAsString())) {
+ valuesToBeRemoved.add(mav.getKey());
+ }
+ }
+ }
+ }
+ for (Long attributeValueId : valuesToBeRemoved) {
+ plainAttrValueDAO.delete(attributeValueId, attrUtil.plainAttrValueClass());
+ }
+
+ // 1.2 add values
+ List<String> valuesToBeAdded = attributeMod.getValuesToBeAdded();
+ if (valuesToBeAdded != null && !valuesToBeAdded.isEmpty()
+ && (!schema.isUniqueConstraint() || attr.getUniqueValue() == null
+ || !valuesToBeAdded.iterator().next().equals(attr.getUniqueValue().getValueAsString()))) {
+
+ fillAttribute(attributeMod.getValuesToBeAdded(), attrUtil, schema, attr, invalidValues);
+ }
+
+ // if no values are in, the attribute can be safely removed
+ if (attr.getValuesAsStrings().isEmpty()) {
+ plainAttrDAO.delete(attr);
+ }
+ }
+ }
+
+ if (!invalidValues.isEmpty()) {
+ scce.addException(invalidValues);
+ }
+
+ LOG.debug("Attributes to be updated:\n{}", propByRes);
+
+ // 5. derived attributes to be removed
+ for (String derAttrToBeRemoved : attributableMod.getDerAttrsToRemove()) {
+ DerSchema derSchema = getDerSchema(derAttrToBeRemoved, attrUtil.derSchemaClass());
+ if (derSchema != null) {
+ DerAttr derAttr = attributable.getDerAttr(derSchema.getKey());
+ if (derAttr == null) {
+ LOG.debug("No derived attribute found for schema {}", derSchema.getKey());
+ } else {
+ derAttrDAO.delete(derAttr);
+ }
+
+ if (attributable instanceof Subject) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (derSchema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.derIntMappingType()
+ && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+
+ if (mapItem.isAccountid() && derAttr != null
+ && !derAttr.getValue(attributable.getPlainAttrs()).isEmpty()) {
+
+ propByRes.addOldAccountId(resource.getKey(),
+ derAttr.getValue(attributable.getPlainAttrs()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ LOG.debug("Derived attributes to be removed:\n{}", propByRes);
+
+ // 6. derived attributes to be added
+ for (String derAttrToBeAdded : attributableMod.getDerAttrsToAdd()) {
+ DerSchema derSchema = getDerSchema(derAttrToBeAdded, attrUtil.derSchemaClass());
+ if (derSchema != null) {
+ if (attributable instanceof Subject) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ for (MappingItem mapItem : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (derSchema.getKey().equals(mapItem.getIntAttrName())
+ && mapItem.getIntMappingType() == attrUtil.derIntMappingType()
+ && ((Subject<?, ?, ?>) attributable).getResources().contains(resource)) {
+
+ propByRes.add(ResourceOperation.UPDATE, resource.getKey());
+ }
+ }
+ }
+ }
+
+ DerAttr derAttr = attrUtil.newDerAttr();
+ setDerAttrSchema(attributable, derAttr, derSchema);
+ if (derAttr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", derAttrToBeAdded);
+ } else {
+ derAttr.setOwner(attributable);
+ attributable.addDerAttr(derAttr);
+ }
+ }
+ }
+
+ LOG.debug("Derived attributes to be added:\n{}", propByRes);
+
+ // 7. virtual attributes: for users and roles this is delegated to PropagationManager
+ if (AttributableType.USER != attrUtil.getType() && AttributableType.ROLE != attrUtil.getType()) {
+ virtAttrHander.fillVirtual(attributable, attributableMod.getVirAttrsToRemove(),
+ attributableMod.getVirAttrsToUpdate(), attrUtil);
+ }
+
+ // Finally, check if mandatory values are missing
+ SyncopeClientException requiredValuesMissing = checkMandatory(attrUtil, attributable);
+ if (!requiredValuesMissing.isEmpty()) {
+ scce.addException(requiredValuesMissing);
+ }
+
+ // Throw composite exception if there is at least one element set in the composing exceptions
+ if (scce.hasExceptions()) {
+ throw scce;
+ }
+
+ return propByRes;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected void fill(final Attributable attributable, final AbstractAttributableTO attributableTO,
+ final AttributableUtil attrUtil, final SyncopeClientCompositeException scce) {
+
+ // 1. attributes
+ SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
+
+ // Only consider attributeTO with values
+ for (AttrTO attributeTO : attributableTO.getPlainAttrs()) {
+ if (attributeTO.getValues() != null && !attributeTO.getValues().isEmpty()) {
+ PlainSchema schema = getPlainSchema(attributeTO.getSchema(), attrUtil.plainSchemaClass());
+
+ if (schema != null) {
+ PlainAttr attr = attributable.getPlainAttr(schema.getKey());
+ if (attr == null) {
+ attr = attrUtil.newPlainAttr();
+ setPlainAttrSchema(attributable, attr, schema);
+ }
+ if (attr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO);
+ } else {
+ fillAttribute(attributeTO.getValues(), attrUtil, schema, attr, invalidValues);
+
+ if (!attr.getValuesAsStrings().isEmpty()) {
+ attributable.addPlainAttr(attr);
+ attr.setOwner(attributable);
+ }
+ }
+ }
+ }
+ }
+
+ if (!invalidValues.isEmpty()) {
+ scce.addException(invalidValues);
+ }
+
+ // 2. derived attributes
+ for (AttrTO attributeTO : attributableTO.getDerAttrs()) {
+ DerSchema derSchema = getDerSchema(attributeTO.getSchema(), attrUtil.derSchemaClass());
+
+ if (derSchema != null) {
+ DerAttr derAttr = attrUtil.newDerAttr();
+ setDerAttrSchema(attributable, derAttr, derSchema);
+ if (derAttr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", attributeTO);
+ } else {
+ derAttr.setOwner(attributable);
+ attributable.addDerAttr(derAttr);
+ }
+ }
+ }
+
+ // 3. user and role virtual attributes will be evaluated by the propagation manager only (if needed).
+ if (AttributableType.USER == attrUtil.getType()
+ || AttributableType.ROLE == attrUtil.getType()) {
+
+ for (AttrTO vattrTO : attributableTO.getVirAttrs()) {
+ VirSchema virSchema = virtAttrHander.getVirSchema(vattrTO.getSchema(), attrUtil.virSchemaClass());
+
+ if (virSchema != null) {
+ VirAttr virAttr = attrUtil.newVirAttr();
+ virtAttrHander.setVirAttrSchema(attributable, virAttr, virSchema);
+ if (virAttr.getSchema() == null) {
+ LOG.debug("Ignoring {} because no valid schema or template was found", vattrTO);
+ } else {
+ virAttr.setOwner(attributable);
+ attributable.addVirAttr(virAttr);
+ }
+ }
+ }
+ }
+
+ virtAttrHander.fillVirtual(attributable, attributableTO.getVirAttrs(), attrUtil);
+
+ // 4. resources
+ if (attributable instanceof Subject && attributableTO instanceof AbstractSubjectTO) {
+ for (String resourceName : ((AbstractSubjectTO) attributableTO).getResources()) {
+ ExternalResource resource = resourceDAO.find(resourceName);
+
+ if (resource != null) {
+ ((Subject<?, ?, ?>) attributable).addResource(resource);
+ }
+ }
+ }
+
+ SyncopeClientException requiredValuesMissing = checkMandatory(attrUtil, attributable);
+ if (!requiredValuesMissing.isEmpty()) {
+ scce.addException(requiredValuesMissing);
+ }
+
+ // Throw composite exception if there is at least one element set in the composing exceptions
+ if (scce.hasExceptions()) {
+ throw scce;
+ }
+ }
+
+ protected void fillTO(final AbstractAttributableTO attributableTO,
+ final Collection<? extends PlainAttr> attrs,
+ final Collection<? extends DerAttr> derAttrs,
+ final Collection<? extends VirAttr> virAttrs,
+ final Collection<? extends ExternalResource> resources) {
+
+ AttrTO attributeTO;
+ for (PlainAttr attr : attrs) {
+ attributeTO = new AttrTO();
+ attributeTO.setSchema(attr.getSchema().getKey());
+ attributeTO.getValues().addAll(attr.getValuesAsStrings());
+ attributeTO.setReadonly(attr.getSchema().isReadonly());
+
+ attributableTO.getPlainAttrs().add(attributeTO);
+ }
+
+ for (DerAttr derAttr : derAttrs) {
+ attributeTO = new AttrTO();
+ attributeTO.setSchema(derAttr.getSchema().getKey());
+ attributeTO.getValues().add(derAttr.getValue(attrs));
+ attributeTO.setReadonly(true);
+
+ attributableTO.getDerAttrs().add(attributeTO);
+ }
+
+ for (VirAttr virAttr : virAttrs) {
+ attributeTO = new AttrTO();
+ attributeTO.setSchema(virAttr.getSchema().getKey());
+ attributeTO.getValues().addAll(virAttr.getValues());
+ attributeTO.setReadonly(virAttr.getSchema().isReadonly());
+
+ attributableTO.getVirAttrs().add(attributeTO);
+ }
+
+ if (attributableTO instanceof AbstractSubjectTO) {
+ for (ExternalResource resource : resources) {
+ ((AbstractSubjectTO) attributableTO).getResources().add(resource.getKey());
+ }
+ }
+ }
+
+ protected Map<String, String> getAccountIds(final Subject<?, ?, ?> subject, final AttributableType type) {
+ Map<String, String> accountIds = new HashMap<>();
+
+ for (ExternalResource resource : subject.getResources()) {
+ if ((type == AttributableType.USER && resource.getUmapping() != null)
+ || (type == AttributableType.ROLE && resource.getRmapping() != null)) {
+
+ MappingItem accountIdItem =
+ attrUtilFactory.getInstance(type).getAccountIdItem(resource);
+ if (accountIdItem == null) {
+ throw new NotFoundException(
+ "AccountId mapping for " + type + " " + subject.getKey()
+ + " on resource '" + resource.getKey() + "'");
+ }
+
+ accountIds.put(resource.getKey(), MappingUtil.getAccountIdValue(subject, resource, accountIdItem));
+ }
+ }
+
+ return accountIds;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
new file mode 100644
index 0000000..f1d9020
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api.data.ConfigurationDataBinder;
+import java.util.Collections;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConfTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+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.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainSchema;
+import org.apache.syncope.core.persistence.api.entity.conf.Conf;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConfigurationDataBinderImpl extends AbstractAttributableDataBinder implements ConfigurationDataBinder {
+
+ @Override
+ public ConfTO getConfTO(final Conf conf) {
+ final ConfTO confTO = new ConfTO();
+ confTO.setKey(conf.getKey());
+
+ fillTO(confTO, conf.getPlainAttrs(),
+ conf.getDerAttrs(), conf.getVirAttrs(), Collections.<ExternalResource>emptySet());
+
+ return confTO;
+ }
+
+ @Override
+ public AttrTO getAttrTO(final CPlainAttr attr) {
+ final AttrTO attributeTO = new AttrTO();
+ attributeTO.setSchema(attr.getSchema().getKey());
+ attributeTO.getValues().addAll(attr.getValuesAsStrings());
+ attributeTO.setReadonly(attr.getSchema().isReadonly());
+
+ return attributeTO;
+ }
+
+ @Override
+ public CPlainAttr getAttribute(final AttrTO attributeTO) {
+ CPlainSchema schema = getPlainSchema(attributeTO.getSchema(), CPlainSchema.class);
+ if (schema == null) {
+ throw new NotFoundException("Conf schema " + attributeTO.getSchema());
+ } else {
+ SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
+
+ CPlainAttr attr = entityFactory.newEntity(CPlainAttr.class);
+ attr.setSchema(schema);
+ fillAttribute(attributeTO.getValues(), attrUtilFactory.getInstance(AttributableType.CONFIGURATION),
+ schema, attr, invalidValues);
+
+ if (!invalidValues.isEmpty()) {
+ throw invalidValues;
+ }
+ return attr;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
new file mode 100644
index 0000000..fbb687e
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
@@ -0,0 +1,245 @@
+/*
+ * 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.api.data.ConnInstanceDataBinder;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ConnConfPropSchema;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.core.provisioning.api.ConnPoolConfUtil;
+import org.identityconnectors.framework.api.ConfigurationProperties;
+import org.identityconnectors.framework.api.ConfigurationProperty;
+import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl;
+import org.apache.syncope.core.misc.spring.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
+
+ private static final String[] IGNORE_PROPERTIES = { "key", "poolConf" };
+
+ @Autowired
+ private ConnIdBundleManager connIdBundleManager;
+
+ @Autowired
+ private ConnInstanceDAO connInstanceDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Override
+ public Set<ConnConfProperty> mergeConnConfProperties(final Set<ConnConfProperty> primary,
+ final Set<ConnConfProperty> secondary) {
+
+ final Set<ConnConfProperty> conf = new HashSet<>();
+
+ // to be used to control managed prop (needed by overridden mechanism)
+ final Set<String> propertyNames = new HashSet<>();
+
+ // get overridden connector configuration properties
+ for (ConnConfProperty prop : primary) {
+ if (!propertyNames.contains(prop.getSchema().getName())) {
+ conf.add(prop);
+ propertyNames.add(prop.getSchema().getName());
+ }
+ }
+
+ // get connector configuration properties
+ for (ConnConfProperty prop : secondary) {
+ if (!propertyNames.contains(prop.getSchema().getName())) {
+ conf.add(prop);
+ propertyNames.add(prop.getSchema().getName());
+ }
+ }
+
+ return conf;
+ }
+
+ @Override
+ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+
+ if (connInstanceTO.getLocation() == null) {
+ sce.getElements().add("location");
+ }
+
+ if (connInstanceTO.getBundleName() == null) {
+ sce.getElements().add("bundlename");
+ }
+
+ if (connInstanceTO.getVersion() == null) {
+ sce.getElements().add("bundleversion");
+ }
+
+ if (connInstanceTO.getConnectorName() == null) {
+ sce.getElements().add("connectorname");
+ }
+
+ if (connInstanceTO.getConfiguration() == null || connInstanceTO.getConfiguration().isEmpty()) {
+ sce.getElements().add("configuration");
+ }
+
+ ConnInstance connInstance = entityFactory.newEntity(ConnInstance.class);
+
+ BeanUtils.copyProperties(connInstanceTO, connInstance, IGNORE_PROPERTIES);
+ if (connInstanceTO.getLocation() != null) {
+ connInstance.setLocation(connInstanceTO.getLocation());
+ }
+ if (connInstanceTO.getPoolConf() != null) {
+ connInstance.setPoolConf(
+ ConnPoolConfUtil.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf()));
+ }
+
+ // Throw exception if there is at least one element set
+ if (!sce.isEmpty()) {
+ throw sce;
+ }
+
+ return connInstance;
+ }
+
+ @Override
+ public ConnInstance updateConnInstance(final long connInstanceId, final ConnInstanceTO connInstanceTO) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+
+ if (connInstanceId == 0) {
+ sce.getElements().add("connector id");
+ }
+
+ ConnInstance connInstance = connInstanceDAO.find(connInstanceId);
+ connInstance.getCapabilities().clear();
+ connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities());
+
+ if (connInstanceTO.getLocation() != null) {
+ connInstance.setLocation(connInstanceTO.getLocation());
+ }
+
+ if (connInstanceTO.getBundleName() != null) {
+ connInstance.setBundleName(connInstanceTO.getBundleName());
+ }
+
+ if (connInstanceTO.getVersion() != null) {
+ connInstance.setVersion(connInstanceTO.getVersion());
+ }
+
+ if (connInstanceTO.getConnectorName() != null) {
+ connInstance.setConnectorName(connInstanceTO.getConnectorName());
+ }
+
+ if (connInstanceTO.getConfiguration() != null && !connInstanceTO.getConfiguration().isEmpty()) {
+ connInstance.setConfiguration(connInstanceTO.getConfiguration());
+ }
+
+ if (connInstanceTO.getDisplayName() != null) {
+ connInstance.setDisplayName(connInstanceTO.getDisplayName());
+ }
+
+ if (connInstanceTO.getConnRequestTimeout() != null) {
+ connInstance.setConnRequestTimeout(connInstanceTO.getConnRequestTimeout());
+ }
+
+ if (connInstanceTO.getPoolConf() == null) {
+ connInstance.setPoolConf(null);
+ } else {
+ connInstance.setPoolConf(
+ ConnPoolConfUtil.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf()));
+ }
+
+ if (!sce.isEmpty()) {
+ throw sce;
+ }
+
+ return connInstance;
+ }
+
+ @Override
+ public ConnConfPropSchema buildConnConfPropSchema(final ConfigurationProperty property) {
+ ConnConfPropSchema connConfPropSchema = new ConnConfPropSchema();
+
+ connConfPropSchema.setName(property.getName());
+ connConfPropSchema.setDisplayName(property.getDisplayName(property.getName()));
+ connConfPropSchema.setHelpMessage(property.getHelpMessage(property.getName()));
+ connConfPropSchema.setRequired(property.isRequired());
+ connConfPropSchema.setType(property.getType().getName());
+ connConfPropSchema.setOrder(((ConfigurationPropertyImpl) property).getOrder());
+ connConfPropSchema.setConfidential(property.isConfidential());
+
+ if (property.getValue() != null) {
+ if (property.getValue().getClass().isArray()) {
+ connConfPropSchema.getDefaultValues().addAll(Arrays.asList((Object[]) property.getValue()));
+ } else if (property.getValue() instanceof Collection<?>) {
+ connConfPropSchema.getDefaultValues().addAll((Collection<?>) property.getValue());
+ } else {
+ connConfPropSchema.getDefaultValues().add(property.getValue());
+ }
+ }
+
+ return connConfPropSchema;
+ }
+
+ @Override
+ public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) {
+ ConnInstanceTO connInstanceTO = new ConnInstanceTO();
+ connInstanceTO.setKey(connInstance.getKey() == null ? 0L : connInstance.getKey());
+
+ // retrieve the ConfigurationProperties
+ ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(
+ connIdBundleManager.getConnectorInfo(connInstance.getLocation(),
+ connInstance.getBundleName(), connInstance.getVersion(), connInstance.getConnectorName()));
+
+ BeanUtils.copyProperties(connInstance, connInstanceTO, IGNORE_PROPERTIES);
+
+ final Map<String, ConnConfProperty> connInstanceToConfMap = connInstanceTO.getConfigurationMap();
+
+ for (String propName : properties.getPropertyNames()) {
+ ConnConfPropSchema schema = buildConnConfPropSchema(properties.getProperty(propName));
+
+ ConnConfProperty property;
+ if (connInstanceToConfMap.containsKey(propName)) {
+ property = connInstanceToConfMap.get(propName);
+ } else {
+ property = new ConnConfProperty();
+ connInstanceTO.getConfiguration().add(property);
+ }
+
+ property.setSchema(schema);
+ }
+
+ if (connInstance.getPoolConf() != null) {
+ ConnPoolConfTO poolConf = new ConnPoolConfTO();
+ BeanUtils.copyProperties(connInstance.getPoolConf(), poolConf);
+ connInstanceTO.setPoolConf(poolConf);
+ }
+
+ return connInstanceTO;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
new file mode 100644
index 0000000..c18e079
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
@@ -0,0 +1,66 @@
+/*
+ * 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.api.data.NotificationDataBinder;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Notification;
+import org.apache.syncope.core.misc.spring.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NotificationDataBinderImpl implements NotificationDataBinder {
+
+ private static final String[] IGNORE_PROPERTIES = { "key", "about", "recipients" };
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Override
+ public NotificationTO getNotificationTO(final Notification notification) {
+ NotificationTO result = new NotificationTO();
+
+ BeanUtils.copyProperties(notification, result, IGNORE_PROPERTIES);
+
+ result.setKey(notification.getKey());
+ result.setUserAbout(notification.getUserAbout());
+ result.setRoleAbout(notification.getRoleAbout());
+ result.setRecipients(notification.getRecipients());
+
+ return result;
+ }
+
+ @Override
+ public Notification create(final NotificationTO notificationTO) {
+ Notification result = entityFactory.newEntity(Notification.class);
+ update(result, notificationTO);
+ return result;
+ }
+
+ @Override
+ public void update(final Notification notification, final NotificationTO notificationTO) {
+ BeanUtils.copyProperties(notificationTO, notification, IGNORE_PROPERTIES);
+
+ notification.setUserAbout(notificationTO.getUserAbout());
+ notification.setRoleAbout(notificationTO.getRoleAbout());
+ notification.setRecipients(notificationTO.getRecipients());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
new file mode 100644
index 0000000..ec735b6
--- /dev/null
+++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
@@ -0,0 +1,189 @@
+/*
+ * 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.api.data.PolicyDataBinder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.to.AccountPolicyTO;
+import org.apache.syncope.common.lib.to.PasswordPolicyTO;
+import org.apache.syncope.common.lib.to.SyncPolicyTO;
+import org.apache.syncope.common.lib.types.AccountPolicySpec;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.Policy;
+import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PolicyDataBinderImpl implements PolicyDataBinder {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(PolicyDataBinder.class);
+
+ @Autowired
+ private ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ private RoleDAO roleDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ /**
+ * Get policy TO from policy bean.
+ *
+ * @param policy bean.
+ * @return policy TO.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends AbstractPolicyTO> T getPolicyTO(final Policy policy) {
+ final T policyTO;
+
+ switch (policy.getType()) {
+ case GLOBAL_PASSWORD:
+ case PASSWORD:
+ policyTO = (T) new PasswordPolicyTO(policy.getType().isGlobal());
+ ((PasswordPolicyTO) policyTO).setSpecification(policy.getSpecification(PasswordPolicySpec.class));
+ break;
+
+ case GLOBAL_ACCOUNT:
+ case ACCOUNT:
+ policyTO = (T) new AccountPolicyTO(policy.getType().isGlobal());
+ ((AccountPolicyTO) policyTO).setSpecification(policy.getSpecification(AccountPolicySpec.class));
+ ((AccountPolicyTO) policyTO).getResources().addAll(((AccountPolicy) policy).getResourceNames());
+ break;
+
+ case GLOBAL_SYNC:
+ case SYNC:
+ default:
+ policyTO = (T) new SyncPolicyTO(policy.getType().isGlobal());
+ ((SyncPolicyTO) policyTO).setSpecification(policy.getSpecification(SyncPolicySpec.class));
+ }
+
+ policyTO.setKey(policy.getKey());
+ policyTO.setDescription(policy.getDescription());
+
+ for (ExternalResource resource : resourceDAO.findByPolicy(policy)) {
+ policyTO.getUsedByResources().add(resource.getKey());
+ }
+ if (policy.getType().isGlobal()) {
+ for (ExternalResource resource : resourceDAO.findWithoutPolicy(policy.getType())) {
+ policyTO.getUsedByResources().add(resource.getKey());
+ }
+ }
+ for (Role role : roleDAO.findByPolicy(policy)) {
+ policyTO.getUsedByRoles().add(role.getKey());
+ }
+ if (policy.getType().isGlobal()) {
+ for (Role role : roleDAO.findWithoutPolicy(policy.getType())) {
+ policyTO.getUsedByRoles().add(role.getKey());
+ }
+ }
+
+ return policyTO;
+ }
+
+ private ExternalResource getResource(final String resourceName) {
+ ExternalResource resource = resourceDAO.find(resourceName);
+ if (resource == null) {
+ LOG.debug("Ignoring invalid resource {} ", resourceName);
+ }
+
+ return resource;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends Policy> T getPolicy(T policy, final AbstractPolicyTO policyTO) {
+ if (policy != null && policy.getType() != policyTO.getType()) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+ sce.getElements().add(String.format("Cannot update %s from %s", policy.getType(), policyTO.getType()));
+ throw sce;
+ }
+
+ switch (policyTO.getType()) {
+ case GLOBAL_PASSWORD:
+ case PASSWORD:
+ if (!(policyTO instanceof PasswordPolicyTO)) {
+ throw new ClassCastException("Expected " + PasswordPolicyTO.class.getName()
+ + ", found " + policyTO.getClass().getName());
+ }
+ if (policy == null) {
+ policy = (T) entityFactory.newPolicy(PasswordPolicy.class, policyTO.getType().isGlobal());
+ }
+ policy.setSpecification(((PasswordPolicyTO) policyTO).getSpecification());
+ break;
+
+ case GLOBAL_ACCOUNT:
+ case ACCOUNT:
+ if (!(policyTO instanceof AccountPolicyTO)) {
+ throw new ClassCastException("Expected " + AccountPolicyTO.class.getName()
+ + ", found " + policyTO.getClass().getName());
+ }
+ if (policy == null) {
+ policy = (T) entityFactory.newPolicy(AccountPolicy.class, policyTO.getType().isGlobal());
+ }
+ policy.setSpecification(((AccountPolicyTO) policyTO).getSpecification());
+
+ if (((AccountPolicy) policy).getResources() != null
+ && !((AccountPolicy) policy).getResources().isEmpty()) {
+ ((AccountPolicy) policy).getResources().clear();
+ }
+ for (String resourceName : ((AccountPolicyTO) policyTO).getResources()) {
+ ExternalResource resource = getResource(resourceName);
+
+ if (resource != null) {
+ ((AccountPolicy) policy).addResource(resource);
+ }
+ }
+ break;
+
+ case GLOBAL_SYNC:
+ case SYNC:
+ default:
+ if (!(policyTO instanceof SyncPolicyTO)) {
+ throw new ClassCastException("Expected " + SyncPolicyTO.class.getName()
+ + ", found " + policyTO.getClass().getName());
+ }
+ if (policy == null) {
+ policy = (T) entityFactory.newPolicy(SyncPolicy.class, policyTO.getType().isGlobal());
+ }
+ policy.setSpecification(((SyncPolicyTO) policyTO).getSpecification());
+ }
+
+ policy.setDescription(policyTO.getDescription());
+
+ return policy;
+ }
+}