blob: d00f6d67fc6adad190ee8e04290a5dada3fe755c [file] [log] [blame]
package com.google.enterprise.adaptor.sharepoint;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.enterprise.adaptor.AbstractAdaptor;
import com.google.enterprise.adaptor.Acl;
import com.google.enterprise.adaptor.AdaptorContext;
import com.google.enterprise.adaptor.Config;
import com.google.enterprise.adaptor.DocId;
import com.google.enterprise.adaptor.DocIdPusher;
import com.google.enterprise.adaptor.GroupPrincipal;
import com.google.enterprise.adaptor.PollingIncrementalAdaptor;
import com.google.enterprise.adaptor.Request;
import com.google.enterprise.adaptor.Response;
import com.microsoft.schemas.sharepoint.soap.authentication.AuthenticationSoap;
import com.microsoft.webservices.sharepointportalserver.userprofilechangeservice.ArrayOfUserProfileChangeData;
import com.microsoft.webservices.sharepointportalserver.userprofilechangeservice.UserProfileChangeData;
import com.microsoft.webservices.sharepointportalserver.userprofilechangeservice.UserProfileChangeDataContainer;
import com.microsoft.webservices.sharepointportalserver.userprofilechangeservice.UserProfileChangeQuery;
import com.microsoft.webservices.sharepointportalserver.userprofilechangeservice.UserProfileChangeServiceSoap;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.ArrayOfContactData;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.ArrayOfPropertyData;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.ContactData;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.GetUserProfileByIndexResult;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.Privacy;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.PropertyData;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.UserProfileServiceSoap;
import com.microsoft.webservices.sharepointportalserver.userprofileservice.ValueData;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder;
/**
* An adaptor for obtaining user profile information from SharePoint.
* @author tvartak
*
*/
public class SharePointUserProfileAdaptor extends AbstractAdaptor
implements PollingIncrementalAdaptor {
private static final Map<String, String> SP_GSA_PROPERTY_MAPPINGS;
private static final Charset encoding = Charset.forName("UTF-8");
/** SharePoint's namespace. */
private static final String AUTH_XMLNS
= "http://schemas.microsoft.com/sharepoint/soap/";
private static final String XMLNS =
"http://microsoft.com/webservices/SharePointPortalServer/UserProfileService";
private static final String XMLNS_CHANGE =
"http://microsoft.com/webservices/SharePointPortalServer/UserProfileChangeService";
public static final String PROFILE_ACCOUNTNAME_PROPERTY = "AccountName";
private static final String USER_PROFILE_SERVICE_ENDPOINT =
"/_vti_bin/UserProfileService.asmx";
private static final String USER_PROFILE_CHANGE_SERVICE_ENDPOINT =
"/_vti_bin/UserProfileChangeService.asmx";
// Social ID prefix required for Expert Search
public static final String SOCIAL_ID_PREFIX = "social:expert:";
public static final String CONTACT_ELEMENT = "gsa:contact";
public static final String CONTACTS_ROOT_ELEMENT = "gsa:Contacts";
public static final String GSA_NAMESPACE
= "http://www.google.com/schemas/gsa";
public static final String PROFILE_PREFERRED_NAME_PROPERTY = "PreferredName";
public static final String GSA_PROPNAME_COLLEAGUES =
"google_social_user_colleagues";
// Mapping for SharePoint user profile properties to
// GSA Expert Search properties
static {
Map<String, String> map = new HashMap<String, String>();
map.put("SPS-Skills", "google_social_user_skills");
map.put("SPS-PastProjects", "google_social_user_pastprojects");
map.put(PROFILE_ACCOUNTNAME_PROPERTY, "google_social_user_accountname");
map.put(PROFILE_PREFERRED_NAME_PROPERTY,
"google_social_user_preferredname");
SP_GSA_PROPERTY_MAPPINGS = Collections.unmodifiableMap(map);
}
private static final Logger log =
Logger.getLogger(SharePointUserProfileAdaptor.class.getName());
private String virtualServer;
private String mySiteHost;
private NtlmAuthenticator ntlmAuthenticator;
private final UserProfileServiceFactory userProfileServiceFactory;
private String userProfileChangeToken;
private boolean setAcl = true;
private String namespace;
private UserProfileServiceClient userProfileServiceClient;
private ScheduledThreadPoolExecutor scheduledExecutor
= new ScheduledThreadPoolExecutor(1);
public static void main(String[] args) {
AbstractAdaptor.main(new SharePointUserProfileAdaptor(), args);
}
public SharePointUserProfileAdaptor() {
this(new UserProfileServiceFactoryImpl());
}
@VisibleForTesting
SharePointUserProfileAdaptor(
UserProfileServiceFactory userProfileServiceFactory) {
if (userProfileServiceFactory == null) {
throw new NullPointerException();
}
this.userProfileServiceFactory = userProfileServiceFactory;
}
@VisibleForTesting
void setUserProfileChangeToken (String changeToken) {
userProfileChangeToken = changeToken;
}
@VisibleForTesting
String getUserProfileChangeToken() {
return userProfileChangeToken;
}
@Override
public void initConfig(Config config) {
config.addKey("sharepoint.server", null);
config.addKey("sharepoint.username", null);
config.addKey("sharepoint.password", null);
config.addKey("profile.setacl", "true");
config.addKey("adaptor.namespace", "Default");
config.addKey("profile.mysitehost", "");
}
@Override
public void init(AdaptorContext context) throws IOException {
Config config = context.getConfig();
virtualServer = config.getValue("sharepoint.server");
if (virtualServer.endsWith("/")) {
virtualServer = virtualServer.substring(0, virtualServer.length() - 1);
}
String username = config.getValue("sharepoint.username");
String password = context.getSensitiveValueDecoder().decodeValue(
config.getValue("sharepoint.password"));
setAcl = Boolean.parseBoolean(config.getValue("profile.setacl"));
namespace = config.getValue("adaptor.namespace");
log.log(Level.CONFIG, "virtualServer: {0}", virtualServer);
log.log(Level.CONFIG, "Username: {0}", username);
log.log(Level.CONFIG, "setAcl: {0}", setAcl);
log.log(Level.CONFIG, "Namespace: {0}", namespace);
mySiteHost = config.getValue("profile.mysitehost");
log.log(Level.CONFIG, "mySiteHost: {0}", mySiteHost);
if (mySiteHost.isEmpty()) {
log.log(Level.WARNING, "My site host is not specified."
+ " Using virtual server url as My site host.");
mySiteHost = virtualServer;
}
if (mySiteHost.endsWith("/")) {
mySiteHost = mySiteHost.substring(0, mySiteHost.length() - 1);
}
ntlmAuthenticator = new NtlmAuthenticator(username, password);
// Unfortunately, this is a JVM-wide modification.
Authenticator.setDefault(ntlmAuthenticator);
String authenticationEndPoint
= virtualServer + "/_vti_bin/Authentication.asmx";
FormsAuthenticationHandler authenticationHandler
= new FormsAuthenticationHandler(username, password, scheduledExecutor,
userProfileServiceFactory.newAuthentication(authenticationEndPoint));
authenticationHandler.start();
log.log(Level.FINEST, "Initializing User profile Service Client for {0}",
virtualServer + USER_PROFILE_SERVICE_ENDPOINT);
userProfileServiceClient = new UserProfileServiceClient(
userProfileServiceFactory.newUserProfileService(
virtualServer + USER_PROFILE_SERVICE_ENDPOINT,
virtualServer + USER_PROFILE_CHANGE_SERVICE_ENDPOINT,
authenticationHandler.getAuthenticationCookies()));
userProfileChangeToken =
userProfileServiceClient.userProfileServiceWS.getCurrentChangeToken();
}
@Override
public void destroy() {
Authenticator.setDefault(null);
scheduledExecutor.shutdown();
try {
scheduledExecutor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
scheduledExecutor.shutdownNow();
}
@Override
public void getDocContent(
Request request, Response response) throws IOException {
userProfileServiceClient.getDocContent(request, response);
}
@Override
public void getDocIds(DocIdPusher pusher) throws IOException,
InterruptedException {
userProfileServiceClient.getDocIds(pusher);
}
@Override
public void getModifiedDocIds(DocIdPusher pusher)
throws InterruptedException, IOException {
userProfileChangeToken = userProfileServiceClient.getModifiedDocIds(pusher,
userProfileChangeToken);
log.log(Level.FINE, "getModifiedDocIds returned change token: {0}",
userProfileChangeToken);
}
private static class NtlmAuthenticator extends Authenticator {
private final String username;
private final char[] password;
public NtlmAuthenticator(String username, String password) {
this.username = username;
this.password = password.toCharArray();
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
}
@VisibleForTesting
interface UserProfileServiceFactory {
public UserProfileServiceWS newUserProfileService(String endpoint,
String endpointChangeService, List<String> cookies);
public AuthenticationSoap newAuthentication(String endpoint);
}
private static class UserProfileServiceFactoryImpl
implements UserProfileServiceFactory {
private final Service userProfileServiceSoap;
private final Service userProfileChangeServiceSoap;
private final Service authenticationService;
public UserProfileServiceFactoryImpl() {
URL urlUserProfileService =
UserProfileServiceSoap.class.getResource("UserProfileService.wsdl");
QName qname = new QName(XMLNS, "UserProfileService");
this.userProfileServiceSoap = Service.create(
urlUserProfileService, qname);
URL urlUserProfileChangeService =
UserProfileChangeServiceSoap.class.getResource(
"UserProfileChangeService.wsdl");
QName qnameChange = new QName(XMLNS_CHANGE, "UserProfileChangeService");
this.userProfileChangeServiceSoap = Service.create(
urlUserProfileChangeService, qnameChange);
this.authenticationService = Service.create(
AuthenticationSoap.class.getResource("Authentication.wsdl"),
new QName(AUTH_XMLNS, "Authentication"));
}
@Override
public UserProfileServiceWS newUserProfileService(String endpoint,
String endpointChangeService, List<String> cookies) {
EndpointReference endpointRef = new W3CEndpointReferenceBuilder()
.address(endpoint).build();
EndpointReference endpointChangeRef = new W3CEndpointReferenceBuilder()
.address(endpointChangeService).build();
UserProfileServiceSoap inUserProfileServiceSoap
= userProfileServiceSoap.getPort(
endpointRef, UserProfileServiceSoap.class);
UserProfileChangeServiceSoap inUserProfileChangeServiceSoap
= userProfileChangeServiceSoap.getPort(
endpointChangeRef, UserProfileChangeServiceSoap.class);
// JAX-WS RT 2.1.4 doesn't handle headers correctly and always assumes the
// list contains precisely one entry, so we work around it here.
if (!cookies.isEmpty()) {
addFormsAuthenticationCookies(
(BindingProvider) inUserProfileServiceSoap, cookies);
addFormsAuthenticationCookies(
(BindingProvider) inUserProfileChangeServiceSoap, cookies);
}
return new SharePointUserProfileServiceWS(inUserProfileServiceSoap,
inUserProfileChangeServiceSoap);
}
private void addFormsAuthenticationCookies(BindingProvider port,
List<String> cookies) {
port.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS,
Collections.singletonMap("Cookie", cookies));
}
@Override
public AuthenticationSoap newAuthentication(String endpoint) {
EndpointReference endpointRef = new W3CEndpointReferenceBuilder()
.address(endpoint).build();
return
authenticationService.getPort(endpointRef, AuthenticationSoap.class);
}
}
@VisibleForTesting
static interface UserProfileServiceWS {
public GetUserProfileByIndexResult getUserProfileByIndex(int index)
throws WebServiceException;
public ArrayOfPropertyData getUserProfileByName(String userName)
throws WebServiceException;
public ArrayOfContactData getUserColleagues(String key)
throws WebServiceException;
public String getCurrentChangeToken() throws WebServiceException;
public UserProfileChangeDataContainer getUserProfileChanges(
String lastChangeToken, UserProfileChangeQuery changeQuery)
throws WebServiceException;
}
// SharePoint implementation for User Profile Service
// and User Profile Change Service
private static class SharePointUserProfileServiceWS
implements UserProfileServiceWS {
private final UserProfileServiceSoap userProfileServiceSoap;
private final UserProfileChangeServiceSoap userProfileChangeServiceSoap;
public SharePointUserProfileServiceWS(
UserProfileServiceSoap userProfileServiceSoap,
UserProfileChangeServiceSoap userProfileChangeServiceSoap) {
this.userProfileServiceSoap = LoggingWSHandler.create(
UserProfileServiceSoap.class, userProfileServiceSoap);
this.userProfileChangeServiceSoap = LoggingWSHandler.create(
UserProfileChangeServiceSoap.class, userProfileChangeServiceSoap);
}
@Override
public GetUserProfileByIndexResult getUserProfileByIndex(int index)
throws WebServiceException {
return userProfileServiceSoap.getUserProfileByIndex(index);
}
@Override
public ArrayOfContactData getUserColleagues(String key)
throws WebServiceException {
return userProfileServiceSoap.getUserColleagues(key);
}
@Override
public ArrayOfPropertyData getUserProfileByName(String userName)
throws WebServiceException {
return userProfileServiceSoap.getUserProfileByName(userName);
}
@Override
public String getCurrentChangeToken() throws WebServiceException {
try {
return userProfileChangeServiceSoap.getCurrentChangeToken();
} catch (Exception ex) {
log.log(Level.WARNING,
"Error fetching change token from SharePoint. Returning null.", ex);
return null;
}
}
@Override
public UserProfileChangeDataContainer getUserProfileChanges(
String lastChangeToken, UserProfileChangeQuery changeQuery) {
return userProfileChangeServiceSoap.getChanges(
lastChangeToken, changeQuery);
}
}
@VisibleForTesting
class UserProfileServiceClient {
private final UserProfileServiceWS userProfileServiceWS;
private DOMImplementation domImpl;
private DOMImplementationLS ls;
public UserProfileServiceClient(
UserProfileServiceWS userProfileServiceWS) {
this.userProfileServiceWS = userProfileServiceWS;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
this.domImpl = db.getDOMImplementation();
ls = (DOMImplementationLS) domImpl;
} catch (ParserConfigurationException pce) {
log.log(Level.WARNING,
"Colleagues information will be missing as " +
"Parser Configuration Exception creating DOMImplementation", pce);
}
}
public void getDocIds(DocIdPusher pusher)
throws RemoteException, InterruptedException {
int index = -1;
List<DocId> profilesToPush = new ArrayList<DocId>();
do {
GetUserProfileByIndexResult nextProfile;
try {
nextProfile = userProfileServiceWS.getUserProfileByIndex(index);
} catch (WebServiceException e) {
log.log(Level.WARNING,
"Error fetching user profile at index {0}", index);
log.log(Level.WARNING,
"Exception for getUserProfileByIndex : ", e);
// Flushing available docids
pusher.pushDocIds(profilesToPush);
throw e;
}
if (nextProfile == null) {
break;
}
index = Integer.parseInt(nextProfile.getNextValue());
log.log(Level.FINEST, "Next Index is {0}", index);
ArrayOfPropertyData profileProperties = nextProfile.getUserProfile();
String userAccountName = getUserProfilePropertySingleValue(
profileProperties, PROFILE_ACCOUNTNAME_PROPERTY);
if (!Strings.isNullOrEmpty(userAccountName)) {
profilesToPush.add(new DocId(SOCIAL_ID_PREFIX + userAccountName));
log.log(Level.FINEST, "Adding Doc ID {0}",
SOCIAL_ID_PREFIX + userAccountName);
}
if (profilesToPush.size() == 500) {
pusher.pushDocIds(profilesToPush);
profilesToPush.clear();
}
} while (index != -1); // For last profile next value will be -1
pusher.pushDocIds(profilesToPush);
}
public void getDocContent(
Request request, Response response) throws IOException {
DocId id = request.getDocId();
String uniqueId = id.getUniqueId();
if (!uniqueId.startsWith(SOCIAL_ID_PREFIX)) {
log.log(Level.WARNING, "Invalid DocID {0}", uniqueId);
response.respondNotFound();
return;
}
String userName = uniqueId.substring(SOCIAL_ID_PREFIX.length());
log.log(Level.FINEST, "Fetching user profile for {0}", userName);
ArrayOfPropertyData userProfileProperties = null;
try {
userProfileProperties =
userProfileServiceWS.getUserProfileByName(userName);
} catch (WebServiceException e) {
log.log(Level.WARNING,
"Error getting User profile {0}", e.getMessage());
// SharePoint 2010 : could not be found
// MOSS 2007 : User Not Found:
if (e.getMessage() == null ||
(!e.getMessage().contains("could not be found") &&
!e.getMessage().contains("User Not Found:"))) {
log.log(Level.WARNING,
"Error getting User profile for {0}", userName);
throw new IOException(e);
}
}
if (userProfileProperties == null) {
log.log(Level.WARNING, "User profile not available for {0}",
userName);
response.respondNotFound();
return;
}
List<PropertyData> properties = userProfileProperties.getPropertyData();
for (PropertyData prop : properties) {
String propertyName = getGSAPropertyMapping(prop.getName());
if (prop.getPrivacy() != Privacy.PUBLIC) {
log.log(Level.FINE, "Excluding non public property {0}",
propertyName);
continue;
}
List<String> values = readUserProfilePropertyValues(prop);
for (String v : values) {
response.addMetadata(propertyName, v);
}
}
if (setAcl) {
List<GroupPrincipal> permitGroups = new ArrayList<GroupPrincipal>();
permitGroups.add(
new GroupPrincipal("NT AUTHORITY\\Authenticated Users", namespace));
response.setAcl(new Acl.Builder().setEverythingCaseInsensitive()
.setInheritanceType(Acl.InheritanceType.LEAF_NODE)
.setPermitGroups(permitGroups).build());
}
// domImpl is required for Colleagues data processing
if (this.domImpl != null) {
ArrayOfContactData colleagues =
userProfileServiceWS.getUserColleagues(userName);
String colleaguesXml = serializeColleagues(colleagues);
if (colleaguesXml != null) {
response.addMetadata(GSA_PROPNAME_COLLEAGUES, colleaguesXml);
}
}
String displayUrl = mySiteHost + "/person.aspx?accountname="
+ URLEncoder.encode(userName, "UTF-8");
response.setDisplayUrl(URI.create(displayUrl));
String userProfileTitle = getUserProfilePropertySingleValue(
userProfileProperties, PROFILE_PREFERRED_NAME_PROPERTY);
if (userProfileTitle == null) {
userProfileTitle = userName;
}
OutputStream os = response.getOutputStream();
os.write(MessageFormat.format("<html><head><title>{0}</title></head>"
+ "<body><h1>{0}</h1></body></html>",
escapeContent(userProfileTitle)).getBytes(encoding));
}
public String getModifiedDocIds(DocIdPusher pusher, String lastChangeToken)
throws InterruptedException, IOException {
log.log(Level.FINE, "Last Change Token available with Adaptor [{0}]",
lastChangeToken);
String changeTokenOnSharePoint = userProfileServiceWS.getCurrentChangeToken();
if (Strings.isNullOrEmpty(lastChangeToken)) {
// Since last token is empty returning current change token
// from SharePoint for processing future updates.
return changeTokenOnSharePoint;
}
String changeTokenToUse = lastChangeToken;
Set<DocIdPusher.Record> profilesToPush =
new HashSet<DocIdPusher.Record>();
UserProfileChangeQuery changeQuery = new UserProfileChangeQuery();
changeQuery.setDelete(true);
changeQuery.setAdd(true);
changeQuery.setUserProfile(true);
changeQuery.setUpdate(true);
changeQuery.setUpdateMetadata(true);
changeQuery.setSingleValueProperty(true);
changeQuery.setMultiValueProperty(true);
changeQuery.setColleague(true);
while (true) {
log.log(Level.FINE, "Getting changes with change token [{0}]",
changeTokenToUse);
UserProfileChangeDataContainer changeContainer = null;
try {
changeContainer = userProfileServiceWS.getUserProfileChanges(
changeTokenToUse, changeQuery);
} catch (WebServiceException e) {
log.log(Level.WARNING,
"Error Getting changes with change token [{0}]",
changeTokenToUse);
log.log(Level.WARNING, "Exception getUserProfileChanges : ", e);
return changeTokenOnSharePoint;
}
if (changeContainer == null) {
log.log(Level.WARNING,
"Recevived null change container with change token [{0}]",
changeTokenToUse);
return changeTokenOnSharePoint;
}
ArrayOfUserProfileChangeData changeData = changeContainer.getChanges();
String changeTokenFromResult = changeContainer.getChangeToken();
if (changeData == null ||
changeData.getUserProfileChangeData().isEmpty()) {
log.log(Level.FINE, "No profile changes with change token [{0}]",
changeTokenToUse);
return changeTokenOnSharePoint;
}
List<UserProfileChangeData> changes =
changeData.getUserProfileChangeData();
for (UserProfileChangeData change : changes) {
String userAccountName = change.getUserAccountName();
log.log(Level.FINE, "Processing change for user [{0}]",
userAccountName);
profilesToPush.add(new DocIdPusher.Record.Builder(
new DocId(SOCIAL_ID_PREFIX + userAccountName))
.setCrawlImmediately(true).build());
}
pusher.pushRecords(profilesToPush);
profilesToPush.clear();
changeTokenToUse = changeTokenFromResult;
log.log(Level.FINE, "Next change token for query [{0}]",
changeTokenToUse);
if (Strings.isNullOrEmpty(changeTokenToUse)) {
return changeTokenOnSharePoint;
}
}
}
private String getGSAPropertyMapping(String spPropertyName) {
return SP_GSA_PROPERTY_MAPPINGS.containsKey(spPropertyName) ?
SP_GSA_PROPERTY_MAPPINGS.get(spPropertyName) :
normalizeSPPropertyNameForGSA(spPropertyName);
}
private String escapeContent(String raw) {
return raw.replace("&", "&amp;").replace("<", "&lt;");
}
/**
* Normalize propertynames so that they become queryable in GSA.
* Replacing '-'with '_'.
* If there are other quirky restrictions we need to add them here.
*/
private String normalizeSPPropertyNameForGSA(String name) {
return name.replace('-', '_');
}
private List<String> getUserProfilePropertyValues(
ArrayOfPropertyData profileProperties, String propertyName) {
if (profileProperties == null) {
return null;
}
for (PropertyData property : profileProperties.getPropertyData()) {
if (propertyName.equalsIgnoreCase(property.getName())) {
return readUserProfilePropertyValues(property);
}
}
return null;
}
private List<String> readUserProfilePropertyValues(
PropertyData property) {
List<String> values = new ArrayList<String>();
if (property.getValues() != null) {
for (ValueData value : property.getValues().getValueData()) {
values.add(value.getValue().toString());
}
}
return values;
}
private String getUserProfilePropertySingleValue(
ArrayOfPropertyData profileProperties, String propertyName) {
List<String> values =
getUserProfilePropertyValues(profileProperties, propertyName);
if (values == null || values.isEmpty()) {
return null;
} else {
return values.get(0);
}
}
@VisibleForTesting
String serializeColleagues(ArrayOfContactData colleaguesData) {
if (colleaguesData == null) {
return null;
}
List<ContactData> colleagues = colleaguesData.getContactData();
if (colleagues == null || colleagues.isEmpty()) {
return null;
}
if (domImpl == null) {
// Return null as domImpl is not available.
log.log(Level.WARNING, "Returing null as DOMImplemenatation is null");
return null;
}
Document colleaguesDocument;
try {
colleaguesDocument = domImpl.createDocument(
GSA_NAMESPACE, CONTACTS_ROOT_ELEMENT, null);
} catch (DOMException de) {
log.log(Level.WARNING, "DOM Exception processing user colleagues",
de);
return null;
}
// Create the root element
for (ContactData oneColleague : colleagues) {
// For each Colleague object create element and attach it to root
if (oneColleague.getPrivacy() == Privacy.PUBLIC) {
Element colleagueElem = createColleagueElement(
colleaguesDocument, oneColleague);
if (colleagueElem != null) {
colleaguesDocument.getDocumentElement().appendChild(colleagueElem);
}
}
}
if (!colleaguesDocument.getDocumentElement().hasChildNodes()) {
return null;
}
LSSerializer lss = ls.createLSSerializer();
LSOutput lso = ls.createLSOutput();
StringWriter writer = new StringWriter();
lso.setCharacterStream(writer);
lss.write(colleaguesDocument, lso);
String result = writer.toString();
return result;
}
@VisibleForTesting
Element createColleagueElement(Document colleaguesDocument,
ContactData oneColleague) {
Element ele = colleaguesDocument.createElementNS(GSA_NAMESPACE, CONTACT_ELEMENT);
String accountName = oneColleague.getAccountName();
if (accountName == null) {
return null;
}
setColleagueAttribute(ele, "gsa:accountname", accountName);
setColleagueAttribute(ele, "gsa:name", oneColleague.getName());
setColleagueAttribute(ele, "gsa:email", oneColleague.getEmail());
setColleagueAttribute(ele, "gsa:url", oneColleague.getUrl());
setColleagueAttribute(ele, "gsa:title", oneColleague.getTitle());
setColleagueAttribute(ele, "gsa:group", oneColleague.getGroup());
setColleagueAttribute(ele, "gsa:isinworkinggroup",
oneColleague.isIsInWorkGroup() ? "true" : "false");
return ele;
}
@VisibleForTesting
void setColleagueAttribute(Element e, String atrbName, String atrbValue) {
atrbValue = atrbValue == null ? "" : atrbValue;
try {
e.setAttributeNS(GSA_NAMESPACE, atrbName, URLEncoder.encode(atrbValue, "UTF-8"));
} catch (UnsupportedEncodingException uee) {
log.log(Level.WARNING, "Error encoding value",
uee);
}
}
}
}