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.backends; 028 029 030 031 import java.io.File; 032 import java.util.*; 033 034 import org.opends.messages.Message; 035 import org.opends.server.admin.Configuration; 036 import org.opends.server.admin.server.ConfigurationChangeListener; 037 import org.opends.server.admin.std.server.BackupBackendCfg; 038 import org.opends.server.api.Backend; 039 import org.opends.server.config.ConfigException; 040 import org.opends.server.core.AddOperation; 041 import org.opends.server.core.DeleteOperation; 042 import org.opends.server.core.DirectoryServer; 043 import org.opends.server.core.ModifyOperation; 044 import org.opends.server.core.ModifyDNOperation; 045 import org.opends.server.core.SearchOperation; 046 import org.opends.server.loggers.debug.DebugTracer; 047 import org.opends.server.protocols.asn1.ASN1OctetString; 048 import org.opends.server.types.Attribute; 049 import org.opends.server.types.AttributeType; 050 import org.opends.server.types.AttributeValue; 051 import org.opends.server.types.BackupConfig; 052 import org.opends.server.types.BackupDirectory; 053 import org.opends.server.types.BackupInfo; 054 import org.opends.server.types.ConditionResult; 055 import org.opends.server.types.ConfigChangeResult; 056 import org.opends.server.types.DebugLogLevel; 057 import org.opends.server.types.DirectoryException; 058 import org.opends.server.types.DN; 059 import org.opends.server.types.Entry; 060 import org.opends.server.types.IndexType; 061 import org.opends.server.types.InitializationException; 062 import org.opends.server.types.LDIFExportConfig; 063 import org.opends.server.types.LDIFImportConfig; 064 import org.opends.server.types.LDIFImportResult; 065 import org.opends.server.types.ObjectClass; 066 import org.opends.server.types.RDN; 067 import org.opends.server.types.RestoreConfig; 068 import org.opends.server.types.ResultCode; 069 import org.opends.server.types.SearchFilter; 070 import org.opends.server.types.SearchScope; 071 import org.opends.server.schema.BooleanSyntax; 072 import org.opends.server.schema.GeneralizedTimeSyntax; 073 import org.opends.server.util.Validator; 074 075 import static org.opends.messages.BackendMessages.*; 076 import static org.opends.server.config.ConfigConstants.*; 077 import static org.opends.server.loggers.debug.DebugLogger.*; 078 import static org.opends.server.util.ServerConstants.*; 079 import static org.opends.server.util.StaticUtils.*; 080 081 082 /** 083 * This class defines a backend used to present information about Directory 084 * Server backups. It will not actually store anything, but upon request will 085 * retrieve information about the backups that it knows about. The backups will 086 * be arranged in a hierarchy based on the directory that contains them, and 087 * it may be possible to dynamically discover new backups if a previously 088 * unknown backup directory is included in the base DN. 089 */ 090 public class BackupBackend 091 extends Backend 092 implements ConfigurationChangeListener<BackupBackendCfg> 093 { 094 /** 095 * The tracer object for the debug logger. 096 */ 097 private static final DebugTracer TRACER = getTracer(); 098 099 100 101 // The current configuration state. 102 private BackupBackendCfg currentConfig; 103 104 // The DN for the base backup entry. 105 private DN backupBaseDN; 106 107 // The set of base DNs for this backend. 108 private DN[] baseDNs; 109 110 // The backup base entry. 111 private Entry backupBaseEntry; 112 113 // The set of supported controls for this backend. 114 private HashSet<String> supportedControls; 115 116 // The set of supported features for this backend. 117 private HashSet<String> supportedFeatures; 118 119 // The set of predefined backup directories that we will use. 120 private LinkedHashSet<File> backupDirectories; 121 122 123 124 /** 125 * Creates a new backend with the provided information. All backend 126 * implementations must implement a default constructor that use 127 * <CODE>super()</CODE> to invoke this constructor. 128 */ 129 public BackupBackend() 130 { 131 super(); 132 133 // Perform all initialization in initializeBackend. 134 } 135 136 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override() 142 public void configureBackend(Configuration config) throws ConfigException 143 { 144 // Make sure that a configuration entry was provided. If not, then we will 145 // not be able to complete initialization. 146 if (config == null) 147 { 148 Message message = ERR_BACKUP_CONFIG_ENTRY_NULL.get(); 149 throw new ConfigException(message); 150 } 151 152 153 Validator.ensureTrue(config instanceof BackupBackendCfg); 154 155 currentConfig = (BackupBackendCfg)config; 156 } 157 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override() 164 public void initializeBackend() 165 throws ConfigException, InitializationException 166 { 167 // Create the set of base DNs that we will handle. In this case, it's just 168 // the DN of the base backup entry. 169 try 170 { 171 backupBaseDN = DN.decode(DN_BACKUP_ROOT); 172 } 173 catch (Exception e) 174 { 175 if (debugEnabled()) 176 { 177 TRACER.debugCaught(DebugLogLevel.ERROR, e); 178 } 179 180 Message message = 181 ERR_BACKUP_CANNOT_DECODE_BACKUP_ROOT_DN.get(getExceptionMessage(e)); 182 throw new InitializationException(message, e); 183 } 184 185 // FIXME -- Deal with this more correctly. 186 this.baseDNs = new DN[] { backupBaseDN }; 187 188 189 // Determine the set of backup directories that we will use by default. 190 Set<String> values = currentConfig.getBackupDirectory(); 191 backupDirectories = new LinkedHashSet<File>(values.size()); 192 for (String s : values) 193 { 194 backupDirectories.add(getFileForPath(s)); 195 } 196 197 198 // Construct the backup base entry. 199 LinkedHashMap<ObjectClass,String> objectClasses = 200 new LinkedHashMap<ObjectClass,String>(2); 201 objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP); 202 203 ObjectClass untypedOC = 204 DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true); 205 objectClasses.put(untypedOC, OC_UNTYPED_OBJECT); 206 207 LinkedHashMap<AttributeType,List<Attribute>> opAttrs = 208 new LinkedHashMap<AttributeType,List<Attribute>>(0); 209 LinkedHashMap<AttributeType,List<Attribute>> userAttrs = 210 new LinkedHashMap<AttributeType,List<Attribute>>(1); 211 212 RDN rdn = backupBaseDN.getRDN(); 213 int numAVAs = rdn.getNumValues(); 214 for (int i=0; i < numAVAs; i++) 215 { 216 LinkedHashSet<AttributeValue> valueSet = 217 new LinkedHashSet<AttributeValue>(1); 218 valueSet.add(rdn.getAttributeValue(i)); 219 220 AttributeType attrType = rdn.getAttributeType(i); 221 ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); 222 attrList.add(new Attribute(attrType, attrType.getNameOrOID(), 223 valueSet)); 224 225 userAttrs.put(attrType, attrList); 226 } 227 228 backupBaseEntry = new Entry(backupBaseDN, objectClasses, userAttrs, 229 opAttrs); 230 231 232 // Define an empty sets for the supported controls and features. 233 supportedControls = new HashSet<String>(0); 234 supportedFeatures = new HashSet<String>(0); 235 236 237 // Register this as a change listener. 238 currentConfig.addBackupChangeListener(this); 239 240 241 // Register the backup base as a private suffix. 242 try 243 { 244 DirectoryServer.registerBaseDN(backupBaseDN, this, true); 245 } 246 catch (Exception e) 247 { 248 if (debugEnabled()) 249 { 250 TRACER.debugCaught(DebugLogLevel.ERROR, e); 251 } 252 253 Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get( 254 backupBaseDN.toString(), getExceptionMessage(e)); 255 throw new InitializationException(message, e); 256 } 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public void finalizeBackend() 266 { 267 currentConfig.removeBackupChangeListener(this); 268 269 try 270 { 271 DirectoryServer.deregisterBaseDN(backupBaseDN); 272 } 273 catch (Exception e) 274 { 275 if (debugEnabled()) 276 { 277 TRACER.debugCaught(DebugLogLevel.ERROR, e); 278 } 279 } 280 } 281 282 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override() 288 public DN[] getBaseDNs() 289 { 290 return baseDNs; 291 } 292 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 @Override() 299 public long getEntryCount() 300 { 301 int numEntries = 1; 302 303 AttributeType backupPathType = 304 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 305 306 for (File f : backupDirectories) 307 { 308 try 309 { 310 // Check to see if the descriptor file exists. If not, then skip this 311 // backup directory. 312 File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); 313 if (! descriptorFile.exists()) 314 { 315 continue; 316 } 317 318 DN backupDirDN = makeChildDN(backupBaseDN, backupPathType, 319 f.getAbsolutePath()); 320 getBackupDirectoryEntry(backupDirDN); 321 numEntries++; 322 } catch (Exception e) {} 323 } 324 325 return numEntries; 326 } 327 328 329 330 /** 331 * {@inheritDoc} 332 */ 333 @Override() 334 public boolean isLocal() 335 { 336 // For the purposes of this method, this is a local backend. 337 return true; 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public boolean isIndexed(AttributeType attributeType, IndexType indexType) 347 { 348 // All searches in this backend will always be considered indexed. 349 return true; 350 } 351 352 353 354 /** 355 * {@inheritDoc} 356 */ 357 @Override() 358 public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException 359 { 360 long ret = numSubordinates(entryDN, false); 361 if(ret < 0) 362 { 363 return ConditionResult.UNDEFINED; 364 } 365 else if(ret == 0) 366 { 367 return ConditionResult.FALSE; 368 } 369 else 370 { 371 return ConditionResult.TRUE; 372 } 373 } 374 375 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override() 381 public long numSubordinates(DN entryDN, boolean subtree) 382 throws DirectoryException 383 { 384 // If the requested entry was null, then return undefined. 385 if (entryDN == null) 386 { 387 return -1; 388 } 389 390 // If the requested entry was the backend base entry, then return 391 // the number of backup directories. 392 if (backupBaseDN.equals(entryDN)) 393 { 394 long count = 0; 395 for (File f : backupDirectories) 396 { 397 // Check to see if the descriptor file exists. If not, then skip this 398 // backup directory. 399 File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); 400 if (! descriptorFile.exists()) 401 { 402 continue; 403 } 404 405 // If subtree is included, count the number of entries for each 406 // backup directory. 407 if (subtree) 408 { 409 try 410 { 411 BackupDirectory backupDirectory = 412 BackupDirectory.readBackupDirectoryDescriptor(f.getPath()); 413 count += backupDirectory.getBackups().keySet().size(); 414 } 415 catch (Exception e) 416 { 417 return -1; 418 } 419 } 420 421 count ++; 422 } 423 return count; 424 } 425 426 // See if the requested entry was one level below the backend base entry. 427 // If so, then it must point to a backup directory. Otherwise, it must be 428 // two levels below the backup base entry and must point to a specific 429 // backup. 430 DN parentDN = entryDN.getParentDNInSuffix(); 431 if (parentDN == null) 432 { 433 return -1; 434 } 435 else if (backupBaseDN.equals(parentDN)) 436 { 437 long count = 0; 438 Entry backupDirEntry = getBackupDirectoryEntry(entryDN); 439 440 AttributeType t = 441 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 442 List<Attribute> attrList = backupDirEntry.getAttribute(t); 443 if ((attrList != null) && (! attrList.isEmpty())) 444 { 445 for (AttributeValue v : attrList.get(0).getValues()) 446 { 447 try 448 { 449 BackupDirectory backupDirectory = 450 BackupDirectory.readBackupDirectoryDescriptor( 451 v.getStringValue()); 452 count += backupDirectory.getBackups().keySet().size(); 453 } 454 catch (Exception e) 455 { 456 return -1; 457 } 458 } 459 } 460 return count; 461 } 462 else if (backupBaseDN.equals(parentDN.getParentDNInSuffix())) 463 { 464 return 0; 465 } 466 else 467 { 468 return -1; 469 } 470 } 471 472 473 474 /** 475 * {@inheritDoc} 476 */ 477 @Override() 478 public Entry getEntry(DN entryDN) 479 throws DirectoryException 480 { 481 // If the requested entry was null, then throw an exception. 482 if (entryDN == null) 483 { 484 Message message = ERR_BACKUP_GET_ENTRY_NULL.get(); 485 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 486 message); 487 } 488 489 490 // If the requested entry was the backend base entry, then retrieve it. 491 if (entryDN.equals(backupBaseDN)) 492 { 493 return backupBaseEntry.duplicate(true); 494 } 495 496 497 // See if the requested entry was one level below the backend base entry. 498 // If so, then it must point to a backup directory. Otherwise, it must be 499 // two levels below the backup base entry and must point to a specific 500 // backup. 501 DN parentDN = entryDN.getParentDNInSuffix(); 502 if (parentDN == null) 503 { 504 Message message = ERR_BACKUP_INVALID_BASE.get(String.valueOf(entryDN)); 505 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 506 } 507 else if (parentDN.equals(backupBaseDN)) 508 { 509 return getBackupDirectoryEntry(entryDN); 510 } 511 else if (backupBaseDN.equals(parentDN.getParentDNInSuffix())) 512 { 513 return getBackupEntry(entryDN); 514 } 515 else 516 { 517 Message message = ERR_BACKUP_INVALID_BASE.get(String.valueOf(entryDN)); 518 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, 519 message, backupBaseDN, null); 520 } 521 } 522 523 524 525 /** 526 * Generates an entry for a backup directory based on the provided DN. The 527 * DN must contain an RDN component that specifies the path to the backup 528 * directory, and that directory must exist and be a valid backup directory. 529 * 530 * @param entryDN The DN of the backup directory entry to retrieve. 531 * 532 * @return The requested backup directory entry. 533 * 534 * @throws DirectoryException If the specified directory does not exist or 535 * is not a valid backup directory, or if the DN 536 * does not specify any backup directory. 537 */ 538 private Entry getBackupDirectoryEntry(DN entryDN) 539 throws DirectoryException 540 { 541 // Make sure that the DN specifies a backup directory. 542 AttributeType t = 543 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 544 AttributeValue v = entryDN.getRDN().getAttributeValue(t); 545 if (v == null) 546 { 547 Message message = 548 ERR_BACKUP_DN_DOES_NOT_SPECIFY_DIRECTORY.get(String.valueOf(entryDN)); 549 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, 550 backupBaseDN, null); 551 } 552 553 554 // Get a handle to the backup directory and the information that it 555 // contains. 556 BackupDirectory backupDirectory; 557 try 558 { 559 backupDirectory = 560 BackupDirectory.readBackupDirectoryDescriptor(v.getStringValue()); 561 } 562 catch (ConfigException ce) 563 { 564 if (debugEnabled()) 565 { 566 TRACER.debugCaught(DebugLogLevel.ERROR, ce); 567 } 568 569 Message message = ERR_BACKUP_INVALID_BACKUP_DIRECTORY.get( 570 String.valueOf(entryDN), ce.getMessage()); 571 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 572 } 573 catch (Exception e) 574 { 575 if (debugEnabled()) 576 { 577 TRACER.debugCaught(DebugLogLevel.ERROR, e); 578 } 579 580 Message message = 581 ERR_BACKUP_ERROR_GETTING_BACKUP_DIRECTORY.get(getExceptionMessage(e)); 582 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 583 message); 584 } 585 586 587 // Construct the backup directory entry to return. 588 LinkedHashMap<ObjectClass,String> ocMap = 589 new LinkedHashMap<ObjectClass,String>(2); 590 ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); 591 592 ObjectClass backupDirOC = 593 DirectoryServer.getObjectClass(OC_BACKUP_DIRECTORY, true); 594 ocMap.put(backupDirOC, OC_BACKUP_DIRECTORY); 595 596 LinkedHashMap<AttributeType,List<Attribute>> opAttrs = 597 new LinkedHashMap<AttributeType,List<Attribute>>(0); 598 LinkedHashMap<AttributeType,List<Attribute>> userAttrs = 599 new LinkedHashMap<AttributeType,List<Attribute>>(3); 600 601 LinkedHashSet<AttributeValue> valueSet = 602 new LinkedHashSet<AttributeValue>(1); 603 valueSet.add(v); 604 605 ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); 606 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 607 userAttrs.put(t, attrList); 608 609 610 t = DirectoryServer.getAttributeType(ATTR_BACKUP_BACKEND_DN, true); 611 valueSet = new LinkedHashSet<AttributeValue>(1); 612 valueSet.add(new AttributeValue(t, 613 backupDirectory.getConfigEntryDN().toString())); 614 attrList = new ArrayList<Attribute>(1); 615 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 616 userAttrs.put(t, attrList); 617 618 619 Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs); 620 e.processVirtualAttributes(); 621 return e; 622 } 623 624 625 626 /** 627 * Generates an entry for a backup based on the provided DN. The DN must 628 * have an RDN component that specifies the backup ID, and the parent DN must 629 * have an RDN component that specifies the backup directory. 630 * 631 * @param entryDN The DN of the backup entry to retrieve. 632 * 633 * @return The requested backup entry. 634 * 635 * @throws DirectoryException If the specified backup does not exist or is 636 * invalid. 637 */ 638 private Entry getBackupEntry(DN entryDN) 639 throws DirectoryException 640 { 641 // First, get the backup ID from the entry DN. 642 AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, 643 true); 644 AttributeValue idValue = entryDN.getRDN().getAttributeValue(idType); 645 if (idValue == null) 646 { 647 Message message = 648 ERR_BACKUP_NO_BACKUP_ID_IN_DN.get(String.valueOf(entryDN)); 649 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 650 } 651 String backupID = idValue.getStringValue(); 652 653 654 // Next, get the backup directory from the parent DN. 655 DN parentDN = entryDN.getParentDNInSuffix(); 656 if (parentDN == null) 657 { 658 Message message = 659 ERR_BACKUP_NO_BACKUP_PARENT_DN.get(String.valueOf(entryDN)); 660 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 661 } 662 663 AttributeType t = 664 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 665 AttributeValue v = parentDN.getRDN().getAttributeValue(t); 666 if (v == null) 667 { 668 Message message = 669 ERR_BACKUP_NO_BACKUP_DIR_IN_DN.get(String.valueOf(entryDN)); 670 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 671 } 672 673 674 BackupDirectory backupDirectory; 675 try 676 { 677 backupDirectory = 678 BackupDirectory.readBackupDirectoryDescriptor(v.getStringValue()); 679 } 680 catch (ConfigException ce) 681 { 682 if (debugEnabled()) 683 { 684 TRACER.debugCaught(DebugLogLevel.ERROR, ce); 685 } 686 687 Message message = 688 ERR_BACKUP_INVALID_BACKUP_DIRECTORY.get( 689 String.valueOf(entryDN), ce.getMessageObject()); 690 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 691 } 692 catch (Exception e) 693 { 694 if (debugEnabled()) 695 { 696 TRACER.debugCaught(DebugLogLevel.ERROR, e); 697 } 698 699 Message message = 700 ERR_BACKUP_ERROR_GETTING_BACKUP_DIRECTORY.get(getExceptionMessage(e)); 701 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 702 message); 703 } 704 705 BackupInfo backupInfo = backupDirectory.getBackupInfo(backupID); 706 if (backupInfo == null) 707 { 708 Message message = 709 ERR_BACKUP_NO_SUCH_BACKUP.get(backupID, backupDirectory.getPath()); 710 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, 711 message, parentDN, null); 712 } 713 714 715 // Construct the backup entry to return. 716 LinkedHashMap<ObjectClass,String> ocMap = 717 new LinkedHashMap<ObjectClass,String>(3); 718 ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); 719 720 ObjectClass oc = DirectoryServer.getObjectClass(OC_BACKUP_INFO, true); 721 ocMap.put(oc, OC_BACKUP_INFO); 722 723 oc = DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC, true); 724 ocMap.put(oc, OC_EXTENSIBLE_OBJECT); 725 726 LinkedHashMap<AttributeType,List<Attribute>> opAttrs = 727 new LinkedHashMap<AttributeType,List<Attribute>>(0); 728 LinkedHashMap<AttributeType,List<Attribute>> userAttrs = 729 new LinkedHashMap<AttributeType,List<Attribute>>(); 730 731 LinkedHashSet<AttributeValue> valueSet = 732 new LinkedHashSet<AttributeValue>(1); 733 valueSet.add(idValue); 734 735 ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); 736 attrList.add(new Attribute(idType, idType.getNameOrOID(), valueSet)); 737 userAttrs.put(idType, attrList); 738 739 740 backupInfo.getBackupDirectory(); 741 valueSet = new LinkedHashSet<AttributeValue>(1); 742 valueSet.add(v); 743 attrList = new ArrayList<Attribute>(1); 744 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 745 userAttrs.put(t, attrList); 746 747 748 Date backupDate = backupInfo.getBackupDate(); 749 if (backupDate != null) 750 { 751 t = DirectoryServer.getAttributeType(ATTR_BACKUP_DATE, true); 752 valueSet = new LinkedHashSet<AttributeValue>(1); 753 valueSet.add(new AttributeValue(t, 754 GeneralizedTimeSyntax.format(backupDate))); 755 attrList = new ArrayList<Attribute>(1); 756 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 757 userAttrs.put(t, attrList); 758 } 759 760 761 t = DirectoryServer.getAttributeType(ATTR_BACKUP_COMPRESSED, true); 762 valueSet = new LinkedHashSet<AttributeValue>(1); 763 valueSet.add(BooleanSyntax.createBooleanValue(backupInfo.isCompressed())); 764 attrList = new ArrayList<Attribute>(1); 765 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 766 userAttrs.put(t, attrList); 767 768 769 t = DirectoryServer.getAttributeType(ATTR_BACKUP_ENCRYPTED, true); 770 valueSet = new LinkedHashSet<AttributeValue>(1); 771 valueSet.add(BooleanSyntax.createBooleanValue(backupInfo.isEncrypted())); 772 attrList = new ArrayList<Attribute>(1); 773 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 774 userAttrs.put(t, attrList); 775 776 777 t = DirectoryServer.getAttributeType(ATTR_BACKUP_INCREMENTAL, true); 778 valueSet = new LinkedHashSet<AttributeValue>(1); 779 valueSet.add(BooleanSyntax.createBooleanValue(backupInfo.isIncremental())); 780 attrList = new ArrayList<Attribute>(1); 781 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 782 userAttrs.put(t, attrList); 783 784 785 HashSet<String> dependencies = backupInfo.getDependencies(); 786 if ((dependencies != null) && (! dependencies.isEmpty())) 787 { 788 t = DirectoryServer.getAttributeType(ATTR_BACKUP_DEPENDENCY, true); 789 valueSet = new LinkedHashSet<AttributeValue>(dependencies.size()); 790 for (String s : dependencies) 791 { 792 valueSet.add(new AttributeValue(t, s)); 793 } 794 attrList = new ArrayList<Attribute>(1); 795 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 796 userAttrs.put(t, attrList); 797 } 798 799 800 byte[] signedHash = backupInfo.getSignedHash(); 801 if (signedHash != null) 802 { 803 t = DirectoryServer.getAttributeType(ATTR_BACKUP_SIGNED_HASH, true); 804 valueSet = new LinkedHashSet<AttributeValue>(1); 805 valueSet.add(new AttributeValue(t, new ASN1OctetString(signedHash))); 806 attrList = new ArrayList<Attribute>(1); 807 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 808 userAttrs.put(t, attrList); 809 } 810 811 812 byte[] unsignedHash = backupInfo.getUnsignedHash(); 813 if (unsignedHash != null) 814 { 815 t = DirectoryServer.getAttributeType(ATTR_BACKUP_UNSIGNED_HASH, true); 816 valueSet = new LinkedHashSet<AttributeValue>(1); 817 valueSet.add(new AttributeValue(t, new ASN1OctetString(unsignedHash))); 818 attrList = new ArrayList<Attribute>(1); 819 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 820 userAttrs.put(t, attrList); 821 } 822 823 824 HashMap<String,String> properties = backupInfo.getBackupProperties(); 825 if ((properties != null) && (! properties.isEmpty())) 826 { 827 for (Map.Entry<String,String> e : properties.entrySet()) 828 { 829 t = DirectoryServer.getAttributeType(toLowerCase(e.getKey()), true); 830 valueSet = new LinkedHashSet<AttributeValue>(1); 831 valueSet.add(new AttributeValue(t, e.getValue())); 832 attrList = new ArrayList<Attribute>(1); 833 attrList.add(new Attribute(t, t.getNameOrOID(), valueSet)); 834 userAttrs.put(t, attrList); 835 } 836 } 837 838 839 Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs); 840 e.processVirtualAttributes(); 841 return e; 842 } 843 844 845 846 /** 847 * {@inheritDoc} 848 */ 849 @Override() 850 public void addEntry(Entry entry, AddOperation addOperation) 851 throws DirectoryException 852 { 853 Message message = ERR_BACKUP_ADD_NOT_SUPPORTED.get(); 854 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 855 } 856 857 858 859 /** 860 * {@inheritDoc} 861 */ 862 @Override() 863 public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) 864 throws DirectoryException 865 { 866 Message message = ERR_BACKUP_DELETE_NOT_SUPPORTED.get(); 867 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 868 } 869 870 871 872 /** 873 * {@inheritDoc} 874 */ 875 @Override() 876 public void replaceEntry(Entry entry, ModifyOperation modifyOperation) 877 throws DirectoryException 878 { 879 Message message = ERR_BACKUP_MODIFY_NOT_SUPPORTED.get(); 880 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 881 } 882 883 884 885 /** 886 * {@inheritDoc} 887 */ 888 @Override() 889 public void renameEntry(DN currentDN, Entry entry, 890 ModifyDNOperation modifyDNOperation) 891 throws DirectoryException 892 { 893 Message message = ERR_BACKUP_MODIFY_DN_NOT_SUPPORTED.get(); 894 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 895 } 896 897 898 899 /** 900 * {@inheritDoc} 901 */ 902 @Override() 903 public void search(SearchOperation searchOperation) 904 throws DirectoryException 905 { 906 // Get the base entry for the search, if possible. If it doesn't exist, 907 // then this will throw an exception. 908 DN baseDN = searchOperation.getBaseDN(); 909 Entry baseEntry = getEntry(baseDN); 910 911 912 // Look at the base DN and see if it's the backup base DN, a backup 913 // directory entry DN, or a backup entry DN. 914 DN parentDN; 915 SearchScope scope = searchOperation.getScope(); 916 SearchFilter filter = searchOperation.getFilter(); 917 if (backupBaseDN.equals(baseDN)) 918 { 919 if ((scope == SearchScope.BASE_OBJECT) || 920 (scope == SearchScope.WHOLE_SUBTREE)) 921 { 922 if (filter.matchesEntry(baseEntry)) 923 { 924 searchOperation.returnEntry(baseEntry, null); 925 } 926 } 927 928 if ((scope != SearchScope.BASE_OBJECT) && (! backupDirectories.isEmpty())) 929 { 930 AttributeType backupPathType = 931 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 932 for (File f : backupDirectories) 933 { 934 // Check to see if the descriptor file exists. If not, then skip this 935 // backup directory. 936 File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); 937 if (! descriptorFile.exists()) 938 { 939 continue; 940 } 941 942 943 DN backupDirDN = makeChildDN(backupBaseDN, backupPathType, 944 f.getAbsolutePath()); 945 946 Entry backupDirEntry; 947 try 948 { 949 backupDirEntry = getBackupDirectoryEntry(backupDirDN); 950 } 951 catch (Exception e) 952 { 953 if (debugEnabled()) 954 { 955 TRACER.debugCaught(DebugLogLevel.ERROR, e); 956 } 957 958 continue; 959 } 960 961 if (filter.matchesEntry(backupDirEntry)) 962 { 963 searchOperation.returnEntry(backupDirEntry, null); 964 } 965 966 if (scope != SearchScope.SINGLE_LEVEL) 967 { 968 List<Attribute> attrList = 969 backupDirEntry.getAttribute(backupPathType); 970 if ((attrList != null) && (! attrList.isEmpty())) 971 { 972 for (AttributeValue v : attrList.get(0).getValues()) 973 { 974 try 975 { 976 BackupDirectory backupDirectory = 977 BackupDirectory.readBackupDirectoryDescriptor( 978 v.getStringValue()); 979 AttributeType idType = 980 DirectoryServer.getAttributeType(ATTR_BACKUP_ID, 981 true); 982 for (String backupID : backupDirectory.getBackups().keySet()) 983 { 984 DN backupEntryDN = makeChildDN(backupDirDN, idType, 985 backupID); 986 Entry backupEntry = getBackupEntry(backupEntryDN); 987 if (filter.matchesEntry(backupEntry)) 988 { 989 searchOperation.returnEntry(backupEntry, null); 990 } 991 } 992 } 993 catch (Exception e) 994 { 995 if (debugEnabled()) 996 { 997 TRACER.debugCaught(DebugLogLevel.ERROR, e); 998 } 999 1000 continue; 1001 } 1002 } 1003 } 1004 } 1005 } 1006 } 1007 } 1008 else if (backupBaseDN.equals(parentDN = baseDN.getParentDNInSuffix())) 1009 { 1010 Entry backupDirEntry = getBackupDirectoryEntry(baseDN); 1011 1012 if ((scope == SearchScope.BASE_OBJECT) || 1013 (scope == SearchScope.WHOLE_SUBTREE)) 1014 { 1015 if (filter.matchesEntry(backupDirEntry)) 1016 { 1017 searchOperation.returnEntry(backupDirEntry, null); 1018 } 1019 } 1020 1021 1022 if (scope != SearchScope.BASE_OBJECT) 1023 { 1024 AttributeType t = 1025 DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); 1026 List<Attribute> attrList = backupDirEntry.getAttribute(t); 1027 if ((attrList != null) && (! attrList.isEmpty())) 1028 { 1029 for (AttributeValue v : attrList.get(0).getValues()) 1030 { 1031 try 1032 { 1033 BackupDirectory backupDirectory = 1034 BackupDirectory.readBackupDirectoryDescriptor( 1035 v.getStringValue()); 1036 AttributeType idType = 1037 DirectoryServer.getAttributeType(ATTR_BACKUP_ID, 1038 true); 1039 for (String backupID : backupDirectory.getBackups().keySet()) 1040 { 1041 DN backupEntryDN = makeChildDN(baseDN, idType, 1042 backupID); 1043 Entry backupEntry = getBackupEntry(backupEntryDN); 1044 if (filter.matchesEntry(backupEntry)) 1045 { 1046 searchOperation.returnEntry(backupEntry, null); 1047 } 1048 } 1049 } 1050 catch (Exception e) 1051 { 1052 if (debugEnabled()) 1053 { 1054 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1055 } 1056 1057 continue; 1058 } 1059 } 1060 } 1061 } 1062 } 1063 else 1064 { 1065 if ((parentDN == null) 1066 || (! backupBaseDN.equals(parentDN.getParentDNInSuffix()))) 1067 { 1068 Message message = ERR_BACKUP_NO_SUCH_ENTRY.get( 1069 String.valueOf(backupBaseDN) 1070 ); 1071 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 1072 } 1073 1074 if ((scope == SearchScope.BASE_OBJECT) || 1075 (scope == SearchScope.WHOLE_SUBTREE)) 1076 { 1077 Entry backupEntry = getBackupEntry(baseDN); 1078 if (backupEntry == null) 1079 { 1080 Message message = ERR_BACKUP_NO_SUCH_ENTRY.get( 1081 String.valueOf(backupBaseDN)); 1082 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 1083 } 1084 1085 if (filter.matchesEntry(backupEntry)) 1086 { 1087 searchOperation.returnEntry(backupEntry, null); 1088 } 1089 } 1090 } 1091 } 1092 1093 1094 1095 /** 1096 * {@inheritDoc} 1097 */ 1098 @Override() 1099 public HashSet<String> getSupportedControls() 1100 { 1101 return supportedControls; 1102 } 1103 1104 1105 1106 /** 1107 * {@inheritDoc} 1108 */ 1109 @Override() 1110 public HashSet<String> getSupportedFeatures() 1111 { 1112 return supportedFeatures; 1113 } 1114 1115 1116 1117 /** 1118 * {@inheritDoc} 1119 */ 1120 @Override() 1121 public boolean supportsLDIFExport() 1122 { 1123 // We do not support LDIF exports. 1124 return false; 1125 } 1126 1127 1128 1129 /** 1130 * {@inheritDoc} 1131 */ 1132 @Override() 1133 public void exportLDIF(LDIFExportConfig exportConfig) 1134 throws DirectoryException 1135 { 1136 Message message = ERR_BACKUP_EXPORT_NOT_SUPPORTED.get(); 1137 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1138 } 1139 1140 1141 1142 /** 1143 * {@inheritDoc} 1144 */ 1145 @Override() 1146 public boolean supportsLDIFImport() 1147 { 1148 // This backend does not support LDIF imports. 1149 return false; 1150 } 1151 1152 1153 1154 /** 1155 * {@inheritDoc} 1156 */ 1157 @Override() 1158 public LDIFImportResult importLDIF(LDIFImportConfig importConfig) 1159 throws DirectoryException 1160 { 1161 // This backend does not support LDIF imports. 1162 Message message = ERR_BACKUP_IMPORT_NOT_SUPPORTED.get(); 1163 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1164 } 1165 1166 1167 1168 /** 1169 * {@inheritDoc} 1170 */ 1171 @Override() 1172 public boolean supportsBackup() 1173 { 1174 // This backend does not provide a backup/restore mechanism. 1175 return false; 1176 } 1177 1178 1179 1180 /** 1181 * {@inheritDoc} 1182 */ 1183 @Override() 1184 public boolean supportsBackup(BackupConfig backupConfig, 1185 StringBuilder unsupportedReason) 1186 { 1187 // This backend does not provide a backup/restore mechanism. 1188 return false; 1189 } 1190 1191 1192 1193 /** 1194 * {@inheritDoc} 1195 */ 1196 @Override() 1197 public void createBackup(BackupConfig backupConfig) 1198 throws DirectoryException 1199 { 1200 // This backend does not provide a backup/restore mechanism. 1201 Message message = ERR_BACKUP_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1202 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1203 } 1204 1205 1206 1207 /** 1208 * {@inheritDoc} 1209 */ 1210 @Override() 1211 public void removeBackup(BackupDirectory backupDirectory, 1212 String backupID) 1213 throws DirectoryException 1214 { 1215 // This backend does not provide a backup/restore mechanism. 1216 Message message = ERR_BACKUP_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1217 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1218 } 1219 1220 1221 1222 /** 1223 * {@inheritDoc} 1224 */ 1225 @Override() 1226 public boolean supportsRestore() 1227 { 1228 // This backend does not provide a backup/restore mechanism. 1229 return false; 1230 } 1231 1232 1233 1234 /** 1235 * {@inheritDoc} 1236 */ 1237 @Override() 1238 public void restoreBackup(RestoreConfig restoreConfig) 1239 throws DirectoryException 1240 { 1241 // This backend does not provide a backup/restore mechanism. 1242 Message message = ERR_BACKUP_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1243 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1244 } 1245 1246 1247 1248 /** 1249 * {@inheritDoc} 1250 */ 1251 public boolean isConfigurationChangeAcceptable( 1252 BackupBackendCfg cfg, List<Message> unacceptableReasons) 1253 { 1254 // We'll accept anything here. The only configurable attribute is the 1255 // default set of backup directories, but that doesn't require any 1256 // validation at this point. 1257 return true; 1258 } 1259 1260 1261 1262 /** 1263 * {@inheritDoc} 1264 */ 1265 public ConfigChangeResult applyConfigurationChange(BackupBackendCfg cfg) 1266 { 1267 ResultCode resultCode = ResultCode.SUCCESS; 1268 boolean adminActionRequired = false; 1269 ArrayList<Message> messages = new ArrayList<Message>(); 1270 1271 1272 Set<String> values = cfg.getBackupDirectory(); 1273 backupDirectories = new LinkedHashSet<File>(values.size()); 1274 for (String s : values) 1275 { 1276 backupDirectories.add(getFileForPath(s)); 1277 } 1278 1279 currentConfig = cfg; 1280 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 1281 } 1282 1283 1284 1285 /** 1286 * Create a new child DN from a given parent DN. The child RDN is formed 1287 * from a given attribute type and string value. 1288 * @param parentDN The DN of the parent. 1289 * @param rdnAttrType The attribute type of the RDN. 1290 * @param rdnStringValue The string value of the RDN. 1291 * @return A new child DN. 1292 */ 1293 public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType, 1294 String rdnStringValue) 1295 { 1296 AttributeValue attrValue = 1297 new AttributeValue(rdnAttrType, rdnStringValue); 1298 return parentDN.concat(RDN.create(rdnAttrType, attrValue)); 1299 } 1300 1301 1302 1303 /** 1304 * {@inheritDoc} 1305 */ 1306 public void preloadEntryCache() throws UnsupportedOperationException { 1307 throw new UnsupportedOperationException("Operation not supported."); 1308 } 1309 } 1310