/** * Adds a new object to the organization. * * @param object object to be added to the organization * @exception AccessRightsException if an access rights exception occurs * @exception EntryAlreadyExistsException if the entry already exists * @exception UMSException Fail to add the object * @supported.api */ public void addChild(PersistentObject object) throws AccessRightsException, EntryAlreadyExistsException, UMSException { Principal principal = getPrincipal(); if (principal == null) { String msg = i18n.getString(IUMSConstants.BAD_PRINCIPAL_HDL); throw new IllegalArgumentException(msg); } else if (object == null) { String msg = i18n.getString(IUMSConstants.BAD_OBJ_TO_ADD); throw new IllegalArgumentException(msg); } if (object instanceof User) { String pcId = getPeopleContainer((User) object); if (pcId != null) { PeopleContainer pc = new PeopleContainer(getPrincipal(), new Guid(pcId)); pc.addUser((User) object); } else { // no match and no default value found // For now, the user will be addedd to the organization. // May want to add to the default people // container(ou=People) instead. super.addChild(object); } } else { super.addChild(object); } }
/** * Helper method to get admin token. This is not amadmin user but the user configured in * serverconfig.xml as super user. * * @return SSOToken of super admin. */ public static SSOToken getAdminToken() throws SSOException { SSOToken adminToken = (SSOToken) AccessController.doPrivileged(AdminTokenAction.getInstance()); if (adminToken == null) { I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG); String rbName = i18n.getResBundleName(); throw new SSOException(rbName, IUMSConstants.NULL_TOKEN, null); } return (adminToken); }
/** get a handle to the Directory Server Configuration Manager sets the value */ protected static void getConfigManager() throws EventException { try { cm = DSConfigMgr.getDSConfigMgr(); } catch (LDAPServiceException lse) { debugger.error( "EventService.getConfigManager() - Failed to get " + "handle to Configuration Manager", lse); throw new EventException(i18n.getString(IUMSConstants.DSCFG_NOCFGMGR), lse); } }
/** * Represents an organization object. An organization can have child organizations and groups, or * other child objects. * * <pre> * o=vortex.com (site) * o=hp (organization) * uid=jdoe * * o=sun (organization) * ou=buyerclub * uid=joe * </pre> * * @supported.api */ public class Organization extends PersistentObject { private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG); /** * No args constructor, used to contruct the right object as entries are read from persistent * storage */ protected Organization() throws UMSException {} /** * Constructs Organization from supplied session and guid identifying the organization to be * constructed. * * @param session authenticated session object maintained by Session Manager * @param guid globally unique identifier for the organization */ Organization(Principal principal, Guid guid) throws UMSException { super(principal, guid); verifyClass(); } /** * Constructs Organization object without a session. Unlike the constructor with a session * parameter , this one simply creates a Group object in memory, using the default template. Call * the save() method to save the object to the persistent store. * * @param attrSet attribute/value set */ Organization(AttrSet attrSet) throws UMSException { this(TemplateManager.getTemplateManager().getCreationTemplate(_class, null), attrSet); } /** * Constructs Organization object without session. Unlike the constructor with session, this one * simply creates Organization object in memory. Call the save() method to save the object to * persistent storage. * * @param template template for the organization * @param attrSet attribute/value set * @supported.api */ public Organization(CreationTemplate template, AttrSet attrSet) throws UMSException { super(template, attrSet); } /** * Adds a new object to the organization. * * @param object object to be added to the organization * @exception AccessRightsException if an access rights exception occurs * @exception EntryAlreadyExistsException if the entry already exists * @exception UMSException Fail to add the object * @supported.api */ public void addChild(PersistentObject object) throws AccessRightsException, EntryAlreadyExistsException, UMSException { Principal principal = getPrincipal(); if (principal == null) { String msg = i18n.getString(IUMSConstants.BAD_PRINCIPAL_HDL); throw new IllegalArgumentException(msg); } else if (object == null) { String msg = i18n.getString(IUMSConstants.BAD_OBJ_TO_ADD); throw new IllegalArgumentException(msg); } if (object instanceof User) { String pcId = getPeopleContainer((User) object); if (pcId != null) { PeopleContainer pc = new PeopleContainer(getPrincipal(), new Guid(pcId)); pc.addUser((User) object); } else { // no match and no default value found // For now, the user will be addedd to the organization. // May want to add to the default people // container(ou=People) instead. super.addChild(object); } } else { super.addChild(object); } } /** * Removes an object from the organization. * * @param object object to be removed to the organization * @exception AccessRightsException if an access rights exception occurs * @exception EntryNotFoundException if the entry is not found * @exception UMSException Fail to remove the object * @supported.api */ public void removeChild(PersistentObject object) throws AccessRightsException, EntryNotFoundException, UMSException { if (object != null && getPrincipal() != null) { super.removeChild(object); } } /** * Returns the name of the organization. * * @return name of the organization * @supported.api */ public String getName() throws UMSException { return getAttribute(getNamingAttribute()).getValue(); } /** * Get roles associated with the organization * * <p>TODO: Not yet implemented * * @return guid identifying roles object under the organization * <p>public String[] getRolesArray(){ //getRoles() { * <p>return null; } */ /** * Get groups of which the organization is a member. * * <p>TODO: Not yet implemented * * @return guids identifying groups that the organization is a member of * <p>public String[] getGroups() { return null; } */ /** * Gets all People Containers under the organization. * * @return guids identifying People Containers under the organization * @exception UMSException Failure * @supported.api */ public Collection getPeopleContainerGuids() throws UMSException { Collection pcs = new ArrayList(); SearchTemplate template = TemplateManager.getTemplateManager() .getSearchTemplate("BasicPeopleContainerSearch", getGuid()); SearchResults results = search(template, null); while (results.hasMoreElements()) { pcs.add(results.next().getGuid()); } return pcs; } /** * Gets People Container associated with the user. * * @param user user object to lookup * @return guid identifying People Container associated with the user, null if no match found * @exception UMSException Failure */ private String getPeopleContainer(User user) throws UMSException { PCMappingTable mt = PCMappingTable.getPCMappingTable(this); return mt.getPeopleContainer(user); } /** * Adds rule for determining which People Container the user is supposed to be in. * * @param filter filter representation of the rule. Accepts filter string with the following * format: * <p> * <PRE> * * <filter> ::= <and> | <item> <and> ::= '(' '&' * <itemlist> ')' <itemlist> ::= <item> | <item> * <itemlist> <item> ::= '(' <attr> '=' <value> * ')' * * </PRE> * * @param guid guid of the People Container to which the rule is applied. * @exception UMSException Failure * @supported.api */ public void addPeopleContainerRule(Guid guid, String filter) throws UMSException { PCMappingTable mt = PCMappingTable.getPCMappingTable(this); mt.addRule(guid, filter); } /** * Removes the rule applying to the given People Container guid with the given filter string. * * @param filter filter string of the rule to be removed * @param guid guid of which the rule applies to * @exception UMSException Failure * @supported.api */ public void removePeopleContainerRule(Guid guid, String filter) throws UMSException { PCMappingTable mt = PCMappingTable.getPCMappingTable(this); mt.removeRule(guid, filter); } /** * Sets the default People Container for user entries under the organization. * * @param guid guid of the default People Container * @exception UMSException Failure * @supported.api */ public void setDefaultPeopleContainer(Guid guid) throws UMSException { PCMappingTable mt = PCMappingTable.getPCMappingTable(this); mt.setDefault(guid); } private static final Class _class = com.iplanet.ums.Organization.class; }
/** * Event Service monitors changes on the server. Implemented with the persistant search control. * Uses ldapjdk asynchronous interfaces so that multiple search requests can be processed by a * single thread * * <p>The Type of changes that can be monitored are: - LDAPPersistSearchControl.ADD - * LDAPPersistSearchControl.DELETE - LDAPPersistSearchControl.MODIFY - * LDAPPersistSearchControl.MODDN * * <p>A single connection is established initially and reused to service all notification requests. * * @supported.api */ public class EventService implements Runnable { protected static DSConfigMgr cm = null; // list that holds notification requests protected Map _requestList = null; // Thread that listens to DS notifications static Thread _monitorThread = null; // search listener for asynch ldap searches static LDAPSearchListener _msgQueue; // A singelton patern protected static EventService _instance = null; // Don't want the server to return all the // entries. return only the changes. private static final boolean CHANGES_ONLY = true; // Want the server to return the entry // change control in the search result private static final boolean RETURN_CONTROLS = true; // Don't perform search if Persistent // Search control is not supported. private static final boolean IS_CRITICAL = true; private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG); protected static Debug debugger = Debug.getInstance("amEventService"); // Parameters in AMConfig, that provide values for connection retries protected static final String EVENT_CONNECTION_NUM_RETRIES = "com.iplanet.am.event.connection.num.retries"; protected static final String EVENT_CONNECTION_RETRY_INTERVAL = "com.iplanet.am.event.connection.delay.between.retries"; protected static final String EVENT_CONNECTION_ERROR_CODES = "com.iplanet.am.event.connection.ldap.error.codes.retries"; // Idle timeout in minutes protected static final String EVENT_IDLE_TIMEOUT_INTERVAL = "com.sun.am.event.connection.idle.timeout"; protected static final String EVENT_LISTENER_DISABLE_LIST = "com.sun.am.event.connection.disable.list"; private static boolean _allDisabled = false; private static int _numRetries = 3; private static int _retryInterval = 3000; private static int _retryMaxInterval = 720000; // 12 minutes private static int _retryCount = 1; private static long _lastResetTime = 0; protected static HashSet _retryErrorCodes; // Connection Time Out parameters protected static int _idleTimeOut = 0; // Idle timeout in minutes. protected static long _idleTimeOutMills; // List of know listeners. The order of the listeners is important // since it is used to enable & disable the listeners private static final String[] ALL_LISTENERS = { "com.iplanet.am.sdk.ldap.ACIEventListener", "com.iplanet.am.sdk.ldap.EntryEventListener", "com.sun.identity.sm.ldap.LDAPEventManager" }; protected static String[] listeners; protected static Hashtable _ideListenersMap = new Hashtable(); protected static volatile boolean _isThreadStarted = false; protected static volatile boolean _shutdownCalled = false; private static HashSet getPropertyRetryErrorCodes(String key) { HashSet codes = new HashSet(); String retryErrorStr = SystemProperties.get(key); if (retryErrorStr != null && retryErrorStr.trim().length() > 0) { StringTokenizer stz = new StringTokenizer(retryErrorStr, ","); while (stz.hasMoreTokens()) { codes.add(stz.nextToken().trim()); } } return codes; } private static int getPropertyIntValue(String key, int defaultValue) { int value = defaultValue; String valueStr = SystemProperties.get(key); if (valueStr != null && valueStr.trim().length() > 0) { try { value = Integer.parseInt(valueStr); } catch (NumberFormatException e) { value = defaultValue; if (debugger.warningEnabled()) { debugger.warning( "EventService.getPropertyIntValue(): " + "Invalid value for property: " + EVENT_CONNECTION_NUM_RETRIES + " Defaulting to value: " + defaultValue); } } } if (debugger.messageEnabled()) { debugger.message("EventService.getPropertyIntValue(): " + key + " = " + value); } return value; } /** * Determine the listener list based on the diable list property and SMS DataStore notification * property in Realm mode */ private static void getListenerList() { String list = SystemProperties.get(EVENT_LISTENER_DISABLE_LIST, ""); if (debugger.messageEnabled()) { debugger.message( "EventService.getListenerList(): " + EVENT_LISTENER_DISABLE_LIST + ": " + list); } boolean enableDataStoreNotification = Boolean.parseBoolean(SystemProperties.get(Constants.SMS_ENABLE_DB_NOTIFICATION)); if (debugger.messageEnabled()) { debugger.message( "EventService.getListenerList(): " + "com.sun.identity.sm.enableDataStoreNotification: " + enableDataStoreNotification); } boolean configTime = Boolean.parseBoolean(SystemProperties.get(Constants.SYS_PROPERTY_INSTALL_TIME)); if (debugger.messageEnabled()) { debugger.message( "EventService.getListenerList(): " + Constants.SYS_PROPERTY_INSTALL_TIME + ": " + configTime); } // Copy the default listeners String[] tmpListeners = new String[ALL_LISTENERS.length]; System.arraycopy(ALL_LISTENERS, 0, tmpListeners, 0, ALL_LISTENERS.length); // Process the configured disabled list first boolean disableACI = false, disableUM = false, disableSM = false; if (list.length() != 0) { StringTokenizer st = new StringTokenizer(list, ","); String listener = ""; while (st.hasMoreTokens()) { listener = st.nextToken().trim(); if (listener.equalsIgnoreCase("aci")) { disableACI = true; } else if (listener.equalsIgnoreCase("um")) { disableUM = true; } else if (listener.equalsIgnoreCase("sm")) { disableSM = true; } else { debugger.error( "EventService.getListenerList() - " + "Invalid listener name: " + listener); } } } if (!disableUM || !disableACI) { // Check if AMSDK is configured boolean disableAMSDK = true; if (!configTime) { try { ServiceSchemaManager scm = new ServiceSchemaManager(getSSOToken(), IdConstants.REPO_SERVICE, "1.0"); ServiceSchema idRepoSubSchema = scm.getOrganizationSchema(); Set idRepoPlugins = idRepoSubSchema.getSubSchemaNames(); if (idRepoPlugins.contains("amSDK")) { disableAMSDK = false; } } catch (SMSException ex) { if (debugger.warningEnabled()) { debugger.warning( "EventService.getListenerList() - " + "Unable to obtain idrepo service", ex); } } catch (SSOException ex) { // Should not happen, ignore the exception } } if (disableAMSDK) { disableUM = true; disableACI = true; if (debugger.messageEnabled()) { debugger.message( "EventService.getListener" + "List(): AMSDK is not configured or config time. " + "Disabling UM and ACI event listeners"); } } } // Verify if SMSnotification should be enabled if (configTime || ServiceManager.isRealmEnabled()) { disableSM = !enableDataStoreNotification; if (debugger.messageEnabled()) { debugger.message( "EventService.getListenerList(): In realm " + "mode or config time, SMS listener is set to datastore " + "notification flag: " + enableDataStoreNotification); } } // Disable the selected listeners if (disableACI) { tmpListeners[0] = null; } if (disableUM) { tmpListeners[1] = null; } if (disableSM) { tmpListeners[2] = null; } listeners = tmpListeners; // if all disabled, signal to not start the thread if (disableACI && disableUM && disableSM) { if (debugger.messageEnabled()) { debugger.message( "EventService.getListenerList() - " + "all listeners are disabled, EventService won't start"); } _allDisabled = true; } else { _allDisabled = false; } } /** Private Constructor */ protected EventService() throws EventException { getConfigManager(); _requestList = Collections.synchronizedMap(new HashMap()); } /** * create the singelton EventService object if it doesn't exist already. Check if directory server * supports the Persistent Search Control and the Proxy Auth Control * * @supported.api */ public static synchronized EventService getEventService() throws EventException, LDAPException { if (_shutdownCalled) { return null; } // Make sure only one instance of this class is created. if (_instance == null) { // Determine the Idle time out value for Event Service (LB/Firewall) // scenarios. Value == 0 imples no idle timeout. _idleTimeOut = getPropertyIntValue(EVENT_IDLE_TIMEOUT_INTERVAL, _idleTimeOut); _idleTimeOutMills = _idleTimeOut * 60000; ShutdownManager shutdownMan = ShutdownManager.getInstance(); if (shutdownMan.acquireValidLock()) { try { if (_idleTimeOut == 0) { _instance = new EventService(); } else { _instance = new EventServicePolling(); } shutdownMan.addShutdownListener( new ShutdownListener() { public void shutdown() { if (_instance != null) { _instance.finalize(); } } }); } finally { shutdownMan.releaseLockAndNotify(); } } } return _instance; } protected static String getName() { return "EventService"; } /** * At the end, close THE Event Manager's connections Abandon all previous persistent search * requests * * @supported.api */ public void finalize() { synchronized (this) { _shutdownCalled = true; if ((_monitorThread != null) && (_monitorThread.isAlive())) { _monitorThread.interrupt(); _isThreadStarted = false; } } synchronized (_requestList) { Collection requestObjs = _requestList.values(); Iterator iter = requestObjs.iterator(); while (iter.hasNext()) { Request request = (Request) iter.next(); removeListener(request); } _requestList.clear(); } } /** * Adds a listener to the directory. * * @supported.api */ protected synchronized String addListener( SSOToken token, IDSEventListener listener, String base, int scope, String filter, int operations) throws LDAPException, EventException { if (_shutdownCalled) { throw new EventException(i18n.getString(IUMSConstants.DSCFG_CONNECTFAIL)); } LDAPConnection lc = null; try { // Check for SMS listener and use "sms" group if present if ((listener.getClass().getName().equals("com.sun.identity.sm.ldap.LDAPEventManager")) && (cm.getServerGroup("sms") != null)) { lc = cm.getNewConnection("sms", LDAPUser.Type.AUTH_ADMIN); } else { lc = cm.getNewAdminConnection(); } } catch (LDAPServiceException le) { throw new EventException(i18n.getString(IUMSConstants.DSCFG_CONNECTFAIL), le); } LDAPSearchConstraints cons = lc.getSearchConstraints(); // Create Persistent Search Control object LDAPPersistSearchControl psearchCtrl = new LDAPPersistSearchControl(operations, CHANGES_ONLY, RETURN_CONTROLS, IS_CRITICAL); // Add LDAPControl array to the search constraint object cons.setServerControls(psearchCtrl); cons.setBatchSize(1); // Listeners can not read attributes from the event. // Request only javaClassName to be able to determine object type String[] attrs = new String[] {"objectclass"}; LDAPSearchListener searchListener = null; // Set (asynchronous) persistent search request in the DS try { if (debugger.messageEnabled()) { debugger.message( "EventService.addListener() - Submiting " + "Persistent Search on: " + base + " for listener: " + listener); } searchListener = lc.search(base, scope, filter, attrs, false, null, cons); } catch (LDAPException le) { if ((lc != null) && lc.isConnected()) { try { lc.disconnect(); } catch (Exception ex) { // ignored } } debugger.error( "EventService.addListener() - Failed to set " + "Persistent Search" + le.getMessage()); throw le; } int[] outstandingRequests = searchListener.getMessageIDs(); int id = outstandingRequests[outstandingRequests.length - 1]; String reqID = Integer.toString(id); long startTime = System.currentTimeMillis(); Request request = new Request( id, reqID, token, base, scope, filter, attrs, operations, listener, lc, startTime); _requestList.put(reqID, request); // Add this search request to the m_msgQueue so it can be // processed by the monitor thread if (_msgQueue == null) { _msgQueue = searchListener; } else { _msgQueue.merge(searchListener); } if (!_isThreadStarted) { startMonitorThread(); } else { if (_requestList.size() == 1) { notify(); } } if (debugger.messageEnabled()) { outstandingRequests = _msgQueue.getMessageIDs(); debugger.message( "EventService.addListener(): merged Listener: " + " requestID: " + reqID + " & Request: " + request + " on to message Queue. No. of current outstanding " + "requests = " + outstandingRequests.length); } // Create new (EventService) Thread, if one doesn't exist. return reqID; } public IDSEventListener getIDSListeners(String className) { return (IDSEventListener) _ideListenersMap.get(className); } public static boolean isThreadStarted() { return _isThreadStarted; } /** * Main monitor thread loop. Wait for persistent search change notifications * * @supported.api */ public void run() { try { if (debugger.messageEnabled()) { debugger.message( "EventService.run(): Event Thread is running! " + "No Idle timeout Set: " + _idleTimeOut + " minutes."); } boolean successState = true; LDAPMessage message = null; while (successState) { try { if (debugger.messageEnabled()) { debugger.message("EventService.run(): Waiting for " + "response"); } synchronized (this) { if (_requestList.isEmpty()) { wait(); } } message = _msgQueue.getResponse(); successState = processResponse(message); } catch (LDAPInterruptedException ex) { if (_shutdownCalled) { break; } else { if (debugger.warningEnabled()) { debugger.warning("EventService.run() " + "LDAPInterruptedException received:", ex); } } } catch (LDAPException ex) { if (_shutdownCalled) { break; } else { int resultCode = ex.getLDAPResultCode(); if (debugger.warningEnabled()) { debugger.warning("EventService.run() LDAPException " + "received:", ex); } _retryErrorCodes = getPropertyRetryErrorCodes(EVENT_CONNECTION_ERROR_CODES); // Catch special error codition in // LDAPSearchListener.getResponse String msg = ex.getLDAPErrorMessage(); if ((resultCode == LDAPException.OTHER) && (msg != null) && msg.equals("Invalid response")) { // We should not try to resetError and retry processNetworkError(ex); } else { if (_retryErrorCodes.contains("" + resultCode)) { resetErrorSearches(true); } else { // Some other network error processNetworkError(ex); } } } } } // end of while loop } catch (InterruptedException ex) { if (!_shutdownCalled) { if (debugger.warningEnabled()) { debugger.warning("EventService.run(): Interrupted exception" + " caught.", ex); } } } catch (RuntimeException ex) { if (debugger.warningEnabled()) { debugger.warning("EventService.run(): Runtime exception " + "caught.", ex); } // rethrow the Runtime exception to let the container handle the // exception. throw ex; } catch (Exception ex) { if (debugger.warningEnabled()) { debugger.warning("EventService.run(): Unknown exception " + "caught.", ex); } // no need to rethrow. } catch (Throwable t) { // Catching Throwable to prevent the thread from exiting. if (debugger.warningEnabled()) { debugger.warning( "EventService.run(): Unknown exception " + "caught. Sleeping for a while.. ", t); } // rethrow the Error to let the container handle the error. throw new Error(t); } finally { synchronized (this) { if (!_shutdownCalled) { // try to restart the monitor thread. _monitorThread = null; startMonitorThread(); } } } } // end of thread private static synchronized void startMonitorThread() { if (((_monitorThread == null) || !_monitorThread.isAlive()) && !_shutdownCalled) { // Even if the monitor thread is not alive, we should use the // same instance of Event Service object (as it maintains all // the listener information) _monitorThread = new Thread(_instance, getName()); _monitorThread.setDaemon(true); _monitorThread.start(); // Since this is a singleton class once a getEventService() // is invoked the thread will be started and the variable // will be set to true. This will help other components // to avoid starting it once again if the thread has // started. _isThreadStarted = true; } } protected boolean retryManager(boolean clearCaches) { long now = System.currentTimeMillis(); // reset _retryCount to 1 after 12 hours if ((now - _lastResetTime) > 43200000) { _retryCount = 1; _lastResetTime = now; } int i = _retryCount * _retryInterval; if (i > _retryMaxInterval) { i = _retryMaxInterval; } else { _retryCount *= 2; } if (debugger.messageEnabled()) { debugger.message( "EventService.retryManager() - wait " + (i / 1000) + " seconds before calling resetAllSearches"); } sleepRetryInterval(i); return resetAllSearches(clearCaches); } /** * Method which process the Response received from the DS. * * @param message - the LDAPMessage received as response * @return true if the reset was successful. False Otherwise. */ protected boolean processResponse(LDAPMessage message) { if ((message == null) && (!_requestList.isEmpty())) { // Some problem with the message queue. We should // try to reset it. debugger.error( "EventService.processResponse() - Received a NULL Response, call retryManager"); return retryManager(false); } if (debugger.messageEnabled()) { debugger.message( "EventService.processResponse() - received " + "DS message => " + message.toString()); } // To determine if the monitor thread needs to be stopped. boolean successState = true; Request request = getRequestEntry(message.getMessageID()); // If no listeners, abandon this message id if (request == null) { // We do not have anything stored about this message id. // So, just log a message and do nothing. if (debugger.messageEnabled()) { debugger.message( "EventService.processResponse() - Received " + "ldap message with unknown id = " + message.getMessageID()); } } else if (message.getMessageType() == LDAPMessage.LDAP_SEARCH_RESULT_MESSAGE) { // then must be a LDAPSearchResult carrying change control processSearchResultMessage((LDAPSearchResult) message, request); request.setLastUpdatedTime(System.currentTimeMillis()); } else if (message.getMessageType() == LDAPMessage.LDAP_RESPONSE_MESSAGE) { // Check for error message ... LDAPResponse rsp = (LDAPResponse) message; successState = processResponseMessage(rsp, request); } else if (message.getMessageType() == LDAPMessage.LDAP_SEARCH_RESULT_REFERENCE_MESSAGE) { // Referral processSearchResultRef((LDAPSearchResultReference) message, request); } return successState; } /** * removes the listener from the list of Persistent Search listeners of the asynchronous seach for * the given search ID. * * @param request The request returned by the addListener * @supported.api */ protected void removeListener(Request request) { LDAPConnection connection = request.getLDAPConnection(); if (connection != null) { if (debugger.messageEnabled()) { debugger.message( "EventService.removeListener(): Removing " + "listener requestID: " + request.getRequestID() + " Listener: " + request.getListener()); } try { if ((connection != null) && (connection.isConnected())) { connection.abandon(request.getId()); connection.disconnect(); } } catch (LDAPException le) { // Might have to check the reset codes and try to reset if (debugger.warningEnabled()) { debugger.warning( "EventService.removeListener(): " + "LDAPException, when trying to remove listener", le); } } } } /** * Reset error searches. Clear cache only if true is passed to argument * * @param clearCaches */ protected void resetErrorSearches(boolean clearCaches) { Hashtable tmpReqList = new Hashtable(); tmpReqList.putAll(_requestList); int[] ids = _msgQueue.getMessageIDs(); if (ids != null) { for (int i = 0; i < ids.length; i++) { String reqID = Integer.toString(ids[i]); tmpReqList.remove(reqID); } } Collection reqList = tmpReqList.values(); for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); _requestList.remove(req.getRequestID()); } _retryInterval = getPropertyIntValue(EVENT_CONNECTION_RETRY_INTERVAL, _retryInterval); RetryTask task = new RetryTask(tmpReqList); task.clearCache(clearCaches); SystemTimer.getTimer() .schedule(task, new Date(((System.currentTimeMillis() + _retryInterval) / 1000) * 1000)); } /** * Reset all searches. Clear cache only if true is passed to argument * * @param clearCaches * @return <code>true</code> if the reset was successful, otherwise <code>false</code> */ public synchronized boolean resetAllSearches(boolean clearCaches) { if (_shutdownCalled) { return false; } // Make a copy of the existing psearches Hashtable tmpReqList = new Hashtable(); tmpReqList.putAll(_requestList); _requestList.clear(); // Will be updated in addListener method Collection reqList = tmpReqList.values(); // Clear the cache, if parameter is set if (clearCaches && !reqList.isEmpty()) { for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); IDSEventListener el = req.getListener(); el.allEntriesChanged(); } } // Get the list of psearches to be enabled getListenerList(); if (_allDisabled) { // All psearches are disabled, remove listeners if any and return if (debugger.messageEnabled()) { debugger.message("EventService.resetAllSearches(): " + "All psearches have been disabled"); } if (!reqList.isEmpty()) { for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); removeListener(req); if (debugger.messageEnabled()) { debugger.message( "EventService.resetAllSearches(): " + "Psearch disabled: " + req.getListener().getClass().getName()); } } } return true; } // Psearches are enabled, verify and reinitilize // Maintain the listeners to reinitialized in tmpListenerList Set tmpListenerList = new HashSet(); Set newListenerList = new HashSet(); for (int i = 0; i < listeners.length; i++) { if (listeners[i] != null) { // Check if the listener is present in reqList boolean present = false; for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); IDSEventListener el = req.getListener(); String listenerClass = el.getClass().getName(); if (listenerClass.equals(listeners[i])) { present = true; iter.remove(); tmpListenerList.add(req); } } if (!present) { // Add the listner object if (debugger.messageEnabled()) { debugger.message( "EventService.resetAllSearches(): " + "Psearch being added: " + listeners[i]); } newListenerList.add(listeners[i]); } } } // Remove the listeners not configured if (!reqList.isEmpty()) { for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); removeListener(req); if (debugger.messageEnabled()) { debugger.message( "EventService.resetAllSearches(): " + "Psearch disabled due to configuration changes: " + req.getListener().getClass().getName()); } } } // Reset the requested list reqList = tmpListenerList; // Determine the number of retry attempts in case of failure // If retry property is set to -1, retries will be done infinitely _numRetries = getPropertyIntValue(EVENT_CONNECTION_NUM_RETRIES, _numRetries); int retry = 1; boolean doItAgain = ((_numRetries == -1) || ((_numRetries != 0) && (retry <= _numRetries))) ? true : false; while (doItAgain) { if (debugger.messageEnabled()) { String str = (_numRetries == -1) ? "indefinitely" : Integer.toString(retry); debugger.message("EventService.resetAllSearches(): " + "retrying = " + str); } // Note: Avoid setting the messageQueue to null and just // try to disconnect the connections. That way we can be sure // that we have not lost any responses. for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { try { Request request = (Request) iter.next(); // First add a new listener and then remove the old one // that we do don't loose any responses to the message // Queue. addListener( request.getRequester(), request.getListener(), request.getBaseDn(), request.getScope(), request.getFilter(), request.getOperations()); removeListener(request); iter.remove(); } catch (LDAPServiceException e) { // Ignore exception and retry as we are in the process of // re-establishing the searches. Notify Listeners after the // attempt if (retry == _numRetries) { processNetworkError(e); } } catch (LDAPException le) { // Ignore exception and retry as we are in the process of // re-establishing the searches. Notify Listeners after the // attempt if (retry == _numRetries) { processNetworkError(le); } } } // Check if new listeners need to be added for (Iterator iter = newListenerList.iterator(); iter.hasNext(); ) { String listnerClass = (String) iter.next(); try { Class thisClass = Class.forName(listnerClass); IDSEventListener listener = (IDSEventListener) thisClass.newInstance(); _ideListenersMap.put(listnerClass, listener); _instance.addListener( getSSOToken(), listener, listener.getBase(), listener.getScope(), listener.getFilter(), listener.getOperations()); if (debugger.messageEnabled()) { debugger.message( "EventService.resetAllSearches() - " + "successfully initialized: " + listnerClass); } iter.remove(); } catch (Exception e) { debugger.error( "EventService.resetAllSearches() " + "Unable to start listener " + listnerClass, e); } } if (reqList.isEmpty() && newListenerList.isEmpty()) { return true; } else { if (_numRetries != -1) { doItAgain = (++retry <= _numRetries) ? true : false; if (!doItAgain) { // remove the requests fail to be resetted // would try to reinitialized the next time for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); removeListener(req); debugger.error( "EventService.resetAll" + "Searches(): unable to restart: " + req.getListener().getClass().getName()); } for (Iterator iter = newListenerList.iterator(); iter.hasNext(); ) { String req = (String) iter.next(); debugger.error("EventService.resetAll" + "Searches(): unable add listener: " + req); } } } } if (doItAgain) { // Sleep before retry sleepRetryInterval(); } } // end while loop return false; } protected void sleepRetryInterval() { _retryInterval = getPropertyIntValue(EVENT_CONNECTION_RETRY_INTERVAL, _retryInterval); try { Thread.sleep(_retryInterval); } catch (InterruptedException ie) { // ignore } } protected void sleepRetryInterval(int interval) { try { Thread.sleep(interval); } catch (InterruptedException ie) { // ignore } } /** get a handle to the Directory Server Configuration Manager sets the value */ protected static void getConfigManager() throws EventException { try { cm = DSConfigMgr.getDSConfigMgr(); } catch (LDAPServiceException lse) { debugger.error( "EventService.getConfigManager() - Failed to get " + "handle to Configuration Manager", lse); throw new EventException(i18n.getString(IUMSConstants.DSCFG_NOCFGMGR), lse); } } private void dispatchException(Exception e, Request request) { IDSEventListener el = request.getListener(); debugger.error( "EventService.dispatchException() - dispatching " + "exception to the listener: " + request.getRequestID() + " Listener: " + request.getListener(), e); el.eventError(e.toString()); } /** Dispatch naming event to all listeners */ private void dispatchEvent(DSEvent dirEvent, Request request) { IDSEventListener el = request.getListener(); el.entryChanged(dirEvent); } /** On network error, create ExceptionEvent and delever it to all listeners on all events. */ protected void processNetworkError(Exception ex) { Hashtable tmpRequestList = new Hashtable(); tmpRequestList.putAll(_requestList); int[] ids = _msgQueue.getMessageIDs(); if (ids != null) { for (int i = 0; i < ids.length; i++) { tmpRequestList.remove(Integer.toString(ids[i])); } } Collection reqList = tmpRequestList.values(); for (Iterator iter = reqList.iterator(); iter.hasNext(); ) { Request request = (Request) iter.next(); dispatchException(ex, request); } } /** * Response message carries a LDAP error. Response with the code 0 (SUCCESS), should never be * received as persistent search never completes, it has to be abandon. Referral messages are * ignored */ protected boolean processResponseMessage(LDAPResponse rsp, Request request) { _retryErrorCodes = getPropertyRetryErrorCodes(EVENT_CONNECTION_ERROR_CODES); int resultCode = rsp.getResultCode(); if (_retryErrorCodes.contains("" + resultCode)) { if (debugger.messageEnabled()) { debugger.message( "EventService.processResponseMessage() - " + "received LDAP Response for requestID: " + request.getRequestID() + " Listener: " + request.getListener() + "Need restarting"); } resetErrorSearches(false); } else if (resultCode != 0 || resultCode != LDAPException.REFERRAL) { // If not neither of the cases then if (resultCode == LDAPException.BUSY) { debugger.error( "EventService.processResponseMessage() - received error BUSY, call retryManager"); return retryManager(false); } LDAPException ex = new LDAPException( "Error result", rsp.getResultCode(), rsp.getErrorMessage(), rsp.getMatchedDN()); dispatchException(ex, request); } return true; } /** Process change notification attached as the change control to the message */ protected void processSearchResultMessage(LDAPSearchResult res, Request req) { LDAPEntry modEntry = res.getEntry(); if (debugger.messageEnabled()) { debugger.message( "EventService.processSearchResultMessage() - " + "Changed " + modEntry.getDN()); } /* Get any entry change controls. */ LDAPControl[] ctrls = res.getControls(); // Can not create event without change control if (ctrls == null) { Exception ex = new Exception("EventService - Cannot create " + "NamingEvent, no change control info"); dispatchException(ex, req); } else { // Multiple controls might be in the message for (int i = 0; i < ctrls.length; i++) { LDAPEntryChangeControl changeCtrl = null; if (ctrls[i].getType() == LDAPControl.LDAP_ENTRY_CHANGE_CONTROL) { changeCtrl = (LDAPEntryChangeControl) ctrls[i]; if (debugger.messageEnabled()) { debugger.message( "EventService." + "processSearchResultMessage() changeCtrl = " + changeCtrl.toString()); } // Can not create event without change control if (changeCtrl.getChangeType() == -1) { Exception ex = new Exception( "EventService - Cannot " + "create NamingEvent, no change control info"); dispatchException(ex, req); } // Convert control into a DSEvent and dispatch to listeners try { DSEvent event = createDSEvent(modEntry, changeCtrl, req); dispatchEvent(event, req); } catch (Exception ex) { dispatchException(ex, req); } } } } } /** Search continuation messages are ignored. */ protected void processSearchResultRef(LDAPSearchResultReference ref, Request req) { // Do nothing, message ignored, do not dispatch ExceptionEvent if (debugger.messageEnabled()) { debugger.message("EventService.processSearchResultRef() - " + "Ignoring.."); } } protected static SSOToken getSSOToken() throws SSOException { return ((SSOToken) AccessController.doPrivileged(AdminTokenAction.getInstance())); } /** Find event entry by message ID */ protected Request getRequestEntry(int id) { return (Request) _requestList.get(Integer.toString(id)); } /** Create naming event from a change control */ private DSEvent createDSEvent(LDAPEntry entry, LDAPEntryChangeControl changeCtrl, Request req) throws Exception { DSEvent dsEvent = new DSEvent(); if (debugger.messageEnabled()) { debugger.message( "EventService.createDSEvent() - Notifying event " + "to: " + req.getListener()); } // Get the dn from the entry String dn = entry.getDN(); dsEvent.setID(dn); // Get information on the type of change made int changeType = changeCtrl.getChangeType(); dsEvent.setEventType(changeType); // Pass the search ID as the event's change info dsEvent.setSearchID(req.getRequestID()); // set the object class name String className = entry.getAttribute("objectclass").toString(); dsEvent.setClassName(className); return dsEvent; } class RetryTask extends GeneralTaskRunnable { private long runPeriod; private Map requests; private boolean clearCaches; private int numRetries; public RetryTask(Map requests) { this.runPeriod = getPropertyIntValue(EVENT_CONNECTION_RETRY_INTERVAL, EventService._retryInterval); this.requests = requests; this.numRetries = _numRetries; } public void clearCache(boolean cc) { clearCaches = cc; } public void run() { for (Iterator iter = requests.values().iterator(); iter.hasNext(); ) { Request req = (Request) iter.next(); try { // First add a new listener and then remove the old one // that we do don't loose any responses to the message // Queue. However before adding check if request list // already has this listener initialized if (!_requestList.containsValue(req)) { addListener( req.getRequester(), req.getListener(), req.getBaseDn(), req.getScope(), req.getFilter(), req.getOperations()); } removeListener(req); if (clearCaches) { // Send all entries changed notifications // only after successful establishment of psearch req.getListener().allEntriesChanged(); } iter.remove(); } catch (Exception e) { debugger.error("RetryTask", e); // Ignore exception and retry as we are in the process of // re-establishing the searches. Notify Listeners after the // attempt } } if (--numRetries == 0) { debugger.error("NumRetries " + numRetries); runPeriod = -1; } } public long getRunPeriod() { return runPeriod; } public boolean isEmpty() { return true; } public boolean addElement(Object obj) { return false; } public boolean removeElement(Object obj) { return false; } } }
/** * Adds a listener to the directory. * * @supported.api */ protected synchronized String addListener( SSOToken token, IDSEventListener listener, String base, int scope, String filter, int operations) throws LDAPException, EventException { if (_shutdownCalled) { throw new EventException(i18n.getString(IUMSConstants.DSCFG_CONNECTFAIL)); } LDAPConnection lc = null; try { // Check for SMS listener and use "sms" group if present if ((listener.getClass().getName().equals("com.sun.identity.sm.ldap.LDAPEventManager")) && (cm.getServerGroup("sms") != null)) { lc = cm.getNewConnection("sms", LDAPUser.Type.AUTH_ADMIN); } else { lc = cm.getNewAdminConnection(); } } catch (LDAPServiceException le) { throw new EventException(i18n.getString(IUMSConstants.DSCFG_CONNECTFAIL), le); } LDAPSearchConstraints cons = lc.getSearchConstraints(); // Create Persistent Search Control object LDAPPersistSearchControl psearchCtrl = new LDAPPersistSearchControl(operations, CHANGES_ONLY, RETURN_CONTROLS, IS_CRITICAL); // Add LDAPControl array to the search constraint object cons.setServerControls(psearchCtrl); cons.setBatchSize(1); // Listeners can not read attributes from the event. // Request only javaClassName to be able to determine object type String[] attrs = new String[] {"objectclass"}; LDAPSearchListener searchListener = null; // Set (asynchronous) persistent search request in the DS try { if (debugger.messageEnabled()) { debugger.message( "EventService.addListener() - Submiting " + "Persistent Search on: " + base + " for listener: " + listener); } searchListener = lc.search(base, scope, filter, attrs, false, null, cons); } catch (LDAPException le) { if ((lc != null) && lc.isConnected()) { try { lc.disconnect(); } catch (Exception ex) { // ignored } } debugger.error( "EventService.addListener() - Failed to set " + "Persistent Search" + le.getMessage()); throw le; } int[] outstandingRequests = searchListener.getMessageIDs(); int id = outstandingRequests[outstandingRequests.length - 1]; String reqID = Integer.toString(id); long startTime = System.currentTimeMillis(); Request request = new Request( id, reqID, token, base, scope, filter, attrs, operations, listener, lc, startTime); _requestList.put(reqID, request); // Add this search request to the m_msgQueue so it can be // processed by the monitor thread if (_msgQueue == null) { _msgQueue = searchListener; } else { _msgQueue.merge(searchListener); } if (!_isThreadStarted) { startMonitorThread(); } else { if (_requestList.size() == 1) { notify(); } } if (debugger.messageEnabled()) { outstandingRequests = _msgQueue.getMessageIDs(); debugger.message( "EventService.addListener(): merged Listener: " + " requestID: " + reqID + " & Request: " + request + " on to message Queue. No. of current outstanding " + "requests = " + outstandingRequests.length); } // Create new (EventService) Thread, if one doesn't exist. return reqID; }