Search LDAP from ITIM Workflow
The following java class allows free-form LDAP lookups from withing IBMJS scripts, like workflows or provisioning policies. You need to compile it and expose to your IBMJS by adding to scriptframework.properties something like this:
ITIM.extension.Workflow.search=com.ibm.itim.custom.ibmjsextensions.Search
You can then invoke it inside of your java script like so (use real names/DNs in your environments if copying this code):
var searchObj = new DirectoryObjectSearchTypes();
var ad_Role = directoryObjectSearch(searchObj.ROLE,"(errolename=AD Approver)",2);
if (ad_Role[0].dn=="erglobalid=1234,ou=roles,erglobalid=00000000000000000000,ou=test,dc=itim,dc=dom")
Enrole.log("script","***DirectoryObjectSearchTypes.ROLE=success");
else
Enrole.log("script","*!*DirectoryObjectSearchTypes.ROLE=failure. Returns " + ad_Role[0].dn );
var ad_ApproverList = directoryObjectSearch(searchObj.PERSON, "Person", "(erroles=" + ad_Role[0].dn + ")" , 2);
if (ad_ApproverList[0].dn=="erglobalid=1234,ou=0,ou=people,erglobalid=00000000000000000000,ou=test,dc=itim,dc=dom")
Enrole.log("script","***DirectoryObjectSearchTypes.PERSON=success");
else
Enrole.log("script","*!*DirectoryObjectSearchTypes.PERSON=failure. Returns " + ad_ApproverList[0].dn );
var serviceObject = ServiceSearch.searchByName("Active Directory Group Service",2)[0];
var results = directoryObjectSearch(searchObj.ACCOUNT, serviceObject, "ADGroup", "(eruid=Admin)",2);
if (results[0].dn=="erglobalid=1234,ou=0,ou=accounts,erglobalid=00000000000000000000,ou=test,dc=itim,dc=dom")
Enrole.log("script","***DirectoryObjectSearchTypes.ACCOUNT.search=success.");
else
Enrole.log("script","*!*DirectoryObjectSearchTypes.ACCOUNT.search=failure. Returns " + results[0].dn);
var RoleObject = directoryObjectLookup(searchObj.ACCOUNT, "erglobalid=1234,ou=0,ou=accounts,erglobalid=00000000000000000000,ou=test,dc=itim,dc=dom");
if (RoleObject.dn=="erglobalid=1234,ou=0,ou=accounts,erglobalid=00000000000000000000,ou=test,dc=itim,dc=dom")
Enrole.log("script","***DirectoryObjectSearchTypes.ACCOUNT.lookup=success");
else
Enrole.log("script","*!*DirectoryObjectSearchTypes.ACCOUNT.lookup=failure. Returns " + RoleObject.dn );
var itimuser = directoryObjectSearch(searchObj.ITIM_USER, "(eruid=1234)");
if (itimuser[0].dn=="eruid=1234,ou=systemUser,ou=itim,ou=test,dc=itim,dc=dom")
Enrole.log("script","***DirectoryObjectSearchTypes.ITIM_USER.lookup=success.");
else
Enrole.log("script","***DirectoryObjectSearchTypes.ITIM_USER.lookup=fail. Returns " + itimuser[0].dn );]]>
The default list of java objects exposed to IBMJS via scriptframework.properties can be found here and here. Below is the class that implements the search functionality.
/********************************************************************
* ITIM IBM JS Extension
* Allow IBMJS scripts to search for directory objects regardless of type, and with a search base.
* @author Brian Davis, Alex Ivkin
********************************************************************/
package com.ibm.itim.custom.ibmjsextensions;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import com.ibm.itim.dataservices.model.CompoundDN;
import com.ibm.itim.dataservices.model.DirectoryObjectEntity;
import com.ibm.itim.dataservices.model.DistinguishedName;
import com.ibm.itim.dataservices.model.ModelException;
import com.ibm.itim.dataservices.model.ObjectNotFoundException;
import com.ibm.itim.dataservices.model.SearchParameters;
import com.ibm.itim.dataservices.model.SearchResults;
import com.ibm.itim.dataservices.model.SearchResultsIterator;
import com.ibm.itim.dataservices.model.domain.AccountSearch;
import com.ibm.itim.dataservices.model.domain.DirectorySystemEntity;
import com.ibm.itim.dataservices.model.domain.DirectorySystemSearch;
import com.ibm.itim.dataservices.model.domain.OrganizationEntity;
import com.ibm.itim.dataservices.model.domain.OrganizationSearch;
import com.ibm.itim.dataservices.model.domain.OrganizationalContainer;
import com.ibm.itim.dataservices.model.domain.OrganizationalContainerEntity;
import com.ibm.itim.dataservices.model.domain.OrganizationalContainerSearch;
import com.ibm.itim.dataservices.model.domain.Service;
import com.ibm.itim.dataservices.model.domain.ServiceEntity;
import com.ibm.itim.dataservices.model.domain.ServiceModel;
import com.ibm.itim.dataservices.model.domain.ServiceSearch;
import com.ibm.itim.dataservices.model.system.SystemUserSearch;
import com.ibm.itim.logging.JLogUtil;
import com.ibm.itim.script.ContextItem;
import com.ibm.itim.script.GlobalFunction;
import com.ibm.itim.script.ScriptContextDAO;
import com.ibm.itim.script.ScriptEvaluationException;
import com.ibm.itim.script.ScriptException;
import com.ibm.itim.script.ScriptExtension;
import com.ibm.itim.script.ScriptInterface;
import com.ibm.itim.script.wrappers.ObjectWrapper;
import com.ibm.itim.script.wrappers.ObjectWrapperManager;
import com.ibm.log.Level;
import com.ibm.log.PDLogger;
public class Search implements ScriptExtension {
// name for extension object name registration
private static final String JS_CLASS_NAME = "directoryObjectSearch_Lookup";
// real class name for log category
private static final String CLASS_NAME = Search.class.getName();
// trace file logger
private static final PDLogger traceLogger = JLogUtil.getTraceLogger(Search.class);
private static final PDLogger msgLogger = JLogUtil.getMessageLogger(Search.class);
// prefix text for all log messages
private static final String LOG_PREFIX = "### JSExtensions:SearchExtension:" + JS_CLASS_NAME + " : ";
private static final HashMap<Object,String> searchTypes = new HashMap<Object,String>();
private List<ContextItem> items;
public List getContextItems() {
return items;
}
public void initialize(ScriptInterface si, ScriptContextDAO dao) throws ScriptException, IllegalArgumentException {
items = new ArrayList<ContextItem>(3);
ContextItem ciDOST = ContextItem.createConstructor("DirectoryObjectSearchTypes", DirObjectConstructor.class);
ContextItem ciDOS = ContextItem.createGlobalFunction("directoryObjectSearch", new DirObjectSearchFunction(dao));
ContextItem ciDOL = ContextItem.createGlobalFunction("directoryObjectLookup", new DirObjectLookupFunction(dao));
items.add(ciDOST);
items.add(ciDOS);
items.add(ciDOL);
return;
}
/*
* Build an entity wrapper using an entity's value object.
*/
private Object wrapEntity(Object entity, ScriptContextDAO dao) throws ScriptEvaluationException {
//return ObjectWrapperManager.getInstance().wrap(null, (DirectoryObjectEntity) entity, null, null);
// Add a context item to the scripting environment, so it can be disposed (memory freed) when the ScriptComtextDAO is disposed;
// and return a wrapped version of the item (ObjectWrapper).
return dao.addContextItem((DirectoryObjectEntity) entity);
}
/*
* Get a reference to the ITIM type-specific search class that matches one
* of the type specifier constants. These contants are static fields on this
* class, and are stored as fields on each search object created by
* DirObjectConstructor.
*/
private Class getSearchClass(Object classSpecifier) throws ScriptEvaluationException {
if (!(classSpecifier instanceof Double)) {
logMsg(Level.DEBUG_MIN, "getSearchClass", "Search class specifier must be a double");
throw new ScriptEvaluationException("Search class specifier must be a double");
}
String className = (String) Search.searchTypes.get(classSpecifier);
if (className == null) {
logMsg(Level.DEBUG_MIN, "getSearchClass", "No search class defined for " + classSpecifier);
throw new ScriptEvaluationException("No search class defined for " + classSpecifier);
}
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
//SystemLog.getInstance().logError(this, "Failed to load search class " + className, e);
logException("getSearchClass", "Failed to load search class " + className, e);
throw new ScriptEvaluationException("Failed to load search class " + className, e);
}
}
public static class DirObjectConstructor {
/*
* These fields MUST be Doubles!! If you try to use Integer you will
* break things.
*
* The reason for this is that when IBMJS passes these values back to us
* as the first argument of the search or lookup method it will convert
* the JavaScript numeric value to the smallest subclass of Numeric that
* will hold the value. So if you make these fields Integer IBMJS will
* still pass them as Double in the args to the call methods. And when
* you try to use the Double object as a lookup key in the searchTypes
* map it will not match the Integer keys that you stored in the map. So
* the values stored in searchTypes map must by Double.
*/
public static final Double ACCOUNT = new Double((double) 100);
public static final Double CONTAINER = new Double((double) 105);
public static final Double ORG = new Double((double) 106);
public static final Double PERSON = new Double((double) 107);
public static final Double ROLE = new Double((double) 108);
public static final Double SERVICE_GROUP = new Double((double) 109);
public static final Double SERVICE = new Double((double) 110);
public static final Double ITIM_GROUP = new Double((double) 111);
public static final Double ITIM_USER = new Double((double) 112);
public static final Double ONELEVEL_SCOPE = new Double((double) SearchParameters.ONELEVEL_SCOPE);
public static final Double SUBTREE_SCOPE = new Double((double) SearchParameters.SUBTREE_SCOPE);
static {
searchTypes.put(ACCOUNT, "com.ibm.itim.dataservices.model.domain.AccountSearch");
searchTypes.put(CONTAINER, "com.ibm.itim.dataservices.model.domain.OrganizationalContainerSearch");
searchTypes.put(ORG, "com.ibm.itim.dataservices.model.domain.OrganizationSearch");
searchTypes.put(PERSON, "com.ibm.itim.dataservices.model.domain.PersonSearch");
searchTypes.put(ROLE, "com.ibm.itim.dataservices.model.domain.RoleSearch");
searchTypes.put(SERVICE_GROUP, "com.ibm.itim.dataservices.model.domain.ServiceModel");
searchTypes.put(SERVICE, "com.ibm.itim.dataservices.model.domain.ServiceSearch");
searchTypes.put(ITIM_GROUP, "com.ibm.itim.dataservices.model.system.SystemRoleSearch");
searchTypes.put(ITIM_USER, "com.ibm.itim.dataservices.model.system.SystemUserSearch");
}
/*
* This method is called whenever someone does
* "new DirectoryObjectSearch()" in JavaScript.
*/
public DirObjectConstructor() {}
}
private class DirObjectSearchFunction implements GlobalFunction {
private final boolean debugging = //SystemLog.getInstance().getPriorityLevel(this) == SystemLog.DEBUG_INFO;
traceLogger.isLoggable(Level.DEBUG_MAX);
/*
* dao will give us access to the scripting environment.
*/
private ScriptContextDAO dao;
public DirObjectSearchFunction(ScriptContextDAO context) {
dao = context;
}
/*
* This method is called whenever someone calls the search
* method on a search object.
*/
public Object call(Object[] args) throws ScriptEvaluationException {
if (args == null || args.length < 2 || args.length > 5) {
//SystemLog.getInstance().logError(this, "Illegal arguments: search(targetType [,base] [,profile] ,filter [,scope])");
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Illegal arguments: search(targetType [,base] [,profile] ,filter [,scope])");
throw new ScriptEvaluationException("Illegal arguments: search(targetType [,base] [,profile] ,filter [,scope])");
}
/*
* The first argument must be the key for a dataservices *Search
* class or ServiceModel.
*/
Class searchClass = getSearchClass(args[0]);
/*
* The next argument might be a search base. We can tell by whether
* the argument is an object wrapper.
*
* DirectoryObjectWrapper isn't part of the official ITIM API. But
* it's what is really created when you use
* JSDirectoryObjectFactory.createDirectoryObject() to wrap the
* entity elements we return from our searches. So when a user
* passes one of these back as a base argument to another search
* it's what you see as the object's class.
*
* DirectoryObjectWrapper also has a getJavaObject method that
* returns the original wrapped object. Using that saves us from
* having to use the dn on the DirectoryObjectWrapper to do a lookup
* of the object.
*/
int nextArg = 1;
Object base = args[1];
if (this.debugging) {
// SystemLog.getInstance().logDebug(this, "Checking arg 1 (" + args[1].getClass().getName() + ") for use as base");
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Checking arg 1 (" + args[1].getClass().getName() + ") for use as base");
}
if (base instanceof ObjectWrapper) {
/*
* We have a base argument. Unwrap it. This will give us a value
* object for the base. Then we need to check whether its a
* valid type to be used as a base.
*/
base = ObjectWrapperManager.getInstance().lookupItem(((ObjectWrapper) base).getKey());
if (searchClass.getName().equals(ServiceModel.class.getName())) {
/*
* If the search class is ServiceModel then the search base
* must be a Service.
*/
if (!(base instanceof Service)) {
//SystemLog.getInstance().logError(this, "Service Group base must be a Service");
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Service Group base must be a Service");
throw new ScriptEvaluationException("Service Group base must be a Service");
}
} else if (searchClass.getName().equals(AccountSearch.class.getName())) {
/*
* If the search class is AccountSearch then the search base
* must be a Service.
*/
if (!(base instanceof Service)) {
//SystemLog.getInstance().logError(this, "Account search base must be a Service");
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Account search base must be a Service");
throw new ScriptEvaluationException("Account search base must be a Service");
}
/*
* Convert the base from a Service to the service's
* CompoundDN.
*/
try {
DistinguishedName serviceDN = ((Service) base).getDistinguishedName();
ServiceEntity serviceEnt = new ServiceSearch().lookup(serviceDN);
/*
* Containers have a handy method that returns their
* CompoundDN. Services don't. So get the CompoundDN of
* the service's parent. Then convert that into the
* service's.
*/
CompoundDN newBase = ((OrganizationalContainerEntity) serviceEnt.getParent()).getLogicalNameContext();
if (newBase.size() == 3)
/*
* Parent is a container. Replace its dn at the end
* of the compound dn.
*/
newBase.replace(2, serviceDN);
else
/*
* Parent is an organization. Append the service dn
* to the compound dn.
*/
newBase.append(serviceDN);
base = newBase;
} catch (Exception e) {
//SystemLog.getInstance().logError(this, "Error getting service's DN", e);
logException("DirObjectSearchFunction.call", "Error getting service's DN", e);
throw new ScriptEvaluationException("Error getting service's DN", e);
}
} else {
/*
* All other search classes use CompoundDN as their base.
* Some can use OrganizationalContainerEntity, but not all.
* So we force the base to be a CompoundDN.
*
* But the objects that users will be passing to us as bases
* will be wrappers for OrganizationalContainer objects. To
* turn these into CompoundDN objects we must use the dn
* from the wrapper to do a lookup. This will give is an
* entity object, and from that we use the
* getLogicalNameContext method to get the CompoundDN for
* that entity.
*/
if (!(base instanceof OrganizationalContainer)) {
//SystemLog.getInstance().logError(this, "Base must be an OrganizationalContainer");
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Base must be an OrganizationalContainer");
throw new ScriptEvaluationException("Base must be an OrganizationalContainer");
}
try {
base = new OrganizationalContainerSearch().lookup(((OrganizationalContainer) base).getDistinguishedName()).getLogicalNameContext();
} catch (Exception e) {
//SystemLog.getInstance().logError(this, "Error getting container's DN", e);
logException("DirObjectSearchFunction.call", "Error getting container's DN", e);
throw new ScriptEvaluationException("Error getting container's DN", e);
}
}
nextArg++;
} else {
/*
* No base was specified. Use the organization or tenant
* depending on the search type.
*/
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "Using default base");
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Using default base");
}
DirectorySystemEntity tenant = null;
try {
tenant = new DirectorySystemSearch().lookupDefault();
} catch (ModelException e) {
//SystemLog.getInstance().logError(this, "Error getting tenant object", e);
logException("DirObjectSearchFunction.call", "Error getting tenant object", e);
throw new ScriptEvaluationException("Error getting tenant object", e);
}
if (searchClass.getName().equals(OrganizationSearch.class.getName()) || searchClass.getName().equals(SystemUserSearch.class.getName())) {
base = tenant.getLogicalNameContext();
} else {
SearchResults results = null;
try {
OrganizationSearch orgSearch = new OrganizationSearch();
/*
* Most installations have only one organization. We
* will do a search for (o=*) and take the first result,
* assuming it's the only result. But for those few
* multiorganization installations we will first look
* for a "baseOrganization" field on the search object.
* Users will set this field to the organization they
* wish to use as their default search base for this
* search object.
*/
Object baseOrganization = dao.lookupItem("baseOrganization");
if (baseOrganization != null) {
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "baseOrganization found");
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "baseOrganization found");
}
base = orgSearch.lookup(new DistinguishedName(baseOrganization.toString())).getLogicalNameContext();
} else {
results = orgSearch.searchByFilter(tenant.getDistinguishedName(), "(o=*)", new SearchParameters());
base = ((OrganizationEntity) results.iterator().next()).getLogicalNameContext();
}
} catch (ModelException e) {
//SystemLog.getInstance().logError(this, "Error getting organization object", e);
logException("DirObjectSearchFunction.call", "Error getting organization object", e);
throw new ScriptEvaluationException("Error getting organization object", e);
} finally {
if (results != null)
results.close();
}
}
}
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "Base is " + base);
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Base is " + base);
}
/*
* The next argument might be a profile name. There is no way to
* tell by the arg's class, but the number of arguments remaining
* will tell. All of the search classes that accept a profile also
* require a filter and scope. So if there are 3 arguments
* remaining, they must be profile, filter and scope. Otherwise it's
* filter and scope, or just filter.
*/
String profile = null;
if (args.length - nextArg == 3) {
profile = args[nextArg++].toString();
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "Profile is " + profile);
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Profile is " + profile);
}
}
/*
* The next argument is the search filter.
*/
String filter = args[nextArg++].toString();
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "Filter is " + filter);
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Filter is " + filter);
}
/*
* The next argument, if it exists, is the scope.
*/
Object scopeArg = (nextArg == args.length) ? null : args[nextArg];
if (this.debugging) {
//SystemLog.getInstance().logDebug(this, "Scope is " + scopeArg);
logMsg(Level.DEBUG_MAX, "DirObjectSearchFunction.call", "Scope is " + scopeArg);
}
if (scopeArg != null && !(scopeArg instanceof Number)) {
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Illegal search scope: must be numeric, not " + scopeArg.getClass().getName());
throw new ScriptEvaluationException("Illegal search scope: must be numeric, not " + scopeArg.getClass().getName());
}
/*
* If no scope argument was given use one level as the default.
*/
int scope = (scopeArg == null) ? SearchParameters.ONELEVEL_SCOPE : ((Number) scopeArg).intValue();
if (scope != SearchParameters.ONELEVEL_SCOPE && scope != SearchParameters.SUBTREE_SCOPE) {
logMsg(Level.DEBUG_MIN, "DirObjectSearchFunction.call", "Illegal search scope (" + scope + "): must be "
+ SearchParameters.ONELEVEL_SCOPE + " or " + SearchParameters.SUBTREE_SCOPE);
throw new ScriptEvaluationException("Illegal search scope (" + scope + "): must be "
+ SearchParameters.ONELEVEL_SCOPE + " or " + SearchParameters.SUBTREE_SCOPE);
}
/*
* Create a SearchParameters with no limit on the number of results,
* all attributes returned, and the scope specified by the caller.
*/
SearchParameters params = new SearchParameters();
params.setScope(scope);
/*
* Use reflection to get an instance of the search class, and find
* the method to call on that class given the argument list.
*/
Object searchInstance;
Method searchMethod;
String methodName = null;
Object[] searchArgs;
Class[] searchArgClasses = null;
try {
if (searchClass.getName().equals(ServiceModel.class.getName())) {
/*
* The ServiceModel constructor takes a service's
* DistinguishedName as an argument.
*/
Constructor cons = searchClass.getConstructor(new Class[] { DistinguishedName.class });
searchInstance = cons.newInstance(new Object[] { ((Service) base).getDistinguishedName() });
methodName = "getByFilter";
searchArgClasses = new Class[] { String.class };
searchMethod = searchClass.getMethod(methodName, searchArgClasses);
searchArgs = new Object[] { filter };
} else {
/*
* All of the other search classes have a 0-argument
* constructor.
*/
Constructor cons = searchClass.getConstructor(new Class[0]);
searchInstance = cons.newInstance(new Object[0]);
searchArgClasses = (profile == null ? new Class[] { base.getClass(), filter.getClass(), params.getClass() } : new Class[] {
base.getClass(), profile.getClass(), filter.getClass(), params.getClass() });
methodName = "searchByFilter";
searchMethod = searchClass.getMethod(methodName, searchArgClasses);
searchArgs = (profile == null ? new Object[] { base, filter, params } : new Object[] { base, profile, filter, params });
}
} catch (Exception e) {
StringBuffer classList = new StringBuffer();
if (searchArgClasses == null)
classList.append("???");
else {
for (int i = 0; i < searchArgClasses.length; i++) {
classList.append(searchArgClasses[i].getName());
classList.append(',');
}
classList.setLength(classList.length() - 1);
}
String msg = "Error getting search method for " + searchClass.getName() + "." + methodName + "(" + classList + ")";
//SystemLog.getInstance().logError(this, msg, e);
logException("DirObjectSearchFunction.call", msg, e);
throw new ScriptEvaluationException(msg, e);
}
/*
* Call the search method and wrap the results.
*/
SearchResults results = null;
try {
results = (SearchResults) searchMethod.invoke(searchInstance, searchArgs);
SearchResultsIterator srIter = results.iterator();
Vector<Object> wrappedResults = new Vector<Object>();
while (srIter.hasNext()) {
wrappedResults.add(wrapEntity(srIter.next(), dao));
}
//if (this.debugging) {
logMsg(Level.DEBUG_MID, "DirObjectSearchFunction.call", "Returning " + wrappedResults.size() + " results for "
+ "searchClass=" + searchClass.getName()
+ "; base=" + base
+ "; profile=" + profile
+ "; filter=" + filter
+ "; scope=" + scopeArg);
//}
return wrappedResults.toArray();
} catch (Exception e) {
//SystemLog.getInstance().logError(this, "Search for directory objects failed", e);
logException("DirObjectSearchFunction.call", "Search for directory objects failed", e);
throw new ScriptEvaluationException("Search for directory objects failed", e);
} finally {
if (results != null)
results.close();
}
}
}
private class DirObjectLookupFunction implements GlobalFunction {
/*
* DAO will give us access to the scripting environment.
*/
private ScriptContextDAO dao;
public DirObjectLookupFunction(ScriptContextDAO context) {
dao = context;
}
/*
* This method is called whenever someone calls the lookup method on a
* search object.
*/
public Object call(Object[] args) throws ScriptEvaluationException {
if (args.length != 2) {
logMsg(Level.DEBUG_MIN, "DirObjectLookupFunction.call", "Illegal Arguments: DirectoryObject(searchClass, dn)");
throw new ScriptEvaluationException("Illegal Arguments: DirectoryObject(searchClass, dn)");
}
Class searchClass = getSearchClass((Double) args[0]);
DistinguishedName searchArg = new DistinguishedName(args[1].toString());
/*
* Use reflection to create an instance of the search class and call
* its lookup method.
*/
Object searchInstance;
Method searchMethod;
String methodName = null;
Class[] searchArgClasses = null;
try {
Constructor cons = searchClass.getConstructor(new Class[0]);
searchInstance = cons.newInstance(new Object[0]);
searchArgClasses = new Class[] { searchArg.getClass() };
methodName = "lookup";
searchMethod = searchClass.getMethod(methodName, searchArgClasses);
} catch (Exception e) {
StringBuffer classList = new StringBuffer();
if (searchArgClasses == null)
classList.append("???");
else {
for (int i = 0; i < searchArgClasses.length; i++) {
classList.append(searchArgClasses[i].getName());
classList.append(',');
}
classList.setLength(classList.length() - 1);
}
String msg = "Error getting search method for " + searchClass.getName() + "." + methodName + "(" + classList + ")";
logException("DirObjectLookupFunction.call", msg, e);
throw new ScriptEvaluationException(msg, e);
}
try {
Object wrappedResult = wrapEntity(searchMethod.invoke(searchInstance, new Object[] { searchArg }), dao);
//if (this.debugging) {
logMsg(Level.DEBUG_MID, "DirObjectLookupFunction.call", "Returning result for "
+ "searchClass=" + searchClass.getName()
+ "; searchArg=" + searchArg);
//}
return wrappedResult;
} catch (Exception e) {
if (e instanceof ObjectNotFoundException) {
logMsg(Level.DEBUG_MIN, "DirObjectLookupFunction.call", "ObjectNotFoundException, returning null");
return null;
}
//SystemLog.getInstance().logError(this, "Lookup failed", e);
logException("DirObjectLookupFunction.call", "Lookup failed", e);
throw new ScriptEvaluationException("Lookup failed", e);
}
}
}
private static void logMsg(Level logLevel, String methodName, String text) {
traceLogger.text(logLevel, CLASS_NAME, methodName, LOG_PREFIX + text);
Level msgLevel = logLevel;
// Message logger (msg.log) typically only logs events at INFO or above, so map DEBUG levels to these
// higher index values are higher priority levels
if (logLevel.getValue() < Level.INFO_INDEX) {
if (logLevel.getValue() >= Level.DEBUG_MIN_INDEX) // DEBUG_MIN => ERROR
msgLevel = Level.ERROR;
else if(logLevel.getValue() >= Level.DEBUG_MID_INDEX) // DEBUG_MID => WARN
msgLevel = Level.WARN;
else { // logLevel >= Level.DEBUG_MAX_INDEX) // DEBUG_MAX => INFO
msgLevel = Level.INFO;
}
}
msgLogger.text(msgLevel, CLASS_NAME, methodName, LOG_PREFIX + text);
}
private static void logException(String methodName, String text, Exception e) {
traceLogger.text(Level.DEBUG_MIN, CLASS_NAME, methodName, LOG_PREFIX + text, e);
msgLogger.text(Level.ERROR, CLASS_NAME, methodName, LOG_PREFIX + text, e);
}
}
@Tools @ITIM