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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2015 ForgeRock AS 025 */ 026package org.opends.server.backends.pluggable; 027 028import java.util.ArrayList; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Set; 032 033import org.opends.server.types.DN; 034import org.opends.server.types.DirectoryException; 035import org.opends.server.types.LDIFImportConfig; 036 037/** 038 * Command that describes how a suffix should be imported. Gives the strategy to use and the data to 039 * drive the import operation of a single suffix. 040 */ 041public class ImportSuffixCommand 042{ 043 /** Strategy for importing a suffix. */ 044 public static enum SuffixImportStrategy { 045 /** 046 * Create a {@link Suffix} specifying just the {@link EntryContainer} for the baseDN, no include or exclude 047 * branches are needed, normally used for append or clear backend modes. 048 */ 049 APPEND_OR_REPLACE, 050 /** Do not create a {@link Suffix}. */ 051 SKIP_SUFFIX, 052 /** Before creating a {@link Suffix}, clear the {@link EntryContainer} of the baseDN. */ 053 CLEAR_SUFFIX, 054 /** Create a temporary {@link EntryContainer} to merge LDIF with original data. */ 055 MERGE_DB_WITH_LDIF, 056 /** 057 * Create a {@link Suffix} specifying include and exclude branches and optionally a source {@link EntryContainer}. 058 */ 059 INCLUDE_EXCLUDE_BRANCHES 060 } 061 062 private List<DN> includeBranches; 063 private List<DN> excludeBranches; 064 private SuffixImportStrategy strategy = SuffixImportStrategy.APPEND_OR_REPLACE; 065 066 List<DN> getIncludeBranches() 067 { 068 return includeBranches; 069 } 070 071 List<DN> getExcludeBranches() 072 { 073 return excludeBranches; 074 } 075 076 SuffixImportStrategy getSuffixImportStrategy() 077 { 078 return strategy; 079 } 080 081 ImportSuffixCommand(DN baseDN, LDIFImportConfig importCfg) throws DirectoryException 082 { 083 strategy = decideSuffixStrategy(baseDN, importCfg); 084 } 085 086 private SuffixImportStrategy decideSuffixStrategy(DN baseDN, LDIFImportConfig importCfg) 087 throws DirectoryException 088 { 089 if (importCfg.appendToExistingData() || importCfg.clearBackend()) 090 { 091 return SuffixImportStrategy.APPEND_OR_REPLACE; 092 } 093 if (importCfg.getExcludeBranches().contains(baseDN)) 094 { 095 // This entire base DN was explicitly excluded. Skip. 096 return SuffixImportStrategy.SKIP_SUFFIX; 097 } 098 excludeBranches = getDescendants(baseDN, importCfg.getExcludeBranches()); 099 if (!importCfg.getIncludeBranches().isEmpty()) 100 { 101 includeBranches = getDescendants(baseDN, importCfg.getIncludeBranches()); 102 if (includeBranches.isEmpty()) 103 { 104 // There are no branches in the explicitly defined include list under this base DN. 105 // Skip this base DN altogether. 106 return SuffixImportStrategy.SKIP_SUFFIX; 107 } 108 109 // Remove any overlapping include branches. 110 Iterator<DN> includeBranchIterator = includeBranches.iterator(); 111 while (includeBranchIterator.hasNext()) 112 { 113 DN includeDN = includeBranchIterator.next(); 114 if (!isAnyNotEqualAndAncestorOf(includeBranches, includeDN)) 115 { 116 includeBranchIterator.remove(); 117 } 118 } 119 120 // Remove any exclude branches that are not are not under a include branch 121 // since they will be migrated as part of the existing entries 122 // outside of the include branches anyways. 123 Iterator<DN> excludeBranchIterator = excludeBranches.iterator(); 124 while (excludeBranchIterator.hasNext()) 125 { 126 DN excludeDN = excludeBranchIterator.next(); 127 if (!isAnyAncestorOf(includeBranches, excludeDN)) 128 { 129 excludeBranchIterator.remove(); 130 } 131 } 132 133 if (excludeBranches.isEmpty() && includeBranches.size() == 1 && includeBranches.get(0).equals(baseDN)) 134 { 135 // This entire base DN is explicitly included in the import with 136 // no exclude branches that we need to migrate. 137 // Just clear the entry container. 138 return SuffixImportStrategy.CLEAR_SUFFIX; 139 } 140 return SuffixImportStrategy.MERGE_DB_WITH_LDIF; 141 } 142 return SuffixImportStrategy.INCLUDE_EXCLUDE_BRANCHES; 143 } 144 145 private List<DN> getDescendants(DN baseDN, Set<DN> dns) 146 { 147 final List<DN> results = new ArrayList<>(); 148 for (DN dn : dns) 149 { 150 if (baseDN.isAncestorOf(dn)) 151 { 152 results.add(dn); 153 } 154 } 155 return results; 156 } 157 158 private boolean isAnyAncestorOf(List<DN> dns, DN childDN) 159 { 160 for (DN dn : dns) 161 { 162 if (dn.isAncestorOf(childDN)) 163 { 164 return true; 165 } 166 } 167 return false; 168 } 169 170 private boolean isAnyNotEqualAndAncestorOf(List<DN> dns, DN childDN) 171 { 172 for (DN dn : dns) 173 { 174 if (!dn.equals(childDN) && dn.isAncestorOf(childDN)) 175 { 176 return false; 177 } 178 } 179 return true; 180 } 181}