org.alfresco.bm.user.UserDataServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.bm.user.UserDataServiceImpl.java

Source

/*
 * Copyright (C) 2005-2014 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package org.alfresco.bm.user;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.alfresco.bm.data.DataCreationState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;

import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.DuplicateKeyException;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;

/**
 * Service providing access to {@link UserData} storage. All {@link UserData} returned from and persisted
 * with this service will be testrun-specific. The testrun-identifier is set in the constructor.
 *
 * @author Frederik Heremans
 * @author Derek Hulley
 * @author steveglover
 * @since 1.1
 */
public class UserDataServiceImpl extends AbstractUserDataService implements InitializingBean {
    public static final String FIELD_RANDOMIZER = "randomizer";
    public static final String FIELD_USERNAME = "username";
    public static final String FIELD_PASSWORD = "password";
    public static final String FIELD_CREATION_STATE = "creationState";
    public static final String FIELD_FIRST_NAME = "firstName";
    public static final String FIELD_LAST_NAME = "lastName";
    public static final String FIELD_EMAIL = "email";
    public static final String FIELD_DOMAIN = "domain";
    public static final String FIELD_GROUPS = "groups";

    public static final String FIELD_ID = "id";
    public static final String FIELD_KEY = "key";

    private static Log logger = LogFactory.getLog(UserDataServiceImpl.class);

    /** The collection of users, which can be reused by derived extensions. */
    protected final DBCollection collection;

    public UserDataServiceImpl(DB db, String collection) {
        this.collection = db.getCollection(collection);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        checkIndexes();
    }

    /**
     * Ensure that the MongoDB collection has the required indexes associated with
     * this user bean.
     */
    private void checkIndexes() {
        collection.setWriteConcern(WriteConcern.SAFE);

        DBObject uidxUserName = BasicDBObjectBuilder.start(FIELD_USERNAME, 1).get();
        DBObject optUserName = BasicDBObjectBuilder.start("name", "uidxUserName").add("unique", Boolean.TRUE).get();
        collection.createIndex(uidxUserName, optUserName);

        DBObject uidxEmail = BasicDBObjectBuilder.start(FIELD_EMAIL, 1).get();
        DBObject optEmail = BasicDBObjectBuilder.start("name", "uidxEmail").add("unique", Boolean.TRUE).get();
        collection.createIndex(uidxEmail, optEmail);

        DBObject idxDomainRand = BasicDBObjectBuilder.start(FIELD_DOMAIN, 1).add(FIELD_RANDOMIZER, 2).get();
        DBObject optDomainRand = BasicDBObjectBuilder.start("name", "idxDomainRand").add("unique", Boolean.FALSE)
                .get();
        collection.createIndex(idxDomainRand, optDomainRand);

        DBObject idxCreationStateRand = BasicDBObjectBuilder.start(FIELD_CREATION_STATE, 1).add(FIELD_RANDOMIZER, 2)
                .get();
        DBObject optCreationStateRand = BasicDBObjectBuilder.start("name", "idxCreationStateRand")
                .add("unique", Boolean.FALSE).get();
        collection.createIndex(idxCreationStateRand, optCreationStateRand);
    }

    /**
     * Helper to convert a Mongo DBObject into the API consumable object
     * <p/>
     * Note that <tt>null</tt> is handled as a <tt>null</tt> return.
     */
    protected UserData fromDBObject(DBObject userDataObj) {
        if (userDataObj == null) {
            return null;
        }

        UserData userData = new UserData();
        userData.setUsername((String) userDataObj.get(FIELD_USERNAME));
        userData.setPassword((String) userDataObj.get(FIELD_PASSWORD));
        String DataCreationStateStr = (String) userDataObj.get(FIELD_CREATION_STATE);
        DataCreationState creationState = DataCreationState.Unknown;
        try {
            creationState = DataCreationState.valueOf(DataCreationStateStr);
            userData.setCreationState(creationState);
        } catch (Exception e) {
            logger.error("User data has unknown state: " + userData.getUsername() + " - " + DataCreationStateStr);
        }
        userData.setFirstName((String) userDataObj.get(FIELD_FIRST_NAME));
        userData.setLastName((String) userDataObj.get(FIELD_LAST_NAME));
        userData.setEmail((String) userDataObj.get(FIELD_EMAIL));
        userData.setDomain((String) userDataObj.get(FIELD_DOMAIN));
        if (userDataObj.get(FIELD_GROUPS) != null) // Conditional for backward compatibility
        {
            @SuppressWarnings("unchecked")
            List<String> groups = (List<String>) userDataObj.get(FIELD_GROUPS);
            userData.setGroups(groups);
        }
        // Done
        return userData;
    }

    /**
     * Turn a cursor into an array of API-friendly objects
     */
    protected List<UserData> fromDBCursor(DBCursor cursor) {
        int count = cursor.count();
        try {
            List<UserData> userDatas = new ArrayList<UserData>(count);
            while (cursor.hasNext()) {
                DBObject userDataObj = cursor.next();
                UserData userData = fromDBObject(userDataObj);
                userDatas.add(userData);
            }
            // Done
            return userDatas;
        } finally {
            cursor.close();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createNewUser(UserData data) {
        BasicDBObjectBuilder insertObjBuilder = BasicDBObjectBuilder.start()
                .add(FIELD_RANDOMIZER, data.getRandomizer()).add(FIELD_USERNAME, data.getUsername())
                .add(FIELD_PASSWORD, data.getPassword())
                .add(FIELD_CREATION_STATE, data.getCreationState().toString())
                .add(FIELD_FIRST_NAME, data.getFirstName()).add(FIELD_LAST_NAME, data.getLastName())
                .add(FIELD_EMAIL, data.getEmail()).add(FIELD_DOMAIN, data.getDomain())
                .add(FIELD_GROUPS, data.getGroups());
        DBObject insertObj = insertObjBuilder.get();

        try {
            collection.insert(insertObj);
        } catch (DuplicateKeyException e) {
            // We just rethrow as per the API
            throw e;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setUserPassword(String username, String password) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_USERNAME, username).get();
        DBObject updateObj = BasicDBObjectBuilder.start().push("$set").add(FIELD_PASSWORD, password).pop().get();
        WriteResult result = collection.update(queryObj, updateObj);
        if (result.getN() != 1) {
            throw new RuntimeException("Failed to update user ticket: \n" + "   Username: " + username + "\n"
                    + "   Password: " + password + "\n" + "   Result:   " + result);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setUserCreationState(String username, DataCreationState creationState) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_USERNAME, username).get();
        DBObject updateObj = BasicDBObjectBuilder.start().push("$set")
                .add(FIELD_CREATION_STATE, creationState.toString()).pop().get();
        WriteResult result = collection.update(queryObj, updateObj);
        if (result.getN() != 1) {
            throw new RuntimeException("Failed to update user creation state: \n" + "   Username:       " + username
                    + "\n" + "   Creation State: " + creationState + "\n" + "   Result:         " + result);
        }
    }

    @Override
    public long countUsers(String domain, DataCreationState creationState) {
        BasicDBObjectBuilder queryObjBuilder = BasicDBObjectBuilder.start();
        if (domain != null) {
            queryObjBuilder.add(FIELD_DOMAIN, domain);
        }
        if (creationState != null) {
            queryObjBuilder.add(FIELD_CREATION_STATE, creationState.toString());
        }
        DBObject queryObj = queryObjBuilder.get();

        return collection.count(queryObj);
    }

    @Override
    public long deleteUsers(DataCreationState creationState) {
        BasicDBObjectBuilder queryObjBuilder = BasicDBObjectBuilder.start();
        if (creationState != null) {
            queryObjBuilder.add(FIELD_CREATION_STATE, creationState.toString());
        }
        DBObject queryObj = queryObjBuilder.get();

        WriteResult result = collection.remove(queryObj);
        return result.getN();
    }

    /**
     * Find a user by username
     * 
     * @return                          the {@link UserData} found otherwise <tt>null</tt.
     */
    @Override
    public UserData findUserByUsername(String username) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_USERNAME, username).get();
        DBObject userDataObj = collection.findOne(queryObj);
        return fromDBObject(userDataObj);
    }

    @Override
    public UserData findUserByEmail(String email) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_EMAIL, email).get();
        DBObject userDataObj = collection.findOne(queryObj);
        return fromDBObject(userDataObj);
    }

    @Override
    public List<UserData> getUsersByCreationState(DataCreationState creationState, int startIndex, int count) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_CREATION_STATE, creationState.toString()).get();
        DBObject sortObj = BasicDBObjectBuilder.start().add(FIELD_RANDOMIZER, 1).get();
        DBCursor cursor = collection.find(queryObj).sort(sortObj).skip(startIndex).limit(count);
        return fromDBCursor(cursor);
    }

    @Override
    public UserData getRandomUser() {
        int random = (int) (Math.random() * (double) 1e6);
        DBObject queryObj = BasicDBObjectBuilder.start()
                .add(FIELD_CREATION_STATE, DataCreationState.Created.toString()).push(FIELD_RANDOMIZER)
                .add("$gte", Integer.valueOf(random)).pop().get();

        DBObject userDataObj = collection.findOne(queryObj);
        if (userDataObj == null) {
            queryObj.put(FIELD_RANDOMIZER, new BasicDBObject("$lt", random));
            userDataObj = collection.findOne(queryObj);
        }
        return fromDBObject(userDataObj);
    }

    /*
     * USER DOMAIN SERVICES
     */

    @Override
    public List<UserData> getUsersInDomain(String domain, int startIndex, int count) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_DOMAIN, domain)
                .add(FIELD_CREATION_STATE, DataCreationState.Created.toString()).get();
        DBCursor cursor = collection.find(queryObj).skip(startIndex).limit(count);

        return fromDBCursor(cursor);
    }

    @Override
    public Iterator<String> getDomainsIterator() {
        @SuppressWarnings("unchecked")
        List<String> domains = (List<String>) collection.distinct(FIELD_DOMAIN);
        return domains.iterator();
    }

    @Override
    public UserData getRandomUserFromDomain(String domain) {
        List<String> domains = Collections.singletonList(domain);
        return getRandomUserFromDomains(domains);
    }

    private Range getRandomizerRange(List<String> domains) {
        BasicDBObjectBuilder queryObjBuilder = BasicDBObjectBuilder.start().add(FIELD_CREATION_STATE,
                DataCreationState.Created.toString());
        if (domains.size() > 0) {
            queryObjBuilder.push(FIELD_DOMAIN).add("$in", domains).pop();
        }
        DBObject queryObj = queryObjBuilder.get();

        DBObject fieldsObj = BasicDBObjectBuilder.start().add(FIELD_RANDOMIZER, Boolean.TRUE).get();

        DBObject sortObj = BasicDBObjectBuilder.start().add(FIELD_RANDOMIZER, -1).get();

        // Find max
        DBObject resultObj = collection.findOne(queryObj, fieldsObj, sortObj);
        int maxRandomizer = resultObj == null ? 0 : (Integer) resultObj.get(FIELD_RANDOMIZER);

        // Find min
        sortObj.put(FIELD_RANDOMIZER, +1);
        resultObj = collection.findOne(queryObj, fieldsObj, sortObj);
        int minRandomizer = resultObj == null ? 0 : (Integer) resultObj.get(FIELD_RANDOMIZER);

        return new Range(minRandomizer, maxRandomizer);
    }

    @Override
    public UserData getRandomUserFromDomains(List<String> domains) {
        Range range = getRandomizerRange(domains);
        int upper = range.getMax();
        int lower = range.getMin();
        int random = lower + (int) (Math.random() * (double) (upper - lower));

        BasicDBObjectBuilder queryObjBuilder = BasicDBObjectBuilder.start()
                .add(FIELD_CREATION_STATE, DataCreationState.Created.toString()).push(FIELD_RANDOMIZER)
                .add("$gte", random).pop();
        if (domains.size() > 0) {
            queryObjBuilder.push(FIELD_DOMAIN).add("$in", domains).pop();
        }
        DBObject queryObj = queryObjBuilder.get();

        DBObject userDataObj = collection.findOne(queryObj);
        return fromDBObject(userDataObj);
    }

    /*
     * USER GROUP SERVICES
     */

    @Override
    public void addUserGroups(String username, List<String> groups) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_USERNAME, username).get();
        DBObject updateObj = BasicDBObjectBuilder.start().push("$addToSet").push(FIELD_GROUPS).add("$each", groups)
                .pop().pop().get();
        collection.update(queryObj, updateObj);
    }

    @Override
    public void removeUserGroups(String username, List<String> groups) {
        DBObject queryObj = BasicDBObjectBuilder.start().add(FIELD_USERNAME, username).get();
        DBObject updateObj = BasicDBObjectBuilder.start().push("$pullAll").add(FIELD_GROUPS, groups).pop().get();
        collection.update(queryObj, updateObj);
    }

    /*
     * CLASSES
     */

    public static class Range {
        private int min;
        private int max;

        public Range(int min, int max) {
            super();
            this.min = min;
            this.max = max;
        }

        public int getMin() {
            return min;
        }

        public int getMax() {
            return max;
        }
    }
}