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