org.opennms.netmgt.config.UserManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.netmgt.config.UserManager.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2002-2016 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2016 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.config;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.lang.StringUtils;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.ValidationException;
import org.jasypt.util.password.PasswordEncryptor;
import org.jasypt.util.password.StrongPasswordEncryptor;
import org.opennms.core.xml.CastorUtils;
import org.opennms.netmgt.config.api.UserConfig;
import org.opennms.netmgt.config.users.Contact;
import org.opennms.netmgt.config.users.DutySchedule;
import org.opennms.netmgt.config.users.Header;
import org.opennms.netmgt.config.users.Password;
import org.opennms.netmgt.config.users.User;
import org.opennms.netmgt.config.users.Userinfo;
import org.opennms.netmgt.config.users.Users;
import org.opennms.netmgt.events.api.EventConstants;
import org.opennms.netmgt.model.OnmsUser;
import org.opennms.netmgt.model.OnmsUserList;

/**
 * <p>Abstract UserManager class.</p>
 *
 * @author <a href="mailto:david@opennms.org">David Hustace</a>
 * @author <a href="mailto:brozow@opennms.org">Matt Brozowski</a>
 * @author <a href="mailto:ranger@opennms.org">Benjamin Reed</a>
 * @author <a href="mailto:jeffg@opennms.org">Jeff Gehlbach</a>
 */
public abstract class UserManager implements UserConfig {
    private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
            'F' };

    private static final PasswordEncryptor m_passwordEncryptor = new StrongPasswordEncryptor();

    private final ReadWriteLock m_readWriteLock = new ReentrantReadWriteLock();
    private final Lock m_readLock = m_readWriteLock.readLock();
    private final Lock m_writeLock = m_readWriteLock.writeLock();

    protected GroupManager m_groupManager;
    /**
     * A mapping of user IDs to the User objects
     */
    protected Map<String, User> m_users;
    /**
     * The duty schedules for each user
     */
    protected Map<String, List<DutySchedule>> m_dutySchedules;
    private Header oldHeader;

    /**
     * <p>Constructor for UserManager.</p>
     *
     * @param groupManager a {@link org.opennms.netmgt.config.GroupManager} object.
     */
    protected UserManager(final GroupManager groupManager) {
        m_groupManager = groupManager;
    }

    /**
     * <p>parseXML</p>
     *
     * @param in a {@link java.io.InputStream} object.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public void parseXML(final InputStream in) throws MarshalException, ValidationException {
        m_writeLock.lock();

        try {
            final Userinfo userinfo = CastorUtils.unmarshal(Userinfo.class, in);
            final Users users = userinfo.getUsers();
            oldHeader = userinfo.getHeader();
            final List<User> usersList = users.getUserCollection();
            m_users = new TreeMap<String, User>();

            for (final User curUser : usersList) {
                m_users.put(curUser.getUserId(), curUser);
            }

            _buildDutySchedules(m_users);
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Adds a new user and overwrites the "users.xml"
     *
     * @param name a {@link java.lang.String} object.
     * @param details a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.lang.Exception if any.
     */
    public void saveUser(final String name, final User details) throws Exception {
        update();

        m_writeLock.lock();

        try {
            _writeUser(name, details);
        } finally {
            m_writeLock.unlock();
        }
    }

    private void _writeUser(final String name, final User details) throws Exception {
        if (name == null || details == null) {
            throw new Exception("UserFactory:saveUser  null");
        } else {
            m_users.put(name, details);
        }

        _saveCurrent();
    }

    public void save(final OnmsUser onmsUser) throws Exception {
        update();

        m_writeLock.lock();

        try {
            User castorUser = _getUser(onmsUser.getUsername());
            if (castorUser == null) {
                castorUser = new User();
                castorUser.setUserId(onmsUser.getUsername());
            }
            castorUser.setFullName(onmsUser.getFullName());
            castorUser.setUserComments(onmsUser.getComments());

            // Contact info
            _setContact(castorUser, ContactType.email, onmsUser.getEmail());

            final Password pass = new Password();
            pass.setContent(onmsUser.getPassword());
            pass.setSalt(onmsUser.getPasswordSalted());
            castorUser.setPassword(pass);

            if (onmsUser.getDutySchedule() != null) {
                castorUser.setDutySchedule(onmsUser.getDutySchedule());
            }

            _writeUser(onmsUser.getUsername(), castorUser);
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Builds a mapping between user IDs and duty schedules. These are used by
     * Notifd when determining to send a notice to a given user. This helps
     * speed up the decision process.
     * 
     * @param users
     *            the map of users parsed from the XML configuration file
     */
    private void _buildDutySchedules(final Map<String, User> users) {
        m_dutySchedules = new HashMap<String, List<DutySchedule>>();

        for (final Entry<String, User> entry : users.entrySet()) {
            final String key = entry.getKey();
            final User curUser = entry.getValue();

            if (curUser.getDutyScheduleCount() > 0) {
                final List<DutySchedule> dutyList = new ArrayList<DutySchedule>();
                for (final String duty : curUser.getDutyScheduleCollection()) {
                    dutyList.add(new DutySchedule(duty));
                }

                m_dutySchedules.put(key, dutyList);
            }
        }
    }

    /**
     * Determines if a user is on duty at a given time. If a user has no duty
     * schedules listed in the configuration file, that user is assumed to always be on
     * duty.
     *
     * @param user
     *            the user id
     * @param time
     *            the time to check for a duty schedule
     * @return boolean, true if the user is on duty, false otherwise.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public boolean isUserOnDuty(final String user, final Calendar time)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            // if the user has no duty schedules then he is on duty
            if (!m_dutySchedules.containsKey(user))
                return true;

            for (final DutySchedule curSchedule : m_dutySchedules.get(user)) {
                if (curSchedule.isInSchedule(time)) {
                    return true;
                }
            }
        } finally {
            m_readLock.unlock();
        }

        return false;
    }

    /**
     * Return a <code>Map</code> of usernames to user instances.
     *
     * @return a {@link java.util.Map} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public Map<String, User> getUsers() throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return Collections.unmodifiableMap(m_users);
        } finally {
            m_readLock.unlock();
        }
    }

    public OnmsUserList getOnmsUserList() throws MarshalException, ValidationException, IOException {
        update();

        final OnmsUserList list = new OnmsUserList();

        m_readLock.lock();

        try {
            for (final String username : _getUserNames()) {
                list.add(_getOnmsUser(username));
            }
            list.setTotalCount(list.getCount());

            return list;
        } finally {
            m_readLock.unlock();
        }
    }

    public OnmsUser getOnmsUser(final String username) throws MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            return _getOnmsUser(username);
        } finally {
            m_readLock.unlock();
        }
    }

    private OnmsUser _getOnmsUser(final String username) throws IOException, MarshalException, ValidationException {
        final User castorUser = _getUser(username);
        if (castorUser == null)
            return null;

        final OnmsUser user = new OnmsUser(username);
        user.setFullName(castorUser.getFullName());
        user.setComments(castorUser.getUserComments());
        user.setPassword(castorUser.getPassword().getContent());
        user.setPasswordSalted(castorUser.getPassword().getSalt());
        user.setDutySchedule(castorUser.getDutyScheduleCollection());
        user.setEmail(_getContactInfo(castorUser, ContactType.email));
        return user;
    }

    private Contact _getContact(final String userId, final ContactType contactType) {
        User user = _getUser(userId);
        if (user != null && contactType != null) {
            for (Contact eachContact : user.getContactCollection()) {
                if (contactType.name().equals(eachContact.getType())) {
                    return eachContact;
                }
            }
        }
        return null;
    }

    /**
     * Returns a boolean indicating if the user name appears in the XML file
     *
     * @return true if the user exists in the XML file, false otherwise
     * @param userName a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public boolean hasUser(final String userName) throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return m_users.containsKey(userName);
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * <p>getUserNames</p>
     *
     * @return a {@link java.util.List} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public List<String> getUserNames() throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return _getUserNames();
        } finally {
            m_readLock.unlock();
        }
    }

    private List<String> _getUserNames() {
        final List<String> userNames = new ArrayList<String>();
        userNames.addAll(m_users.keySet());
        return userNames;
    }

    /**
     * Get a user by name
     *
     * @param name
     *            the name of the user to return
     * @return the user specified by name
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public User getUser(final String name) throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return _getUser(name);
        } finally {
            m_readLock.unlock();
        }
    }

    private User _getUser(final String name) {
        return m_users.get(name);
    }

    /**
     * Get a user's telephone PIN by name
     *
     * @param name
     *            the name of the user to return
     * @return the telephone PIN of the user specified by name
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTuiPin(final String name) throws IOException, MarshalException, ValidationException {

        update();

        m_readLock.lock();
        try {
            return m_users.get(name).getTuiPin();
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * Get a user's telephone PIN by User object
     *
     * @return the telephone PIN of the user specified by user
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTuiPin(final User user) throws IOException, MarshalException, ValidationException {

        update();

        m_readLock.lock();
        try {
            return m_users.get(user.getUserId()).getTuiPin();
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * Get a user's microblog username by username
     *
     * @param name
     *            the username of the user whose microblog username should be returned
     * @return the microblog username of the specified user
     * @throws java.io.IOException if any.
     * @throws java.io.FileNotFoundException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     */
    public String getMicroblogName(final String name)
            throws MarshalException, ValidationException, FileNotFoundException, IOException {
        return getContactInfo(name, ContactType.microblog.toString());
    }

    /**
     * Get a user's microblog username by User
     *
     * @param user
     *            the user object of the user whose microblog username should be returned
     * @return the microblog username of the specified user
     * @throws java.io.IOException if any.
     * @throws java.io.FileNotFoundException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     */
    public String getMicroblogName(final User user)
            throws MarshalException, ValidationException, FileNotFoundException, IOException {
        return getContactInfo(user, ContactType.microblog.toString());
    }

    public void setContactInfo(final String userId, final ContactType contactType, final String contactValue)
            throws Exception {
        update();
        m_writeLock.lock();

        try {
            final User user = _getUser(userId);
            if (user != null) {
                _setContact(user, contactType, contactValue);
            }
            _saveCurrent();
        } finally {
            m_writeLock.unlock();
        }
    }

    private void _setContact(final User user, final ContactType contactType, final String value) {
        if (user != null && !StringUtils.isEmpty(value)) {
            Contact contact = _getContact(user.getUserId(), contactType);
            if (contact == null) {
                contact = new Contact();
                user.addContact(contact);
            }
            contact.setInfo(value);
            contact.setType(contactType.name());
        }
    }

    /**
     * @see {@link #getContactInfo(String, String)} 
     */
    public String getContactInfo(final String userId, final ContactType contactType)
            throws MarshalException, ValidationException, IOException {
        if (userId == null || contactType == null)
            return null;
        return getContactInfo(userId, contactType.name());
    }

    /**
     * Get the contact info given a command string
     *
     * @param userID
     *            the name of the user
     * @param command
     *            the command to look up the contact info for
     * @return the contact information
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getContactInfo(final String userID, final String command)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            final User user = m_users.get(userID);
            return _getContactInfo(user, command);
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * <p>getContactInfo</p>
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @param command a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getContactInfo(final User user, final String command)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();

        try {
            return _getContactInfo(user, command);
        } finally {
            m_readLock.unlock();
        }
    }

    private String _getContactInfo(final User user, final ContactType contactType) {
        return _getContactInfo(user, contactType.name());
    }

    private String _getContactInfo(final User user, final String command) {
        if (user == null)
            return "";

        for (final Contact contact : user.getContactCollection()) {
            if (contact != null && contact.getType().equals(command)) {
                return contact.getInfo();
            }
        }
        return "";
    }

    /**
     * Get the contact service provider, given a command string
     *
     * @param userID
     *            the name of the user
     * @param command
     *            the command to look up the contact info for
     * @return the contact information
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getContactServiceProvider(final String userID, final String command)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            final User user = m_users.get(userID);
            return _getContactServiceProvider(user, command);
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * <p>getContactServiceProvider</p>
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @param command a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getContactServiceProvider(final User user, final String command)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return _getContactServiceProvider(user, command);
        } finally {
            m_readLock.unlock();
        }
    }

    private String _getContactServiceProvider(final User user, final String command) {
        if (user == null)
            return "";

        for (final Contact contact : user.getContactCollection()) {
            if (contact != null && contact.getType().equals(command)) {
                return contact.getServiceProvider();
            }
        }

        return "";
    }

    /**
     * Get a email by name
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the email specified by name
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getEmail(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactInfo(userID, ContactType.email.toString());
    }

    /**
     * Get a email by user
     *
     * @param user the user to find the email for
     * @return String the email specified by name
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getEmail(final User user) throws IOException, MarshalException, ValidationException {
        return getContactInfo(user, ContactType.email.toString());
    }

    /**
     * Get a pager email by name
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the pager email
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getPagerEmail(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactInfo(userID, ContactType.pagerEmail.toString());
    }

    /**
     * Get a pager email by user
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @return String the pager email
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getPagerEmail(final User user) throws IOException, MarshalException, ValidationException {
        return getContactInfo(user, ContactType.pagerEmail.toString());
    }

    /**
     * Get a numeric pin
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the numeric pin
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getNumericPin(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactInfo(userID, ContactType.numericPage.toString());
    }

    /**
     * Get a numeric pin
     *
     * @return String the numeric pin
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getNumericPin(final User user) throws IOException, MarshalException, ValidationException {
        return getContactInfo(user, ContactType.numericPage.toString());
    }

    /**
     * Get an XMPP address by name
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the XMPP address
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getXMPPAddress(final String userID) throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            final User user = m_users.get(userID);
            return _getXMPPAddress(user);
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * Get an XMPP address by name
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @return String the XMPP address
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getXMPPAddress(final User user) throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return _getXMPPAddress(user);
        } finally {
            m_readLock.unlock();
        }
    }

    private String _getXMPPAddress(final User user) {
        if (user == null)
            return "";

        for (final Contact contact : user.getContactCollection()) {
            if (contact != null && contact.getType().equals(ContactType.xmppAddress.toString())) {
                return contact.getInfo();
            }
        }

        return "";
    }

    /**
     * Get a numeric service provider
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the service provider
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getNumericPage(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactServiceProvider(userID, ContactType.numericPage.toString());
    }

    /**
     * Get a numeric service provider
     *
     * @return String the service provider
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getNumericPage(final User user) throws IOException, MarshalException, ValidationException {
        return getContactServiceProvider(user, ContactType.numericPage.toString());
    }

    /**
     * Get a text pin
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the text pin
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTextPin(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactInfo(userID, ContactType.textPage.toString());
    }

    /**
     * Get a text pin
     *
     * @return String the text pin
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTextPin(final User user) throws IOException, MarshalException, ValidationException {
        return getContactInfo(user, ContactType.textPage.toString());
    }

    /**
     * Get a Text Page Service Provider
     *
     * @param userID
     *            the user ID of the user to return
     * @return String the text page service provider.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTextPage(final String userID) throws IOException, MarshalException, ValidationException {
        return getContactServiceProvider(userID, ContactType.textPage.toString());
    }

    /**
     * Get a Text Page Service Provider
     *
     * @return String the text page service provider.
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String getTextPage(final User user) throws IOException, MarshalException, ValidationException {
        return getContactServiceProvider(user, ContactType.textPage.toString());
    }

    /**
     * Get a work phone number
     *
     * @param userID
     *             the user ID of the user to return
     * @return String the work phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     */
    public String getWorkPhone(final String userID) throws MarshalException, ValidationException, IOException {
        return getContactInfo(userID, ContactType.workPhone.toString());
    }

    /**
     * Get a work phone number
     *
     * @return String the work phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     */
    public String getWorkPhone(final User user) throws MarshalException, ValidationException, IOException {
        return getContactInfo(user, ContactType.workPhone.toString());
    }

    /**
     * Get a mobile phone number
     *
     * @param userID
     *             the user ID of the user to return
     * @return String the mobile phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     */
    public String getMobilePhone(final String userID) throws MarshalException, ValidationException, IOException {
        return getContactInfo(userID, ContactType.mobilePhone.toString());
    }

    /**
     * Get a mobile phone number
     *
     * @return String the mobile phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     */
    public String getMobilePhone(final User user) throws MarshalException, ValidationException, IOException {
        return getContactInfo(user, ContactType.mobilePhone.toString());
    }

    /**
     * Get a home phone number
     *
     * @param userID
     *             the user ID of the user to return
     * @return String the home phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     */
    public String getHomePhone(final String userID) throws MarshalException, ValidationException, IOException {
        return getContactInfo(userID, ContactType.homePhone.toString());
    }

    /**
     * Get a home phone number
     *
     * @return String the home phone number
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     */
    public String getHomePhone(final User user) throws MarshalException, ValidationException, IOException {
        return getContactInfo(user, ContactType.homePhone.toString());
    }

    /**
     * <p>saveUsers</p>
     *
     * @param usersList a {@link java.util.Collection} object.
     * @throws java.lang.Exception if any.
     */
    public void saveUsers(final Collection<User> usersList) throws Exception {
        update();

        m_writeLock.lock();

        try {
            // clear out the internal structure and reload it
            m_users.clear();

            for (final User curUser : usersList) {
                m_users.put(curUser.getUserId(), curUser);
            }
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Removes the user from the list of users. Then overwrites to the
     * "users.xml"
     *
     * @param name a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    public void deleteUser(final String name) throws Exception {
        m_writeLock.lock();

        try {
            // Check if the user exists
            if (m_users.containsKey(name)) {
                // Delete the user in the user map.
                m_users.remove(name);

                // Refresh the groups config first
                m_groupManager.update();

                // Delete the user in the group.
                m_groupManager.deleteUser(name);

                // Delete the user in the view.
                // viewFactory.deleteUser(name);
            } else {
                throw new Exception("UserFactory:delete The old user name " + name + " is not found");
            }

            _saveCurrent();
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Saves into "users.xml" file
     */
    private void _saveCurrent() throws Exception {
        final Users users = new Users();

        for (final User user : m_users.values()) {
            users.addUser(user);
        }

        final Userinfo userinfo = new Userinfo();
        userinfo.setUsers(users);

        final Header header = oldHeader;
        if (header != null) {
            header.setCreated(EventConstants.formatToString(new Date()));
            userinfo.setHeader(header);
        }
        oldHeader = header;

        // marshal to a string first, then write the string to the file. This
        // way the original configuration
        // isn't lost if the XML from the marshal is hosed.
        final StringWriter stringWriter = new StringWriter();
        Marshaller.marshal(userinfo, stringWriter);
        final String writerString = stringWriter.toString();
        saveXML(writerString);
    }

    /**
     * <p>saveXML</p>
     *
     * @param writerString a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     */
    protected abstract void saveXML(final String writerString) throws IOException;

    /**
     * When this method is called users name is changed, so also is the username
     * belonging to the group and the view. Also overwrites the "users.xml" file
     *
     * @param oldName a {@link java.lang.String} object.
     * @param newName a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    public void renameUser(final String oldName, final String newName) throws Exception {
        update();

        m_writeLock.lock();

        try {
            // Get the old data
            if (m_users.containsKey(oldName)) {
                final User data = m_users.get(oldName);
                if (data == null) {
                    m_users.remove(oldName);
                    throw new Exception(
                            "UserFactory:rename the data contained for old user " + oldName + " is null");
                } else {
                    // Rename the user in the user map.
                    m_users.remove(oldName);
                    data.setUserId(newName);
                    m_users.put(newName, data);

                    // Refresh the groups config first
                    m_groupManager.update();

                    // Rename the user in the group.
                    m_groupManager.renameUser(oldName, newName);

                    // Rename the user in the view.
                    // viewFactory.renameUser(oldName, newName);
                }
            } else {
                throw new Exception("UserFactory:rename the old user name " + oldName + " is not found");
            }

            _saveCurrent();
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Sets the password for this user, assuming that the value passed in is
     * already encrypted properly
     *
     * @param userID
     *            the user ID to change the password for
     * @param aPassword
     *            the encrypted password
     * @throws java.lang.Exception if any.
     */
    public void setEncryptedPassword(final String userID, final String aPassword, final boolean salted)
            throws Exception {
        update();

        m_writeLock.lock();

        try {
            final User user = m_users.get(userID);
            if (user != null) {
                final Password pass = new Password();
                pass.setContent(aPassword);
                pass.setSalt(salted);
                user.setPassword(pass);
            }

            _saveCurrent();
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * Sets the password for this user, first encrypting it
     *
     * @param userID
     *            the user ID to change the password for
     * @param aPassword
     *            the password
     * @throws java.lang.Exception if any.
     */
    public void setUnencryptedPassword(final String userID, final String aPassword) throws Exception {
        update();

        m_writeLock.lock();

        try {
            final User user = m_users.get(userID);
            if (user != null) {
                final Password pass = new Password();
                pass.setContent(encryptedPassword(aPassword, true));
                pass.setSalt(true);
                user.setPassword(pass);
            }

            _saveCurrent();
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * <p>encryptedPassword</p>
     *
     * @param aPassword a {@link java.lang.String} object.
     * @param useSalt TODO
     * @return a {@link java.lang.String} object.
     */
    public String encryptedPassword(final String aPassword, final boolean useSalt) {
        String encryptedPassword = null;

        if (useSalt) {
            encryptedPassword = m_passwordEncryptor.encryptPassword(aPassword);
        } else {
            // old crappy algorithm
            try {
                final MessageDigest digest = MessageDigest.getInstance("MD5");

                // build the digest, get the bytes, convert to hexadecimal string
                // and return
                encryptedPassword = hexToString(digest.digest(aPassword.getBytes()));
            } catch (final NoSuchAlgorithmException e) {
                throw new IllegalStateException(e.toString());
            }
        }

        return encryptedPassword;
    }

    /**
     * @param data
     * @return
     */
    private String hexToString(final byte[] data) {
        // check to see if the byte array has an even number of elements
        if ((data.length % 2) != 0)
            return null;

        // there will be two hexadecimal characters for each byte element
        final char[] buffer = new char[data.length * 2];

        for (int i = 0; i < data.length; i++) {
            final int low = (int) (data[i] & 0x0f);
            final int high = (int) ((data[i] & 0xf0) >> 4);
            buffer[i * 2] = HEX[high];
            buffer[i * 2 + 1] = HEX[low];
        }

        return new String(buffer);
    }

    /**
     * This method compares two encrypted strings for equality.
     *
     * @param userID
     *            the user ID to check against.
     * @param aPassword
     *            the password to check for equality
     * @return true if the two passwords are equal (after encryption), false
     *         otherwise
     */
    public boolean comparePasswords(final String userID, final String aPassword) {
        m_readLock.lock();

        try {
            final User user = m_users.get(userID);
            if (user == null)
                return false;

            final String password = user.getPassword().getContent().trim();
            final boolean isSalted = user.getPassword().getSalt();
            if (isSalted) {
                return checkSaltedPassword(aPassword, password);
            } else {
                return password.equals(encryptedPassword(aPassword, false));
            }
        } finally {
            m_readLock.unlock();
        }
    }

    public boolean checkSaltedPassword(final String raw, final String encrypted) {
        return m_passwordEncryptor.checkPassword(raw, encrypted);
    }

    /**
     * <p>update</p>
     *
     * @throws java.io.IOException if any.
     * @throws java.io.FileNotFoundException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    protected abstract void doUpdate()
            throws IOException, FileNotFoundException, MarshalException, ValidationException;

    public final void update() throws IOException, FileNotFoundException, MarshalException, ValidationException {
        m_writeLock.lock();
        try {
            doUpdate();
        } finally {
            m_writeLock.unlock();
        }
    }

    /**
     * <p>getUsersWithRole</p>
     *
     * @param roleid a {@link java.lang.String} object.
     * @return an array of {@link java.lang.String} objects.
     * @throws java.io.IOException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     */
    public String[] getUsersWithRole(final String roleid)
            throws IOException, MarshalException, ValidationException {
        update();

        m_readLock.lock();
        try {
            return _getUsersWithRole(roleid);
        } finally {
            m_readLock.unlock();
        }
    }

    private String[] _getUsersWithRole(final String roleid)
            throws MarshalException, ValidationException, IOException {
        final List<String> usersWithRole = new ArrayList<String>();

        for (final User user : m_users.values()) {
            if (_userHasRole(user, roleid)) {
                usersWithRole.add(user.getUserId());
            }
        }

        return usersWithRole.toArray(new String[usersWithRole.size()]);
    }

    /**
     * <p>userHasRole</p>
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @param roleid a {@link java.lang.String} object.
     * @return a boolean.
     * @throws java.io.FileNotFoundException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public boolean userHasRole(final User user, final String roleid)
            throws FileNotFoundException, MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            return _userHasRole(user, roleid);
        } finally {
            m_readLock.unlock();
        }
    }

    private boolean _userHasRole(final User user, final String roleid)
            throws MarshalException, ValidationException, IOException {
        if (roleid == null)
            throw new NullPointerException("roleid is null");

        return m_groupManager.userHasRole(user.getUserId(), roleid);
    }

    /**
     * <p>isUserScheduledForRole</p>
     *
     * @param user a {@link org.opennms.netmgt.config.users.User} object.
     * @param roleid a {@link java.lang.String} object.
     * @param time a {@link java.util.Date} object.
     * @return a boolean.
     * @throws java.io.FileNotFoundException if any.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public boolean isUserScheduledForRole(final User user, final String roleid, final Date time)
            throws FileNotFoundException, MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            return _isUserScheduledForRole(user, roleid, time);
        } finally {
            m_readLock.unlock();
        }
    }

    private boolean _isUserScheduledForRole(final User user, final String roleid, final Date time)
            throws MarshalException, ValidationException, IOException {
        if (roleid == null)
            throw new NullPointerException("roleid is null");

        return m_groupManager.isUserScheduledForRole(user.getUserId(), roleid, time);
    }

    /**
     * <p>getUsersScheduledForRole</p>
     *
     * @param roleid a {@link java.lang.String} object.
     * @param time a {@link java.util.Date} object.
     * @return an array of {@link java.lang.String} objects.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public String[] getUsersScheduledForRole(final String roleid, final Date time)
            throws MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            final List<String> usersScheduledForRole = new ArrayList<String>();

            for (final User user : m_users.values()) {
                if (_isUserScheduledForRole(user, roleid, time)) {
                    usersScheduledForRole.add(user.getUserId());
                }
            }

            return usersScheduledForRole.toArray(new String[usersScheduledForRole.size()]);
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * <p>hasRole</p>
     *
     * @param roleid a {@link java.lang.String} object.
     * @return a boolean.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public boolean hasRole(final String roleid) throws MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            return m_groupManager.getRole(roleid) != null;
        } finally {
            m_readLock.unlock();
        }
    }

    /**
     * <p>countUsersWithRole</p>
     *
     * @param roleid a {@link java.lang.String} object.
     * @return a int.
     * @throws org.exolab.castor.xml.MarshalException if any.
     * @throws org.exolab.castor.xml.ValidationException if any.
     * @throws java.io.IOException if any.
     */
    public int countUsersWithRole(final String roleid) throws MarshalException, ValidationException, IOException {
        update();

        m_readLock.lock();
        try {
            final String[] users = _getUsersWithRole(roleid);
            if (users == null)
                return 0;
            return users.length;
        } finally {
            m_readLock.unlock();
        }
    }

    public abstract boolean isUpdateNeeded();

    public abstract long getLastModified();

    public abstract long getFileSize();

    public abstract void reload() throws IOException, FileNotFoundException, MarshalException, ValidationException;
}