>From ff380d53abb1c12a1808c731b8542d4e4e9e65d1 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Mon, 22 Sep 2014 03:22:57 -0400 Subject: [PATCH] Monitor database for changes to LDAP profiles. Use a persistent query to monitor the database for changes to LDAP profiles, and update the contents of the ProfileSubsystem according to the changes (Add/Modify/Delete) that occur. The monitoring occurs within its own thread. --- .../netscape/cmscore/profile/ProfileSubsystem.java | 190 ++++++++++++++++++--- 1 file changed, 168 insertions(+), 22 deletions(-) diff --git a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java index ca0e09b785877da2a98fd2dd54977b8d3ebeaa24..8de92f5bb6c9bc3e9a5c430aa7c1269077f5148b 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java +++ b/base/server/cmscore/src/com/netscape/cmscore/profile/ProfileSubsystem.java @@ -17,15 +17,20 @@ // --- END COPYRIGHT BLOCK --- package com.netscape.cmscore.profile; +import java.lang.Thread; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import netscape.ldap.LDAPAttribute; import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPControl; import netscape.ldap.LDAPEntry; import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchConstraints; import netscape.ldap.LDAPSearchResults; +import netscape.ldap.controls.LDAPEntryChangeControl; +import netscape.ldap.controls.LDAPPersistSearchControl; import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.EBaseException; @@ -55,6 +60,8 @@ public class ProfileSubsystem implements IProfileSubsystem { private ILdapConnFactory dbFactory; + private Monitor monitor; + /** * Retrieves the name of this subsystem. */ @@ -81,14 +88,16 @@ public class ProfileSubsystem implements IProfileSubsystem { throws EBaseException { CMS.debug("ProfileSubsystem: start init"); + if (monitor != null) { + monitor.stopMonitoring(); + monitor = null; + } + // (re)init member collections mProfileIds = new Vector(); mProfiles = new Hashtable(); mProfileClassIds = new Hashtable(); - IPluginRegistry registry = (IPluginRegistry) - CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); - IConfigStore cs = CMS.getConfigStore(); IConfigStore dbCfg = cs.getSubStore("internaldb"); dbFactory = CMS.getLdapBoundConnFactory(); @@ -115,26 +124,10 @@ public class ProfileSubsystem implements IProfileSubsystem { dn, LDAPConnection.SCOPE_ONE, "(objectclass=*)", attrs, false); while (ldapProfiles.hasMoreElements()) { - String id = ""; try { - LDAPEntry ldapProfile = ldapProfiles.next(); - - id = (String) - ldapProfile.getAttribute("cn").getStringValues().nextElement(); - - String classid = (String) - ldapProfile.getAttribute("classId").getStringValues().nextElement(); - - IPluginInfo info = registry.getPluginInfo("profile", classid); - if (info == null) { - CMS.debug("Error loading profile: No plugins for type : profile, with id " + classid); - } else { - CMS.debug("Start Profile Creation - " + id + " " + classid + " " + info.getClassName()); - createProfile(id, classid, info.getClassName()); - CMS.debug("Done Profile Creation - " + id); - } + readProfile(ldapProfiles.next()); } catch (LDAPException e) { - CMS.debug("Error reading profile '" + id + "'; skipping."); + CMS.debug("Error retrieving profile; skipping."); } } } catch (LDAPException e) { @@ -154,13 +147,53 @@ public class ProfileSubsystem implements IProfileSubsystem { CMS.debug("Registered Confirmation - " + id); } + + monitor = new Monitor(this, dn, dbFactory); + monitor.start(); + } + + /** + * Read the given LDAPEntry into the profile subsystem. + */ + private void readProfile(LDAPEntry ldapProfile) { + IPluginRegistry registry = (IPluginRegistry) + CMS.getSubsystem(CMS.SUBSYSTEM_REGISTRY); + + String profileId = ""; + profileId = (String) + ldapProfile.getAttribute("cn").getStringValues().nextElement(); + + String classId = (String) + ldapProfile.getAttribute("classId").getStringValues().nextElement(); + + IPluginInfo info = registry.getPluginInfo("profile", classId); + if (info == null) { + CMS.debug("Error loading profile: No plugins for type : profile, with classId " + classId); + } else { + try { + CMS.debug("Start Profile Creation - " + profileId + " " + classId + " " + info.getClassName()); + createProfile(profileId, classId, info.getClassName()); + CMS.debug("Done Profile Creation - " + profileId); + } catch (EProfileException e) { + CMS.debug("Error creating profile '" + profileId + "'; skipping."); + } + } } /** * Creates a profile instance. + * + * createProfile could theoretically be called simultaneously + * with the same profileId from Monitor and ProfileService. + * Therefore this method is synchronized and forgetProfile() is + * called prior to creating the profile, so that the profile + * will not appear twice. + * */ - public IProfile createProfile(String id, String classid, String className) + public synchronized IProfile createProfile(String id, String classid, String className) throws EProfileException { + forgetProfile(id); + try { String[] objectClasses = {"top", "certProfile"}; LDAPAttribute[] createAttrs = { @@ -211,11 +244,31 @@ public class ProfileSubsystem implements IProfileSubsystem { } } + forgetProfile(id); + } + + /** + * Forget a profile without deleting it from the database. + * + * This method is used when the profile change monitor receives + * notification that a profile was deleted. + */ + private void forgetProfile(String id) { mProfileIds.removeElement(id); mProfiles.remove(id); mProfileClassIds.remove(id); } + private void forgetProfile(LDAPEntry entry) { + String profileId = (String) + entry.getAttribute("cn").getStringValues().nextElement(); + if (profileId == null) { + CMS.debug("forgetProfile: error retrieving cn (profileId) from LDAPEntry"); + } else { + forgetProfile(profileId); + } + } + /** * Notifies this subsystem if owner is in running mode. */ @@ -229,6 +282,8 @@ public class ProfileSubsystem implements IProfileSubsystem { *

*/ public void shutdown() { + monitor.stopMonitoring(); + monitor = null; mProfileIds.clear(); mProfiles.clear(); mProfileClassIds.clear(); @@ -348,4 +403,95 @@ public class ProfileSubsystem implements IProfileSubsystem { } return "cn=" + id + ",ou=certificateProfiles,ou=ca," + basedn; } + + private class Monitor extends Thread { + private volatile boolean stopped = false; + private ProfileSubsystem ps; + private String dn; + private ILdapConnFactory dbFactory; + + public Monitor(ProfileSubsystem ps, String dn, ILdapConnFactory dbFactory) { + setName("profileChangeMonitor"); + this.ps = ps; + this.dn = dn; + this.dbFactory = dbFactory; + } + + public void stopMonitoring() { + stopped = true; + } + + public void run() { + int op = LDAPPersistSearchControl.ADD + | LDAPPersistSearchControl.MODIFY + | LDAPPersistSearchControl.DELETE + | LDAPPersistSearchControl.MODDN; + LDAPPersistSearchControl persistCtrl = + new LDAPPersistSearchControl(op, true, true, true); + + LDAPConnection conn; + try { + conn = dbFactory.getConn(); + } catch (ELdapException e) { + CMS.debug("Profile change monitor: failed to get LDAPConnection"); + return; + } + try { + LDAPSearchConstraints cons = conn.getSearchConstraints(); + cons.setServerControls(persistCtrl); + cons.setBatchSize(1); + cons.setServerTimeLimit(0 /* seconds */); + LDAPSearchResults results = conn.search( + dn, LDAPConnection.SCOPE_ONE, "(objectclass=*)", + null, false, cons); + while (!stopped && results.hasMoreElements()) { + LDAPEntry entry = results.next(); + LDAPEntryChangeControl changeControl = null; + for (LDAPControl control : results.getResponseControls()) { + if (control instanceof LDAPEntryChangeControl) { + changeControl = (LDAPEntryChangeControl) control; + break; + } + } + CMS.debug("Profile change monitor: Processed change controls."); + if (changeControl != null) { + int changeType = changeControl.getChangeType(); + switch (changeType) { + case LDAPPersistSearchControl.ADD: + CMS.debug("Profile change monitor: ADD"); + readProfile(entry); + break; + case LDAPPersistSearchControl.DELETE: + CMS.debug("Profile change monitor: DELETE"); + forgetProfile(entry); + break; + case LDAPPersistSearchControl.MODIFY: + CMS.debug("Profile change monitor: MODIFY"); + forgetProfile(entry); + readProfile(entry); + break; + case LDAPPersistSearchControl.MODDN: + CMS.debug("Profile change monitor: MODDN"); + CMS.debug("Profile change monitor: MODDN shouldn't happen; ignoring."); + break; + default: + CMS.debug("Profile change monitor: unknown change type: " + changeType); + break; + } + } else { + CMS.debug("Profile change monitor: no LDAPEntryChangeControl in result."); + } + } + } catch (LDAPException e) { + CMS.debug("Profile change monitor: Caught exception: " + e.toString()); + } finally { + CMS.debug("Profile change monitor: stopping."); + try { + dbFactory.returnConn(conn); + } catch (Exception e) { + CMS.debug("Profile change monitor: Error releasing the LDAPConnection" + e.toString()); + } + } + } + } } -- 1.9.3