001/* 002 * Copyright 2010-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2010-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2010-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk; 037 038 039 040import java.util.Arrays; 041import java.util.Collection; 042import java.util.Iterator; 043 044import com.unboundid.util.Debug; 045import com.unboundid.util.NotNull; 046import com.unboundid.util.Nullable; 047import com.unboundid.util.StaticUtils; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050import com.unboundid.util.Validator; 051 052import static com.unboundid.ldap.sdk.LDAPMessages.*; 053 054 055 056/** 057 * This class provides an {@link EntrySource} that will retrieve entries 058 * referenced by a provided set of DNs. The connection will remain open after 059 * all entries have been read. 060 * <BR><BR> 061 * It is not necessary to close this entry source when it is no longer needed, 062 * although there is no cost or penalty in doing so. Any exceptions thrown by 063 * the {@link #nextEntry()} method will have the {@code mayContinueReading} 064 * value set to {@code true}. 065 * <H2>Example</H2> 066 * The following example demonstrates the process for retrieving a static group 067 * entry and using a {@code DNEntrySource} to iterate across the members of that 068 * group: 069 * <PRE> 070 * Entry groupEntry = 071 * connection.getEntry("cn=My Group,ou=Groups,dc=example,dc=com"); 072 * String[] memberValues = groupEntry.getAttributeValues("member"); 073 * int entriesReturned = 0; 074 * int exceptionsCaught = 0; 075 * 076 * if (memberValues != null) 077 * { 078 * DNEntrySource entrySource = 079 * new DNEntrySource(connection, memberValues, "cn"); 080 * try 081 * { 082 * while (true) 083 * { 084 * Entry memberEntry; 085 * try 086 * { 087 * memberEntry = entrySource.nextEntry(); 088 * } 089 * catch (EntrySourceException ese) 090 * { 091 * // A problem was encountered while attempting to obtain an entry. 092 * // We may be able to continue reading entries (e.g., if the problem 093 * // was that the group referenced an entry that doesn't exist), or 094 * // we may not (e.g., if the problem was a significant search error 095 * // or problem with the connection). 096 * exceptionsCaught++; 097 * if (ese.mayContinueReading()) 098 * { 099 * continue; 100 * } 101 * else 102 * { 103 * break; 104 * } 105 * } 106 * 107 * if (memberEntry == null) 108 * { 109 * // We've retrieved all of the entries for the given set of DNs. 110 * break; 111 * } 112 * else 113 * { 114 * entriesReturned++; 115 * } 116 * } 117 * } 118 * finally 119 * { 120 * entrySource.close(); 121 * } 122 * } 123 * </PRE> 124 */ 125@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 126public final class DNEntrySource 127 extends EntrySource 128{ 129 // The iterator to use to access the DNs. It will either be across DN or 130 // String objects. 131 @NotNull private final Iterator<?> dnIterator; 132 133 // The connection to use to communicate with the directory server. 134 @NotNull private final LDAPInterface connection; 135 136 // The set of attributes to include in entries that are returned. 137 @NotNull private final String[] attributes; 138 139 140 141 /** 142 * Creates a new DN entry source with the provided information. 143 * 144 * @param connection The connection to the directory server from which the 145 * entries will be read. It must not be {@code null}. 146 * @param dns The set of DNs to be read. It must not be 147 * {@code null}. 148 * @param attributes The set of attributes to include in entries that are 149 * returned. If this is empty or {@code null}, then all 150 * user attributes will be requested. 151 */ 152 public DNEntrySource(@NotNull final LDAPInterface connection, 153 @NotNull final DN[] dns, 154 @Nullable final String... attributes) 155 { 156 Validator.ensureNotNull(connection, dns); 157 158 this.connection = connection; 159 dnIterator = Arrays.asList(dns).iterator(); 160 161 if (attributes == null) 162 { 163 this.attributes = StaticUtils.NO_STRINGS; 164 } 165 else 166 { 167 this.attributes = attributes; 168 } 169 } 170 171 172 173 /** 174 * Creates a new DN entry source with the provided information. 175 * 176 * @param connection The connection to the directory server from which the 177 * entries will be read. It must not be {@code null}. 178 * @param dns The set of DNs to be read. It must not be 179 * {@code null}. 180 * @param attributes The set of attributes to include in entries that are 181 * returned. If this is empty or {@code null}, then all 182 * user attributes will be requested. 183 */ 184 public DNEntrySource(@NotNull final LDAPInterface connection, 185 @NotNull final String[] dns, 186 @Nullable final String... attributes) 187 { 188 this(connection, Arrays.asList(dns), attributes); 189 } 190 191 192 193 /** 194 * Creates a new DN entry source with the provided information. 195 * 196 * @param connection The connection to the directory server from which the 197 * entries will be read. It must not be {@code null}. 198 * @param dns The set of DNs to be read. It must not be 199 * {@code null}. 200 * @param attributes The set of attributes to include in entries that are 201 * returned. If this is empty or {@code null}, then all 202 * user attributes will be requested. 203 */ 204 public DNEntrySource(@NotNull final LDAPInterface connection, 205 @NotNull final Collection<String> dns, 206 @Nullable final String... attributes) 207 { 208 Validator.ensureNotNull(connection, dns); 209 210 this.connection = connection; 211 dnIterator = dns.iterator(); 212 213 if (attributes == null) 214 { 215 this.attributes = StaticUtils.NO_STRINGS; 216 } 217 else 218 { 219 this.attributes = attributes; 220 } 221 } 222 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override() 229 @Nullable() 230 public Entry nextEntry() 231 throws EntrySourceException 232 { 233 if (! dnIterator.hasNext()) 234 { 235 return null; 236 } 237 238 final String dn = String.valueOf(dnIterator.next()); 239 try 240 { 241 final Entry e = connection.getEntry(dn, attributes); 242 if (e == null) 243 { 244 throw new EntrySourceException(true, 245 ERR_DN_ENTRY_SOURCE_NO_SUCH_ENTRY.get(dn), 246 new LDAPException(ResultCode.NO_RESULTS_RETURNED)); 247 } 248 else 249 { 250 return e; 251 } 252 } 253 catch (final LDAPException le) 254 { 255 Debug.debugException(le); 256 throw new EntrySourceException(true, 257 ERR_DN_ENTRY_SOURCE_ERR_RETRIEVING_ENTRY.get(dn, 258 StaticUtils.getExceptionMessage(le)), 259 le); 260 } 261 } 262 263 264 265 /** 266 * {@inheritDoc} 267 */ 268 @Override() 269 public void close() 270 { 271 // No implementation is required. 272 } 273}