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 }