class AttributesServiceImpl implements AttributesService {

    @Override
    public List<ClientAttribute> putAttributes(RecipientIdentity holder,
                                                   List<? extends ClientAttribute> attributes,
                                                   Set<String> names, boolean includeEffectivePermissionsInResult) throws ErrorDescriptorException {
        Object attributeHolder = recipientIdentityResolver.resolveRecipientObject(holder);

        List<ProfileAttribute> attributesToDelete = new ArrayList<ProfileAttribute>();
        List<ClientAttribute> attributesToPut;

        Set<String> newAttributesNamesSet = createUniqueNamesSet(attributes);

        if (attributes != null) {
            attributesToPut = new LinkedList<ClientAttribute>(attributes);
        } else {
            attributesToPut = new LinkedList<ClientAttribute>();
        }

        AttributesSearchCriteria searchCriteria = new AttributesSearchCriteria.Builder().setNames(names).build();
        List<ProfileAttribute> oldCollection =
                profileAttributeService.getProfileAttributesForPrincipal(null, attributeHolder, searchCriteria).getList();

        if (names == null || names.size() == 0) {
            for (ProfileAttribute pa : oldCollection) {
                if (!newAttributesNamesSet.contains(pa.getAttrName())) {
                    attributesToDelete.add(pa);
                }
            }
        } else {
            Iterator<ClientAttribute> iterator = attributesToPut.iterator();
            while (iterator.hasNext()) {
                if (!names.contains(iterator.next().getName())) {
                    iterator.remove();
                }
            }
            for (String attrName : names) {
                if (!newAttributesNamesSet.contains(attrName)) {
                    for (ProfileAttribute attr : oldCollection) {
                        if (attr.getAttrName().equals(attrName)) {
                            attributesToDelete.add(attr);
                            break;
                        }
                    }
                }
            }
        }

        for (ProfileAttribute attr : attributesToDelete) {
            try {
                profileAttributeService.deleteProfileAttribute(null, attr);
            } catch (AccessDeniedException ex) {
                throw new com.jaspersoft.jasperserver.remote.exception.AccessDeniedException("Access denied for attribute ",
                        attr.getAttrName());
            }
        }

        List<ClientAttribute> result = new ArrayList<ClientAttribute>();
        for (ClientAttribute client : attributesToPut) {
            try {
                client.setHolder(recipientIdentityResolver.toRecipientUri(holder));

                ProfileAttribute serverAttr = attributesConverter.toServer(client, null);
                profileAttributeService.putProfileAttribute(null, serverAttr);

                if (client instanceof HypermediaAttribute) {
                    //setup
                    HypermediaAttribute hypermediaAttribute = (HypermediaAttribute) client;
                    if (hypermediaAttribute.getEmbedded() != null &&
                            ((HypermediaAttribute) client).getEmbedded().getRepositoryPermissions() != null) {
                        List<ObjectPermission> objectPermissions = new ArrayList<ObjectPermission>();
                        for (RepositoryPermission repositoryPermission :
                                ((HypermediaAttribute) client).getEmbedded().getRepositoryPermissions()) {
                            repositoryPermission.setUri(serverAttr.getURI());
                            objectPermissions.add(permissionConverter.toServer(repositoryPermission, null));
                        }
                        attributesPermissionService.putPermissions(serverAttr, objectPermissions);
                    }
                }

                ClientAttribute updatedClient = attributesConverter.toClient(serverAttr, null);

                if (includeEffectivePermissionsInResult) {
                    HypermediaAttributeEmbeddedContainer embeddedContainer = new HypermediaAttributeEmbeddedContainer();
                    embeddedContainer.setRepositoryPermissions(getEffectivePermissions(serverAttr));
                    HypermediaAttribute hypermediaAttribute = new HypermediaAttribute(updatedClient);
                    hypermediaAttribute.setEmbedded(embeddedContainer);

                    result.add(hypermediaAttribute);
                } else {
                    result.add(updatedClient);
                }
            } catch (AccessDeniedException ex) {
                throw new com.jaspersoft.jasperserver.remote.exception.AccessDeniedException("Access denied for attribute ", client.getName());
            } catch (JSDuplicateResourceException ex) {
                throw new ResourceAlreadyExistsException(new ErrorDescriptor()
                        .setErrorCode("attribute.duplicate.server.level")
                        .setMessage("The attribute name is reserved for use at the server level")
                        .setParameters(new String[]{client.getName()}));
            }
        }

        return result;
    }

}