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 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.tools; 028 import org.opends.messages.Message; 029 030 031 032 import java.io.OutputStream; 033 import java.io.PrintStream; 034 import java.util.Iterator; 035 import java.util.LinkedList; 036 import java.util.TreeMap; 037 import java.util.TreeSet; 038 039 import org.opends.server.config.ConfigEntry; 040 import org.opends.server.config.ConfigException; 041 import org.opends.server.config.DNConfigAttribute; 042 import org.opends.server.config.StringConfigAttribute; 043 import org.opends.server.core.DirectoryServer; 044 import org.opends.server.extensions.ConfigFileHandler; 045 import org.opends.server.types.DirectoryException; 046 import org.opends.server.types.DN; 047 import org.opends.server.types.InitializationException; 048 import org.opends.server.types.NullOutputStream; 049 import org.opends.server.util.args.ArgumentException; 050 import org.opends.server.util.args.ArgumentParser; 051 import org.opends.server.util.args.BooleanArgument; 052 import org.opends.server.util.args.StringArgument; 053 import org.opends.server.util.table.TableBuilder; 054 import org.opends.server.util.table.TextTablePrinter; 055 056 import static org.opends.server.config.ConfigConstants.*; 057 import static org.opends.messages.ConfigMessages.*; 058 import static org.opends.messages.ToolMessages.*; 059 import static org.opends.server.util.ServerConstants.*; 060 import static org.opends.server.util.StaticUtils.*; 061 import static org.opends.server.tools.ToolConstants.*; 062 063 064 065 066 /** 067 * This program provides a utility that may be used to list the backends in the 068 * server, as well as to determine which backend holds a given entry. 069 */ 070 public class ListBackends 071 { 072 /** 073 * Parses the provided command-line arguments and uses that information to 074 * list the backend information. 075 * 076 * @param args The command-line arguments provided to this program. 077 */ 078 public static void main(String[] args) 079 { 080 int retCode = listBackends(args, true, System.out, System.err); 081 082 if(retCode != 0) 083 { 084 System.exit(filterExitCode(retCode)); 085 } 086 } 087 088 089 090 /** 091 * Parses the provided command-line arguments and uses that information to 092 * list the backend information. 093 * 094 * @param args The command-line arguments provided to this program. 095 * 096 * @return A return code indicating whether the processing was successful. 097 */ 098 public static int listBackends(String[] args) 099 { 100 return listBackends(args, true, System.out, System.err); 101 } 102 103 104 105 /** 106 * Parses the provided command-line arguments and uses that information to 107 * list the backend information. 108 * 109 * @param args The command-line arguments provided to this 110 * program. 111 * @param initializeServer Indicates whether to initialize the server. 112 * @param outStream The output stream to use for standard output, or 113 * <CODE>null</CODE> if standard output is not 114 * needed. 115 * @param errStream The output stream to use for standard error, or 116 * <CODE>null</CODE> if standard error is not 117 * needed. 118 * 119 * @return A return code indicating whether the processing was successful. 120 */ 121 public static int listBackends(String[] args, boolean initializeServer, 122 OutputStream outStream, OutputStream errStream) 123 { 124 PrintStream out; 125 if (outStream == null) 126 { 127 out = NullOutputStream.printStream(); 128 } 129 else 130 { 131 out = new PrintStream(outStream); 132 } 133 134 PrintStream err; 135 if (errStream == null) 136 { 137 err = NullOutputStream.printStream(); 138 } 139 else 140 { 141 err = new PrintStream(errStream); 142 } 143 144 // Define the command-line arguments that may be used with this program. 145 BooleanArgument displayUsage = null; 146 StringArgument backendID = null; 147 StringArgument baseDN = null; 148 StringArgument configClass = null; 149 StringArgument configFile = null; 150 151 152 // Create the command-line argument parser for use with this program. 153 Message toolDescription = INFO_LISTBACKENDS_TOOL_DESCRIPTION.get(); 154 ArgumentParser argParser = 155 new ArgumentParser("org.opends.server.tools.ListBackends", 156 toolDescription, false); 157 158 159 // Initialize all the command-line argument types and register them with the 160 // parser. 161 try 162 { 163 configClass = 164 new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, 165 OPTION_LONG_CONFIG_CLASS, true, false, 166 true, INFO_CONFIGCLASS_PLACEHOLDER.get(), 167 ConfigFileHandler.class.getName(), null, 168 INFO_DESCRIPTION_CONFIG_CLASS.get()); 169 configClass.setHidden(true); 170 argParser.addArgument(configClass); 171 172 173 configFile = 174 new StringArgument("configfile", 'f', "configFile", true, false, 175 true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, 176 null, 177 INFO_DESCRIPTION_CONFIG_FILE.get()); 178 configFile.setHidden(true); 179 argParser.addArgument(configFile); 180 181 182 backendID = new StringArgument( 183 "backendid", 'n', "backendID", false, 184 true, true, INFO_BACKENDNAME_PLACEHOLDER.get(), null, null, 185 INFO_LISTBACKENDS_DESCRIPTION_BACKEND_ID.get()); 186 argParser.addArgument(backendID); 187 188 189 baseDN = new StringArgument( 190 "basedn", OPTION_SHORT_BASEDN, 191 OPTION_LONG_BASEDN, false, true, true, 192 INFO_BASEDN_PLACEHOLDER.get(), null, null, 193 INFO_LISTBACKENDS_DESCRIPTION_BASE_DN.get()); 194 argParser.addArgument(baseDN); 195 196 197 displayUsage = new BooleanArgument( 198 "help", OPTION_SHORT_HELP, 199 OPTION_LONG_HELP, 200 INFO_LISTBACKENDS_DESCRIPTION_HELP.get()); 201 argParser.addArgument(displayUsage); 202 argParser.setUsageArgument(displayUsage, out); 203 } 204 catch (ArgumentException ae) 205 { 206 Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()); 207 208 err.println(wrapText(message, MAX_LINE_WIDTH)); 209 return 1; 210 } 211 212 213 // Parse the command-line arguments provided to this program. 214 try 215 { 216 argParser.parseArguments(args); 217 } 218 catch (ArgumentException ae) 219 { 220 Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); 221 222 err.println(wrapText(message, MAX_LINE_WIDTH)); 223 err.println(argParser.getUsage()); 224 return 1; 225 } 226 227 228 // If we should just display usage or version information, 229 // then it's already been done so just return. 230 if (argParser.usageOrVersionDisplayed()) 231 { 232 return 0; 233 } 234 235 236 // Make sure that the user did not provide both the backend ID and base DN 237 // arguments. 238 if (backendID.isPresent() && baseDN.isPresent()) 239 { 240 Message message = ERR_TOOL_CONFLICTING_ARGS.get( 241 backendID.getLongIdentifier(), 242 baseDN.getLongIdentifier()); 243 err.println(wrapText(message, MAX_LINE_WIDTH)); 244 return 1; 245 } 246 247 248 // Perform the initial bootstrap of the Directory Server and process the 249 // configuration. 250 DirectoryServer directoryServer = DirectoryServer.getInstance(); 251 252 if (initializeServer) 253 { 254 try 255 { 256 directoryServer.bootstrapClient(); 257 directoryServer.initializeJMX(); 258 } 259 catch (Exception e) 260 { 261 Message message = ERR_SERVER_BOOTSTRAP_ERROR.get( 262 getExceptionMessage(e)); 263 err.println(wrapText(message, MAX_LINE_WIDTH)); 264 return 1; 265 } 266 267 try 268 { 269 directoryServer.initializeConfiguration(configClass.getValue(), 270 configFile.getValue()); 271 } 272 catch (InitializationException ie) 273 { 274 Message message = ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage()); 275 err.println(wrapText(message, MAX_LINE_WIDTH)); 276 return 1; 277 } 278 catch (Exception e) 279 { 280 Message message = ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e)); 281 err.println(wrapText(message, MAX_LINE_WIDTH)); 282 return 1; 283 } 284 285 286 287 // Initialize the Directory Server schema elements. 288 try 289 { 290 directoryServer.initializeSchema(); 291 } 292 catch (ConfigException ce) 293 { 294 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ce.getMessage()); 295 err.println(wrapText(message, MAX_LINE_WIDTH)); 296 return 1; 297 } 298 catch (InitializationException ie) 299 { 300 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ie.getMessage()); 301 err.println(wrapText(message, MAX_LINE_WIDTH)); 302 return 1; 303 } 304 catch (Exception e) 305 { 306 Message message = ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e)); 307 err.println(wrapText(message, MAX_LINE_WIDTH)); 308 return 1; 309 } 310 } 311 312 313 // Retrieve a list of the backkends defined in the server. 314 TreeMap<String,TreeSet<DN>> backends; 315 try 316 { 317 backends = getBackends(); 318 } 319 catch (ConfigException ce) 320 { 321 Message message = ERR_LISTBACKENDS_CANNOT_GET_BACKENDS.get( 322 ce.getMessage()); 323 err.println(wrapText(message, MAX_LINE_WIDTH)); 324 return 1; 325 } 326 catch (Exception e) 327 { 328 Message message = ERR_LISTBACKENDS_CANNOT_GET_BACKENDS.get( 329 getExceptionMessage(e)); 330 err.println(wrapText(message, MAX_LINE_WIDTH)); 331 return 1; 332 } 333 334 335 // See what action we need to take based on the arguments provided. If the 336 // backend ID argument was present, then list the base DNs for that backend. 337 // If the base DN argument was present, then list the backend for that base 338 // DN. If no arguments were provided, then list all backends and base DNs. 339 boolean invalidDn = false; 340 if (baseDN.isPresent()) 341 { 342 // Create a map from the base DNs of the backends to the corresponding 343 // backend ID. 344 TreeMap<DN,String> baseToIDMap = new TreeMap<DN,String>(); 345 for (String id : backends.keySet()) 346 { 347 for (DN dn : backends.get(id)) 348 { 349 baseToIDMap.put(dn, id); 350 } 351 } 352 353 354 // Iterate through the base DN values specified by the user. Determine 355 // the backend for that entry, and whether the provided DN is a base DN 356 // for that backend. 357 for (String dnStr : baseDN.getValues()) 358 { 359 DN dn; 360 try 361 { 362 dn = DN.decode(dnStr); 363 } 364 catch (DirectoryException de) 365 { 366 Message message = ERR_LISTBACKENDS_INVALID_DN.get( 367 dnStr, de.getMessage()); 368 err.println(wrapText(message, MAX_LINE_WIDTH)); 369 return 1; 370 } 371 catch (Exception e) 372 { 373 Message message = ERR_LISTBACKENDS_INVALID_DN.get( 374 dnStr, getExceptionMessage(e)); 375 err.println(wrapText(message, MAX_LINE_WIDTH)); 376 return 1; 377 } 378 379 380 String id = baseToIDMap.get(dn); 381 if (id == null) 382 { 383 Message message = INFO_LISTBACKENDS_NOT_BASE_DN.get( 384 dn.toString()); 385 out.println(message); 386 387 DN parentDN = dn.getParent(); 388 while (true) 389 { 390 if (parentDN == null) 391 { 392 message = INFO_LISTBACKENDS_NO_BACKEND_FOR_DN.get( 393 dn.toString()); 394 out.println(message); 395 invalidDn = true; 396 break; 397 } 398 else 399 { 400 id = baseToIDMap.get(parentDN); 401 if (id != null) 402 { 403 message = INFO_LISTBACKENDS_DN_BELOW_BASE.get( 404 dn.toString(), parentDN.toString(), id); 405 out.println(message); 406 break; 407 } 408 } 409 410 parentDN = parentDN.getParent(); 411 } 412 } 413 else 414 { 415 Message message = INFO_LISTBACKENDS_BASE_FOR_ID.get( 416 dn.toString(), id); 417 out.println(message); 418 } 419 } 420 } 421 else 422 { 423 LinkedList<String> backendIDs; 424 if (backendID.isPresent()) 425 { 426 backendIDs = backendID.getValues(); 427 } 428 else 429 { 430 backendIDs = new LinkedList<String>(backends.keySet()); 431 } 432 433 // Figure out the length of the longest backend ID and base DN defined in 434 // the server. We'll use that information to try to align the output. 435 Message backendIDLabel = INFO_LISTBACKENDS_LABEL_BACKEND_ID.get(); 436 Message baseDNLabel = INFO_LISTBACKENDS_LABEL_BASE_DN.get(); 437 int backendIDLength = 10; 438 int baseDNLength = 7; 439 440 Iterator<String> iterator = backendIDs.iterator(); 441 while (iterator.hasNext()) 442 { 443 String id = iterator.next(); 444 TreeSet<DN> baseDNs = backends.get(id); 445 if (baseDNs == null) 446 { 447 Message message = ERR_LISTBACKENDS_NO_SUCH_BACKEND.get(id); 448 err.println(wrapText(message, MAX_LINE_WIDTH)); 449 iterator.remove(); 450 } 451 else 452 { 453 backendIDLength = Math.max(id.length(), backendIDLength); 454 for (DN dn : baseDNs) 455 { 456 baseDNLength = Math.max(dn.toString().length(), baseDNLength); 457 } 458 } 459 } 460 461 if (backendIDs.isEmpty()) 462 { 463 Message message = ERR_LISTBACKENDS_NO_VALID_BACKENDS.get(); 464 err.println(wrapText(message, MAX_LINE_WIDTH)); 465 return 1; 466 } 467 468 TableBuilder table = new TableBuilder(); 469 Message[] headers = {backendIDLabel, baseDNLabel}; 470 for (int i=0; i< headers.length; i++) 471 { 472 table.appendHeading(headers[i]); 473 } 474 for (String id : backendIDs) 475 { 476 table.startRow(); 477 table.appendCell(id); 478 StringBuffer buf = new StringBuffer(); 479 480 TreeSet<DN> baseDNs = backends.get(id); 481 boolean isFirst = true; 482 for (DN dn : baseDNs) 483 { 484 if (!isFirst) 485 { 486 buf.append(","); 487 } 488 else 489 { 490 isFirst = false; 491 } 492 if (dn.getNumComponents() > 1) 493 { 494 buf.append("\""+dn.toString()+"\""); 495 } 496 else 497 { 498 buf.append(dn.toString()); 499 } 500 } 501 table.appendCell(buf.toString()); 502 } 503 TextTablePrinter printer = new TextTablePrinter(out); 504 printer.setColumnSeparator(ToolConstants.LIST_TABLE_SEPARATOR); 505 table.print(printer); 506 } 507 508 509 // If we've gotten here, then everything completed successfully. 510 return invalidDn ? 1 : 0 ; 511 } 512 513 514 515 /** 516 * Retrieves information about the backends configured in the Directory Server 517 * mapped between the backend ID to the set of base DNs for that backend. 518 * 519 * @return Information about the backends configured in the Directory Server. 520 * 521 * @throws ConfigException If a problem occurs while reading the server 522 * configuration. 523 */ 524 private static TreeMap<String,TreeSet<DN>> getBackends() 525 throws ConfigException 526 { 527 // Get the base entry for all backend configuration. 528 DN backendBaseDN = null; 529 try 530 { 531 backendBaseDN = DN.decode(DN_BACKEND_BASE); 532 } 533 catch (DirectoryException de) 534 { 535 Message message = ERR_CANNOT_DECODE_BACKEND_BASE_DN.get( 536 DN_BACKEND_BASE, de.getMessageObject()); 537 throw new ConfigException(message, de); 538 } 539 catch (Exception e) 540 { 541 Message message = ERR_CANNOT_DECODE_BACKEND_BASE_DN.get( 542 DN_BACKEND_BASE, getExceptionMessage(e)); 543 throw new ConfigException(message, e); 544 } 545 546 ConfigEntry baseEntry = null; 547 try 548 { 549 baseEntry = DirectoryServer.getConfigEntry(backendBaseDN); 550 } 551 catch (ConfigException ce) 552 { 553 Message message = ERR_CANNOT_RETRIEVE_BACKEND_BASE_ENTRY.get( 554 DN_BACKEND_BASE, ce.getMessage()); 555 throw new ConfigException(message, ce); 556 } 557 catch (Exception e) 558 { 559 Message message = ERR_CANNOT_RETRIEVE_BACKEND_BASE_ENTRY.get( 560 DN_BACKEND_BASE, getExceptionMessage(e)); 561 throw new ConfigException(message, e); 562 } 563 564 565 // Iterate through the immediate children, attempting to parse them as 566 // backends. 567 TreeMap<String,TreeSet<DN>> backendMap = new TreeMap<String,TreeSet<DN>>(); 568 for (ConfigEntry configEntry : baseEntry.getChildren().values()) 569 { 570 // Get the backend ID attribute from the entry. If there isn't one, then 571 // skip the entry. 572 String backendID = null; 573 try 574 { 575 Message msg = INFO_CONFIG_BACKEND_ATTR_DESCRIPTION_BACKEND_ID.get(); 576 StringConfigAttribute idStub = 577 new StringConfigAttribute(ATTR_BACKEND_ID, msg, 578 true, false, true); 579 StringConfigAttribute idAttr = 580 (StringConfigAttribute) configEntry.getConfigAttribute(idStub); 581 if (idAttr == null) 582 { 583 continue; 584 } 585 else 586 { 587 backendID = idAttr.activeValue(); 588 } 589 } 590 catch (ConfigException ce) 591 { 592 Message message = ERR_CANNOT_DETERMINE_BACKEND_ID.get( 593 String.valueOf(configEntry.getDN()), ce.getMessage()); 594 throw new ConfigException(message, ce); 595 } 596 catch (Exception e) 597 { 598 Message message = ERR_CANNOT_DETERMINE_BACKEND_ID.get( 599 String.valueOf(configEntry.getDN()), getExceptionMessage(e)); 600 throw new ConfigException(message, e); 601 } 602 603 604 // Get the base DN attribute from the entry. If there isn't one, then 605 // just skip this entry. 606 TreeSet<DN> baseDNs = new TreeSet<DN>(); 607 try 608 { 609 Message msg = INFO_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS.get(); 610 DNConfigAttribute baseDNStub = 611 new DNConfigAttribute(ATTR_BACKEND_BASE_DN, msg, 612 true, true, true); 613 DNConfigAttribute baseDNAttr = 614 (DNConfigAttribute) configEntry.getConfigAttribute(baseDNStub); 615 if (baseDNAttr != null) 616 { 617 baseDNs.addAll(baseDNAttr.activeValues()); 618 } 619 } 620 catch (Exception e) 621 { 622 Message message = ERR_CANNOT_DETERMINE_BASES_FOR_BACKEND.get( 623 String.valueOf(configEntry.getDN()), getExceptionMessage(e)); 624 throw new ConfigException(message, e); 625 } 626 627 backendMap.put(backendID, baseDNs); 628 } 629 630 return backendMap; 631 } 632 } 633