Commit 05134318 authored by Gabriel Guardincerri's avatar Gabriel Guardincerri Committed by gguardin

[JM-1281] Fixed an exception using a user with a not visible email and CS as a provider.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches@10018 b35dd754-fafc-0310-a699-88a17e54d16e
parent 6d34b403
......@@ -23,22 +23,36 @@ import org.xmpp.packet.JID;
import java.util.*;
/**
* @author Daniel Henninger
* The ClearspaceUserProvider uses the UserService and ProfileSearchService web service inside of Clearspace
* to retrieve user information and to search for users from Clearspace.
*
* @author Gabriel Guardincerri
*/
public class ClearspaceUserProvider implements UserProvider {
// The UserService webservice url prefix
protected static final String USER_URL_PREFIX = "userService/";
// The ProfileSearchService webservice url prefix
protected static final String SEARCH_URL_PREFIX = "profileSearchService/";
private ClearspaceManager manager;
// Used to know it CS is a read only user provider
private Boolean readOnly;
public ClearspaceUserProvider() {
// Gets the manager
manager = ClearspaceManager.getInstance();
loadReadOnly();
}
/**
* Loads the user using the userService/users GET service. Only loads local users.
* Throws a UserNotFoundException exception if the user could not be found.
*
* @param username the username of the user to load
* @return a user instance with the user information
* @throws UserNotFoundException if the user could not be found
*/
public User loadUser(String username) throws UserNotFoundException {
// Checks if the user is local
if (username.contains("@")) {
......@@ -52,6 +66,19 @@ public class ClearspaceUserProvider implements UserProvider {
return translate(getUserByUsername(username));
}
/**
* Creates user using the userService/users POST service. If Clearspace is a read only
* provider throws an UnsupportedOperationException. If there is already a user with
* the username throws a UserAlreadyExistsException.
*
* @param username the username of the user
* @param password the password of the user
* @param name the name of the user (optional)
* @param email the email of the user
* @return an instance of the created user
* @throws UserAlreadyExistsException If there is already a user with the username
* @throws UnsupportedOperationException If Clearspace is a read only provider
*/
public User createUser(String username, String password, String name, String email) throws UserAlreadyExistsException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -100,6 +127,11 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Creates user using the userService/users DELETE service. If the user is not found returns.
*
* @param username the username of the user to delete
*/
public void deleteUser(String username) {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -119,6 +151,11 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Gets the user count using the userService/users/count GET service.
*
* @return the user count
*/
public int getUserCount() {
try {
String path = USER_URL_PREFIX + "users/count";
......@@ -131,11 +168,21 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Gets all users using the userService/userNames GET service.
*
* @return a list of all users
*/
public Collection<User> getUsers() {
Collection<String> usernames = getUsernames();
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
/**
* Gets all usernames using the userService/userNames GET service.
*
* @return a list of all the usernames
*/
public Collection<String> getUsernames() {
try {
String path = USER_URL_PREFIX + "userNames";
......@@ -148,6 +195,13 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Gets a bounded list of users using the userService/userNames GET service.
*
* @param startIndex the start index
* @param numResults the number of result
* @return a bounded list of users
*/
public Collection<User> getUsers(int startIndex, int numResults) {
String[] usernamesAll = getUsernames().toArray(new String[0]);
Collection<String> usernames = new ArrayList<String>();
......@@ -160,6 +214,13 @@ public class ClearspaceUserProvider implements UserProvider {
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
/**
* Updates the name of the user using the userService/update service.
*
* @param username the username of the user
* @param name the new name of the user
* @throws UserNotFoundException if there is no user with that username
*/
public void setName(String username, String name) throws UserNotFoundException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -177,6 +238,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams);
}
/**
* Updates the email of the user using the userService/update service.
*
* @param username the username of the user
* @param email the new email of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setEmail(String username, String email) throws UserNotFoundException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -194,6 +262,14 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams);
}
/**
* Updates the creationDate of the user using the userService/update service.
*
* @param username the username of the user
* @param creationDate the new email of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -212,6 +288,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams);
}
/**
* Updates the modificationDate of the user using the userService/update service.
*
* @param username the username of the user
* @param modificationDate the new modificationDate of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
......@@ -230,6 +313,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams);
}
/**
* Creates the parameters to send in a update user request based on the information of <code>username</code>
*
* @param username the username of the user
* @return the parameters to send in a update user request
* @throws UserNotFoundException if the user could not be found
*/
protected Element getUserUpdateParams(String username) throws UserNotFoundException {
// Creates the user update params element
Element userUpdateParams = DocumentHelper.createDocument().addElement("updateUser");
......@@ -246,6 +336,12 @@ public class ClearspaceUserProvider implements UserProvider {
return userUpdateParams;
}
/**
* Updates the user using the userService/users PUT service.
*
* @param userUpdateParams the request parameters
* @throws UserNotFoundException if the user could not be found
*/
protected void updateUser(Element userUpdateParams) throws UserNotFoundException {
try {
String path = USER_URL_PREFIX + "users";
......@@ -259,32 +355,44 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Clearsapce can search using three fields: username, name and email.
*
* @return a list of username, name and email
* @throws UnsupportedOperationException
*/
public Set<String> getSearchFields() throws UnsupportedOperationException {
return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email"));
}
/**
* Search for the user using the userService/search POST method.
*
* @param fields the fields to search on.
* @param query the query string.
* @return a Collection of users that match the search.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
// Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("searchProfile");
Element queryE = rootE.addElement("WSProfileSearchQuery");
Element keywords = queryE.addElement("keywords");
keywords.addText(query);
Element searchUsername = queryE.addElement("searchUsername");
searchUsername.addText("true");
Element searchName = queryE.addElement("searchName");
searchName.addText("true");
Element searchEmail = queryE.addElement("searchEmail");
searchEmail.addText("true");
Element searchProfile = queryE.addElement("searchProfile");
searchProfile.addText("false");
Element paramsE = DocumentHelper.createDocument().addElement("search");
Element queryE = paramsE.addElement("query");
queryE.addElement("keywords").addText(query);
queryE.addElement("searchUsername").addText("true");
queryE.addElement("searchName").addText("true");
queryE.addElement("searchEmail").addText("true");
queryE.addElement("searchProfile").addText("false");
try {
List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile";
//TODO create a service on CS to get only the username field
Element element = manager.executeRequest(GET, path);
String path = SEARCH_URL_PREFIX + "searchProfile";
Element element = manager.executeRequest(POST, path, paramsE.asXML());
List<Node> userNodes = (List<Node>) element.selectNodes("return");
for (Node userNode : userNodes) {
......@@ -299,28 +407,40 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Search for the user using the userService/searchBounded POST method.
*
* @param fields the fields to search on.
* @param query the query string.
* @param startIndex the starting index in the search result to return.
* @param numResults the number of users to return in the search result.
* @return a Collection of users that match the search.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException {
// Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("searchBounded");
Element queryE = rootE.addElement("WSProfileSearchQuery");
Element keywords = queryE.addElement("keywords");
keywords.addText(query);
Element searchUsername = queryE.addElement("searchUsername");
searchUsername.addText("true");
Element searchName = queryE.addElement("searchName");
searchName.addText("true");
Element searchEmail = queryE.addElement("searchEmail");
searchEmail.addText("true");
Element searchProfile = queryE.addElement("searchProfile");
searchProfile.addText("false");
Element paramsE = DocumentHelper.createDocument().addElement("searchBounded");
Element queryE = paramsE.addElement("query");
queryE.addElement("keywords").addText(query);
queryE.addElement("searchUsername").addText("true");
queryE.addElement("searchName").addText("true");
queryE.addElement("searchEmail").addText("true");
queryE.addElement("searchProfile").addText("false");
paramsE.addElement("startIndex").addText(String.valueOf(startIndex));
paramsE.addElement("numResults").addText(String.valueOf(numResults));
try {
List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile/" + startIndex + "/" + numResults;
Element element = manager.executeRequest(GET, path);
//TODO create a service on CS to get only the username field
String path = SEARCH_URL_PREFIX + "searchProfile";
Element element = manager.executeRequest(POST, path, paramsE.asXML());
List<Node> userNodes = (List<Node>) element.selectNodes("return");
for (Node userNode : userNodes) {
String username = userNode.selectSingleNode("username").getText();
......@@ -334,22 +454,44 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Returns true if Clearspace is a read only user provider.
*
* @return true if Clearspace is a read only user provider
*/
public boolean isReadOnly() {
if (readOnly == null) {
synchronized (this) {
if (readOnly == null) {
loadReadOnly();
}
}
}
// If it is null returns the most restrictive answer.
return (readOnly == null ? false : readOnly);
}
/**
* In Clearspace name is optional.
*
* @return false
*/
public boolean isNameRequired() {
return false;
}
/**
* In Clearspace email is required
*
* @return true
*/
public boolean isEmailRequired() {
return true;
}
/**
* Tries to load the read only attribute using the userService/isReadOnly service.
*/
private void loadReadOnly() {
try {
// See if the is read only
......@@ -361,6 +503,12 @@ public class ClearspaceUserProvider implements UserProvider {
}
}
/**
* Translates a Clearspace xml user response into a Openfire User
*
* @param responseNode the Clearspace response
* @return a User instance with its information
*/
private User translate(Node responseNode) {
String username = null;
String name = null;
......@@ -376,21 +524,21 @@ public class ClearspaceUserProvider implements UserProvider {
// Gets the name if it is visible
boolean nameVisible = Boolean.valueOf(userNode.selectSingleNode("nameVisible").getText());
if (nameVisible) {
// Gets the name
tmpNode = userNode.selectSingleNode("name");
if (tmpNode != null) {
name = tmpNode.getText();
}
}
// Gets the email if it is visible
boolean emailVisible = Boolean.valueOf(userNode.selectSingleNode("emailVisible").getText());
if (emailVisible) {
// Gets the email
tmpNode = userNode.selectSingleNode("email");
if (tmpNode != null) {
email = tmpNode.getText();
}
}
// Gets the creation date
tmpNode = userNode.selectSingleNode("creationDate");
......@@ -405,9 +553,19 @@ public class ClearspaceUserProvider implements UserProvider {
}
// Creates the user
return new User(username, name, email, creationDate, modificationDate);
User user = new User(username, name, email, creationDate, modificationDate);
user.setNameVisible(nameVisible);
user.setEmailVisible(emailVisible);
return user;
}
/**
* Gets a user using the userService/users GET service.
*
* @param username the username of the user
* @return the user xml response
* @throws UserNotFoundException if the user could not be found
*/
private Element getUserByUsername(String username) throws UserNotFoundException {
try {
......
......@@ -139,10 +139,10 @@ class ClearspaceVCardTranslator {
/**
* Init the fields of clearspace based on they name.
*
* @param fieldsId
* @param fields the fields information
*/
protected void initClearspaceFieldsId(Element fieldsId) {
List<Element> fieldsList = fieldsId.elements("return");
protected void initClearspaceFieldsId(Element fields) {
List<Element> fieldsList = fields.elements("return");
for (Element field : fieldsList) {
String fieldName = field.elementText("name");
long fieldID = Long.valueOf(field.elementText("ID"));
......@@ -155,6 +155,16 @@ class ClearspaceVCardTranslator {
}
/**
* Translates a VCard of Openfire into profiles, user information and a avatar of Clearspace.
* Returns what can of action has been made over the the profilesElement, userElement and avatarElement/
*
* @param vCardElement the VCard information
* @param profilesElement the profile to add/modify/delete the information
* @param userElement the user to add/modify/delete the information
* @param avatarElement the avatar to add/modify/delete the information
* @return a list of actions performed over the profilesElement, userElement and avatarElement
*/
protected Action[] translateVCard(Element vCardElement, Element profilesElement, Element userElement, Element avatarElement) {
Action[] actions = new Action[3];
......@@ -171,6 +181,13 @@ class ClearspaceVCardTranslator {
return actions;
}
/**
* Updates the avatar values based on the vCard values
*
* @param avatarElement the avatar element to update
* @param vCardValues the vCard values with the information
* @return the action performed
*/
private Action updateAvatarValues(Element avatarElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
......@@ -201,6 +218,13 @@ class ClearspaceVCardTranslator {
return action;
}
/**
* Updates the user values based on the vCard values
*
* @param userElement the user element to update
* @param vCardValues the vCard values
* @return the action performed
*/
private Action updateUserValues(Element userElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
......@@ -228,9 +252,9 @@ class ClearspaceVCardTranslator {
/**
* Updates the values of the profiles with the values of the vCard
*
* @param profiles
* @param vCardValues
* @return
* @param profiles the profiles element to update
* @param vCardValues the vCard values
* @return the action performed
*/
private Action updateProfilesValues(Element profiles, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
......@@ -259,32 +283,32 @@ class ClearspaceVCardTranslator {
String oldValue;
switch (field) {
case TITLE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TITLE)) {
if (modifyProfileValue(vCardValues, value, VCardField.TITLE)) {
action = Action.MODIFY;
}
break;
case DEPARTMENT:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ORG_ORGUNIT)) {
if (modifyProfileValue(vCardValues, value, VCardField.ORG_ORGUNIT)) {
action = Action.MODIFY;
}
break;
case ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_WORK)) {
if (modifyProfileValue(vCardValues, value, VCardField.ADR_WORK)) {
action = Action.MODIFY;
}
break;
case HOME_ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_HOME)) {
if (modifyProfileValue(vCardValues, value, VCardField.ADR_HOME)) {
action = Action.MODIFY;
}
break;
case TIME_ZONE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TZ)) {
if (modifyProfileValue(vCardValues, value, VCardField.TZ)) {
action = Action.MODIFY;
}
break;
case URL:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.URL)) {
if (modifyProfileValue(vCardValues, value, VCardField.URL)) {
action = Action.MODIFY;
}
break;
......@@ -454,6 +478,14 @@ class ClearspaceVCardTranslator {
return action;
}
/**
* Adds a profiles element to the profiles if it is not empty
*
* @param profiles the profiles to add a profile to
* @param field the field type to add
* @param newValue the value to add
* @return true if the field was added
*/
private boolean addProfile(Element profiles, ClearspaceField field, String newValue) {
// Don't add empty vales
if (newValue == null || "".equals(newValue.trim())) {
......@@ -470,7 +502,15 @@ class ClearspaceVCardTranslator {
return true;
}
private boolean modifyProfileValue(Map<VCardField, String> vCardValues, Element profiles, Element value, VCardField vCardField) {
/**
* Modifies the value of a profile if it is different from the original one.
*
* @param vCardValues the vCard values with the new values
* @param value the current value of the profile
* @param vCardField the vCard field
* @return true if the field was modified
*/
private boolean modifyProfileValue(Map<VCardField, String> vCardValues, Element value, VCardField vCardField) {
boolean modified = false;
String newValue = vCardValues.get(vCardField);
......@@ -486,6 +526,13 @@ class ClearspaceVCardTranslator {
return modified;
}
/**
* Adds the field type to the field value. Returns null if the value is empty.
*
* @param value the value
* @param type the type
* @return the field value with the type
*/
private String addFieldType(String value, String type) {
if (value == null || "".equals(value.trim())) {
return null;
......@@ -493,6 +540,13 @@ class ClearspaceVCardTranslator {
return value + "|" + type;
}
/**
* Returns the field type of a field. Return null if the field doesn't
* contains a type
*
* @param field the field with the type
* @return the field type
*/
private String getFieldType(String field) {
int i = field.indexOf("|");
if (i == -1) {
......@@ -502,6 +556,13 @@ class ClearspaceVCardTranslator {
}
}
/**
* Returns the field value of a field. Return the field if the field doesn't
* contains a type.
*
* @param field the field
* @return the field value
*/
private String getFieldValue(String field) {
int i = field.indexOf("|");
if (i == -1) {
......@@ -513,10 +574,10 @@ class ClearspaceVCardTranslator {
/**
* Collects the vCard values and store them into a map.
* They are stored with this constants:
* They are stored with the VCardField enum.
*
* @param vCardElement
* @return
* @param vCardElement the vCard with the information
* @return a map of the value of the vCard.
*/
private Map<VCardField, String> collectVCardValues(Element vCardElement) {
......@@ -602,10 +663,10 @@ class ClearspaceVCardTranslator {
/**
* Translates the information from Clearspace into a VCard.
*
* @param profile
* @param user
* @param avatar
* @return
* @param profile the profile
* @param user the user
* @param avatar the avatar
* @return the vCard with the information
*/
protected Element translateClearspaceInfo(Element profile, User user, Element avatar) {
......@@ -619,7 +680,12 @@ class ClearspaceVCardTranslator {
return vCard;
}
/**
* Translates the profile information to the vCard
*
* @param profiles the profile information
* @param vCard the vCard to add the information to
*/
private void translateProfileInformation(Element profiles, Element vCard) {
// Translate the profile XML
......@@ -747,15 +813,21 @@ class ClearspaceVCardTranslator {
}
}
/**
* Translates the user information to the vCard
*
* @param user the user information
* @param vCard the vCard to add the information to
*/
private void translateUserInformation(User user, Element vCard) {
// The name could be null (if in Clearspace the name is not visible in Openfire it is null)
if (user.getName() != null && !"".equals(user.getName().trim())) {
// Only set the name to the VCard if it is visible
if (user.isNameVisible()) {
vCard.addElement("FN").setText(user.getName());
vCard.addElement("N").addElement("FAMILY").setText(user.getName());
}
// Email is mandatory, but may be invisible
if (user.getEmail() != null && !"".equals(user.getName().trim())) {
// Only set the eamail to the VCard if it is visible
if (user.isEmailVisible()) {
Element email = vCard.addElement("EMAIL");
email.addElement("PREF");
email.addElement("USERID").setText(user.getEmail());
......@@ -765,6 +837,12 @@ class ClearspaceVCardTranslator {
vCard.addElement("JABBERID").setText(jid);
}
/**
* Translates the avatar information to the vCard.
*
* @param avatarResponse the avatar information
* @param vCard the vCard to add the information to
*/
private void translateAvatarInformation(Element avatarResponse, Element vCard) {
Element avatar = avatarResponse.element("return");
if (avatar != null) {
......@@ -781,6 +859,12 @@ class ClearspaceVCardTranslator {
}
}
/**
* Translates the address string of Clearspace to the vCard format.
*
* @param address the address string of Clearspae
* @param addressE the address element to add the address to
*/
private void translateAddress(String address, Element addressE) {
StringTokenizer strTokenize = new StringTokenizer(address, ",");
while (strTokenize.hasMoreTokens()) {
......@@ -819,6 +903,12 @@ class ClearspaceVCardTranslator {
}
/**
* Translates the address form the vCard format to the Clearspace string.
*
* @param addressElement the address in the vCard format
* @return the address int the Clearspace format
*/
private String translateAddress(Element addressElement) {
StringBuilder sb = new StringBuilder();
......@@ -850,8 +940,16 @@ class ClearspaceVCardTranslator {
return sb.toString();
}
private void translateAddressField(Element addressElement, String elementName, String fieldName, StringBuilder sb) {
String field = addressElement.elementTextTrim(elementName);
/**
* Translates the address field from the vCard format to the Clearspace format.
*
* @param addressElement the vCard address
* @param vCardFieldName the vCard field name
* @param fieldName the Clearspace field name
* @param sb the string builder to append the field string to
*/
private void translateAddressField(Element addressElement, String vCardFieldName, String fieldName, StringBuilder sb) {
String field = addressElement.elementTextTrim(vCardFieldName);
if (field != null && !"".equals(field)) {
sb.append(fieldName).append(":").append(field).append(",");
}
......
......@@ -57,13 +57,18 @@ public class User implements Cacheable, Externalizable, Result {
private static final String INSERT_PROPERTY =
"INSERT INTO jiveUserProp (username, name, propValue) VALUES (?, ?, ?)";
// The name of the name visible property
private static final String NAME_VISIBLE_PROPERTY = "name.visible";
// The name of the email visible property
private static final String EMAIL_VISIBLE_PROPERTY = "email.visible";
private String username;
private String name;
private String email;
private Date creationDate;
private Date modificationDate;
private Map<String,String> properties = null;
private Map<String, String> properties = null;
/**
* Returns the value of the specified property for the given username. This method is
......@@ -92,10 +97,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
return propertyValue;
}
......@@ -120,8 +133,7 @@ public class User implements Cacheable, Externalizable, Result {
* @param modificationDate the date the user was last modified.
*/
public User(String username, String name, String email, Date creationDate,
Date modificationDate)
{
Date modificationDate) {
if (username == null) {
throw new NullPointerException("Username cannot be null");
}
......@@ -162,7 +174,7 @@ public class User implements Cacheable, Externalizable, Result {
AuthFactory.getAuthProvider().setPassword(username, password);
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "passwordModified");
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
params);
......@@ -194,7 +206,7 @@ public class User implements Cacheable, Externalizable, Result {
this.name = name;
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "nameModified");
params.put("originalValue", originalName);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -205,6 +217,24 @@ public class User implements Cacheable, Externalizable, Result {
}
}
/**
* Returns true if name is visible to everyone or not.
*
* @return true if name is visible to everyone, false if not.
*/
public boolean isNameVisible() {
return !getProperties().containsKey(NAME_VISIBLE_PROPERTY) || Boolean.valueOf(getProperties().get(NAME_VISIBLE_PROPERTY));
}
/**
* Sets if name is visible to everyone or not.
*
* @param visible true if name is visible, false if not.
*/
public void setNameVisible(boolean visible) {
getProperties().put(NAME_VISIBLE_PROPERTY, String.valueOf(visible));
}
/**
* Returns the email address of the user or <tt>null</tt> if none is defined.
*
......@@ -224,11 +254,11 @@ public class User implements Cacheable, Externalizable, Result {
}
try {
String originalEmail= this.email;
String originalEmail = this.email;
UserManager.getUserProvider().setEmail(username, email);
this.email = email;
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "emailModified");
params.put("originalValue", originalEmail);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -239,6 +269,24 @@ public class User implements Cacheable, Externalizable, Result {
}
}
/**
* Returns true if email is visible to everyone or not.
*
* @return true if email is visible to everyone, false if not.
*/
public boolean isEmailVisible() {
return !getProperties().containsKey(EMAIL_VISIBLE_PROPERTY) || Boolean.valueOf(getProperties().get(EMAIL_VISIBLE_PROPERTY));
}
/**
* Sets if the email is visible to everyone or not.
*
* @param visible true if the email is visible, false if not.
*/
public void setEmailVisible(boolean visible) {
getProperties().put(EMAIL_VISIBLE_PROPERTY, String.valueOf(visible));
}
public Date getCreationDate() {
return creationDate;
}
......@@ -254,7 +302,7 @@ public class User implements Cacheable, Externalizable, Result {
this.creationDate = creationDate;
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "creationDateModified");
params.put("originalValue", originalCreationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -280,7 +328,7 @@ public class User implements Cacheable, Externalizable, Result {
this.modificationDate = modificationDate;
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "nameModified");
params.put("originalValue", originalModificationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
......@@ -298,7 +346,7 @@ public class User implements Cacheable, Externalizable, Result {
*
* @return the extended properties.
*/
public Map<String,String> getProperties() {
public Map<String, String> getProperties() {
synchronized (this) {
if (properties == null) {
properties = new ConcurrentHashMap<String, String>();
......@@ -352,9 +400,8 @@ public class User implements Cacheable, Externalizable, Result {
return true;
}
if (object != null && object instanceof User) {
return username.equals(((User)object).getUsername());
}
else {
return username.equals(((User) object).getUsername());
} else {
return false;
}
}
......@@ -365,22 +412,21 @@ public class User implements Cacheable, Externalizable, Result {
private class PropertiesMap extends AbstractMap {
public Object put(Object key, Object value) {
Map<String,Object> eventParams = new HashMap<String,Object>();
Map<String, Object> eventParams = new HashMap<String, Object>();
Object answer;
String keyString = (String) key;
synchronized (keyString.intern()) {
if (properties.containsKey(keyString)) {
String originalValue = properties.get(keyString);
answer = properties.put(keyString, (String)value);
updateProperty(keyString, (String)value);
answer = properties.put(keyString, (String) value);
updateProperty(keyString, (String) value);
// Configure event.
eventParams.put("type", "propertyModified");
eventParams.put("propertyKey", key);
eventParams.put("originalValue", originalValue);
}
else {
answer = properties.put(keyString, (String)value);
insertProperty(keyString, (String)value);
} else {
answer = properties.put(keyString, (String) value);
insertProperty(keyString, (String) value);
// Configure event.
eventParams.put("type", "propertyAdded");
eventParams.put("propertyKey", key);
......@@ -417,7 +463,7 @@ public class User implements Cacheable, Externalizable, Result {
}
public Object next() {
current = (Map.Entry)iter.next();
current = (Map.Entry) iter.next();
return current;
}
......@@ -425,11 +471,11 @@ public class User implements Cacheable, Externalizable, Result {
if (current == null) {
throw new IllegalStateException();
}
String key = (String)current.getKey();
String key = (String) current.getKey();
deleteProperty(key);
iter.remove();
// Fire event.
Map<String,Object> params = new HashMap<String,Object>();
Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "propertyDeleted");
params.put("propertyKey", key);
UserEventDispatcher.dispatchEvent(User.this,
......@@ -456,10 +502,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(sqle);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
}
......@@ -478,10 +532,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
}
......@@ -500,10 +562,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
}
......@@ -521,10 +591,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
}
......@@ -553,8 +631,7 @@ public class User implements Cacheable, Externalizable, Result {
* (non-Javadoc)
* @see org.jivesoftware.util.resultsetmanager.Result#getUID()
*/
public String getUID()
{
public String getUID() {
return username;
}
}
......@@ -95,9 +95,8 @@ public class UserNameManager {
if (server.isLocal(entity)) {
// Contact is a local entity so search for his user name
User localUser = UserManager.getInstance().getUser(entity.getNode());
return "".equals(localUser.getName()) ? entity.getNode() : localUser.getName();
}
else {
return !localUser.isNameVisible() || "".equals(localUser.getName()) ? entity.getNode() : localUser.getName();
} else {
UserNameProvider provider = providersByDomain.get(entity.getDomain());
if (provider != null) {
return provider.getUserName(entity);
......
......@@ -43,7 +43,7 @@ import java.util.Map.Entry;
/**
* Provides support for Jabber Search
* (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).<p>
*
* <p/>
* The basic functionality is to query an information repository
* regarding the possible search fields, to send a search query,
* and to receive search results. This implementation was primarily designed to use
......@@ -76,6 +76,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* A list of field names that are valid in jabber:iq:search
*/
public final static Collection<String> validSearchRequestFields = new ArrayList<String>();
static {
validSearchRequestFields.add("first");
validSearchRequestFields.add("last");
......@@ -229,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST
* be replied to.
*
* @param iq
* The IQ stanza that forms the request.
* @param iq The IQ stanza that forms the request.
* @return The response to the request.
*/
private IQ handleIQRequest(IQ iq) {
......@@ -284,8 +284,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/**
* Creates a response specific to the search plugin to Disco#Info requests.
*
* @param iq
* The IQ stanza that contains the request.
* @param iq The IQ stanza that contains the request.
* @return An IQ stanza, formulated as an answer to the received request.
*/
private static IQ handleDiscoInfo(IQ iq) {
......@@ -340,8 +339,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* provided as an argument. The stanza tells the recipient that this service
* is currently unavailable.
*
* @param packet
* The request IQ stanza to which a result will be returned.
* @param packet The request IQ stanza to which a result will be returned.
* @return A result stanza, telling the user that this service is
* unavailable.
*/
......@@ -363,8 +361,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* Processes an IQ stanza of type 'get', which in the context of 'Jabber
* Search' is a request for available search fields.
*
* @param packet
* An IQ stanza of type 'get'
* @param packet An IQ stanza of type 'get'
* @return A result IQ stanza that contains the possbile search fields.
*/
private IQ processGetPacket(IQ packet) {
......@@ -424,8 +421,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* Processes an IQ stanza of type 'set', which in the context of 'Jabber
* Search' is a search request.
*
* @param packet
* An IQ stanza of type 'get'
* @param packet An IQ stanza of type 'get'
* @return A result IQ stanza that contains the possbile search fields.
*/
private IQ processSetPacket(IQ packet) {
......@@ -497,8 +493,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* <li>if the stanza child element is has valid children itself.</li>
* </ul>
*
* @param iq
* The IQ object that should include a jabber:iq:search request.
* @param iq The IQ object that should include a jabber:iq:search request.
* @return ''true'' if the supplied IQ stanza is a spec compliant search
* request, ''false'' otherwise.
*/
......@@ -553,8 +548,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/**
* Performs a search based on form data, and returns the search results.
*
* @param incomingForm
* The form containing the search data
* @param incomingForm The form containing the search data
* @return A set of users that matches the search criteria.
*/
private Set<User> performSearch(Element incomingForm) {
......@@ -593,7 +587,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* defined as a set of key->value pairs, where the key denotes a search
* field, and the value contains the value that was filled out by the user
* for that field.
*
* <p/>
* The query can be specified in one of two ways. The first way is a query
* is formed is by filling out any of the the standard search fields. The
* other search method makes use of extended data forms. Search queries that
......@@ -601,8 +595,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* of this last method get forwarded to
* {@link #extractExtendedSearchQuery(Element)}.
*
* @param incomingForm
* The form from which to extract the query
* @param incomingForm The form from which to extract the query
* @return The search query for a particular user search request.
*/
private Hashtable<String, String> extractSearchQuery(Element incomingForm) {
......@@ -637,8 +630,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* specify the search request. This 'extended' way of constructing a search
* request is documented in XEP-0055, chapter 3.
*
* @param incomingForm
* The form from which to extract the query
* @param incomingForm The form from which to extract the query
* @return The search query for a particular user search request.
* @see #extractSearchQuery(Element)
*/
......@@ -713,11 +705,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
items.add(fieldUsername);
XFormFieldImpl fieldName = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.name", "search"));
fieldName.addValue(removeNull(user.getName()));
fieldName.addValue((user.isNameVisible() ? removeNull(user.getName()) : ""));
items.add(fieldName);
XFormFieldImpl fieldEmail = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.email", "search"));
fieldEmail.addValue(removeNull(user.getEmail()));
fieldEmail.addValue((user.isEmailVisible() ? removeNull(user.getEmail()) : ""));
items.add(fieldEmail);
searchResults.addItemFields(items);
......@@ -759,13 +751,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
if ("Name".equals(field)) {
Element element = item.addElement(reverseFieldLookup
.get(field));
element.addText(removeNull(user.getName()));
element.addText(user.isNameVisible() ? removeNull(user.getName()) : "");
}
if ("Email".equals(field)) {
Element element = item.addElement(reverseFieldLookup
.get(field));
element.addText(removeNull(user.getEmail()));
element.addText(user.isEmailVisible() ? removeNull(user.getEmail()) : "");
}
}
}
......@@ -859,13 +851,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
*/
public void propertySet(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = Boolean.parseBoolean((String)params.get("value"));
}
else if (property.equals(SERVICENAME)) {
changeServiceName((String)params.get("value"));
}
else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String)params.get("value")));
this.serviceEnabled = Boolean.parseBoolean((String) params.get("value"));
} else if (property.equals(SERVICENAME)) {
changeServiceName((String) params.get("value"));
} else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String) params.get("value")));
}
}
......@@ -878,11 +868,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
public void propertyDeleted(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = true;
}
else if (property.equals(SERVICENAME)) {
} else if (property.equals(SERVICENAME)) {
changeServiceName("search");
}
else if (property.equals(EXCLUDEDFIELDS)) {
} else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = new ArrayList<String>();
}
}
......@@ -947,8 +935,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* Returns the trimmed argument, or an empty String object of null was
* supplied as an argument.
*
* @param s
* The String to be trimmed.
* @param s The String to be trimmed.
* @return String object that does not start or end with whitespace
* characters.
*/
......@@ -976,7 +963,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* field name of "email" and query of "jsmith@example.com" would search for
* the user with that email address. Wildcard (*) characters are allowed as
* part of queries.
*
* <p/>
* A possible future improvement would be to have a third parameter that
* sets the maximum number of users returned and/or the number of users
* that are searched.
......@@ -1004,33 +991,30 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
catch (UserNotFoundException e) {
Log.error("Error getting user", e);
}
}
else if (field.equals("Name")) {
} else if (field.equals("Name")) {
if (user.isNameVisible()) {
if (query.equalsIgnoreCase(user.getName())) {
foundUsers.add(user);
}
}
else if (field.equals("Email")) {
if (user.getEmail() != null) {
} else if (field.equals("Email")) {
if (user.isEmailVisible() && user.getEmail() != null) {
if (query.equalsIgnoreCase(user.getEmail())) {
foundUsers.add(user);
}
}
}
}
}
else {
} else {
String prefix = query.substring(0, index);
Collection<User> users = userManager.getUsers();
for (User user : users) {
String userInfo = "";
if (field.equals("Username")) {
userInfo = user.getUsername();
}
else if (field.equals("Name")) {
} else if (field.equals("Name")) {
userInfo = user.getName();
}
else if (field.equals("Email")) {
} else if (field.equals("Email")) {
userInfo = user.getEmail() == null ? "" : user.getEmail();
}
......
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