Commit 39e33fe1 authored by Guus der Kinderen's avatar Guus der Kinderen

OF-1336: Introduce UserPropertyProvider

This commit adds a new feature to Openfire, that allows one to store user properties in an external system. This is quite similar to concepts like UserProvider, AuthProvider, GroupProvider, and so on.

The default behavior, moved from the User class to DefaultUserPropertyProvider, continues to use the ofUserProp database table. Provider implementations matching those of other types of providers have been added.
parent 9df410ae
......@@ -29,6 +29,8 @@ import org.dom4j.Element;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.event.UserEventDispatcher;
import org.jivesoftware.openfire.event.UserEventListener;
import org.jivesoftware.openfire.user.property.DefaultUserPropertyProvider;
import org.jivesoftware.openfire.user.property.UserPropertyProvider;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher;
......@@ -50,7 +52,7 @@ import org.xmpp.packet.JID;
*/
public class UserManager implements IQResultListener {
private static final Logger Log = LoggerFactory.getLogger(UserManager.class);
private static final Logger Log = LoggerFactory.getLogger(UserManager.class);
// Wrap this guy up so we can mock out the UserManager class.
private static class UserManagerContainer {
......@@ -69,6 +71,21 @@ public class UserManager implements IQResultListener {
return UserManagerContainer.instance.provider;
}
/**
* Returns the currently-installed UserPropertyProvider.
*
* <b>Warning:</b> in virtually all cases the user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @return the current UserPropertyProvider.
* @see User#getProperties
*/
public static UserPropertyProvider getUserPropertyProvider() {
return UserManagerContainer.instance.propertyProvider;
}
/**
* Returns a singleton UserManager instance.
*
......@@ -83,14 +100,16 @@ public class UserManager implements IQResultListener {
/** Cache if a local or remote user exists. */
private Cache<String, Boolean> remoteUsersCache;
private UserProvider provider;
private UserPropertyProvider propertyProvider;
private UserManager() {
// Initialize caches.
userCache = CacheFactory.createCache("User");
remoteUsersCache = CacheFactory.createCache("Remote Users Existence");
// Load a user provider.
// Load a user & property provider.
initProvider();
initPropertyProvider();
// Detect when a new auth provider class is set
PropertyEventListener propListener = new PropertyEventListener() {
......@@ -99,6 +118,9 @@ public class UserManager implements IQResultListener {
if ("provider.user.className".equals(property)) {
initProvider();
}
if ("provider.userproperty.className".equals(property)) {
initPropertyProvider();
}
}
@Override
......@@ -106,6 +128,9 @@ public class UserManager implements IQResultListener {
if ("provider.user.className".equals(property)) {
initProvider();
}
if ("provider.userproperty.className".equals(property)) {
initPropertyProvider();
}
}
@Override
......@@ -492,4 +517,23 @@ public class UserManager implements IQResultListener {
}
}
}
private void initPropertyProvider() {
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty("provider.userproperty.className");
String className = JiveGlobals.getProperty("provider.userproperty.className",
"org.jivesoftware.openfire.user.property.DefaultUserPropertyProvider");
// Check if we need to reset the provider class
if (propertyProvider == null || !className.equals(propertyProvider.getClass().getName())) {
try {
Class c = ClassUtils.forName(className);
propertyProvider = (UserPropertyProvider) c.newInstance();
}
catch (Exception e) {
Log.error("Error loading user property provider: " + className, e);
propertyProvider = new DefaultUserPropertyProvider();
}
}
}
}
\ No newline at end of file
/*
* Copyright 2017 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.user.property;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Default implementation of the UserPropertyProvider interface, which reads and writes data from the
* <tt>ofUserProp</tt> database table.
*
* This implementation will not explicitly verify if a user exists, when operating on its properties. The methods of
* this implementation will <em>not</em> throw {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* <b>Warning:</b> in virtually all cases a user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see User#getProperties
*/
public class DefaultUserPropertyProvider implements UserPropertyProvider
{
private static final Logger Log = LoggerFactory.getLogger( DefaultUserPropertyProvider.class );
private static final String LOAD_PROPERTIES = "SELECT name, propValue FROM ofUserProp WHERE username=?";
private static final String LOAD_PROPERTY = "SELECT propValue FROM ofUserProp WHERE username=? AND name=?";
private static final String DELETE_PROPERTY = "DELETE FROM ofUserProp WHERE username=? AND name=?";
private static final String UPDATE_PROPERTY = "UPDATE ofUserProp SET propValue=? WHERE name=? AND username=?";
private static final String INSERT_PROPERTY = "INSERT INTO ofUserProp (username, name, propValue) VALUES (?, ?, ?)";
@Override
public Map<String, String> loadProperties( String username )
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
final Map<String, String> properties = new ConcurrentHashMap<>();
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( LOAD_PROPERTIES );
pstmt.setString( 1, username );
rs = pstmt.executeQuery();
while ( rs.next() )
{
properties.put( rs.getString( 1 ), rs.getString( 2 ) );
}
}
catch ( SQLException sqle )
{
Log.error( sqle.getMessage(), sqle );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
return properties;
}
@Override
public String loadProperty( String username, String propertyName )
{
String propertyValue = null;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( LOAD_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propertyName );
rs = pstmt.executeQuery();
while ( rs.next() )
{
propertyValue = rs.getString( 1 );
}
}
catch ( SQLException sqle )
{
Log.error( sqle.getMessage(), sqle );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
return propertyValue;
}
@Override
public void insertProperty( String username, String propName, String propValue )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( INSERT_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
pstmt.setString( 3, propValue );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public void updateProperty( String username, String propName, String propValue )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( UPDATE_PROPERTY );
pstmt.setString( 1, propValue );
pstmt.setString( 2, propName );
pstmt.setString( 3, username );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public void deleteProperty( String username, String propName )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( DELETE_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public boolean isReadOnly()
{
return false;
}
}
/*
* Copyright 2017 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.user.property;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
/**
* The JDBC user property provider allows you to use an external database to define the user properties. It is best used
* with the JDBCUserProvider, JDBCAuthProvider &and JDBCGroupProvider to provide integration between your external
* system and Openfire. All data is treated as read-only so any set operations will result in an exception.
*
* This implementation will not explicitly verify if a user exists, when operating on its properties. The methods of
* this implementation will <em>not</em> throw {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* To enable this provider, set the following in the system properties:
*
* <ul>
* <li><tt>provider.userproperty.className = org.jivesoftware.openfire.user.property.JDBCUserPropertyProvider</tt></li>
* </ul>
*
* Then you need to set your driver, connection string and SQL statements:
*
* <ul>
* <li><tt>jdbcUserPropertyProvider.driver = com.mysql.jdbc.Driver</tt></li>
* <li><tt>jdbcUserPropertyProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&amp;password=secret</tt></li>
* <li><tt>jdbcUserPropertyProvider.loadPropertySQL = SELECT propName, propValue FROM myUser WHERE user = ? AND propName = ?</tt></li>
* <li><tt>jdbcUserPropertyProvider.loadPropertiesSQL = SELECT propValue FROM myUser WHERE user = ?</tt></li>
* </ul>
*
* In order to use the configured JDBC connection provider do not use a JDBCconnection string, set the following
* property:
*
* <tt>jdbcUserPropertyProvider.useConnectionProvider = true</tt></li>
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class JDBCUserPropertyProvider implements UserPropertyProvider
{
private static final Logger Log = LoggerFactory.getLogger( JDBCUserPropertyProvider.class );
private String loadPropertySQL;
private String loadPropertiesSQL;
private String connectionString;
private boolean useConnectionProvider;
/**
* Constructs a new JDBC user property provider.
*/
public JDBCUserPropertyProvider()
{
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.driver" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.connectionString" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.loadPropertySQL" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.loadPropertiesSQL" );
useConnectionProvider = JiveGlobals.getBooleanProperty( "jdbcUserProvider.useConnectionProvider" );
// Load the JDBC driver and connection string.
if ( !useConnectionProvider )
{
String jdbcDriver = JiveGlobals.getProperty( "jdbcUserPropertyProvider.driver" );
try
{
Class.forName( jdbcDriver ).newInstance();
}
catch ( Exception e )
{
Log.error( "Unable to load JDBC driver: " + jdbcDriver, e );
return;
}
connectionString = JiveGlobals.getProperty( "jdbcProvider.connectionString" );
}
// Load database statements for user data.
loadPropertySQL = JiveGlobals.getProperty( "jdbcUserPropertyProvider.loadPropertySQL" );
loadPropertiesSQL = JiveGlobals.getProperty( "jdbcUserPropertyProvider.loadPropertiesSQL" );
}
private Connection getConnection() throws SQLException
{
if ( useConnectionProvider )
{
return DbConnectionManager.getConnection();
}
else
{
return DriverManager.getConnection( connectionString );
}
}
@Override
public Map<String, String> loadProperties( String username ) throws UnsupportedOperationException
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = getConnection();
pstmt = con.prepareStatement( loadPropertiesSQL );
pstmt.setString( 1, username );
rs = pstmt.executeQuery();
final Map<String, String> result = new HashMap<>();
while ( rs.next() )
{
final String propName = rs.getString( 1 );
final String propValue = rs.getString( 2 );
result.put( propName, propValue );
}
return result;
}
catch ( Exception e )
{
throw new UnsupportedOperationException( e );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
}
@Override
public String loadProperty( String username, String propName )
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = getConnection();
pstmt = con.prepareStatement( loadPropertySQL );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
rs = pstmt.executeQuery();
final Map<String, String> result = new HashMap<>();
if ( rs.next() )
{
return rs.getString( 1 );
}
return null;
}
catch ( Exception e )
{
throw new UnsupportedOperationException( e );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
}
@Override
public void insertProperty( String username, String propName, String propValue ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public void updateProperty( String username, String propName, String propValue ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public void deleteProperty( String username, String propName ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public boolean isReadOnly()
{
return true;
}
}
/*
* Copyright 2017 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.user.property;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* A {@link UserPropertyProvider} that delegates to a user-specific UserPropertyProvider.
*
* This implementation will explicitly verify if a user exists, when operating on its properties, but only if the
* corresponding mapped provider does so. If that is the case, then the methods of this implementation will throw
* {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* This class related to, but is distinct from {@link HybridUserPropertyProvider}. The Hybrid variant of the provider
* iterates over providers, operating on the first applicable instance. This Mapped variant, however, maps each user to
* exactly one provider.
*
* To use this provider, use the following system property definition:
*
* <ul>
* <li><tt>provider.userproperty.className = org.jivesoftware.openfire.user.MappedUserPropertyProvider</tt></li>
* </ul>
*
* To be usable, a {@link UserPropertyProviderMapper} must be configured using the <tt>mappedUserPropertyProvider.mapper.className</tt>
* system property. It is of importance to note that most UserPropertyProviderMapper implementations will require additional
* configuration.
*
* @author Guus der Kinderen, guus@goodbytes.nl
*/
public class MappedUserPropertyProvider implements UserPropertyProvider
{
/**
* Name of the property of which the value is expected to be the classname of the UserPropertyProviderMapper
* instance to be used by instances of this class.
*/
public static final String PROPERTY_MAPPER_CLASSNAME = "mappedUserPropertyProvider.mapper.className";
private static final Logger Log = LoggerFactory.getLogger( MappedUserPropertyProvider.class );
/**
* Used to determine what provider is to be used to operate on a particular user.
*/
protected final UserPropertyProviderMapper mapper;
public MappedUserPropertyProvider()
{
// Migrate properties.
JiveGlobals.migrateProperty( PROPERTY_MAPPER_CLASSNAME );
// Instantiate mapper.
final String mapperClass = JiveGlobals.getProperty( PROPERTY_MAPPER_CLASSNAME );
if ( mapperClass == null )
{
throw new IllegalStateException( "A mapper must be specified via openfire.xml or the system properties." );
}
try
{
final Class c = ClassUtils.forName( mapperClass );
mapper = (UserPropertyProviderMapper) c.newInstance();
}
catch ( Exception e )
{
throw new IllegalStateException( "Unable to create new instance of UserPropertyProviderMapper class: " + mapperClass, e );
}
}
/**
* Instantiates a UserPropertyProvider based on a property value (that is expected to be a class name). When the
* property is not set, this method returns null. When the property is set, but an exception occurs while
* instantiating the class, this method logs the error and returns null.
*
* UserProvider classes are required to have a public, no-argument constructor.
*
* @param propertyName A property name (cannot ben ull).
* @return A user provider (can be null).
*/
public static UserPropertyProvider instantiate( String propertyName )
{
final String className = JiveGlobals.getProperty( propertyName );
if ( className == null )
{
Log.debug( "Property '{}' is undefined. Skipping.", propertyName );
return null;
}
Log.debug( "About to to instantiate an UserPropertyProvider '{}' based on the value of property '{}'.", className, propertyName );
try
{
final Class c = ClassUtils.forName( className );
final UserPropertyProvider provider = (UserPropertyProvider) c.newInstance();
Log.debug( "Instantiated UserPropertyProvider '{}'", className );
return provider;
}
catch ( Exception e )
{
Log.error( "Unable to load UserPropertyProvider '{}'. Users in this provider will be disabled.", className, e );
return null;
}
}
@Override
public Map<String, String> loadProperties( String username ) throws UserNotFoundException
{
return mapper.getUserPropertyProvider( username ).loadProperties( username );
}
@Override
public String loadProperty( String username, String propName ) throws UserNotFoundException
{
return mapper.getUserPropertyProvider( username ).loadProperty( username, propName );
}
@Override
public void insertProperty( String username, String propName, String propValue ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).insertProperty( username, propName, propValue );
}
@Override
public void updateProperty( String username, String propName, String propValue ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).updateProperty( username, propName, propValue );
}
@Override
public void deleteProperty( String username, String propName ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).deleteProperty( username, propName );
}
/**
* Returns whether <em>all</em> backing providers are read-only. When read-only, properties can not be created,
* deleted, or modified. If at least one provider is not read-only, this method returns false.
*
* @return true when all backing providers are read-only, otherwise false.
*/
@Override
public boolean isReadOnly()
{
// TODO Make calls concurrent for improved throughput.
for ( final UserPropertyProvider provider : mapper.getUserPropertyProviders() )
{
// If at least one provider is not readonly, neither is this proxy.
if ( !provider.isReadOnly() )
{
return false;
}
}
return true;
}
}
/*
* Copyright 2017 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.user.property;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserNotFoundException;
import java.util.Map;
/**
* A provider for user properties. User properties are defined Map of String key and values that does not support null.
* value.
*
* Some, but not all, implementations are expected to store user properties in a relation to an existing user object.
* This interface definition does not require implementations to verify that a user object indeed exists, when
* processing data. As a result, methods defined here may, but are not required to throw {@link UserNotFoundException}
* when processing property data for non-existing users. Implementations should clearly document their behavior in
* this respect.
*
* <b>Warning:</b> in virtually all cases a user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see User#getProperties
*/
public interface UserPropertyProvider
{
/**
* Returns true if this UserPropertyProvider is read-only. When read-only, properties can not be created, deleted or
* modified. Invocation of the corresponding methods should result in an {@link UnsupportedOperationException}.
*
* @return true if the user provider is read-only.
*/
boolean isReadOnly();
/**
* Retrieves all properties for a particular user.
*
* @param username The identifier of the user (cannot be null or empty).
* @return A collection, possibly empty, but never null.
*/
Map<String, String> loadProperties( String username ) throws UserNotFoundException;
/**
* Retrieves a property value for a user.
*
* This method will return null when the desired property was not defined for the user (null values are not
* supported).
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @return The property value (possibly null).
*/
String loadProperty( String username, String propName ) throws UserNotFoundException;
/**
* Adds a property for an user.
*
* The behavior of inserting a duplicate property name is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @param propValue The property value (cannot be null).
*/
void insertProperty( String username, String propName, String propValue ) throws UserNotFoundException, UnsupportedOperationException;
/**
* Changes a property value for an user.
*
* The behavior of updating a non-existing property is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @param propValue The property value (cannot be null).
*/
void updateProperty( String username, String propName, String propValue ) throws UserNotFoundException, UnsupportedOperationException;
/**
* Removes one particular property for a particular user.
*
* The behavior of deleting a non-existing property is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
*/
void deleteProperty( String username, String propName ) throws UserNotFoundException, UnsupportedOperationException;
}
/*
* Copyright 2017 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.user.property;
import java.util.Set;
/**
* Implementations are used to determine what UserPropertyProvider is to be used for a particular username.
*
* Note that the provided username need not reflect a pre-existing user (the instance might be used to determine in
* which provider a new user is to be created).
*
* Implementation must have a no-argument constructor.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see MappedUserPropertyProvider
*/
public interface UserPropertyProviderMapper
{
/**
* Finds a suitable UserPropertyProvider for the user.
*
* Note that the provided username need not reflect a pre-existing user (the instance might be used to determine in
* which provider a new user is to be created).
*
* Implementations are expected to be able to find a UserPropertyProvider for any username. If an implementation
* fails to do so, such a failure is assumed to be the result of a problem in implementation or configuration.
*
* @param username A user identifier (cannot be null or empty).
* @return A UserPropertyProvider for the user (never null).
*/
UserPropertyProvider getUserPropertyProvider( String username );
/**
* Returns all providers that are used by this instance.
*
* The returned collection should have a consistent, predictable iteration order.
*
* @return all providers (never null).
*/
Set<UserPropertyProvider> getUserPropertyProviders();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment