[Pki-devel] [PATCH] pki-ftweedal-0015-Monitor-database-for-changes-to-LDAP-profiles.patch

Fraser Tweedale ftweedal at redhat.com
Wed Oct 1 06:42:30 UTC 2014


On Mon, Sep 22, 2014 at 05:36:26PM +1000, Fraser Tweedale wrote:
> This is the first cut of the LDAP profile change monitoring.  It
> depends on patches 0004..0009 and 0014
> (https://www.redhat.com/archives/pki-devel/2014-September/msg00052.html).
> 
> To summarise the implementation: a separate thread carries out a
> persistent LDAP search and calls back into the ProfileSubsystem when
> changes occur.  I haven't had much experience with persistent
> searches or multithreaded programming in Java, so eyeballs familiar
> with those areas are needed.
> 
> I haven't yet tested with changes replicating between clones (a task
> for tomorrow) but I wanted to get the patch on list for feedback as
> early as possible.
> 
Further to earlier email, I tested profile change replication
between clones and it is working well.

> Cheers,
> 
> Fraser

> >From ff380d53abb1c12a1808c731b8542d4e4e9e65d1 Mon Sep 17 00:00:00 2001
> From: Fraser Tweedale <ftweedal at redhat.com>
> 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<String>();
>          mProfiles = new Hashtable<String, IProfile>();
>          mProfileClassIds = new Hashtable<String, String>();
>  
> -        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 = "<unknown>";
>                  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 = "<unknown>";
> +        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 {
>       * <P>
>       */
>      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
> 

> _______________________________________________
> Pki-devel mailing list
> Pki-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/pki-devel




More information about the Pki-devel mailing list