001/* 002 * Copyright 2010-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2010-2020 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-2020 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.StaticUtils; 046import com.unboundid.util.ThreadSafety; 047import com.unboundid.util.ThreadSafetyLevel; 048import com.unboundid.util.Validator; 049 050import static com.unboundid.ldap.sdk.LDAPMessages.*; 051 052 053 054/** 055 * This class provides an {@link EntrySource} that will retrieve entries 056 * referenced by a provided set of DNs. The connection will remain open after 057 * all entries have been read. 058 * <BR><BR> 059 * It is not necessary to close this entry source when it is no longer needed, 060 * although there is no cost or penalty in doing so. Any exceptions thrown by 061 * the {@link #nextEntry()} method will have the {@code mayContinueReading} 062 * value set to {@code true}. 063 * <H2>Example</H2> 064 * The following example demonstrates the process for retrieving a static group 065 * entry and using a {@code DNEntrySource} to iterate across the members of that 066 * group: 067 * <PRE> 068 * Entry groupEntry = 069 * connection.getEntry("cn=My Group,ou=Groups,dc=example,dc=com"); 070 * String[] memberValues = groupEntry.getAttributeValues("member"); 071 * int entriesReturned = 0; 072 * int exceptionsCaught = 0; 073 * 074 * if (memberValues != null) 075 * { 076 * DNEntrySource entrySource = 077 * new DNEntrySource(connection, memberValues, "cn"); 078 * try 079 * { 080 * while (true) 081 * { 082 * Entry memberEntry; 083 * try 084 * { 085 * memberEntry = entrySource.nextEntry(); 086 * } 087 * catch (EntrySourceException ese) 088 * { 089 * // A problem was encountered while attempting to obtain an entry. 090 * // We may be able to continue reading entries (e.g., if the problem 091 * // was that the group referenced an entry that doesn't exist), or 092 * // we may not (e.g., if the problem was a significant search error 093 * // or problem with the connection). 094 * exceptionsCaught++; 095 * if (ese.mayContinueReading()) 096 * { 097 * continue; 098 * } 099 * else 100 * { 101 * break; 102 * } 103 * } 104 * 105 * if (memberEntry == null) 106 * { 107 * // We've retrieved all of the entries for the given set of DNs. 108 * break; 109 * } 110 * else 111 * { 112 * entriesReturned++; 113 * } 114 * } 115 * } 116 * finally 117 * { 118 * entrySource.close(); 119 * } 120 * } 121 * </PRE> 122 */ 123@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 124public final class DNEntrySource 125 extends EntrySource 126{ 127 // The iterator to use to access the DNs. It will either be across DN or 128 // String objects. 129 private final Iterator<?> dnIterator; 130 131 // The connection to use to communicate with the directory server. 132 private final LDAPInterface connection; 133 134 // The set of attributes to include in entries that are returned. 135 private final String[] attributes; 136 137 138 139 /** 140 * Creates a new DN entry source with the provided information. 141 * 142 * @param connection The connection to the directory server from which the 143 * entries will be read. It must not be {@code null}. 144 * @param dns The set of DNs to be read. It must not be 145 * {@code null}. 146 * @param attributes The set of attributes to include in entries that are 147 * returned. If this is empty or {@code null}, then all 148 * user attributes will be requested. 149 */ 150 public DNEntrySource(final LDAPInterface connection, final DN[] dns, 151 final String... attributes) 152 { 153 Validator.ensureNotNull(connection, dns); 154 155 this.connection = connection; 156 dnIterator = Arrays.asList(dns).iterator(); 157 158 if (attributes == null) 159 { 160 this.attributes = StaticUtils.NO_STRINGS; 161 } 162 else 163 { 164 this.attributes = attributes; 165 } 166 } 167 168 169 170 /** 171 * Creates a new DN entry source with the provided information. 172 * 173 * @param connection The connection to the directory server from which the 174 * entries will be read. It must not be {@code null}. 175 * @param dns The set of DNs to be read. It must not be 176 * {@code null}. 177 * @param attributes The set of attributes to include in entries that are 178 * returned. If this is empty or {@code null}, then all 179 * user attributes will be requested. 180 */ 181 public DNEntrySource(final LDAPInterface connection, final String[] dns, 182 final String... attributes) 183 { 184 this(connection, Arrays.asList(dns), attributes); 185 } 186 187 188 189 /** 190 * Creates a new DN entry source with the provided information. 191 * 192 * @param connection The connection to the directory server from which the 193 * entries will be read. It must not be {@code null}. 194 * @param dns The set of DNs to be read. It must not be 195 * {@code null}. 196 * @param attributes The set of attributes to include in entries that are 197 * returned. If this is empty or {@code null}, then all 198 * user attributes will be requested. 199 */ 200 public DNEntrySource(final LDAPInterface connection, 201 final Collection<String> dns, final String... attributes) 202 { 203 Validator.ensureNotNull(connection, dns); 204 205 this.connection = connection; 206 dnIterator = dns.iterator(); 207 208 if (attributes == null) 209 { 210 this.attributes = StaticUtils.NO_STRINGS; 211 } 212 else 213 { 214 this.attributes = attributes; 215 } 216 } 217 218 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override() 224 public Entry nextEntry() 225 throws EntrySourceException 226 { 227 if (! dnIterator.hasNext()) 228 { 229 return null; 230 } 231 232 final String dn = String.valueOf(dnIterator.next()); 233 try 234 { 235 final Entry e = connection.getEntry(dn, attributes); 236 if (e == null) 237 { 238 throw new EntrySourceException(true, 239 ERR_DN_ENTRY_SOURCE_NO_SUCH_ENTRY.get(dn), 240 new LDAPException(ResultCode.NO_RESULTS_RETURNED)); 241 } 242 else 243 { 244 return e; 245 } 246 } 247 catch (final LDAPException le) 248 { 249 Debug.debugException(le); 250 throw new EntrySourceException(true, 251 ERR_DN_ENTRY_SOURCE_ERR_RETRIEVING_ENTRY.get(dn, 252 StaticUtils.getExceptionMessage(le)), 253 le); 254 } 255 } 256 257 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override() 263 public void close() 264 { 265 // No implementation is required. 266 } 267}