opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -289,10 +289,9 @@ supportedFeatures = new HashSet<String>(0); // Set the backend ID for this backend. We don't have to worry about // potential conflicts because this backend will never get registered with // the Directory Server like other backends. setBackendID("rootdse"); // Set the backend ID for this backend. The identifier needs to be // specific enough to avoid conflict with user backend identifiers. setBackendID("__root.dse__"); // Register as a change listener. opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2587,32 +2587,49 @@ /** * Deregister a workflow from a network group. * Deregisters a set of workflows each of which is identified with * a baseDN. * * In the first implementation, workflows are stored in the default network * group. * group only. * * @param backend the backend which is handled by the workflows to * deregister * @param baseDNs the DNs of the workflows to deregister */ private static void deregisterWorkflows( Backend backend DN[] baseDNs ) { for (DN baseDN: baseDNs) { deregisterWorkflow(baseDN); } } /** * Deregisters one workflow with the appropriate network group. * * In the first implementation, workflows are stored in the default network * group only. * * @param baseDN the DN of the workflow to deregister */ private static void deregisterWorkflow( DN baseDN ) { // Get the default network group and deregister all the workflows // being configured for the backend (reminder: there is one worklfow // per base DN configured in the backend). NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup(); for (DN baseDN: backend.getBaseDNs()) { defaultNetworkGroup.deregisterWorkflow (baseDN); } defaultNetworkGroup.deregisterWorkflow (baseDN); } /** * Create a workflow for the a backend then register the workflow * with the appropriate network group. * Creates a set of workflows for a given backend. There are as many * workflows as base DNs defined in the backend. Each workflow is * registered with the appropriate network group. * * TODO implement the registration with the appropriate network group. * @@ -2622,28 +2639,55 @@ * workflow conflicts with the workflow * ID of an existing workflow. */ private static void createAndRegisterWorkflows( public static void createAndRegisterWorkflows( Backend backend ) throws DirectoryException { // Create a root workflow element to encapsulate the backend LocalBackendWorkflowElement rootWE = new LocalBackendWorkflowElement(backend.getBackendID(), backend); // Create a worklfow for each baseDN being configured // in the backend and register the workflow with the network groups. // The workflow identifier is the same as the backend ID. // In the automatic configuration mode, the workflow identifier is // set to the backend ID. for (DN curBaseDN: backend.getBaseDNs()) { WorkflowImpl workflowImpl = new WorkflowImpl( curBaseDN.toString(), curBaseDN, (WorkflowElement) rootWE); registerWorkflowInNetworkGroups(workflowImpl); createAndRegisterWorkflow(curBaseDN, backend); } } /** * Register a workflow with the appropriate network groups. * Creates one workflow for a given base DN in a backend. The workflow * is registered with the appropriate network group. * * TODO implement the registration with the appropriate network group. * * @param baseDN the base DN of the workflow to create * @param backend the backend handled by the workflow * * @throws DirectoryException If the workflow ID for the provided * workflow conflicts with the workflow * ID of an existing workflow. */ public static void createAndRegisterWorkflow( DN baseDN, Backend backend ) throws DirectoryException { String backendID = backend.getBackendID(); // Create a root workflow element to encapsulate the backend LocalBackendWorkflowElement rootWE = LocalBackendWorkflowElement.create(backendID, backend); // Create the worklfow for the base DN and register the workflow with // the appropriate network groups. WorkflowImpl workflowImpl = new WorkflowImpl( baseDN.toString(), baseDN, (WorkflowElement) rootWE); registerWorkflowInNetworkGroups(workflowImpl); } /** * Registers a workflow with the appropriate network groups. * * In the first implementation, the workflow is registered with the * default network group only. @@ -2661,7 +2705,6 @@ WorkflowImpl workflowImpl ) throws DirectoryException { // Register first the workflow with the default network group NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup(); defaultNetworkGroup.registerWorkflow(workflowImpl); @@ -2674,14 +2717,8 @@ /** * Create the workflows for the backends that were not registered through * registerBackend method, nemely cn=config and RootDSE. * * TODO jdemendi - read the Workflow config and create them accordingly * * For the prototype: there is no configuration for the workflows. * So we create one workflow per local backend, and we register it * to the pool. * Creates the workflows for the backends whose baseDNs were not registered * with registerBaseDN method, namely config backend and RootDSE backend. * * @throws ConfigException If there is a configuration problem with any of * the workflows. @@ -2691,10 +2728,7 @@ { try { // Create a workflow for the cn=config backend createAndRegisterWorkflows (configHandler); // Create a workflows for the rootDSE backend createAndRegisterWorkflows (rootDSEBackend); } catch (DirectoryException de) @@ -6190,15 +6224,6 @@ monitor.initializeMonitorProvider(null); backend.setBackendMonitor(monitor); registerMonitorProvider(monitor); // FIXME jdemendi - temporary code: create one workflow for each // base DN being configured in the backend. We should not rely on the // backend registration to create and register workflows because // a workflow can be created for different type of "backend", including // remote lDAP server. Instead, we should create the workflows using // the administration framework. We will be doing so as soon as we // have a configuration section for the workflows. createAndRegisterWorkflows (backend); } } } @@ -6225,12 +6250,9 @@ directoryServer.backends = newBackends; // Delete all the workflows registered for the backend. // FIXME jdemendi - This task should be performed in the scope of the // administration framework. However the administration framework // requires a configuration section for the workflows which we don't have // as of today. deregisterWorkflows (backend); // Don't need anymore the local backend workflow element LocalBackendWorkflowElement.remove(backend.getBackendID()); BackendMonitor monitor = backend.getBackendMonitor(); if (monitor != null) @@ -6547,6 +6569,15 @@ directoryServer.baseDNs = newBaseDNs; directoryServer.publicNamingContexts = newPublicNamingContexts; directoryServer.privateNamingContexts = newPrivateNamingContexts; // Now create a workflow for the registered baseDN and register // the workflow with the network groups, but don't register the // workflow if the backend happens to be the configuration backend // because it's too soon. if (! baseDN.equals(DN.decode("cn=config"))) { createAndRegisterWorkflow(baseDN, backend); } } } } @@ -6693,6 +6724,9 @@ directoryServer.baseDNs = newBaseDNs; directoryServer.publicNamingContexts = newPublicNamingContexts; directoryServer.privateNamingContexts = newPrivateNamingContexts; // Now deregister the workflow that was associated with the base DN. deregisterWorkflow(baseDN); } } } @@ -8396,6 +8430,10 @@ { try { // Deregister all the local backend workflow elements that have been // registered with the server. LocalBackendWorkflowElement.removeAll(); for (BackendInitializationListener listener : directoryServer.backendInitializationListeners) { opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -731,6 +731,10 @@ try { // Set a backend ID for the config backend. Try to avoid potential // conflict with user backend identifiers. setBackendID("__config.ldif__"); DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true, false); } opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -46,6 +46,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.locks.Lock; import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn; @@ -139,8 +140,17 @@ */ private static final DebugTracer TRACER = DebugLogger.getTracer(); // the backend associated to that workflow element Backend backend; // the backend associated with the local workflow element private Backend backend; // the set of local backend workflow elements registered with the server private static TreeMap<String, LocalBackendWorkflowElement> registeredLocalBackends = new TreeMap<String, LocalBackendWorkflowElement>(); // a lock to guarantee safe concurrent access to the registeredLocalBackends // variable private static Object registeredLocalBackendsLock = new Object(); /** @@ -149,7 +159,7 @@ * @param workflowElementID the workflow element identifier * @param backend the backend associated to that workflow element */ public LocalBackendWorkflowElement( private LocalBackendWorkflowElement( String workflowElementID, Backend backend ) @@ -166,6 +176,122 @@ /** * Creates and registers a local backend with the server. * * @param workflowElementID the identifier of the workflow element to create * @param backend the backend to associate with the local backend * workflow element * * @return the existing local backend workflow element if it was * already created or a newly created local backend workflow * element. */ public static LocalBackendWorkflowElement create( String workflowElementID, Backend backend ) { LocalBackendWorkflowElement localBackend = null; // If the requested workflow element does not exist then create one. localBackend = registeredLocalBackends.get(workflowElementID); if (localBackend == null) { localBackend = new LocalBackendWorkflowElement( workflowElementID, backend); // store the new local backend in the list of registered backends registerLocalBackend(localBackend); } return localBackend; } /** * Removes a local backend that was registered with the server. * * @param workflowElementID the identifier of the workflow element to remove */ public static void remove( String workflowElementID ) { deregisterLocalBackend(workflowElementID); } /** * Removes all the local backends that were registered with the server. * This function is intended to be called when the server is shutting down. */ public static void removeAll() { synchronized (registeredLocalBackendsLock) { for (LocalBackendWorkflowElement localBackend: registeredLocalBackends.values()) { deregisterLocalBackend(localBackend.getWorkflowElementID()); } } } /** * Registers a local backend with the server. * * @param localBackend the local backend to register with the server */ private static void registerLocalBackend( LocalBackendWorkflowElement localBackend ) { synchronized (registeredLocalBackendsLock) { String localBackendID = localBackend.getWorkflowElementID(); LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(localBackendID); if (existingLocalBackend == null) { TreeMap<String, LocalBackendWorkflowElement> newLocalBackends = new TreeMap <String, LocalBackendWorkflowElement>(registeredLocalBackends); newLocalBackends.put(localBackendID, localBackend); registeredLocalBackends = newLocalBackends; } } } /** * Deregisters a local backend with the server. * * @param workflowElementID the identifier of the workflow element to remove */ private static void deregisterLocalBackend( String workflowElementID ) { synchronized (registeredLocalBackendsLock) { LocalBackendWorkflowElement existingLocalBackend = registeredLocalBackends.get(workflowElementID); if (existingLocalBackend != null) { TreeMap<String, LocalBackendWorkflowElement> newLocalBackends = new TreeMap <String, LocalBackendWorkflowElement>(registeredLocalBackends); newLocalBackends.remove(workflowElementID); registeredLocalBackends = newLocalBackends; } } } /** * {@inheritDoc} */ public void execute(Operation operation) opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
@@ -31,11 +31,22 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.opends.server.config.ConfigConstants.DN_BACKEND_BASE; import java.util.ArrayList; import org.opends.server.TestCaseUtils; import org.opends.server.DirectoryServerTestCase; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.types.Attribute; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchScope; import org.opends.server.workflowelement.WorkflowElement; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -473,6 +484,160 @@ /** * This test checks that network groups are updated as appropriate when * backend base DNs are added or removed. When a new backend base DN is * added, the new suffix should be accessible for the route process - ie. * a workflow should be created and be a potential candidate for the route * process. Simillarly, when a backend base DN is removed its associated * workflow should be removed; subsequently, any request targetting the * removed suffix should be rejected and a no such entry status code be * returned. */ @Test public void testBackendBaseDNModification() throws Exception { String suffix = "dc=example,dc=com"; String suffix2 = "o=networkgroup suffix"; String backendBaseDNName = "ds-cfg-backend-base-dn"; // Initialize a backend with a base entry. TestCaseUtils.clearJEBackend(true, "userRoot", suffix); // Create a client connection for the test. InternalClientConnection connection = InternalClientConnection.getRootConnection(); // Check that suffix is accessible while suffix2 is not. searchEntry(connection, suffix, true); searchEntry(connection, suffix2, false); // Add a new suffix in the backend and create a base entry for the // new suffix. String backendConfigDN = "ds-cfg-backend-id=userRoot," + DN_BACKEND_BASE; modifyAttribute( connection, backendConfigDN, ModificationType.ADD, backendBaseDNName, suffix2); addBaseEntry(connection, suffix2, "networkgroup suffix"); // Both old and new suffix should be accessible. searchEntry(connection, suffix, true); searchEntry(connection, suffix2, true); // Remove the new suffix... modifyAttribute( connection, backendConfigDN, ModificationType.DELETE, backendBaseDNName, suffix2); // ...and check that the removed suffix is no more accessible. searchEntry(connection, suffix, true); searchEntry(connection, suffix2, false); // Replace the suffix with suffix2 in the backend modifyAttribute( connection, backendConfigDN, ModificationType.REPLACE, backendBaseDNName, suffix2); // Now none of the suffixes are accessible: this means the entries // under the old suffix are not moved to the new suffix. searchEntry(connection, suffix, false); searchEntry(connection, suffix2, false); // Add a base entry for the new suffix addBaseEntry(connection, suffix2, "networkgroup suffix"); // The new suffix is accessible while the old one is not. searchEntry(connection, suffix, false); searchEntry(connection, suffix2, true); } /** * Searches an entry on a given connection. * * @param connection the connection to use for the search request * @param baseDN the request base DN string * @param shouldExist if true the searched entry is expected to be found */ private void searchEntry( InternalClientConnection connection, String baseDN, boolean shouldExist ) throws Exception { SearchOperation search = connection.processSearch( DN.decode(baseDN), SearchScope.BASE_OBJECT, LDAPFilter.decode("(objectClass=*)").toSearchFilter()); // Compare the result code with the expected one ResultCode resultCode = search.getResultCode(); if (shouldExist) { assertEquals(resultCode, ResultCode.SUCCESS); } else { assertEquals(resultCode, ResultCode.NO_SUCH_OBJECT); } } /** * Creates a base entry for the given suffix. * * @param connection the connection to use for the add request * @param suffix the suffix for which the base entry is to be created */ private void addBaseEntry( InternalClientConnection connection, String suffix, String namingAttribute ) throws Exception { Entry e = TestCaseUtils.makeEntry( "dn: " + suffix, "objectClass: top", "objectClass: organization", "o: " + namingAttribute); AddOperation addOperation = connection.processAdd( e.getDN(), e.getObjectClasses(), e.getUserAttributes(), e.getOperationalAttributes()); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); } /** * Adds/Deletes/Replaces an attribute in a given entry. * * @param connection the connection to use for the modify request * @param baseDN the request base DN string * @param modType the modification type (add/delete/replace) * @param attributeName the name of the attribute to add/delete/replace * @param attributeValue the value of the attribute to add/delete/replace */ private void modifyAttribute( InternalClientConnection connection, String baseDN, ModificationType modType, String attributeName, String attributeValue ) throws Exception { ArrayList<Modification> mods = new ArrayList<Modification>(); Attribute attributeToModify = new Attribute(attributeName, attributeValue); mods.add(new Modification(modType, attributeToModify)); ModifyOperation modifyOperation = connection.processModify( DN.decode(baseDN), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } /** * Checks the DN routing through a network group. * * @param networkGroup the network group to use for the check