de.escidoc.core.aa.business.UserAccountHandler.java Source code

Java tutorial

Introduction

Here is the source code for de.escidoc.core.aa.business.UserAccountHandler.java

Source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at license/ESCIDOC.LICENSE
 * or http://www.escidoc.de/license.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at license/ESCIDOC.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006-2008 Fachinformationszentrum Karlsruhe Gesellschaft
 * fuer wissenschaftlich-technische Information mbH and Max-Planck-
 * Gesellschaft zur Foerderung der Wissenschaft e.V.  
 * All rights reserved.  Use is subject to license terms.
 */
package de.escidoc.core.aa.business;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import de.escidoc.core.aa.business.filter.PermissionsQuery;
import de.escidoc.core.aa.business.filter.RoleGrantFilter;
import de.escidoc.core.aa.business.filter.UserAccountFilter;
import de.escidoc.core.aa.business.interfaces.PolicyDecisionPointInterface;
import de.escidoc.core.aa.business.interfaces.UserAccountHandlerInterface;
import de.escidoc.core.aa.business.interfaces.UserGroupHandlerInterface;
import de.escidoc.core.aa.business.persistence.EscidocRole;
import de.escidoc.core.aa.business.persistence.EscidocRoleDaoInterface;
import de.escidoc.core.aa.business.persistence.RoleGrant;
import de.escidoc.core.aa.business.persistence.ScopeDef;
import de.escidoc.core.aa.business.persistence.UserAccount;
import de.escidoc.core.aa.business.persistence.UserAccountDaoInterface;
import de.escidoc.core.aa.business.persistence.UserAttribute;
import de.escidoc.core.aa.business.persistence.UserGroup;
import de.escidoc.core.aa.business.persistence.UserGroupDaoInterface;
import de.escidoc.core.aa.business.persistence.UserGroupMember;
import de.escidoc.core.aa.business.persistence.UserLoginData;
import de.escidoc.core.aa.business.persistence.UserPreference;
import de.escidoc.core.aa.business.renderer.interfaces.UserAccountRendererInterface;
import de.escidoc.core.aa.business.stax.handler.GrantStaxHandler;
import de.escidoc.core.aa.business.stax.handler.RevokeStaxHandler;
import de.escidoc.core.aa.business.stax.handler.UserAccountPropertiesStaxHandler;
import de.escidoc.core.aa.business.stax.handler.UserAttributeReadHandler;
import de.escidoc.core.aa.business.stax.handler.UserPreferenceReadHandler;
import de.escidoc.core.common.business.Constants;
import de.escidoc.core.common.business.fedora.TripleStoreUtility;
import de.escidoc.core.common.business.fedora.Utility;
import de.escidoc.core.common.business.fedora.resources.ResourceType;
import de.escidoc.core.common.business.fedora.resources.interfaces.FilterInterface;
import de.escidoc.core.common.business.filter.DbRequestParameters;
import de.escidoc.core.common.business.filter.SRURequestParameters;
import de.escidoc.core.common.business.indexing.IndexingHandler;
import de.escidoc.core.common.exceptions.application.invalid.InvalidContentException;
import de.escidoc.core.common.exceptions.application.invalid.InvalidScopeException;
import de.escidoc.core.common.exceptions.application.invalid.InvalidSearchQueryException;
import de.escidoc.core.common.exceptions.application.invalid.InvalidStatusException;
import de.escidoc.core.common.exceptions.application.invalid.InvalidXmlException;
import de.escidoc.core.common.exceptions.application.invalid.LastModificationDateMissingException;
import de.escidoc.core.common.exceptions.application.invalid.XmlCorruptedException;
import de.escidoc.core.common.exceptions.application.missing.MissingAttributeValueException;
import de.escidoc.core.common.exceptions.application.missing.MissingMethodParameterException;
import de.escidoc.core.common.exceptions.application.notfound.GrantNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.OrganizationalUnitNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.PreferenceNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.ResourceNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.RoleNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.UserAccountNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.UserAttributeNotFoundException;
import de.escidoc.core.common.exceptions.application.notfound.UserGroupNotFoundException;
import de.escidoc.core.common.exceptions.application.security.AuthenticationException;
import de.escidoc.core.common.exceptions.application.security.AuthorizationException;
import de.escidoc.core.common.exceptions.application.violated.AlreadyActiveException;
import de.escidoc.core.common.exceptions.application.violated.AlreadyDeactiveException;
import de.escidoc.core.common.exceptions.application.violated.AlreadyExistsException;
import de.escidoc.core.common.exceptions.application.violated.AlreadyRevokedException;
import de.escidoc.core.common.exceptions.application.violated.OptimisticLockingException;
import de.escidoc.core.common.exceptions.application.violated.ReadonlyElementViolationException;
import de.escidoc.core.common.exceptions.application.violated.UniqueConstraintViolationException;
import de.escidoc.core.common.exceptions.system.SqlDatabaseSystemException;
import de.escidoc.core.common.exceptions.system.SystemException;
import de.escidoc.core.common.exceptions.system.WebserverSystemException;
import de.escidoc.core.common.util.configuration.EscidocConfiguration;
import de.escidoc.core.common.util.service.UserContext;
import de.escidoc.core.common.util.stax.handler.TaskParamHandler;
import de.escidoc.core.common.util.stax.handler.filter.ExtendedFilterHandler;
import de.escidoc.core.common.util.string.StringUtility;
import de.escidoc.core.common.util.xml.Elements;
import de.escidoc.core.common.util.xml.XmlUtility;
import de.escidoc.core.common.util.xml.factory.ExplainXmlProvider;
import de.escidoc.core.common.util.xml.stax.StaxParser;
import de.escidoc.core.common.util.xml.stax.handler.LinkStaxHandler;
import de.escidoc.core.common.util.xml.stax.handler.OptimisticLockingStaxHandler;

/**
 * Implementation for the account handle.
 *
 * @author Michael Schneider
 */
@Service("business.UserAccountHandler")
public class UserAccountHandler implements UserAccountHandlerInterface {

    /**
     * The logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(UserAccountHandler.class);

    private static final String XPATH_GRANT_ASSIGNED_ON = '/' + XmlUtility.NAME_GRANT + '/'
            + XmlUtility.NAME_PROPERTIES + '/' + XmlUtility.NAME_ASSIGNED_ON;

    private static final String XPATH_GRANT_ROLE = '/' + XmlUtility.NAME_GRANT + '/' + XmlUtility.NAME_PROPERTIES
            + '/' + XmlUtility.NAME_ROLE;

    private static final Pattern GROUP_FILTER_PATTERN = Pattern.compile("(?s)\"{0,1}(" + Constants.FILTER_GROUP
            + '|' + Constants.FILTER_PATH_USER_ACCOUNT_GROUP_ID + ")(\"*\\s*([=<>]+)\\s*\"*|\"*\\s*(any)\\s*\"*"
            + "|\"*\\s*(cql.any)\\s*\"*)" + "([^\\s\"\\(\\)]*)\"{0,1}");

    private static final String MSG_WRONG_HREF = "Referenced object href is wrong, object has another type.";

    private static final String MSG_GRANT_RESTRICTION_VIOLATED = "Grants can be created on containers, content models, contexts"
            + ", items, components, organizational units, user-accounts, "
            + "user-groups and scopes, only. No resource of one " + "of these types with the provided id exists.";

    private static final String MSG_USER_NOT_FOUND_BY_ID = "User with provided id does not exist.";

    private static final String MSG_USER_NOT_FOUND_BY_IDENTITY_INFO = "User with provided user identity does not exist.";

    private static final String MSG_GROUP_NOT_FOUND_BY_ID = "User-Group with provided id does not exist.";

    private static final String MSG_UNEXPECTED_EXCEPTION = "Unexpected exception in ";

    private static final String MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS = "Unexpected exception during evaluating access rights.";

    private static final String MSG_XML_SCHEMA_ENSURE = "Should be ensured by XML Schema.";

    private static final int MAX_FIELD_LENGTH = 245;

    @Autowired
    @Qualifier("persistence.UserAccountDao")
    private UserAccountDaoInterface dao;

    @Autowired
    @Qualifier("persistence.UserGroupDao")
    private UserGroupDaoInterface userGroupDao;

    @Autowired
    @Qualifier("eSciDoc.core.aa.ObjectAttributeResolver")
    private ObjectAttributeResolver objectAttributeResolver;

    @Autowired
    @Qualifier("eSciDoc.core.aa.business.renderer.VelocityXmlUserAccountRenderer")
    private UserAccountRendererInterface renderer;

    @Autowired
    @Qualifier("persistence.EscidocRoleDao")
    private EscidocRoleDaoInterface roleDao;

    @Autowired
    @Qualifier("business.PolicyDecisionPoint")
    private PolicyDecisionPointInterface pdp;

    @Autowired
    @Qualifier("business.UserGroupHandler")
    private UserGroupHandlerInterface userGroupHandler;

    @Autowired
    @Qualifier("filter.PermissionsQuery")
    private PermissionsQuery permissionsQuery;

    @Autowired
    @Qualifier("business.TripleStoreUtility")
    private TripleStoreUtility tripleStoreUtility;

    @Autowired
    @Qualifier("business.Utility")
    private Utility utility;

    @Autowired
    @Qualifier("common.business.indexing.IndexingHandler")
    private IndexingHandler indexingHandler;

    @Autowired
    @Qualifier("security.SecurityHelper")
    private SecurityHelper securityHelper;

    /**
     * Protected constructor to prevent instantiation outside of the Spring-context.
     */
    protected UserAccountHandler() {
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return user-account data as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieve(java.lang.String)
     */
    @Override
    public String retrieve(final String userId) throws UserAccountNotFoundException, SystemException {

        final UserAccount userAccount = dao.retrieveUserAccount(userId);
        assertUserAccount(userId, userAccount);

        return renderer.render(userAccount);
    }

    /**
     * See Interface for functional description.
     *
     * @return user-account data as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveCurrentUser(java.lang.String)
     */
    @Override
    public String retrieveCurrentUser() throws UserAccountNotFoundException, SystemException {
        if (StringUtils.isEmpty(UserContext.getId())) {
            throw new UserAccountNotFoundException("No user logged in");
        }
        final UserAccount userAccount = dao.retrieveUserAccount(UserContext.getId());
        assertUserAccount(UserContext.getId(), userAccount);

        return renderer.render(userAccount);
    }

    /**
     * See Interface for functional description.
     *
     * @param xmlData data for user-account
     * @return user-data as xml
     * @throws UniqueConstraintViolationException
     *                                e
     * @throws XmlCorruptedException  e
     * @throws OrganizationalUnitNotFoundException
     *                                e
     * @throws SystemException        e
     * @throws InvalidStatusException e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #create(java.lang.String)
     */
    @Override
    public String create(final String xmlData) throws UniqueConstraintViolationException, XmlCorruptedException,
            OrganizationalUnitNotFoundException, SystemException, InvalidStatusException {

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(xmlData);

        final UserAccount userAccount = new UserAccount();

        final StaxParser sp = new StaxParser(XmlUtility.NAME_USER_ACCOUNT);

        final UserAccountPropertiesStaxHandler propertiesHandler = new UserAccountPropertiesStaxHandler(userAccount,
                this.dao, true);
        sp.addHandler(propertiesHandler);

        try {
            sp.parse(in);
        } catch (final UniqueConstraintViolationException e) {
            throw e;
        } catch (final InvalidStatusException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".create: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        // A created user account is in state active
        userAccount.setActive(Boolean.TRUE);

        setModificationValues(userAccount);
        setCreationValues(userAccount);

        // FIXME: The default password "PubManR2" is set to the created
        // user to test creation of user accounts (discussed with MPDL, Natasa
        // Bulatovic). Once the identity management has been integrated, this
        // should be removed, because after that, user passwords will not be
        // used, anymore.
        userAccount.setPassword("PubManR2");

        dao.save(userAccount);

        return renderer.render(userAccount);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @throws UserAccountNotFoundException e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #delete(java.lang.String)
     */
    @Override
    public void delete(final String userId)
            throws UserAccountNotFoundException, SqlDatabaseSystemException, WebserverSystemException {

        dao.delete(retrieveUserAccountById(userId));

        sendUserAccountUpdateEvent(userId);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId  userId
     * @param xmlData xml with updated data
     * @return updated user as xml
     * @throws UserAccountNotFoundException   e
     * @throws UniqueConstraintViolationException
     *                                        e
     * @throws XmlCorruptedException          e
     * @throws MissingAttributeValueException e
     * @throws OptimisticLockingException     e
     * @throws OrganizationalUnitNotFoundException
     *                                        e
     * @throws SystemException                e
     * @throws InvalidStatusException         e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #update(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String update(final String userId, final String xmlData)
            throws UserAccountNotFoundException, UniqueConstraintViolationException, XmlCorruptedException,
            MissingAttributeValueException, OptimisticLockingException, OrganizationalUnitNotFoundException,
            SystemException, InvalidStatusException {

        final UserAccount userAccount = retrieveUserAccountById(userId);

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(xmlData);

        final StaxParser sp = new StaxParser(XmlUtility.NAME_USER_ACCOUNT);

        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);

        final UserAccountPropertiesStaxHandler propertiesHandler = new UserAccountPropertiesStaxHandler(userAccount,
                this.dao, false);
        sp.addHandler(propertiesHandler);

        try {
            sp.parse(in);
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (LastModificationDateMissingException e) {
            throw new XmlCorruptedException(e);
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final UniqueConstraintViolationException e) {
            throw e;
        } catch (final InvalidStatusException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".parse: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        setModificationValues(userAccount);

        dao.update(userAccount);

        return renderer.render(userAccount);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId    userId
     * @param taskParam password
     * @throws UserAccountNotFoundException e
     * @throws InvalidStatusException       e
     * @throws XmlCorruptedException        e
     * @throws MissingMethodParameterException
     *                                      e
     * @throws OptimisticLockingException   e
     * @throws AuthenticationException      e
     * @throws AuthorizationException       e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface#updatePassword(String, String)
     */
    @Override
    public void updatePassword(final String userId, final String taskParam) throws UserAccountNotFoundException,
            InvalidStatusException, XmlCorruptedException, MissingMethodParameterException,
            OptimisticLockingException, AuthenticationException, AuthorizationException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        if (!userAccount.getActive()) {
            throw new InvalidStatusException("Password must not be updated on inactive user-account!");
        }

        final TaskParamHandler handler = XmlUtility.parseTaskParam(taskParam);

        final String password = handler.getPassword();
        if (password == null || password.isEmpty()) {
            throw new MissingMethodParameterException("Password must not be null or empty!");
        }
        userAccount.setPassword(password);
        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(taskParam);
        final StaxParser sp = new StaxParser("param");
        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);
        try {
            sp.parse(in);
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (LastModificationDateMissingException e) {
            throw new XmlCorruptedException(e);
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            XmlUtility.handleUnexpectedStaxParserException("", e);
        }
        setModificationValues(userAccount);
        dao.update(userAccount);
        sendUserAccountUpdateEvent(userId);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return resources as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveResources(java.lang.String)
     */
    @Override
    public String retrieveResources(final String userId) throws UserAccountNotFoundException, SystemException {

        return renderer.renderResources(retrieveUserAccountById(userId));
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return grants as map
     * @throws UserAccountNotFoundException e
     * @see UserAccountHandlerInterface #retrieveCurrentGrantsAsMap(java.lang.String)
     */
    @Override
    public Map<String, Map<String, List<RoleGrant>>> retrieveCurrentGrantsAsMap(final String userId)
            throws UserAccountNotFoundException, SqlDatabaseSystemException {

        retrieveUserAccountById(userId);
        final List<RoleGrant> currentGrants = fetchCurrentGrants(userId);
        if (currentGrants == null || currentGrants.isEmpty()) {
            return null;
        }

        final Iterator<RoleGrant> iter = currentGrants.iterator();
        final Map<String, Map<String, List<RoleGrant>>> ret = new HashMap<String, Map<String, List<RoleGrant>>>();
        while (iter.hasNext()) {
            final RoleGrant grant = iter.next();
            final String roleId = grant.getEscidocRole().getId();
            Map<String, List<RoleGrant>> grantsOfRole = ret.get(roleId);
            if (grantsOfRole == null) {
                grantsOfRole = new HashMap<String, List<RoleGrant>>();
                ret.put(roleId, grantsOfRole);
            }
            String key = grant.getObjectId();
            if (key == null) {
                // For grants of unlimited roles an empty string is used
                // as the key.
                key = "";
            }
            List<RoleGrant> grantsOfObject = grantsOfRole.get(key);
            if (grantsOfObject == null) {
                grantsOfObject = new ArrayList<RoleGrant>();
                grantsOfRole.put(key, grantsOfObject);
            }
            grantsOfObject.add(grant);
        }
        return ret;
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return grants as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveCurrentGrants(java.lang.String)
     */
    @Override
    public String retrieveCurrentGrants(final String userId) throws UserAccountNotFoundException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final List<RoleGrant> currentGrants = fetchCurrentGrants(userId);
        final Map<String, RoleGrant> grantsMap = new HashMap<String, RoleGrant>();
        final List<Object[]> argumentList = new ArrayList<Object[]>();
        final List<RoleGrant> filteredCurrentGrants = new ArrayList<RoleGrant>();

        // AA-filter
        for (final RoleGrant roleGrant : currentGrants) {
            grantsMap.put(roleGrant.getId(), roleGrant);
            final Object[] args = { userId, roleGrant.getId() };
            argumentList.add(args);
        }
        try {
            final List<Object[]> returnList = pdp.evaluateMethodForList("user-account", "retrieveGrant",
                    argumentList);
            for (final Object[] obj : returnList) {
                filteredCurrentGrants.add(grantsMap.get(obj[1]));
            }
        } catch (final MissingMethodParameterException e) {
            throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
        } catch (final ResourceNotFoundException e) {
            throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
        }

        return renderer.renderCurrentGrants(userAccount, filteredCurrentGrants);
    }

    /**
     * See Interface for functional description.
     *
     * @param filter role grant filter
     * @return list of filtered role grants
     * @throws InvalidSearchQueryException e
     * @throws SystemException             e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveGrants(java.util.Map)
     */
    @Override
    public String retrieveGrants(final Map<String, String[]> filter)
            throws InvalidSearchQueryException, SystemException {

        final SRURequestParameters parameters = new DbRequestParameters(filter);

        final String query = parameters.getQuery();
        final int limit = parameters.getMaximumRecords();
        final int offset = parameters.getStartRecord();
        final boolean explain = parameters.isExplain();

        final String result;
        if (explain) {
            final Map<String, Object> values = new HashMap<String, Object>();

            values.put("PROPERTY_NAMES", new RoleGrantFilter(null).getPropertyNames());
            result = ExplainXmlProvider.getInstance().getExplainRoleGrantXml(values);
        } else if (limit == 0) {
            result = renderer.renderGrants(new ArrayList<RoleGrant>(0), Integer.toString(0),
                    Integer.toString(offset), Integer.toString(limit), parameters.getRecordPacking());
        } else {
            final int needed = offset + limit;
            final List<RoleGrant> permittedRoleGrants = new ArrayList<RoleGrant>();

            final List<RoleGrant> tmpRoleGrants = dao.retrieveGrants(query, 0, 0, this.userGroupHandler);
            if (tmpRoleGrants != null && !tmpRoleGrants.isEmpty()) {
                final List<String> userIds = new ArrayList<String>();
                final List<String> groupIds = new ArrayList<String>();
                for (final RoleGrant roleGrant : tmpRoleGrants) {
                    if (roleGrant.getUserId() != null && !userIds.contains(roleGrant.getUserId())) {
                        userIds.add(roleGrant.getUserId());
                    } else if (roleGrant.getGroupId() != null && !groupIds.contains(roleGrant.getGroupId())) {
                        groupIds.add(roleGrant.getGroupId());
                    }
                }

                try {
                    List<String> tmpUsersPermitted = new ArrayList<String>();
                    List<String> tmpGroupsPermitted = new ArrayList<String>();
                    if (!userIds.isEmpty()) {
                        tmpUsersPermitted = pdp.evaluateRetrieve("user-account", userIds);
                    }
                    if (!groupIds.isEmpty()) {
                        tmpGroupsPermitted = pdp.evaluateRetrieve("user-group", groupIds);
                    }
                    if (!tmpUsersPermitted.isEmpty() || !tmpGroupsPermitted.isEmpty()) {
                        for (final RoleGrant roleGrant : tmpRoleGrants) {
                            if (roleGrant.getUserId() != null) {
                                if (tmpUsersPermitted.contains(roleGrant.getUserId())) {
                                    permittedRoleGrants.add(roleGrant);
                                }
                            } else if (roleGrant.getGroupId() != null
                                    && tmpGroupsPermitted.contains(roleGrant.getGroupId())) {
                                permittedRoleGrants.add(roleGrant);
                            }
                        }
                    }
                } catch (final MissingMethodParameterException e) {
                    throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
                } catch (final ResourceNotFoundException e) {
                    throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
                }
            }

            final List<RoleGrant> offsetRoleGrants;
            final int numberPermitted = permittedRoleGrants.size();
            if (offset >= 0 && offset < numberPermitted) {
                offsetRoleGrants = new ArrayList<RoleGrant>(limit);
                for (int i = offset; i < numberPermitted && i < needed; i++) {
                    offsetRoleGrants.add(permittedRoleGrants.get(i));
                }
            } else {
                offsetRoleGrants = new ArrayList<RoleGrant>(0);
            }
            result = renderer.renderGrants(offsetRoleGrants, Integer.toString(numberPermitted),
                    Integer.toString(offset), Integer.toString(limit), parameters.getRecordPacking());
        }
        return result;
    }

    /**
     * See Interface for functional description.
     *
     * @param userId  userId
     * @param grantId grantId
     * @return grant as xml
     * @throws GrantNotFoundException       e
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveGrant(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String retrieveGrant(final String userId, final String grantId)
            throws GrantNotFoundException, UserAccountNotFoundException, SystemException {

        return renderer.renderGrant(retrieveGrantByIds(userId, grantId));
    }

    /**
     * See Interface for functional description.
     *
     * @param userId    userId
     * @param taskParam taskParam
     * @throws AlreadyActiveException         e
     * @throws UserAccountNotFoundException   e
     * @throws XmlCorruptedException          e
     * @throws MissingAttributeValueException e
     * @throws OptimisticLockingException     e
     * @throws SystemException                e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #activate(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public void activate(final String userId, final String taskParam)
            throws AlreadyActiveException, UserAccountNotFoundException, XmlCorruptedException,
            MissingAttributeValueException, OptimisticLockingException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);

        // TODO: validation missing, check if needed or if it shall be skipped

        final StaxParser sp = new StaxParser(XmlUtility.NAME_PARAM);

        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);

        try {
            sp.parse(XmlUtility.convertToByteArrayInputStream(taskParam));
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (LastModificationDateMissingException e) {
            throw new XmlCorruptedException(e);
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".activate: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        // check active flag and change value
        if (userAccount.getActive()) {
            throw new AlreadyActiveException();
        }
        userAccount.setActive(Boolean.TRUE);

        setModificationValues(userAccount);

        dao.update(userAccount);

        sendUserAccountUpdateEvent(userId);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId    userId
     * @param taskParam taskParam
     * @throws AlreadyDeactiveException       e
     * @throws UserAccountNotFoundException   e
     * @throws XmlCorruptedException          e
     * @throws MissingAttributeValueException e
     * @throws OptimisticLockingException     e
     * @throws SystemException                e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #deactivate(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public void deactivate(final String userId, final String taskParam)
            throws AlreadyDeactiveException, UserAccountNotFoundException, XmlCorruptedException,
            MissingAttributeValueException, OptimisticLockingException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);

        // TODO: validation missing, check if needed or if it shall be skipped

        final StaxParser sp = new StaxParser(XmlUtility.NAME_PARAM);

        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);

        try {
            sp.parse(XmlUtility.convertToByteArrayInputStream(taskParam));
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (LastModificationDateMissingException e) {
            throw new XmlCorruptedException(e);
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".deactivate: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        // check ative flag and change value
        if (!userAccount.getActive()) {
            throw new AlreadyDeactiveException();
        }
        userAccount.setActive(Boolean.FALSE);

        setModificationValues(userAccount);

        dao.update(userAccount);

        sendUserAccountUpdateEvent(userId);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId   userId
     * @param grantXML grantXml
     * @return created grant as xml
     * @throws AlreadyExistsException       e
     * @throws UserAccountNotFoundException e
     * @throws InvalidScopeException        e
     * @throws RoleNotFoundException        e
     * @throws XmlCorruptedException        e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #createGrant(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String createGrant(final String userId, final String grantXML)
            throws AlreadyExistsException, UserAccountNotFoundException, InvalidScopeException,
            RoleNotFoundException, XmlCorruptedException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final RoleGrant grant = new RoleGrant();
        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(grantXML);

        final StaxParser sp = new StaxParser(XmlUtility.NAME_GRANT);

        final LinkStaxHandler roleLinkHandler = new LinkStaxHandler(XPATH_GRANT_ROLE, XmlUtility.BASE_ROLE,
                RoleNotFoundException.class);
        sp.addHandler(roleLinkHandler);
        final LinkStaxHandler objectLinkHandler = new LinkStaxHandler(XPATH_GRANT_ASSIGNED_ON);
        sp.addHandler(objectLinkHandler);

        final GrantStaxHandler grantHandler = new GrantStaxHandler(grant);
        sp.addHandler(grantHandler);

        try {
            sp.parse(in);
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (final RoleNotFoundException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".createGrant: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        final Date creationDate = new Date();
        grant.setCreationDate(creationDate);
        grant.setUserAccountByCreatorId(getAuthenticatedUser(this.dao));

        final String roleId = roleLinkHandler.getObjid();
        final EscidocRole role = roleDao.retrieveRole(roleId);
        if (role == null) {
            throw new RoleNotFoundException(StringUtility.format("Role with provided id not found", roleId));
        }
        grant.setEscidocRole(role);

        final String objectId = objectLinkHandler.getObjid();

        // check if referenced object exists and determine its object type
        // it is assumed, that each resource stored in fedora has an object
        // type stored in the triple store.

        // FIXME: inject Triplestoreutility

        if (objectId != null) {
            final Map<String, String> objectAttributes;
            try {
                objectAttributes = objectAttributeResolver.resolveObjectAttributes(objectId);
            } catch (final Exception e) {
                throw new SystemException(e);
            }

            if (objectAttributes == null) {
                throw new XmlCorruptedException(StringUtility.format(MSG_GRANT_RESTRICTION_VIOLATED, objectId));
            }
            final String objectType = objectAttributes.get(ObjectAttributeResolver.ATTR_OBJECT_TYPE);
            String objectTitle = objectAttributes.get(ObjectAttributeResolver.ATTR_OBJECT_TITLE);

            // check if objectType may be scope
            boolean checkOk = false;
            if (role.getScopeDefs() != null && objectType != null) {
                for (final ScopeDef scopeDef : role.getScopeDefs()) {
                    if (scopeDef.getAttributeObjectType() != null
                            && scopeDef.getAttributeObjectType().equals(objectType)) {
                        checkOk = true;
                        break;
                    }
                }
            }
            if (!checkOk) {
                throw new InvalidScopeException("objectId " + objectId + " has objectType " + objectType
                        + " and may not be scope for role " + role.getRoleName());
            }

            // see issue 358. The title of an object stored in fedora may
            // not be explicitly stored in the triple store.
            // Therefore, a default title will be set, if it is null, here.
            if (objectTitle == null) {
                objectTitle = StringUtility.convertToUpperCaseLetterFormat(objectType) + " " + objectId;
            } else if (objectTitle.length() > MAX_FIELD_LENGTH) {
                objectTitle = objectTitle.substring(0, MAX_FIELD_LENGTH);
            }

            // get the href of the object.
            final String objectHref = tripleStoreUtility.getHref(objectType, objectId);

            // In case of REST it has to be checked if the provided href points
            // to the correct href.
            if (objectLinkHandler.getHref() != null && !objectLinkHandler.getHref().equals(objectHref)) {
                // FIXME: exception should be a resource not found exception
                // but this changes the interface. To prevent problems on
                // application side, currently an XmlCorruptedException is
                // thrown.
                throw new XmlCorruptedException(
                        StringUtility.format(MSG_WRONG_HREF, objectLinkHandler.getHref(), objectType));
            }

            // check if grant already exists
            if (dao.retrieveCurrentGrant(userAccount, role, objectId) != null) {
                throw new AlreadyExistsException(
                        StringUtility.format("Grant already exists", userId, role.getId(), objectId));
            }

            // set object values in grant
            grant.setObjectId(objectId);
            grant.setObjectTitle(objectTitle);
            grant.setObjectHref(objectHref);
        }

        grant.setUserAccountByUserId(userAccount);

        dao.save(grant);

        sendUserAccountUpdateEvent(userId);

        return renderer.renderGrant(grant);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId    userId
     * @param grantId   grantId
     * @param taskParam taskParam
     * @throws UserAccountNotFoundException   e
     * @throws GrantNotFoundException         e
     * @throws AlreadyRevokedException        e
     * @throws XmlCorruptedException          e
     * @throws MissingAttributeValueException e
     * @throws SystemException                e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #revokeGrant(java.lang.String,
     *      java.lang.String, java.lang.String)
     */
    @Override
    public void revokeGrant(final String userId, final String grantId, final String taskParam)
            throws UserAccountNotFoundException, GrantNotFoundException, AlreadyRevokedException,
            XmlCorruptedException, MissingAttributeValueException, SystemException {

        final RoleGrant grant = retrieveGrantByIds(userId, grantId);

        // TODO: validation missing, check if needed or if it shall be skipped

        final StaxParser sp = new StaxParser(XmlUtility.NAME_PARAM);

        final GrantStaxHandler grantHandler = new GrantStaxHandler(grant);
        sp.addHandler(grantHandler);

        final RevokeStaxHandler revokeStaxHandler = new RevokeStaxHandler(grant, this.dao);
        sp.addHandler(revokeStaxHandler);

        try {
            sp.parse(XmlUtility.convertToByteArrayInputStream(taskParam));
        } catch (final AlreadyRevokedException e) {
            throw e;
        } catch (final InvalidXmlException e) {
            throw new XmlCorruptedException(e);
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final SystemException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".parse: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        dao.update(grant);

        sendUserAccountUpdateEvent(userId);

    }

    /**
     * See Interface for functional description.
     *
     * @param userId    userId
     * @param taskParam taskParam
     * @throws UserAccountNotFoundException   e
     * @throws GrantNotFoundException         e
     * @throws AlreadyRevokedException        e
     * @throws XmlCorruptedException          e
     * @throws MissingAttributeValueException e
     * @throws SystemException                e
     * @throws AuthorizationException         e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #revokeGrants(java.lang.String,
     *      java.lang.String)
     */
    @Override
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "WMI_WRONG_MAP_ITERATOR")
    public void revokeGrants(final String userId, final String taskParam)
            throws UserAccountNotFoundException, GrantNotFoundException, AlreadyRevokedException,
            XmlCorruptedException, MissingAttributeValueException, SystemException, AuthorizationException {

        // check if user exists
        retrieveUserAccountById(userId);

        // get all current grants of user
        final List<RoleGrant> grants = fetchCurrentGrants(userId);
        // build HashMap with grantId
        final HashMap<String, RoleGrant> grantsHash = new HashMap<String, RoleGrant>();
        for (final RoleGrant grant : grants) {
            grantsHash.put(grant.getId(), grant);
        }

        // Parse taskParam
        final de.escidoc.core.common.util.stax.StaxParser fp = new de.escidoc.core.common.util.stax.StaxParser();

        final TaskParamHandler tph = new TaskParamHandler(fp);
        tph.setCheckLastModificationDate(false);
        fp.addHandler(tph);
        final ExtendedFilterHandler fh = new ExtendedFilterHandler(fp);
        fp.addHandler(fh);
        try {
            fp.parse(new ByteArrayInputStream(taskParam.getBytes(XmlUtility.CHARACTER_ENCODING)));
        } catch (final InvalidContentException e) {
            throw new XmlCorruptedException(e);
        } catch (final Exception e) {
            XmlUtility.handleUnexpectedStaxParserException("", e);
        }

        final Map<String, Set<String>> filters = fh.getRules();

        final Collection<String> grantIds;
        if (filters.isEmpty()) {
            // if no filters are provided, remove all current grants
            grantIds = new HashSet<String>();
            for (final String grantId : grantsHash.keySet()) {
                grantIds.add(grantId);
            }
        } else {
            // get ids of grants to revoke
            grantIds = (Collection<String>) filters.get(Constants.DC_IDENTIFIER_URI);
        }

        if (grantIds == null || grantIds.isEmpty()) {
            return;
        }

        // check if all grants that shall get revoked are currentGrants
        for (final String grantId : grantIds) {
            if (!grantsHash.containsKey(grantId)) {
                throw new GrantNotFoundException(
                        "Grant with id " + grantId + " is no current grant of user " + userId);
            }
        }

        // AA-filter grants to revoke
        final List<Object[]> argumentList = new ArrayList<Object[]>();
        for (final String grantId : grantIds) {
            final Object[] args = { userId, grantId };
            argumentList.add(args);
        }
        try {
            final List<Object[]> returnList = pdp.evaluateMethodForList("user-account", "revokeGrant",
                    argumentList);
            if (returnList.size() < grantIds.size()) {
                // user is not allowed to revoke at least one of the grants
                // so throw AuthorizationException
                throw new AuthorizationException(
                        "You are not allowed to revoke at least " + "one of the specified grants");
            }
        } catch (final MissingMethodParameterException e) {
            throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
        } catch (final ResourceNotFoundException e) {
            throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
        }

        final UserAccount authenticateUser = getAuthenticatedUser(this.dao);
        try {
            for (final String grantId : grantIds) {
                final RoleGrant roleGrant = grantsHash.get(grantId);
                // set revoke-date, -user and -remark
                roleGrant.setUserAccountByRevokerId(authenticateUser);
                roleGrant.setRevocationDate(new Date());
                roleGrant.setRevocationRemark(tph.getRevokationRemark());
                // update grant
                dao.update(roleGrant);
            }

        } catch (final Exception e) {
            throw new SqlDatabaseSystemException(e);
        }

        sendUserAccountUpdateEvent(userId);

    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return List of userHandles
     * @throws UserAccountNotFoundException e
     * @see UserAccountHandlerInterface #retrieveUserHandles(java.lang.String)
     */
    @Override
    public List<UserLoginData> retrieveUserHandles(final String userId)
            throws UserAccountNotFoundException, SqlDatabaseSystemException {

        final List<UserLoginData> ret = dao.retrieveUserLoginDataByUserId(userId);
        if (ret == null || ret.isEmpty()) {
            assertUserAccount(userId, dao.retrieveUserAccountById(userId));
        }
        return ret;
    }

    /**
     * See Interface for functional description.
     *
     * @param filter userAccountFilter
     * @return list of filtered user-accounts
     * @throws InvalidSearchQueryException e
     * @throws SystemException             e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveUserAccounts(java.util.Map)
     */
    @Override
    public String retrieveUserAccounts(final Map<String, String[]> filter)
            throws InvalidSearchQueryException, SystemException {

        Map<String, String[]> castedFilter = filter;

        // check if filter for groupId is provided
        // if yes, get users for group and add ids to filter
        // then remove groupId from filter
        castedFilter = fixCqlGroupFilter(castedFilter);

        final SRURequestParameters parameters = new DbRequestParameters(castedFilter);

        final String query = parameters.getQuery();
        final int limit = parameters.getMaximumRecords();
        final int offset = parameters.getStartRecord();
        final boolean explain = parameters.isExplain();

        final String result;
        if (explain) {
            final Map<String, Object> values = new HashMap<String, Object>();

            values.put("PROPERTY_NAMES", new UserAccountFilter(null).getPropertyNames());
            result = ExplainXmlProvider.getInstance().getExplainUserAccountXml(values);
        } else if (limit == 0) {
            result = renderer.renderUserAccounts(new ArrayList<UserAccount>(0), parameters.getRecordPacking());
        } else {
            final int currentLimit = offset + limit;
            int currentOffset = 0;
            final List<UserAccount> permittedUserAccounts = new ArrayList<UserAccount>();
            final int size = permittedUserAccounts.size();
            while (size <= currentLimit) {

                final List<UserAccount> tmpUserAccounts = dao.retrieveUserAccounts(query, currentOffset,
                        currentLimit);
                if (tmpUserAccounts == null || tmpUserAccounts.isEmpty()) {
                    break;
                }
                Iterator<UserAccount> userAccountIter = tmpUserAccounts.iterator();
                final List<String> ids = new ArrayList<String>(tmpUserAccounts.size());
                while (userAccountIter.hasNext()) {
                    final UserAccount userAccount = userAccountIter.next();
                    ids.add(userAccount.getId());
                }

                try {
                    final List<String> tmpPermitted = pdp.evaluateRetrieve("user-account", ids);
                    final int numberPermitted = tmpPermitted.size();
                    if (numberPermitted == 0) {
                        break;
                    } else {
                        int permittedIndex = 0;
                        String currentPermittedId = tmpPermitted.get(permittedIndex);
                        userAccountIter = tmpUserAccounts.iterator();
                        while (userAccountIter.hasNext()) {
                            final UserAccount userAccount = userAccountIter.next();
                            if (currentPermittedId.equals(userAccount.getId())) {
                                permittedUserAccounts.add(userAccount);
                                ++permittedIndex;
                                if (permittedIndex < numberPermitted) {
                                    currentPermittedId = tmpPermitted.get(permittedIndex);
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                } catch (final MissingMethodParameterException e) {
                    throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
                } catch (final ResourceNotFoundException e) {
                    throw new SystemException(MSG_UNEXPECTED_EXCEPTION_ACCESS_RIGHTS, e);
                }
                currentOffset += currentLimit;
            }

            final List<UserAccount> offsetUserAccounts;
            final int numberPermitted = permittedUserAccounts.size();
            if (offset < numberPermitted) {
                offsetUserAccounts = new ArrayList<UserAccount>(limit);
                for (int i = offset; i < numberPermitted && i < currentLimit; i++) {
                    offsetUserAccounts.add(permittedUserAccounts.get(i));
                }
            } else {
                offsetUserAccounts = new ArrayList<UserAccount>(0);
            }
            result = renderer.renderUserAccounts(offsetUserAccounts, parameters.getRecordPacking());
        }
        return result;
    }

    /**
     * replaces group-id-filter with resolved userIds.
     *
     * @param filter cql-filter
     * @return Map with replaced cql-query (groupId replaced with userIds)
     * @throws InvalidSearchQueryException e
     * @throws SystemException             e
     */
    private Map<String, String[]> fixCqlGroupFilter(final Map<String, String[]> filter)
            throws InvalidSearchQueryException, SystemException {

        Map<String, String[]> returnFilter = filter;
        final Object[] queryPartsObject = filter.get(Constants.SRU_PARAMETER_QUERY);
        if (queryPartsObject != null) {
            final String[] queryParts = new String[queryPartsObject.length];
            for (int i = 0; i < queryPartsObject.length; i++) {
                if (queryPartsObject[i] != null) {
                    queryParts[i] = queryPartsObject[i].toString();
                }
            }
            boolean groupFilterFound = false;
            for (int i = 0; i < queryParts.length; i++) {
                final Matcher matcher = GROUP_FILTER_PATTERN.matcher(queryParts[i]);
                if (matcher.find()) {
                    groupFilterFound = true;
                    final Matcher groupFilterMatcher = GROUP_FILTER_PATTERN.matcher(queryParts[i]);
                    final StringBuffer result = new StringBuffer("");
                    while (groupFilterMatcher.find()) {
                        if (groupFilterMatcher.group(6).matches(".*?%.*")) {
                            throw new InvalidSearchQueryException("Wildcards not allowed in group-filter");
                        }
                        if (groupFilterMatcher.group(3) != null
                                && groupFilterMatcher.group(3).matches(">|<|<=|>=|<>")
                                || groupFilterMatcher.group(4) != null || groupFilterMatcher.group(5) != null) {
                            throw new InvalidSearchQueryException("non-supported relation in group-filter");
                        }
                        // get users for group
                        final StringBuilder replacement = new StringBuilder(" (");
                        try {
                            final Set<String> userIds = retrieveUsersForGroup(groupFilterMatcher.group(6));
                            // write user-cql-query
                            // and replace group-expression with it.
                            if (userIds != null && !userIds.isEmpty()) {
                                for (final String userId : userIds) {
                                    if (replacement.length() > 2) {
                                        replacement.append(" or ");
                                    }
                                    replacement.append('\"');
                                    replacement.append(Constants.FILTER_PATH_ID);
                                    replacement.append("\"=").append(userId).append(' ');
                                }
                            } else {
                                throw new UserGroupNotFoundException("");
                            }
                        } catch (final UserGroupNotFoundException e) {
                            if (LOGGER.isWarnEnabled()) {
                                LOGGER.warn("Error on getting users for group.");
                            }
                            if (LOGGER.isDebugEnabled()) {
                                LOGGER.debug("Error on getting users for group.", e);
                            }
                            // if group has no users or group not found,
                            // write nonexisting user in query
                            replacement.append('\"');
                            replacement.append(Constants.FILTER_PATH_ID);
                            replacement.append("\"=").append("nonexistinguser").append(' ');
                        }

                        replacement.append(") ");
                        groupFilterMatcher.appendReplacement(result, replacement.toString());
                    }
                    groupFilterMatcher.appendTail(result);
                    queryParts[i] = result.toString();
                }
            }
            if (groupFilterFound) {
                final Map<String, String[]> filter1 = new HashMap<String, String[]>();
                for (final Entry<String, String[]> entry : filter.entrySet()) {
                    if (entry.getValue() != null) {
                        // noinspection RedundantCast
                        filter1.put(entry.getKey(), new String[((Object[]) entry.getValue()).length]);
                        // noinspection RedundantCast
                        for (int j = 0; j < ((Object[]) entry.getValue()).length; j++) {
                            filter1.get(entry.getKey())[j] = entry.getValue()[j];
                        }
                    } else {
                        filter1.put(entry.getKey(), null);
                    }
                }
                filter1.put(Constants.SRU_PARAMETER_QUERY, queryParts);
                returnFilter = filter1;
            }
        }
        return returnFilter;
    }

    /**
     * Retrieve all users that belong to the given group. Groups are gone through hierarchically.
     *
     * @param groupId id of group
     * @return set of userIds (hierarchy)
     * @throws UserGroupNotFoundException e
     * @throws SystemException            e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveUsersForGroup(java.lang.String)
     */
    private Set<String> retrieveUsersForGroup(final String groupId)
            throws UserGroupNotFoundException, SystemException {
        // may not return null but empty list!!
        final Set<String> userIds = new HashSet<String>();

        // Try getting the userGroup
        final UserGroup userGroup = userGroupDao.retrieveUserGroup(groupId);
        if (userGroup == null) {
            throw new UserGroupNotFoundException(StringUtility.format(MSG_GROUP_NOT_FOUND_BY_ID, groupId));
        }

        final Set<UserGroupMember> members = userGroup.getMembers();

        // Get users that are integrated via their userId
        for (final UserGroupMember member : members) {
            if (member.getType().equals(Constants.TYPE_USER_GROUP_MEMBER_INTERNAL)
                    && member.getName().equals(Constants.NAME_USER_GROUP_MEMBER_USER_ACCOUNT)) {
                userIds.add(member.getValue());
            }
        }

        // Get users that are integrated via their user-attributes
        final String ouAttributeName = EscidocConfiguration.getInstance()
                .get(EscidocConfiguration.ESCIDOC_CORE_AA_OU_ATTRIBUTE_NAME);

        final Set<HashMap<String, String>> attributesSet = new HashSet<HashMap<String, String>>();
        for (final UserGroupMember member : members) {
            if (member.getType().equals(Constants.TYPE_USER_GROUP_MEMBER_USER_ATTRIBUTE)) {
                final HashMap<String, String> attributeHash = new HashMap<String, String>();
                attributeHash.put(member.getName(), member.getValue());
                attributesSet.add(attributeHash);
                // check if attribute-name is ou-attribute
                // if yes, resolve children-path-list
                if (ouAttributeName != null && ouAttributeName.length() != 0
                        && member.getName().equals(ouAttributeName)) {
                    final List<String> initialList = new ArrayList<String>();
                    initialList.add(member.getValue());
                    final List<String> pathList = getOrgUnitChildrenPathList(member.getValue(), initialList);
                    for (final String ouId : pathList) {
                        final HashMap<String, String> ouAttributeHash = new HashMap<String, String>();
                        ouAttributeHash.put(ouAttributeName, ouId);
                        attributesSet.add(ouAttributeHash);
                    }
                }
            }
        }
        if (!attributesSet.isEmpty()) {
            final List<UserAttribute> userAttributes = dao.retrieveAttributes(attributesSet);
            for (final UserAttribute userAttribute : userAttributes) {
                userIds.add(userAttribute.getUserAccountByUserId().getId());
            }
        }

        // Get users that are integrated via other groups
        for (final UserGroupMember member : members) {
            if (member.getType().equals(Constants.TYPE_USER_GROUP_MEMBER_INTERNAL)
                    && member.getName().equals(Constants.NAME_USER_GROUP_MEMBER_USER_GROUP)) {
                userIds.addAll(retrieveUsersForGroup(member.getValue()));
            }
        }

        return userIds;
    }

    /**
     * Compute the child paths of the actual organizational unit.
     *
     * @param orgUnitId the orgUnitId where pathList has to get retrieved.
     * @param totalList total list of all Children.
     * @return List of child-orgUnits
     * @throws SystemException If anything fails while computing the paths.
     */
    private List<String> getOrgUnitChildrenPathList(final String orgUnitId, final List<String> totalList)
            throws SystemException {

        List<String> addableList = totalList;
        final List<String> orgUnitIds = tripleStoreUtility.getChildren(orgUnitId);
        if (orgUnitIds != null && !orgUnitIds.isEmpty()) {
            addableList.addAll(orgUnitIds);
            for (final String childOrgUnitId : orgUnitIds) {
                addableList = getOrgUnitChildrenPathList(childOrgUnitId, addableList);
            }
        }
        return addableList;
    }

    /**
     * See Interface for functional description.
     *
     * @param handle the handle
     * @return UserDetails object
     * @throws MissingMethodParameterException
     *                                      e
     * @throws AuthenticationException      e
     * @throws AuthorizationException       e
     * @throws UserAccountNotFoundException e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveUserDetails(java.lang.String)
     */
    @Override
    public UserDetails retrieveUserDetails(final String handle)
            throws MissingMethodParameterException, AuthenticationException, AuthorizationException,
            UserAccountNotFoundException, SqlDatabaseSystemException {
        final UserDetails ret = dao.retrieveUserDetails(handle);
        // FIXME: use this as the authentication service?
        // In this case, additional values have to be set in the user details
        // and in case of user not found an authentication exception has to be
        // thrown.?

        if (ret == null) {
            throw new UserAccountNotFoundException(
                    StringUtility.format("User not authenticated by provided handle", handle));
        }
        return ret;
    }

    /**
     * Gets the grant for the provided user id and grant id.
     *
     * @param userId  The id of the user account.
     * @param grantId The id of the grant.
     * @return Returns the fetched grant object.
     * @throws SqlDatabaseSystemException   Thrown in case of an internal database error.
     * @throws UserAccountNotFoundException Thrown if the user account does not exists.
     * @throws GrantNotFoundException       Thrown if the grant does not exists for the user.
     */
    private RoleGrant retrieveGrantByIds(final String userId, final String grantId)
            throws SqlDatabaseSystemException, UserAccountNotFoundException, GrantNotFoundException {

        final RoleGrant grant = dao.retrieveGrant(userId, grantId);
        if (grant == null) {
            if (dao.retrieveUserAccountById(userId) == null) {
                throw new UserAccountNotFoundException();
            }
            throw new GrantNotFoundException(StringUtility.format("Grant not found", userId, grantId));
        }
        return grant;
    }

    /**
     * Gets the attribute for the provided user id and attribute id.
     *
     * @param userId      The id of the user account.
     * @param attributeId The id of the attribute.
     * @param forReadOnly if this attribute is used for read only.
     * @return Returns the fetched attribute object.
     * @throws SqlDatabaseSystemException     Thrown in case of an internal database error.
     * @throws UserAccountNotFoundException   Thrown if the user account does not exists.
     * @throws UserAttributeNotFoundException Thrown if the attribute does not exists for the user.
     * @throws ReadonlyElementViolationException
     *                                        Thrown if the attribute is external and may not get changed.
     */
    private UserAttribute retrieveAttributeById(final String userId, final String attributeId,
            final boolean forReadOnly) throws SqlDatabaseSystemException, UserAccountNotFoundException,
            UserAttributeNotFoundException, ReadonlyElementViolationException {

        final UserAttribute attribute = dao.retrieveAttribute(userId, attributeId);
        if (attribute == null) {
            if (dao.retrieveUserAccountById(userId) == null) {
                throw new UserAccountNotFoundException();
            }
            throw new UserAttributeNotFoundException(
                    StringUtility.format("Attribute not found", userId, attributeId));
        }
        if (!(forReadOnly || attribute.getInternal())) {
            throw new ReadonlyElementViolationException("Attribute is external and may not get changed");

        }
        return attribute;
    }

    /**
     * Sends userAccountUpdateEvent to AA.
     *
     * @param userId The id of the updated user account.
     * @throws UserAccountNotFoundException Thrown if an user account with the provided id cannot be found.
     * @throws SqlDatabaseSystemException   In case of a database error.
     * @throws WebserverSystemException     Thrown in case of an internal error.
     */
    private void sendUserAccountUpdateEvent(final String userId) {

        securityHelper.clearUserPoliciesCaches(userId);
        securityHelper.clearUserGroups(userId);
    }

    /**
     * Sends userAttributeUpdateEvent to AA.
     *
     * @param userId The id of the updated user account.
     * @throws UserAccountNotFoundException Thrown if an user account with the provided id cannot be found.
     * @throws SqlDatabaseSystemException   In case of a database error.
     * @throws WebserverSystemException     Thrown in case of an internal error.
     */
    private void sendUserAttributeUpdateEvent(final String userId)
            throws SqlDatabaseSystemException, UserAccountNotFoundException {

        securityHelper.clearUserGroups(userId);
        retrieveUserAccountById(userId).touch();
    }

    /**
     * Retrieves the user account data identified by the provided user id and asserts that an user has been found.
     * Before the user account is retrieved, it is asserted that an user id has been provided.
     *
     * @param userId The id of the user to retrieve the data for.
     * @return Returns the identified {@code UserAccount} object.
     * @throws UserAccountNotFoundException Thrown if an user account with the provided id cannot be found.
     * @throws SqlDatabaseSystemException   In case of a database error.
     */
    private UserAccount retrieveUserAccountById(final String userId)
            throws UserAccountNotFoundException, SqlDatabaseSystemException {

        final UserAccount user = dao.retrieveUserAccountById(userId);
        if (user == null) {
            throw new UserAccountNotFoundException(StringUtility.format(MSG_USER_NOT_FOUND_BY_ID, userId));
        }
        return user;
    }

    /**
     * Asserts that the user account is provided, i.e. it is not {@code null}.
     *
     * @param userId The user id for which the account should be provided (should exist).
     * @param user   The user account to assert.
     * @throws UserAccountNotFoundException Thrown if assertion fails.
     */
    private static void assertUserAccount(final String userId, final UserAccount user)
            throws UserAccountNotFoundException {

        if (user == null) {
            throw new UserAccountNotFoundException(
                    StringUtility.format(MSG_USER_NOT_FOUND_BY_IDENTITY_INFO, userId));
        }
    }

    /**
     * Fetches the current grants of the user account identified by the provided id.
     *
     * @param userId The id of the user account.
     * @return Returns a {@code List} containing the grants of the user account that are currently valid. If the
     *         user does not have a grant, an empty {@code List} is returned.
     * @throws SqlDatabaseSystemException Thrown in case of an internal database error.
     */
    private List<RoleGrant> fetchCurrentGrants(final String userId) throws SqlDatabaseSystemException {

        final List<RoleGrant> grants = dao.retrieveGrantsByUserId(userId);
        final List<RoleGrant> currentGrants = new ArrayList<RoleGrant>(grants.size());
        if (!grants.isEmpty()) {
            for (final RoleGrant grant : grants) {
                if (grant.getRevocationDate() == null) {
                    currentGrants.add(grant);
                }
            }
        }
        return currentGrants;
    }

    /**
     * Sets the creation date and the created-by user in the provided {@code UserAccount} object.<br/> The values
     * are set with the values of modification date and modifying user of the provided user account.<br/> Before calling
     * this method, the last modification date and the modifying user must be set.
     *
     * @param userAccount The {@code UserAccount} object to modify.
     * @throws SystemException Thrown in case of an internal error.
     */
    private static void setCreationValues(final UserAccount userAccount) {

        // initialize creation-date value
        userAccount.setCreationDate(userAccount.getLastModificationDate());

        // initialize created-by values
        userAccount.setUserAccountByCreatorId(userAccount.getUserAccountByModifiedById());
    }

    /**
     * Sets the last modification date and the modified-by user in the provided {@code UserAccount} object.<br/>
     * The last modification date is set to the current time, and the modified by user to the user account of the
     * current, authenticated user.
     *
     * @param userAccount The {@code UserAccount} object to modify.
     * @throws de.escidoc.core.common.exceptions.system.WebserverSystemException
     * @throws de.escidoc.core.common.exceptions.system.SqlDatabaseSystemException
     */
    private void setModificationValues(final UserAccount userAccount)
            throws SqlDatabaseSystemException, WebserverSystemException {

        userAccount.setLastModificationDate(new Date());
        userAccount.setUserAccountByModifiedById(getAuthenticatedUser(this.dao));
    }

    /**
     * Gets the user account of the authenticated user.<br> The authenticated user is retrieved from the
     * {@code UserContext}.
     *
     * @param dao The data access object to use.
     * @return Returns the fetched user account.
     * @throws SqlDatabaseSystemException Thrown in case of a database error.
     * @throws WebserverSystemException   Thrown if the account of the authenticated user cannot be found.
     */
    public static UserAccount getAuthenticatedUser(final UserAccountDaoInterface dao)
            throws SqlDatabaseSystemException, WebserverSystemException {

        final UserAccount userAccount = dao.retrieveUserAccountById(UserContext.getId());
        if (userAccount == null) {
            throw new WebserverSystemException(
                    StringUtility.format("Account of authenticated user not found", UserContext.getId()));
        }
        return userAccount;
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @return list of preferences as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrievePreferences(java.lang.String)
     */
    @Override
    public String retrievePreferences(final String userId) throws UserAccountNotFoundException, SystemException {
        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserPreference> currentPreferences = userAccount.getUserPreferencesByUserId();
        return renderer.renderPreferences(userAccount, currentPreferences);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId userId
     * @param name   name of the preference to retrieve
     * @return preference as xml
     * @throws UserAccountNotFoundException e
     * @throws PreferenceNotFoundException  e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrievePreference(java.lang.String)
     */
    @Override
    public String retrievePreference(final String userId, final String name)
            throws UserAccountNotFoundException, PreferenceNotFoundException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserPreference> currentPreferences = userAccount.getUserPreferencesByUserId();

        String result = null;
        for (final UserPreference preference : currentPreferences) {
            final String preferenceName = preference.getName();
            if (preferenceName.equals(name)) {
                result = renderer.renderPreference(userAccount, preference);
            }
        }
        if (result == null) {
            throw new PreferenceNotFoundException("Preference with name " + name + " not found");
        }

        return result;
    }

    /**
     * See Interface for functional description.
     *
     * @param userId        userId
     * @param preferenceXML preference as xml
     * @return created preference as xml
     * @throws AlreadyExistsException       e
     * @throws UserAccountNotFoundException e
     * @throws PreferenceNotFoundException  e
     * @throws XmlCorruptedException        e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #createPreference(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String createPreference(final String userId, final String preferenceXML) throws AlreadyExistsException,
            UserAccountNotFoundException, PreferenceNotFoundException, XmlCorruptedException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(preferenceXML);
        final StaxParser sp = new StaxParser(Elements.ELEMENT_USER_PREFERENCE);

        final UserPreferenceReadHandler uprh = new UserPreferenceReadHandler();
        sp.addHandler(uprh);

        try {
            sp.parse(in);
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".createPreference: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        final UserPreference preference = new UserPreference();
        final Map<String, String> preferences = uprh.getPreferences();
        final Set<String> preferenceNames = preferences.keySet();
        // there is only one entry
        // TODO ensure by xml schema that is true
        if (preferenceNames.size() > 1) {
            throw new XmlCorruptedException("Only one preference allowed. " + MSG_XML_SCHEMA_ENSURE);
        }
        final Iterator<String> it = preferenceNames.iterator();
        final String preferenceName = it.next();
        final String preferenceValue = preferences.get(preferenceName);
        preference.setUserAccountByUserId(userAccount);
        preference.setName(preferenceName);
        preference.setValue(preferenceValue);

        final Set<UserPreference> userPreferences = userAccount.getUserPreferencesByUserId();
        // TODO check for same preference already set by getting preference by
        // PrimKey(userId,name)
        for (final UserPreference userPreference : userPreferences) {
            if (preferenceName.equals(userPreference.getName())) {
                throw new AlreadyExistsException(
                        "Preference " + preferenceName + " already exists for user " + userId);
            }
        }
        userPreferences.add(preference);

        // update user in policy cache; rights may depend on preferences
        sendUserAccountUpdateEvent(userId);

        userAccount.touch();

        return renderer.renderPreference(userAccount, preference);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId         userId
     * @param preferenceName name of preference to update
     * @param preferenceXML  preference as xml
     * @return updated preference as xml
     * @throws AlreadyExistsException         e
     * @throws UserAccountNotFoundException   e
     * @throws PreferenceNotFoundException    e
     * @throws XmlCorruptedException          e
     * @throws SystemException                e
     * @throws OptimisticLockingException     If the give last modification timestamp does not match the current one.
     * @throws MissingAttributeValueException If there is no last modificate date attribute.
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #updatePreference(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String updatePreference(final String userId, final String preferenceName, final String preferenceXML)
            throws AlreadyExistsException, UserAccountNotFoundException, PreferenceNotFoundException,
            XmlCorruptedException, SystemException, OptimisticLockingException, MissingAttributeValueException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserPreference> userPreferences = userAccount.getUserPreferencesByUserId();

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(preferenceXML);
        final StaxParser sp = new StaxParser(Elements.ELEMENT_USER_PREFERENCE);

        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);

        final UserPreferenceReadHandler uprh = new UserPreferenceReadHandler();
        sp.addHandler(uprh);

        try {
            sp.parse(in);
        } catch (LastModificationDateMissingException e) {
            throw new MissingAttributeValueException(e);
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".createPreference: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        final Map<String, String> preferences = uprh.getPreferences();
        final Set<String> preferenceNames = preferences.keySet();
        // there is only one entry
        // TODO ensure by xml schema that is true
        if (preferenceNames.size() > 1) {
            throw new XmlCorruptedException("Only one preference allowed. " + MSG_XML_SCHEMA_ENSURE);
        }
        final Iterator<String> it = preferenceNames.iterator();
        final String xmlPreferenceName = it.next();
        if (!xmlPreferenceName.equals(preferenceName)) {
            throw new XmlCorruptedException(
                    "Given preference name does not match " + "preference name inside the xml representation.");
        }

        // TODO check for existence of preference by getting preference by
        // PrimKey(userId,name)
        final Iterator<UserPreference> prefIt = userPreferences.iterator();
        UserPreference preference = null;
        while (prefIt.hasNext()) {
            final UserPreference curPref = prefIt.next();
            if (preferenceName.equals(curPref.getName())) {
                preference = curPref;
            }
        }
        if (preference == null) {
            // FIXME NotFoundException ?(FRS)
            throw new AlreadyExistsException("Preference " + preferenceName + " does not exist for user " + userId);
        }
        preference.setValue(preferences.get(preferenceName));
        // unnecessary: userPreferences.add(preference);

        // update user in policy cache; rights may depend on preferences
        sendUserAccountUpdateEvent(userId);

        userAccount.touch();

        return renderer.renderPreference(userAccount, preference);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId         userId
     * @param preferenceName name of preference to update
     * @throws UserAccountNotFoundException e
     * @throws PreferenceNotFoundException  e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #deletePreference(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public void deletePreference(final String userId, final String preferenceName)
            throws UserAccountNotFoundException, PreferenceNotFoundException, SqlDatabaseSystemException,
            WebserverSystemException {
        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserPreference> userPreferences = userAccount.getUserPreferencesByUserId();

        // PrimKey(userId,name)
        for (final UserPreference userPreference : userPreferences) {
            if (userPreference.getName().equals(preferenceName)) {
                userPreferences.remove(userPreference);
                // update user in policy cache; rights may depend on preferences
                sendUserAccountUpdateEvent(userId);
                userAccount.touch();
                return;
            }
        }
        throw new PreferenceNotFoundException(
                "The preference '" + preferenceName + "' does not exist for user '" + userId + "'.");
    }

    /**
     * See Interface for functional description.
     *
     * @param userId         The userId.
     * @param preferencesXML The xml.
     * @return updated XML
     * @throws UserAccountNotFoundException   If
     * @throws XmlCorruptedException          If
     * @throws SystemException                If
     * @throws OptimisticLockingException     If the give last modification timestamp does not match the current one.
     * @throws MissingAttributeValueException If there is no last modificate date attribute.
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #createPreference(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String updatePreferences(final String userId, final String preferencesXML)
            throws UserAccountNotFoundException, XmlCorruptedException, SystemException, OptimisticLockingException,
            MissingAttributeValueException {

        final UserAccount userAccount = retrieveUserAccountById(userId);

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(preferencesXML);
        final StaxParser sp = new StaxParser(Elements.ELEMENT_USER_PREFERENCES);

        final UserPreferenceReadHandler uprh = new UserPreferenceReadHandler();
        sp.addHandler(uprh);

        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);

        try {
            sp.parse(in);
        } catch (LastModificationDateMissingException e) {
            throw new MissingAttributeValueException(e);
        } catch (final MissingAttributeValueException e) {
            throw e;
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".updatePreference: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        // delete all existing preferences
        // FIXME name/value may be defined as primary key
        final Set<UserPreference> currentPreferences = userAccount.getUserPreferencesByUserId();
        // Iterator<UserPreference> curPrefsIterator =
        // currentPreferences.iterator();
        // while (curPrefsIterator.hasNext()) {
        // UserPreference preference = curPrefsIterator.next();
        // dao.delete(preference);
        // }
        currentPreferences.clear();

        // add all given preferences
        final Map<String, String> preferences = uprh.getPreferences();
        for (final Entry<String, String> e : preferences.entrySet()) {
            final UserPreference preference = new UserPreference();
            final String preferenceName = e.getKey();
            final String preferenceValue = e.getValue();
            preference.setUserAccountByUserId(userAccount);
            preference.setName(preferenceName);
            preference.setValue(preferenceValue);

            // dao.save(preference);
            // FIXME ? set does not prevent dublicate keys but dublicate objects
            // (FRS)
            currentPreferences.add(preference);
        }

        // update user in policy cache; rights may depend on preferences
        sendUserAccountUpdateEvent(userId);

        userAccount.touch();

        // TODO create XML via renderPreference
        return renderer.renderPreferences(userAccount, currentPreferences);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId       id of user
     * @param attributeXML xml with attribute to create
     * @return String xml with created attribute
     * @throws AlreadyExistsException       e
     * @throws UserAccountNotFoundException e
     * @throws XmlCorruptedException        e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #createAttribute(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String createAttribute(final String userId, final String attributeXML)
            throws AlreadyExistsException, UserAccountNotFoundException, XmlCorruptedException, SystemException {

        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(attributeXML);
        final StaxParser sp = new StaxParser(Elements.ELEMENT_USER_ATTRIBUTE);

        final UserAttributeReadHandler uarh = new UserAttributeReadHandler();
        sp.addHandler(uarh);

        try {
            sp.parse(in);
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".createAttribute: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        final UserAttribute attribute = new UserAttribute();
        final Map<String, String> attributes = uarh.getAttributes();
        final Set<String> attributeNames = attributes.keySet();
        // there is only one entry
        // TODO ensure by xml schema that is true
        if (attributeNames.size() > 1) {
            throw new XmlCorruptedException("Only one attribute allowed. " + MSG_XML_SCHEMA_ENSURE);
        }

        final UserAccount userAccount = retrieveUserAccountById(userId);

        final Iterator<String> it = attributeNames.iterator();
        final String attributeName = it.next();
        final String attributeValue = attributes.get(attributeName);
        attribute.setUserAccountByUserId(userAccount);
        attribute.setName(attributeName);
        attribute.setValue(attributeValue);
        attribute.setInternal(true);

        final Set<UserAttribute> userAttributes = userAccount.getUserAttributesByUserId();

        for (final UserAttribute userAttribute : userAttributes) {
            if (attributeName.equals(userAttribute.getName()) && attributeValue.equals(userAttribute.getValue())) {
                throw new AlreadyExistsException("Attribute " + attributeName + " with value " + attributeValue
                        + " already exists for user " + userId);
            }
        }
        dao.save(attribute);
        sendUserAttributeUpdateEvent(userId);
        return renderer.renderAttribute(attribute);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId id of user
     * @return String attributes as xml
     * @throws UserAccountNotFoundException e
     * @throws SystemException              e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveAttributes(java.lang.String)
     */
    @Override
    public String retrieveAttributes(final String userId) throws UserAccountNotFoundException, SystemException {
        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserAttribute> currentAttributes = userAccount.getUserAttributesByUserId();
        return renderer.renderAttributes(userAccount, currentAttributes);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId id of user
     * @param name   name of attribute
     * @return String xml with user-attributes
     * @throws UserAccountNotFoundException   e
     * @throws UserAttributeNotFoundException e
     * @throws SystemException                e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveAttribute(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String retrieveNamedAttributes(final String userId, final String name)
            throws UserAccountNotFoundException, UserAttributeNotFoundException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final Set<UserAttribute> currentAttributes = userAccount.getUserAttributesByUserId();

        final Set<UserAttribute> selectedAttributes = new HashSet<UserAttribute>();
        if (currentAttributes != null) {
            for (final UserAttribute attribute : currentAttributes) {
                final String attributeName = attribute.getName();
                if (attributeName.equals(name)) {
                    selectedAttributes.add(attribute);
                }
            }
        }
        return renderer.renderAttributes(userAccount, selectedAttributes);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId      id of user
     * @param attributeId id of attribute
     * @return String xml with user-attribute
     * @throws UserAccountNotFoundException   e
     * @throws UserAttributeNotFoundException e
     * @throws SystemException                e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #retrieveAttribute(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String retrieveAttribute(final String userId, final String attributeId)
            throws UserAccountNotFoundException, UserAttributeNotFoundException, SystemException {
        final UserAttribute attribute;
        try {
            attribute = retrieveAttributeById(userId, attributeId, true);
        } catch (final ReadonlyElementViolationException e) {
            throw new SystemException(e);
        }
        return renderer.renderAttribute(attribute);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId       id of user
     * @param attributeId  id of attribute
     * @param attributeXML xml with attribute
     * @return String xml with updated attribute
     * @throws UserAccountNotFoundException   e
     * @throws UserAttributeNotFoundException e
     * @throws ReadonlyElementViolationException
     *                                        e
     * @throws XmlCorruptedException          e
     * @throws SystemException                e
     * @throws OptimisticLockingException     If the give last modification timestamp does not match the current one.
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #updateAttribute(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public String updateAttribute(final String userId, final String attributeId, final String attributeXML)
            throws UserAccountNotFoundException, OptimisticLockingException, ReadonlyElementViolationException,
            UserAttributeNotFoundException, XmlCorruptedException, SystemException {

        final UserAccount userAccount = retrieveUserAccountById(userId);
        final UserAttribute userAttribute = retrieveAttributeById(userId, attributeId, false);
        final ByteArrayInputStream in = XmlUtility.convertToByteArrayInputStream(attributeXML);
        final StaxParser sp = new StaxParser(Elements.ELEMENT_USER_ATTRIBUTE);
        final UserAttributeReadHandler uarh = new UserAttributeReadHandler();
        final OptimisticLockingStaxHandler optimisticLockingHandler = new OptimisticLockingStaxHandler(
                userAccount.getLastModificationDate());
        sp.addHandler(optimisticLockingHandler);
        sp.addHandler(uarh);
        try {
            sp.parse(in);
        } catch (LastModificationDateMissingException e) {
            throw new XmlCorruptedException(e);
        } catch (final OptimisticLockingException e) {
            throw e;
        } catch (final Exception e) {
            final String msg = MSG_UNEXPECTED_EXCEPTION + getClass().getName() + ".updateAttribute: "
                    + e.getClass().getName();
            throw new SystemException(msg, e);
        }

        final Map<String, String> attributes = uarh.getAttributes();
        final Set<String> attributeNames = attributes.keySet();
        // there is only one entry
        // TODO ensure by xml schema that is true
        if (attributeNames.size() > 1) {
            throw new XmlCorruptedException("Only one attribute allowed. " + MSG_XML_SCHEMA_ENSURE);
        }
        final Iterator<String> it = attributeNames.iterator();
        final String xmlAttributeName = it.next();
        if (!xmlAttributeName.equals(userAttribute.getName())) {
            throw new XmlCorruptedException(
                    "Given attribute name does not match " + "attribute name inside the xml representation.");
        }
        userAttribute.setValue(attributes.get(xmlAttributeName));
        sendUserAttributeUpdateEvent(userId);
        return renderer.renderAttribute(userAttribute);
    }

    /**
     * See Interface for functional description.
     *
     * @param userId      The userId.
     * @param attributeId The attributeId.
     * @throws UserAccountNotFoundException   e
     * @throws UserAttributeNotFoundException e
     * @throws ReadonlyElementViolationException
     *                                        e
     * @see de.escidoc.core.aa.service.interfaces.UserAccountHandlerInterface #deleteAttribute(java.lang.String,
     *      java.lang.String)
     */
    @Override
    public void deleteAttribute(final String userId, final String attributeId)
            throws UserAccountNotFoundException, UserAttributeNotFoundException, ReadonlyElementViolationException,
            SqlDatabaseSystemException, WebserverSystemException {

        final UserAttribute userAttribute = retrieveAttributeById(userId, attributeId, false);

        dao.delete(userAttribute);
        sendUserAttributeUpdateEvent(userId);
    }

    /**
     * See Interface for functional description.
     *
     * @param parameters parameter map
     * @return filter sub query with permission rules
     * @throws SystemException             e
     * @throws InvalidSearchQueryException e
     */
    @Override
    public String retrievePermissionFilterQuery(final Map<String, String[]> parameters)
            throws InvalidSearchQueryException, SystemException {
        final Set<ResourceType> resourceTypes = EnumSet.noneOf(ResourceType.class);
        final String[] types = parameters.get("index");
        if (types != null) {
            final Collection<String> hashedTypes = new HashSet<String>();
            hashedTypes.addAll(Arrays.asList(types));
            final Map<String, Map<String, Map<String, Object>>> objectTypeParameters = this.indexingHandler
                    .getObjectTypeParameters();
            for (final Entry<String, Map<String, Map<String, Object>>> entry : objectTypeParameters.entrySet()) {
                final Map<String, Map<String, Object>> index = entry.getValue();
                for (final String indexName : index.keySet()) {
                    if (hashedTypes.contains(indexName)) {
                        resourceTypes.add(ResourceType.getResourceTypeFromUri(entry.getKey()));
                    }
                }
            }
        }
        return Utility.prepareReturnXml(null, "<filter>" + permissionsQuery.getFilterQuery(resourceTypes,
                this.utility.getCurrentUserId(), new FilterInterface() {
                    @Override
                    public String getRoleId() {
                        final String[] parameter = parameters.get("role");

                        return parameter != null && parameter.length > 0 ? parameter[0] : null;
                    }

                    @Override
                    public String getUserId() {
                        final String[] parameter = parameters.get("user");

                        return parameter != null && parameter.length > 0 ? parameter[0] : null;
                    }

                    @Override
                    public String toString() {
                        return "[userId=" + getUserId() + ",roleId=" + getRoleId() + ']';
                    }
                }) + "</filter>");
    }
}