001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2007-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    import org.opends.messages.Message;
029    import static org.opends.messages.CoreMessages.*;
030    import static org.opends.server.util.Validator.ensureNotNull;
031    
032    import java.util.TreeMap;
033    import java.util.Collection;
034    
035    import org.opends.server.types.DN;
036    import org.opends.server.types.DirectoryException;
037    import org.opends.server.types.ResultCode;
038    import org.opends.server.workflowelement.WorkflowElement;
039    
040    
041    /**
042     * This class defines the network group. A network group is used to categorize
043     * client connections. A network group is defined by a set of criteria, a
044     * set of policies and a set of workflow nodes. A client connection belongs to
045     * a network group whenever it satisfies all the network group criteria. As
046     * soon as a client connection belongs to a network group, it has to comply
047     * with all the network group policies. Any cleared client operation can be
048     * routed to one the network group workflow nodes.
049     */
050    public class NetworkGroup
051    {
052      // Workflow nodes registered with the current network group.
053      // Keys are workflowIDs.
054      private TreeMap<String, WorkflowTopologyNode> registeredWorkflowNodes =
055          new TreeMap<String, WorkflowTopologyNode>();
056    
057    
058      // A lock to protect concurrent access to the registered Workflow nodes.
059      private Object registeredWorkflowNodesLock = new Object();
060    
061    
062      // The workflow node for the rootDSE entry. The RootDSE workflow node
063      // is not stored in the list of registered workflow nodes.
064      private RootDseWorkflowTopology rootDSEWorkflowNode = null;
065    
066    
067      // List of naming contexts handled by the network group.
068      private NetworkGroupNamingContexts namingContexts =
069          new NetworkGroupNamingContexts();
070    
071    
072      // The default network group (singleton).
073      // The default network group has no criterion, no policy, and gives
074      // access to all the workflows. The purpose of the default network
075      // group is to allow new clients to perform a first operation before
076      // they can be attached to a specific network group.
077      private static NetworkGroup defaultNetworkGroup =
078          new NetworkGroup ("default");
079    
080    
081      // The list of all network groups that are registered with the server.
082      // The defaultNetworkGroup is not in the list of registered network groups.
083      private static TreeMap<String, NetworkGroup> registeredNetworkGroups =
084          new TreeMap<String, NetworkGroup>();
085    
086    
087      // A lock to protect concurrent access to the registeredNetworkGroups.
088      private static Object registeredNetworkGroupsLock = new Object();
089    
090    
091      // The network group internal identifier.
092      private String networkGroupID = null;
093    
094    
095    
096      /**
097       * Creates a new instance of the network group.
098       *
099       * @param networkGroupID  the network group internal identifier
100       */
101      public NetworkGroup(
102          String networkGroupID
103          )
104      {
105        this.networkGroupID = networkGroupID;
106      }
107    
108    
109      /**
110       * Performs any finalization that might be required when this
111       * network group is unloaded.  No action is taken in the
112       * default implementation.
113       */
114      public void finalizeNetworkGroup()
115      {
116        // No action is required by default.
117      }
118    
119    
120      /**
121       * Registers the current network group (this) with the server.
122       *
123       * @throws  DirectoryException  If the network group ID for the provided
124       *                              network group conflicts with the network
125       *                              group ID of an existing network group.
126       */
127      public void register()
128          throws DirectoryException
129      {
130        ensureNotNull(networkGroupID);
131    
132        synchronized (registeredNetworkGroupsLock)
133        {
134          // The network group must not be already registered
135          if (registeredNetworkGroups.containsKey(networkGroupID))
136          {
137            Message message = ERR_REGISTER_NETWORK_GROUP_ALREADY_EXISTS.get(
138                              networkGroupID);
139            throw new DirectoryException(
140                ResultCode.UNWILLING_TO_PERFORM, message);
141          }
142    
143          TreeMap<String, NetworkGroup> newRegisteredNetworkGroups =
144            new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
145          newRegisteredNetworkGroups.put(networkGroupID, this);
146          registeredNetworkGroups = newRegisteredNetworkGroups;
147        }
148      }
149    
150    
151      /**
152       * Deregisters the current network group (this) with the server.
153       */
154      public void deregister()
155      {
156        synchronized (registeredNetworkGroupsLock)
157        {
158          TreeMap<String, NetworkGroup> networkGroups =
159            new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
160          networkGroups.remove(networkGroupID);
161          registeredNetworkGroups = networkGroups;
162        }
163      }
164    
165    
166      /**
167       * Registers a workflow with the network group.
168       *
169       * @param workflow  the workflow to register
170       *
171       * @throws  DirectoryException  If the workflow ID for the provided
172       *                              workflow conflicts with the workflow
173       *                              ID of an existing workflow.
174       */
175      public void registerWorkflow(
176          WorkflowImpl workflow
177          ) throws DirectoryException
178      {
179        // The workflow is rgistered with no pre/post workflow element.
180        registerWorkflow(workflow, null, null);
181      }
182    
183    
184      /**
185       * Registers a workflow with the network group and the workflow may have
186       * pre and post workflow element.
187       *
188       * @param workflow              the workflow to register
189       * @param preWorkflowElements   the tasks to execute before the workflow
190       * @param postWorkflowElements  the tasks to execute after the workflow
191       *
192       * @throws  DirectoryException  If the workflow ID for the provided
193       *                              workflow conflicts with the workflow
194       *                              ID of an existing workflow.
195       */
196      private void registerWorkflow(
197          WorkflowImpl workflow,
198          WorkflowElement[] preWorkflowElements,
199          WorkflowElement[] postWorkflowElements
200          ) throws DirectoryException
201      {
202        // Is it the rootDSE workflow?
203        DN baseDN = workflow.getBaseDN();
204        if (baseDN.isNullDN())
205        {
206          // NOTE - The rootDSE workflow is stored with the registeredWorkflows.
207          rootDSEWorkflowNode =
208            new RootDseWorkflowTopology(workflow, namingContexts);
209        }
210        else
211        {
212          // This workflow is not the rootDSE workflow. Try to insert it in the
213          // workflow topology.
214          WorkflowTopologyNode workflowNode = new WorkflowTopologyNode(
215              workflow, preWorkflowElements, postWorkflowElements);
216    
217          // Register the workflow node with the network group. If the workflow
218          // ID is already existing then an exception is raised.
219          registerWorkflowNode(workflowNode);
220    
221          // Now add the workflow in the workflow topology...
222          for (WorkflowTopologyNode curNode: registeredWorkflowNodes.values())
223          {
224            // Try to insert the new workflow under an existing workflow...
225            if (curNode.insertSubordinate(workflowNode))
226            {
227              // new workflow has been inserted in the topology
228              continue;
229            }
230    
231            // ... or try to insert the existing workflow below the new
232            // workflow
233            if (workflowNode.insertSubordinate(curNode))
234            {
235              // new workflow has been inserted in the topology
236              continue;
237            }
238          }
239    
240          // Rebuild the list of naming context handled by the network group
241          rebuildNamingContextList();
242        }
243      }
244    
245    
246      /**
247       * Deregisters a workflow with the network group. The workflow to
248       * deregister is identified by its baseDN.
249       *
250       * @param baseDN  the baseDN of the workflow to deregister, may be null
251       *
252       * @return the deregistered workflow
253       */
254      public Workflow deregisterWorkflow(
255          DN baseDN
256          )
257      {
258        Workflow workflow = null;
259    
260        if (baseDN == null)
261        {
262          return workflow;
263        }
264    
265        if (baseDN.isNullDN())
266        {
267          // deregister the rootDSE
268          deregisterWorkflow(rootDSEWorkflowNode);
269          workflow = rootDSEWorkflowNode.getWorkflowImpl();
270        }
271        else
272        {
273          // deregister a workflow node
274          synchronized (registeredWorkflowNodesLock)
275          {
276            for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
277            {
278              DN curDN = node.getBaseDN();
279              if (curDN.equals(baseDN))
280              {
281                // Call deregisterWorkflow() instead of deregisterWorkflowNode()
282                // because we want the naming context list to be updated as well.
283                deregisterWorkflow(node);
284                workflow = node.getWorkflowImpl();
285    
286                // Only one workflow can match the baseDN, so we can break
287                // the loop here.
288                break;
289              }
290            }
291          }
292        }
293    
294        return workflow;
295      }
296    
297    
298      /**
299       * Deregisters a workflow with the network group. The workflow to
300       * deregister is identified by its workflow ID.
301       *
302       * @param workflowID the workflow identifier of the workflow to deregister
303       */
304      public void deregisterWorkflow(
305          String workflowID
306          )
307      {
308        String rootDSEWorkflowID = null;
309        if (rootDSEWorkflowNode != null)
310        {
311          rootDSEWorkflowID = rootDSEWorkflowNode.getWorkflowImpl().getWorkflowId();
312        }
313    
314        if (workflowID.equalsIgnoreCase(rootDSEWorkflowID))
315        {
316          // deregister the rootDSE
317          deregisterWorkflow(rootDSEWorkflowNode);
318        }
319        else
320        {
321          // deregister a workflow node
322          synchronized (registeredWorkflowNodesLock)
323          {
324            for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
325            {
326              String curID = node.getWorkflowImpl().getWorkflowId();
327              if (curID.equals(workflowID))
328              {
329                // Call deregisterWorkflow() instead of deregisterWorkflowNode()
330                // because we want the naming context list to be updated as well.
331                deregisterWorkflow(node);
332    
333                // Only one workflow can match the baseDN, so we can break
334                // the loop here.
335                break;
336              }
337            }
338          }
339        }
340      }
341    
342    
343      /**
344       * Deregisters a workflow node with the network group.
345       *
346       * @param workflow  the workflow node to deregister
347       */
348      private void deregisterWorkflow(Workflow workflow)
349      {
350        // true as soon as the workflow has been deregistered
351        boolean deregistered = false;
352    
353        // Is it the rootDSE workflow?
354        if (workflow == rootDSEWorkflowNode)
355        {
356          rootDSEWorkflowNode = null;
357          deregistered = true;
358        }
359        else
360        {
361          // Deregister the workflow with the network group.
362          WorkflowTopologyNode workflowNode = (WorkflowTopologyNode) workflow;
363          deregisterWorkflowNode(workflowNode);
364          deregistered = true;
365    
366          // The workflow to deregister is not the root DSE workflow.
367          // Remove it from the workflow topology.
368          workflowNode.remove();
369    
370          // Rebuild the list of naming context handled by the network group
371          rebuildNamingContextList();
372        }
373    
374        // If the workflow has been deregistered then deregister it with
375        // the default network group as well
376        if (deregistered && (this != defaultNetworkGroup))
377        {
378          defaultNetworkGroup.deregisterWorkflow(workflow);
379        }
380      }
381    
382    
383      /**
384       * Registers a workflow node with the network group.
385       *
386       * @param workflowNode  the workflow node to register
387       *
388       * @throws  DirectoryException  If the workflow node ID for the provided
389       *                              workflow node conflicts with the workflow
390       *                              node ID of an existing workflow node.
391       */
392      private void registerWorkflowNode(
393          WorkflowTopologyNode workflowNode
394          ) throws DirectoryException
395      {
396        String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
397        ensureNotNull(workflowID);
398    
399        synchronized (registeredWorkflowNodesLock)
400        {
401          // The workflow must not be already registered
402          if (registeredWorkflowNodes.containsKey(workflowID))
403          {
404            Message message = ERR_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS.get(
405              workflowID, networkGroupID);
406            throw new DirectoryException(
407                ResultCode.UNWILLING_TO_PERFORM, message);
408          }
409    
410          TreeMap<String, WorkflowTopologyNode> newRegisteredWorkflowNodes =
411            new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
412          newRegisteredWorkflowNodes.put(workflowID, workflowNode);
413          registeredWorkflowNodes = newRegisteredWorkflowNodes;
414        }
415      }
416    
417    
418      /**
419       * Deregisters the current worklow (this) with the server.
420       *
421       * @param workflowNode  the workflow node to deregister
422       */
423      private void deregisterWorkflowNode(
424          WorkflowTopologyNode workflowNode
425          )
426      {
427        synchronized (registeredWorkflowNodesLock)
428        {
429          TreeMap<String, WorkflowTopologyNode> newWorkflowNodes =
430            new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
431          newWorkflowNodes.remove(workflowNode.getWorkflowImpl().getWorkflowId());
432          registeredWorkflowNodes = newWorkflowNodes;
433        }
434      }
435    
436    
437      /**
438       * Gets the highest workflow in the topology that can handle the baseDN.
439       *
440       * @param baseDN  the base DN of the request
441       * @return the highest workflow in the topology that can handle the base DN,
442       *         <code>null</code> if none was found
443       */
444      public Workflow getWorkflowCandidate(
445          DN baseDN
446          )
447      {
448        // the top workflow to return
449        Workflow workflowCandidate = null;
450    
451        // get the list of workflow candidates
452        if (baseDN.isNullDN())
453        {
454          // The rootDSE workflow is the candidate.
455          workflowCandidate = rootDSEWorkflowNode;
456        }
457        else
458        {
459          // Search the highest workflow in the topology that can handle
460          // the baseDN.
461          for (WorkflowTopologyNode curWorkflow: namingContexts.getNamingContexts())
462          {
463            workflowCandidate = curWorkflow.getWorkflowCandidate (baseDN);
464            if (workflowCandidate != null)
465            {
466              break;
467            }
468          }
469        }
470    
471        return workflowCandidate;
472      }
473    
474    
475      /**
476       * Returns the default network group. The default network group is always
477       * defined and has no criterion, no policy and provide full access to
478       * all the registered workflows.
479       *
480       * @return the default network group
481       */
482      public static NetworkGroup getDefaultNetworkGroup()
483      {
484        return defaultNetworkGroup;
485      }
486    
487    
488      /**
489       * Rebuilds the list of naming contexts handled by the network group.
490       * This operation should be performed whenever a workflow topology
491       * has been updated (workflow registration or de-registration).
492       */
493      private void rebuildNamingContextList()
494      {
495        // reset lists of naming contexts
496        namingContexts.resetLists();
497    
498        // a registered workflow with no parent is a naming context
499        for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
500        {
501          WorkflowTopologyNode parent = workflowNode.getParent();
502          if (parent == null)
503          {
504            namingContexts.addNamingContext (workflowNode);
505          }
506        }
507      }
508    
509    
510      /**
511       * Returns the list of naming contexts handled by the network group.
512       *
513       * @return the list of naming contexts
514       */
515      public NetworkGroupNamingContexts getNamingContexts()
516      {
517        return namingContexts;
518      }
519    
520    
521      /**
522       * Dumps info from the current network group for debug purpose.
523       *
524       * @param  leftMargin  white spaces used to indent traces
525       * @return a string buffer that contains trace information
526       */
527      public StringBuilder toString(String leftMargin)
528      {
529        StringBuilder sb = new StringBuilder();
530        String newMargin = leftMargin + "   ";
531    
532        sb.append (leftMargin + "Networkgroup (" + networkGroupID+ "\n");
533        sb.append (leftMargin + "List of registered workflows:\n");
534        for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
535        {
536          sb.append (node.toString (newMargin));
537        }
538    
539        namingContexts.toString (leftMargin);
540    
541        sb.append (leftMargin + "rootDSEWorkflow:\n");
542        if (rootDSEWorkflowNode == null)
543        {
544          sb.append (newMargin + "null\n");
545        }
546        else
547        {
548          sb.append (rootDSEWorkflowNode.toString (newMargin));
549        }
550    
551        return sb;
552      }
553    
554    
555      /**
556       * Deregisters all network groups that have been registered.  This should be
557       * called when the server is shutting down.
558       */
559      public static void deregisterAllOnShutdown()
560      {
561        synchronized (registeredNetworkGroupsLock)
562        {
563          // Invalidate all NetworkGroups so they cannot accidentally be used
564          // after a restart.
565          Collection<NetworkGroup> networkGroups = registeredNetworkGroups.values();
566          for (NetworkGroup networkGroup: networkGroups)
567          {
568            networkGroup.invalidate();
569          }
570          defaultNetworkGroup.invalidate();
571    
572          registeredNetworkGroups = new TreeMap<String,NetworkGroup>();
573          defaultNetworkGroup = new NetworkGroup ("default");
574        }
575      }
576    
577      /**
578       * We've seen parts of the server hold references to a NetworkGroup
579       * during an in-core server restart.  To help detect when this happens,
580       * we null out the member variables, so we will fail fast with an NPE if an
581       * invalidate NetworkGroup is used.
582       */
583      private void invalidate()
584      {
585        namingContexts = null;
586        networkGroupID = null;
587        rootDSEWorkflowNode = null;
588        registeredWorkflowNodes = null;
589      }
590    
591    
592      /**
593       * Provides the list of network group registered with the server.
594       *
595       * @return the list of registered network groups
596       */
597      public static Collection<NetworkGroup> getRegisteredNetworkGroups()
598      {
599        return registeredNetworkGroups.values();
600      }
601    
602    
603      /**
604       * Resets the configuration of all the registered network groups.
605       */
606      public static void resetConfig()
607      {
608        // Reset the default network group
609        defaultNetworkGroup.reset();
610    
611        // Reset all the registered network group
612        synchronized (registeredNetworkGroupsLock)
613        {
614          registeredNetworkGroups = new TreeMap<String, NetworkGroup>();
615        }
616      }
617    
618    
619      /**
620       * Resets the configuration of the current network group.
621       */
622      public void reset()
623      {
624        synchronized (registeredWorkflowNodesLock)
625        {
626          registeredWorkflowNodes = new TreeMap<String, WorkflowTopologyNode>();
627          rootDSEWorkflowNode = null;
628          namingContexts = new NetworkGroupNamingContexts();
629        }
630      }
631    }