/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2007 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev: 1107 $
* Last modified by $Author: kobit $
* $Date: 2008-09-02 18:43:45 +0100 (Tue, 02 Sep 2008) $
*/
package tigase.db.xml;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;
import tigase.db.AuthorizationException;
import tigase.db.DBInitException;
import tigase.db.TigaseDBException;
import tigase.db.UserAuthRepository;
import tigase.db.UserAuthRepositoryImpl;
import tigase.db.UserExistsException;
import tigase.db.UserNotFoundException;
import tigase.db.UserRepository;
import tigase.xml.db.NodeExistsException;
import tigase.xml.db.NodeNotFoundException;
import tigase.xml.db.XMLDB;
/**
* Class <code>XMLRepository</code> is a <em>XML</em> implementation of
* <code>UserRepository</code>.
* It uses <code>tigase.xml.db</code> package as repository backend and uses
* <em>Bridge</em> design pattern to translate <code>XMLDB</code> calls to
* <code>UserRepository</code> functions.
*
* <p>
* Created: Tue Oct 26 15:27:33 2004
* </p>
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev: 1107 $
*/
public class XMLRepository implements UserAuthRepository, UserRepository {
private static final String USER_STR = "User: ";
private static final String NOT_FOUND_STR =
" has not been found in repository.";
private static final Logger log =
Logger.getLogger("tigase.db.xml.XMLRepository");
private XMLDB xmldb = null;
private UserAuthRepository auth = null;
private boolean autoCreateUser = false;
// Implementation of tigase.xmpp.rep.UserRepository
public synchronized void initRepository(String file,
Map<String, String> params) {
String file_name = file;
try {
int idx = file.indexOf("?");
if (idx > 0) {
file_name = file.substring(0, idx);
}
if (file.contains("autoCreateUser=true")) {
autoCreateUser=true;
} // end of if (db_conn.contains())
auth = new UserAuthRepositoryImpl(this);
xmldb = new XMLDB(file_name);
} catch (Exception e) {
log.warning("Can not open existing user repository file, creating new one, "
+ e);
xmldb = XMLDB.createDB(file_name, "users", "user");
} // end of try-catch
}
public String getResourceUri() { return xmldb.getDBFileName(); }
/**
* This <code>addUser</code> method allows to add new user to reposiotry.
* It <b>must</b> throw en exception <code>UserExistsException</code> if such
* user already exists because user <b>must</b> be unique within user
* repository data base.<br/>
* As one <em>XMPP</em> server can support many virtual internet domains it
* is required that <code>user</code> id consists of user name and domain
* address: <em>username@domain.address.net</em> for example.
*
* @param user a <code>String</code> value of user id consisting of user name
* and domain address.
* @exception UserExistsException if user with the same id already exists.
*/
public synchronized void addUser(final String user) throws UserExistsException {
try {
xmldb.addNode1(user);
} catch (NodeExistsException e) {
throw new UserExistsException(USER_STR+user+" already exists.", e);
} // end of try-catch
}
public synchronized boolean userExists(final String user) {
return xmldb.findNode1(user) != null;
}
/**
* This <code>removeUser</code> method allows to remove user and all his data
* from user repository.
* If given user id does not exist <code>UserNotFoundException</code> must be
* thrown. As one <em>XMPP</em> server can support many virtual internet
* domains it is required that <code>user</code> id consists of user name and
* domain address: <em>username@domain.address.net</em> for example.
*
* @param user a <code>String</code> value of user id consisting of user name
* and domain address.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void removeUser(final String user)
throws UserNotFoundException {
try {
xmldb.removeNode1(user);
} catch (NodeNotFoundException e) {
throw new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);
} // end of try-catch
}
public synchronized List<String> getUsers() {
return xmldb.getAllNode1s();
}
public synchronized long getUsersCount() {
return xmldb.getAllNode1sCount();
}
/**
* <code>setData</code> method sets data value for
* given user ID in repository under given node path and associates it with
* given key.
* If there already exists value for given key in given node, old value is
* replaced with new value. No warning or exception is thrown in case if
* methods overwrites old value.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the specified value is to be
* associated.
* @param value a <code>String</code> value to be associated with the
* specified key.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void setData(final String user, final String subnode,
final String key, final String value)
throws UserNotFoundException, TigaseDBException {
try {
xmldb.setData(user, subnode, key, value);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
xmldb.setData(user, subnode, key, value);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* This <code>setData</code> method sets data value for given user ID
* associated with given key in default repository node.
* Default node is dependent on implementation and usually it is root user
* node. If there already exists value for given key in given node, old value
* is replaced with new value. No warning or exception is thrown in case if
* methods overwrites old value.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param key a <code>String</code> with which the specified value is to be
* associated.
* @param value a <code>String</code> value to be associated with the
* specified key.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public void setData(final String user, final String key,
final String value)
throws UserNotFoundException, TigaseDBException {
setData(user, null, key, value);
}
/**
* <code>setDataList</code> method sets list of values for given user
* associated given key in repository under given node path.
* If there already exist values for given key in given node, all old values are
* replaced with new values. No warning or exception is thrown in case if
* methods overwrites old value.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the specified values list is to
* be associated.
* @param list a <code>String[]</code> is an array of values to be assosiated
* with the specified key.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void setDataList(final String user, final String subnode,
final String key, final String[] list)
throws UserNotFoundException, TigaseDBException {
try {
xmldb.setData(user, subnode, key, list);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
xmldb.setData(user, subnode, key, list);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>addDataList</code> method adds mode entries to existing data list
* associated with given key in repository under given node path.
* This method is very similar to <code>setDataList(...)</code> except it
* doesn't remove existing data.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the specified values list is to
* be associated.
* @param list a <code>String[]</code> is an array of values to be assosiated
* with the specified key.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void addDataList(final String user, final String subnode,
final String key, final String[] list)
throws UserNotFoundException, TigaseDBException {
try {
String[] old_data = getDataList(user, subnode, key);
String[] all = null;
if (old_data != null) {
all = new String[old_data.length+list.length];
System.arraycopy(old_data, 0, all, 0, old_data.length);
System.arraycopy(list, 0, all, old_data.length, list.length);
xmldb.setData(user, subnode, key, all);
} else {
xmldb.setData(user, subnode, key, list);
} // end of else
} catch (NodeNotFoundException e) {
throw new UserNotFoundException(USER_STR+user+
NOT_FOUND_STR, e);
} // end of try-catch
}
/**
* <code>getDataList</code> method returns array of values associated with
* given key or <code>null</code> if given key does not exist for given user
* ID in given node path.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the needed values list is
* associated.
* @return a <code>String[]</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized String[] getDataList(final String user, final String subnode,
final String key)
throws UserNotFoundException, TigaseDBException {
try {
return xmldb.getDataList(user, subnode, key);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
return xmldb.getDataList(user, subnode, key);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>getData</code> method returns a value associated with given key for
* user repository in given subnode.
* If key is not found in repository given default value is returned.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the needed value is
* associated.
* @param def a <code>String</code> value which is returned in case if data
* for specified key does not exixist in repository.
* @return a <code>String</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized String getData(final String user, final String subnode,
final String key, final String def)
throws UserNotFoundException, TigaseDBException {
try {
return (String)xmldb.getData(user, subnode, key, def);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
return (String)xmldb.getData(user, subnode, key, def);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>getData</code> method returns a value associated with given key for
* user repository in given subnode.
* If key is not found in repository <code>null</code> value is returned.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> with which the needed value is
* associated.
* @return a <code>String</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public String getData(final String user, final String subnode,
final String key)
throws UserNotFoundException, TigaseDBException {
return getData(user, subnode, key, null);
}
/**
* <code>getData</code> method returns a value associated with given key for
* user repository in default subnode.
* If key is not found in repository <code>null</code> value is returned.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param key a <code>String</code> with which the needed value is
* associated.
* @return a <code>String</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public String getData(final String user, final String key)
throws UserNotFoundException, TigaseDBException {
return getData(user, null, key, null);
}
/**
* <code>getSubnodes</code> method returns list of all direct subnodes from
* given node.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @return a <code>String[]</code> value is an array of all direct subnodes.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized String[] getSubnodes(final String user, final String subnode)
throws UserNotFoundException, TigaseDBException {
try {
return xmldb.getSubnodes(user, subnode);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
return xmldb.getSubnodes(user, subnode);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>getSubnodes</code> method returns list of all <em>root</em> nodes for
* given user.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @return a <code>String[]</code> value is an array of all <em>root</em>
* nodes for given user.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public String[] getSubnodes(final String user)
throws UserNotFoundException, TigaseDBException {
return getSubnodes(user, null);
}
/**
* <code>getKeys</code> method returns list of all keys stored in given
* subnode in user repository.
* There is a value (or list of values) associated with each key. It is up to
* user (developer) to know what key keeps one value and what key keeps list
* of values.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @return a <code>String[]</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized String[] getKeys(final String user, final String subnode)
throws UserNotFoundException, TigaseDBException {
try {
return xmldb.getKeys(user, subnode);
} catch (NodeNotFoundException e) {
if (autoCreateUser) {
try {
addUser(user);
return xmldb.getKeys(user, subnode);
} catch (Exception ex) {
throw new TigaseDBException("Unknown repository problem: ", ex);
}
} else {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>getKeys</code> method returns list of all keys stored in default user
* repository node.
* There is some a value (or list of values) associated with each key. It is
* up to user (developer) to know what key keeps one value and what key keeps
* list of values.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @return a <code>String[]</code> value
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public String[] getKeys(final String user)
throws UserNotFoundException, TigaseDBException {
return getKeys(user, null);
}
/**
* <code>removeData</code> method removes pair (key, value) from user
* reposiotry in given subnode.
* If the key exists in user repository there is always a value
* associated with this key - even empty <code>String</code>. If key does not
* exist the <code>null</code> value is returned from repository backend or
* given default value.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path where data is
* stored. Node path has the same form as directory path on file system:
* <pre>/root/subnode1/subnode2</pre>.
* @param key a <code>String</code> for which the value is to be removed.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void removeData(final String user, final String subnode,
final String key)
throws UserNotFoundException {
try {
xmldb.removeData(user, subnode, key);
} catch (NodeNotFoundException e) {
if (!autoCreateUser) {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
/**
* <code>removeData</code> method removes pair (key, value) from user
* reposiotry in default repository node.
* If the key exists in user repository there is always a value
* associated with this key - even empty <code>String</code>. If key does not
* exist the <code>null</code> value is returned from repository backend or
* given default value.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param key a <code>String</code> for which the value is to be removed.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public void removeData(final String user, final String key)
throws UserNotFoundException {
removeData(user, null, key);
}
/**
* <code>removeSubnode</code> method removes given subnode with all subnodes
* in this node and all data stored in this node and in all subnodes.
* Effectively it removes entire repository tree starting from given node.
*
* @param user a <code>String</code> value of user ID for which data must be
* stored. User ID consists of user name and domain name.
* @param subnode a <code>String</code> value is a node path to subnode which
* has to be removed. Node path has the same form as directory path on file
* system: <pre>/root/subnode1/subnode2</pre>.
* @exception UserNotFoundException if user id hasn't been found in reository.
*/
public synchronized void removeSubnode(final String user, final String subnode)
throws UserNotFoundException {
try {
xmldb.removeSubnode(user, subnode);
} catch (NodeNotFoundException e) {
if (!autoCreateUser) {
throw new UserNotFoundException(USER_STR+user+NOT_FOUND_STR, e);
}
} // end of try-catch
}
// Implementation of tigase.db.UserAuthRepository
/**
* Describe <code>plainAuth</code> method here.
*
* @param user a <code>String</code> value
* @param password a <code>String</code> value
* @return a <code>boolean</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
*/
public synchronized boolean plainAuth(final String user, final String password)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
return auth.plainAuth(user, password);
}
/**
* Describe <code>digestAuth</code> method here.
*
* @param user a <code>String</code> value
* @param digest a <code>String</code> value
* @param id a <code>String</code> value
* @param alg a <code>String</code> value
* @return a <code>boolean</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
*/
public synchronized boolean digestAuth(final String user, final String digest,
final String id, final String alg)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
return auth.digestAuth(user, digest, id, alg);
}
/**
* Describe <code>otherAuth</code> method here.
*
* @param props a <code>Map</code> value
* @return a <code>boolean</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
* @exception AuthorizationException if an error occurs
*/
public synchronized boolean otherAuth(final Map<String, Object> props)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
return auth.otherAuth(props);
}
public synchronized void logout(final String user)
throws UserNotFoundException, TigaseDBException {
auth.logout(user);
}
public synchronized void updatePassword(final String user, final String password)
throws UserExistsException, TigaseDBException {
auth.updatePassword(user, password);
}
/**
* Describe <code>addUser</code> method here.
*
* @param user a <code>String</code> value
* @param password a <code>String</code> value
* @exception UserExistsException if an error occurs
* @exception TigaseDBException if an error occurs
*/
public synchronized void addUser(final String user, final String password)
throws UserExistsException, TigaseDBException {
auth.addUser(user, password);
}
public synchronized void queryAuth(Map<String, Object> authProps) {
auth.queryAuth(authProps);
}
} // SaslCallbackHandler} // XMLRepository