/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/examples/org/any_openeai_enterprise/gateways/directoryservice/DirectoryServiceBase.java,v $
$Revision: 1.4 $
*******************************************************************************/
/**********************************************************************
This file is part of the OpenEAI sample, reference implementation,
and deployment management suite created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.
Copyright (C) 2004 The OpenEAI Software Foundation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For specific licensing details and examples of how this software
can be used to implement integrations for your enterprise, visit
http://www.OpenEai.org/licensing.
*/
package org.any_openeai_enterprise.gateways.directoryservice;
// Core Java
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.any_openeai_enterprise.moa.jmsobjects.coreapplication.v1_0.EnterpriseUser;
import org.any_openeai_enterprise.moa.jmsobjects.coreapplication.v1_0.EnterpriseUserPassword;
import org.jdom.Element;
import org.openeai.config.CommandConfig;
import org.openeai.config.EnterpriseConfigurationObjectException;
import org.openeai.config.PropertyConfig;
import org.openeai.jms.consumer.commands.SyncCommand;
import org.openeai.jms.consumer.commands.SyncCommandImpl;
import org.openeai.moa.objects.resources.Error;
import com.any_erp_vendor.moa.objects.resources.v1_0.Email;
import com.any_erp_vendor.moa.objects.resources.v1_0.Phone;
/**
* This command handles BasicPerson.Update-Sync messages to apply changes to
* person data to the LDAP directory server.
* <P>
*
*/
public abstract class DirectoryServiceBase extends SyncCommandImpl implements SyncCommand {
protected abstract String getServiceName();
private String m_userDirectoryTreeBase;
private String m_providerUrl;
private int m_reconnectAttemptLimit;
private DirContext m_dirCtx;
private boolean m_publishErrorsForMissingEntries = false;
protected Element eControlArea;
private String m_enterpriseIdDomain;
private boolean m_createMissingUsers;
/**
* Constructor
*/
public DirectoryServiceBase(CommandConfig cConfig) throws InstantiationException {
super(cConfig);
// Get the general properties for this command.
try {
PropertyConfig pConfig = new PropertyConfig();
pConfig = (PropertyConfig)getAppConfig().getObject("GeneralProperties");
setProperties(pConfig.getProperties());
}
catch (EnterpriseConfigurationObjectException ecoe) {
// An error occurred retreiving the general properties objects. Log it and
// throw an exception.
String errMsg = "An error occurred retrieving the general properties " +
"objects from AppConfig. The exception is: " + ecoe.getMessage();
logger.fatal("[" + getServiceName() + "] " + errMsg);
throw new InstantiationException(errMsg);
}
// Get the reconnect attempt limit property.
String sReconnectAttemptLimit = getProperties()
.getProperty("reconnectAttemptLimit", "2");
int reconnectAttemptLimit = Integer.parseInt(sReconnectAttemptLimit);
setReconnectAttemptLimit(reconnectAttemptLimit);
logger.info("[" + getServiceName() + "] reconnectAttemptLimit is: " +
getReconnectAttemptLimit() + ".");
// Set the user directory tree base to use when updating user entries.
String userDirectoryTreeBase = getProperties()
.getProperty("userDirectoryTreeBase");
setUserDirectoryTreeBase(userDirectoryTreeBase);
if (userDirectoryTreeBase == null) {
// The user directory tree base is null. Log it and throw an exception.
String errMsg = "Missing 'userDirectoryTreeBase' property in the " +
"deployment descriptor. Can't continue.";
throw new InstantiationException(errMsg);
}
logger.info("[" + getServiceName() + "] " + "userDirectoryTreeBase is: " +
getUserDirectoryTreeBase() + ".");
// Get an initial context to the directory server.
try {
DirContext dirCtx = getInitialContext(getProperties());
setDirContext(dirCtx);
}
catch (NamingException ne) {
String errMsg = "Error creating initial context. The exception is: " +
ne.getMessage();
logger.fatal("[" + getServiceName() + "] " + errMsg);
throw new InstantiationException(errMsg);
}
logger.info("[" + getServiceName() + "] instantiated successfully.");
}
protected void deleteEmail(String uid){
try {
String newEmail = null;
logger.info("[" + getServiceName() + ".execute] The current email of the user '" + uid + "' will be deleted.");
//Build the dn for the directory user.
String dn = "uid=" + uid + "," + getUserDirectoryTreeBase();
Attribute mail = new BasicAttribute("mail");
mail.add(newEmail);
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mail);
// Perform the modification.
logger.debug("[" + getServiceName() + ".execute] Modifying the email attribute for user " + dn + ".");
getDirContext().modifyAttributes(dn, mods);
logger.info("[" + getServiceName() + ".execute] Modified the email attribute for user " + dn + ".");
}
catch (NamingException ne) {
// An error occurred updating the user's name in the directory.
// Log it and publish a sync error message.
publishError ("system", "DirectoryServiceGateway-1005", "An error occurred deleting the " +
"user's email from the directory. The exception is: " + ne.getMessage(), "[" + getServiceName() + ".execute] ", ne);
return;
}
return;
}
protected void deletePhone(String uid, String type){
try {
String outputType = " ";
if(type == "telephoneNumber")outputType = "office";
if(type == "homePhone")outputType = "home";
if(type == "mobile")outputType = "mobile";
String newPhone = null;
logger.info("[" + getServiceName() + ".execute] The current " + outputType + " phone " +
"of the user will be deleted.");
//Build the dn for the directory user.
String dn = "uid=" + uid + "," + getUserDirectoryTreeBase();
Attribute phone = new BasicAttribute(type);
phone.add(newPhone);
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
phone);
// Perform the modification.
logger.debug("[" + getServiceName() + ".execute] Modifying the "
+ outputType + " phone attribute for user " + dn + ".");
getDirContext().modifyAttributes(dn, mods);
logger.info("[" + getServiceName() + ".execute] Modified the "
+ outputType + " phone attribute for user " + dn + ".");
}
catch (NamingException ne) {
// An error occurred while deleting the mobile number in the directory.
// Log it and publish a sync error message.
publishError ("system", "DirectoryServiceGateway-1006", "An error occurred while deleting a " +
"phone number from the user's directory. The exception is: " +
ne.getMessage(), "[" + getServiceName() + ".execute] ", ne);
return;
}
return;
}
/**
* @param uid uid used as a part of the LDAP DN
* @param newEmail new e-mail address to store
*/
protected void updateEmail(String uid, String newEmail) {
try {
// Build the dn for the directory user.
String dn = "uid=" + uid + "," + getUserDirectoryTreeBase();
Attribute mail = new BasicAttribute("mail");
mail.add(newEmail);
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mail);
// Perform the modification.
logger.debug("[" + getServiceName() + ".updateEmail] Modifying the mail attribute for user " + dn + " to " + newEmail + ".");
getDirContext().modifyAttributes(dn, mods);
logger.debug("[" + getServiceName() + ".updateEmail] Modified the mail attribute for user " + dn + " to " + newEmail + ".");
} catch (NamingException ne) {
// An error occurred updating the user's name in the directory.
// Log it and publish a sync error message.
publishError("system", "DirectoryServiceGateway-1005", "An error occurred updating the " + "user's email in the directory. The exception is: "
+ ne.getMessage(), "[" + getServiceName() + ".updateEmail] ", ne);
return;
}
return;
}
/**
* @param uid uid used as a part of the LDAP DN
* @param eMails a List of Email objects
*/
protected void updateEmail (String uid, List eMails) {
boolean preferredEmailUpdated = false;
if (eMails != null && eMails.size() > 0) {
// Look for preferred e-mail and update that
for (int i = 0;i < eMails.size();i++) {
Email email = (Email)eMails.get(i);
if (email.getPreferred().equalsIgnoreCase("yes")) {
updateEmail (uid, email.getEmailAddress());
preferredEmailUpdated = true;
break;
}
}
// If preferred e-mail wasn't found, update the first in the list
if (!preferredEmailUpdated) {
Email email = (Email)eMails.get(0);
String emailAddress = email.getEmailAddress();
updateEmail (uid, emailAddress);
}
}
}
protected void updatePhone (String uid, List phones) {
for (int i = 0;i < phones.size();i++) {
Phone phone = (Phone)phones.get(i);
String phoneType = phone.getType();
String phoneNumber = phone.getPhoneArea() + "-" + phone.getPhoneNumber();
logger.debug("[" + getServiceName() + ".updatePhone] Modifying the phone " + phoneType + "to " + phoneNumber + " for user " + uid + ".");
}
}
/**
* @param uid uid used as a part of the LDAP DN
* @param newPhone new phone number to store
* @param type the phone number type to store
*/
protected void updatePhone(String uid, String newPhone, String type) {
try {
String outputType = " ";
if (type == "telephoneNumber")
outputType = "office";
if (type == "homePhone")
outputType = "home";
if (type == "mobile")
outputType = "mobile";
logger.info("[" + getServiceName() + ".updatePhone] Updating " + outputType + " phone of the user ('" + uid + "') with '" + newPhone
+ "'.");
// Build the dn for the directory user.
String dn = "uid=" + uid + "," + getUserDirectoryTreeBase();
Attribute phone = new BasicAttribute(type);
phone.add(newPhone);
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, phone);
// Perform the modification.
logger.debug("[" + getServiceName() + ".execute] Modifying the " + outputType + " phone attribute for user " + dn + ".");
getDirContext().modifyAttributes(dn, mods);
logger.info("[" + getServiceName() + ".execute] Modified the " + outputType + " phone attribute for user " + dn + ".");
} catch (NamingException ne) {
// An error occurred updating the user's name in the directory.
// Log it and publish a sync error message.
publishError("system", "DirectoryServiceGateway-1007", "An error occurred updating the " + "user's " + type
+ " number in the directory. The exception is: " + ne.getMessage(), "[" + getServiceName() + ".execute] ", ne);
}
}
/**
* @return DirContext for the LDAP directory as configured in the deployment descriptor
* @throws NamingException
*/
protected final DirContext getDirContext() throws NamingException {
logger.debug("[" + getServiceName() + ".getDirContext] Getting a directory " +
"context to use in a transaction.");
DirContext dirCtx = null;
for (int i=0; i <= getReconnectAttemptLimit(); i++) {
try {
dirCtx = (DirContext)m_dirCtx.lookup("");
}
catch (NamingException ne1) {
// An error occurred retrieving a directory context. Log it, attempt
// to create a new initial context, and then try to get another context
// to return for use.
String errMsg = "An error occurred retrieving a usable directory " +
"context. The exception is: " + ne1.getMessage() + ".";
logger.warn("[" + getServiceName() + ".getDirContext] " + errMsg);
logger.warn("[" + getServiceName() + ".getDirContext] Attempting to " +
"create a new initial context to the directory server...");
try {
dirCtx = getInitialContext(getProperties());
setDirContext(dirCtx);
}
catch (NamingException ne2) {
// An error occurred re-creating a new initial context. Log it and
// throw an exception.
errMsg = "An error occurred re-creating a new initial context. "
+ "The exception is: " + ne2.getMessage();
logger.fatal("[" + getServiceName() + ".getDirContext] " + errMsg);
throw new NamingException(errMsg);
}
try {
dirCtx = (DirContext)getDirContext().lookup("");
}
catch (NamingException ne3) {
// An error occurred retrieving a usable directory context using the
// re-created directory context. Log it and throw an exception.
errMsg = "An error occurred retrieving a usable directory context " +
"using the re-created directory context. The exception is: " +
ne3.getMessage();
logger.fatal("[" + getServiceName() + ".getDirContext] " + errMsg);
throw new NamingException(errMsg);
}
}
}
return dirCtx;
}
protected final synchronized DirContext getInitialContext(Properties props) throws NamingException {
// Get the providerUrl, initialContextFactory, securityPrincipal, and
// securityCredentials property values from the properties object.
String providerUrl = props.getProperty("providerUrl");
if (providerUrl == null) {
throw new NamingException("Null 'providerUrl' not allowed.");
}
String initialCtxFactory= props.getProperty("initialContextFactory");
if (initialCtxFactory == null) {
throw new NamingException("Null 'initialContextFactory' not allowed.");
}
String principal = props.getProperty("securityPrincipal");
if (principal == null) {
throw new NamingException("Null 'securityPrincipal' not allowed.");
}
String credentials= props.getProperty("securityCredentials");
if (credentials == null) {
throw new NamingException("Null 'securityCredentials' not allowed.");
}
// Initialize a directory context and environment hash table with which
// we will create the initial context object.
DirContext ic = null;
Hashtable env = new Hashtable(5, 0.75f);
// Determine if we should connect to the directory server securely. If so,
// rewrite the providerUrl and set the security protocol in the environment
// hash table to SSL.
if (providerUrl.indexOf("ldaps:") != -1) {
String newProviderUrl = "ldap:" +
providerUrl.substring(providerUrl.indexOf(":") + 1);
providerUrl = newProviderUrl;
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
// Set the initial context factory, security authentication, principal,
// and credentials environment values
env.put(Context.INITIAL_CONTEXT_FACTORY, initialCtxFactory);
env.put(Context.PROVIDER_URL, providerUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
// Use the environment hash table to create the initial context.
logger.info("[" + getServiceName() + ".getInitialContext] Creating a new " +
"initial context object (establishing a connection "+
"to the directory server with which to query for " +
"EnterprisePermissions during command executions)...");
ic = new InitialDirContext(env);
logger.info("[" + getServiceName() + ".getInitialContext] Created the " +
"InitialContext object...");
// Set the providerUrl, so it can be used in subsequent queries.
setProviderUrl(providerUrl);
return ic;
}
protected int getReconnectAttemptLimit() {
return m_reconnectAttemptLimit;
}
protected void setReconnectAttemptLimit(int limit) {
m_reconnectAttemptLimit = limit;
}
protected String getUserDirectoryTreeBase() {
return m_userDirectoryTreeBase;
}
protected void setUserDirectoryTreeBase(String base) {
m_userDirectoryTreeBase = base;
}
protected boolean getPublishErrorsForMissingEntries() {
return m_publishErrorsForMissingEntries;
}
protected void setPublishErrorsForMissingEntries(boolean publishErrors) {
m_publishErrorsForMissingEntries = publishErrors;
}
protected String getProviderUrl() {
return m_providerUrl;
}
protected void setProviderUrl(String url) {
m_providerUrl = url;
}
protected void setDirContext(DirContext dirCtx) {
m_dirCtx = dirCtx;
}
/**
* @param errType - Type of the error, for example, "system" or "application"
* @param errNumber - Error number
* @param errDescription - Detailed description of the error
* @return ArrayList containing exactly one Error
*/
protected ArrayList fillErrors (String errType, String errNumber, String errDescription) {
ArrayList<Error> errors = new ArrayList<Error>();
Error error = new Error();
error.setType(errType);
error.setErrorNumber(errNumber);
error.setErrorDescription(errDescription);
errors.add(error);
return errors;
}
/**
* @param errType - Type of the error, for example, "system" or "application"
* @param errNumber - Error number
* @param errDescription - Detailed description of the error
* @param errLogPrefix - Prefix for the error message in the log
* @param ex - Exception to log. This is usually the exception that caused this error.
*/
protected void publishError (String errType, String errNumber, String errDescription, String errLogPrefix, Throwable ex) {
ArrayList errors = fillErrors (errType, errNumber, errDescription);
logger.fatal(errLogPrefix + errDescription);
publishSyncError(eControlArea, errors, ex);
}
/**
* @param errType - Type of the error, for example, "system" or "application"
* @param errNumber - Error number
* @param errDescription - Detailed description of the error
* @param errLogPrefix - Prefix for the error message in the log
*/
protected void publishError (String errType, String errNumber, String errDescription, String errLogPrefix) {
ArrayList errors = fillErrors (errType, errNumber, errDescription);
logger.fatal(errLogPrefix + errDescription);
publishSyncError(eControlArea, errors);
}
/**
* @param searchAttr - The name of attribute by which to search
* @param ID - seach value
* @return true if user exists in the directory; otherwise, false
*/
protected boolean personExists (String searchAttr, String ID) {
boolean userExists = false;
DirContext searchContext = null;
try {
searchContext = getDirContext();
}
catch (NamingException ne) {
logger.fatal("[" + getServiceName() + ".userExists] - Error getting Directory context", ne);
return false;
}
try {
String filter = searchAttr + "=" + ID;
String[] attrs = {searchAttr};
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
searchControls.setReturningAttributes(attrs);
logger.debug("[" + getServiceName() + ".userExists] Search base DN is: \"" + m_userDirectoryTreeBase + "\"");
logger.debug("[" + getServiceName() + ".userExists] Search attribute is: \"" + searchAttr + "\"");
logger.debug("[" + getServiceName() + ".userExists] Search filter is: \"" + filter + "\"");
NamingEnumeration results = searchContext.search (m_userDirectoryTreeBase, filter, searchControls);
if (results != null && results.hasMore())
userExists = true;
results.close();
searchContext.close();
}
catch (NamingException ne) {
logger.info("[" + getServiceName() + ".userExists] - Exception searching for user.", ne);
if (searchContext != null) {
// In this particular case it should be OK to discard NamingException
try {
searchContext.close();
}
catch (NamingException ne2) {}
}
}
return userExists;
}
private final javax.naming.directory.BasicAttributes buildDirectoryUser(String uid) {
logger.debug("[" + getServiceName() + ".buildDirectoryUser] Building the " + "directory person from the EnterpriseUserPassword...");
// Create attributes to be associated with the new context.
BasicAttributes attrs = new BasicAttributes(true); // case-ignore
// Set the values for objectClass.
Attribute objclass = new BasicAttribute("objectClass");
objclass.add("top");
objclass.add("person");
objclass.add("organizationalPerson");
objclass.add("inetOrgPerson");
objclass.add("eduPerson");
attrs.put(objclass);
// Set the value for uid.
Attribute attribute = new BasicAttribute("uid");
attribute.add(uid);
attrs.put(attribute);
// Set the value for sn. This attribute is mandatory, but its value will be updated with actual data.
attribute = new BasicAttribute("sn");
attribute.add("NoSurname");
attrs.put(attribute);
// Set the value for cn. This attribute is mandatory, but its value will be updated with actual data.
attribute = new BasicAttribute("cn");
attribute.add("NoCommonName");
attrs.put(attribute);
logger.debug("[" + getServiceName() + ".buildDirectoryUser] Built the directory person with attributes: " + attrs.toString());
return attrs;
}
/**
* @param searchAttr - The name of attribute by which to search
* @param ID - seach value
* @throws NamingException
*/
protected void createPerson (String searchAttr, String ID) {
DirContext dirContext = null;
try {
dirContext = getDirContext();
}
catch (NamingException ne) {
logger.fatal("[" + getServiceName() + ".createUser] - Error getting Directory context", ne);
return;
}
try {
String filter = searchAttr + "=" + ID + "," + getUserDirectoryTreeBase();
logger.debug("[" + getServiceName() + ".createPerson] Creating directory subcontext '" + filter + "'.");
Attributes attrs = buildDirectoryUser (ID);
DirContext personContext = dirContext.createSubcontext(filter, attrs);
personContext.close();
dirContext.close();
}
catch (NamingException ne) {
logger.fatal("[" + getServiceName() + ".createUser] - Error creating a new person in Directory.", ne);
if (dirContext != null) {
// In this particular case it should be OK to discard NamingException
try {
dirContext.close();
}
catch (NamingException ne2) {}
}
}
}
protected void setEnterpriseIdDomain(String domain) {
m_enterpriseIdDomain = domain;
}
protected String getEnterpriseIdDomain() {
return m_enterpriseIdDomain;
}
protected void setCreateMissingUsers(boolean createMissingUsers) {
m_createMissingUsers = createMissingUsers;
}
protected boolean getCreateMissingUsers() {
return m_createMissingUsers;
}
/**
* @param uid uid used as a part of the LDAP DN
* @param firstName First Name (may be null)
* @param lastName Last Name (may be null)
* @param middleName Middle Name (may be null)
* @return true if successful
*/
protected boolean updateName (String uid, String firstName, String lastName, String middleName) {
boolean success = true;
String newCommonName = lastName;
String newDisplayName = newCommonName;
if (firstName != null) {
newCommonName = firstName + " " + newCommonName;
if (middleName != null && middleName.length() > 0) {
newDisplayName = firstName + " " + middleName.charAt(0) + ". " + lastName;
}
else
newDisplayName = newCommonName;
}
// Build the dn for the directory user.
String dn = "uid=" + uid + "," + getUserDirectoryTreeBase();
// Specify the modification.
Attribute givenName = new BasicAttribute("givenName");
givenName.add(firstName);
Attribute sn = new BasicAttribute("sn");
sn.add(lastName);
Attribute cn = new BasicAttribute("cn");
cn.add(newCommonName);
Attribute displayName = new BasicAttribute("displayName");
displayName.add(newDisplayName);
ModificationItem[] mods = new ModificationItem[4];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, givenName);
mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, sn);
mods[2] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, cn);
mods[3] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, displayName);
// Perform the modification.
try {
logger.debug("[" + getServiceName() + ".updateName] Modifying the name attributes for user " + dn + ".");
getDirContext().modifyAttributes(dn, mods);
logger.debug("[" + getServiceName() + ".updateName] Modified the name attributes for user " + dn + ".");
}
catch (NamingException ne) {
// An error occurred updating the user's name in the directory.
// Log it and publish a sync error message.
publishError ("system", "DirectoryServiceGateway-1004", "An error occurred updating the " +
"user's name in the directory. The exception is: " + ne.getMessage(), "[" + getServiceName() + ".execute] ", ne);
success = false;
}
return success;
}
}