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.listener;
037
038
039
040import java.io.Serializable;
041import java.util.ArrayList;
042import java.util.Collection;
043import java.util.Collections;
044import java.util.List;
045
046import com.unboundid.ldap.protocol.AddRequestProtocolOp;
047import com.unboundid.ldap.protocol.AddResponseProtocolOp;
048import com.unboundid.ldap.protocol.BindRequestProtocolOp;
049import com.unboundid.ldap.protocol.BindResponseProtocolOp;
050import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
051import com.unboundid.ldap.protocol.CompareResponseProtocolOp;
052import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
053import com.unboundid.ldap.protocol.DeleteResponseProtocolOp;
054import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
055import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
056import com.unboundid.ldap.protocol.LDAPMessage;
057import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
058import com.unboundid.ldap.protocol.ModifyResponseProtocolOp;
059import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
060import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp;
061import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
062import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp;
063import com.unboundid.ldap.protocol.SearchResultEntryProtocolOp;
064import com.unboundid.ldap.protocol.SearchResultReferenceProtocolOp;
065import com.unboundid.ldap.sdk.Control;
066import com.unboundid.ldap.sdk.Entry;
067import com.unboundid.ldap.sdk.LDAPException;
068import com.unboundid.ldap.sdk.ResultCode;
069import com.unboundid.ldap.sdk.SearchResultReference;
070import com.unboundid.util.Debug;
071import com.unboundid.util.NotMutable;
072import com.unboundid.util.NotNull;
073import com.unboundid.util.Nullable;
074import com.unboundid.util.ThreadSafety;
075import com.unboundid.util.ThreadSafetyLevel;
076import com.unboundid.util.Validator;
077
078
079
080/**
081 * This class provides a very simple LDAP listener request handler
082 * implementation that simply returns a canned response to the client for each
083 * type of operation.
084 */
085@NotMutable()
086@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
087public final class CannedResponseRequestHandler
088       extends LDAPListenerRequestHandler
089       implements Serializable
090{
091  /**
092   * The serial version UID for this serializable class.
093   */
094  private static final long serialVersionUID = 6199105854736880833L;
095
096
097
098  // The protocol ops that will be used in responses.
099  @NotNull private final AddResponseProtocolOp addResponseProtocolOp;
100  @NotNull private final BindResponseProtocolOp bindResponseProtocolOp;
101  @NotNull private final CompareResponseProtocolOp compareResponseProtocolOp;
102  @NotNull private final DeleteResponseProtocolOp deleteResponseProtocolOp;
103  @NotNull private final ExtendedResponseProtocolOp extendedResponseProtocolOp;
104  @NotNull private final ModifyResponseProtocolOp modifyResponseProtocolOp;
105  @NotNull private final ModifyDNResponseProtocolOp modifyDNResponseProtocolOp;
106  @NotNull private final List<SearchResultEntryProtocolOp>
107       searchEntryProtocolOps;
108  @NotNull private final List<SearchResultReferenceProtocolOp>
109       searchReferenceProtocolOps;
110  @NotNull private final SearchResultDoneProtocolOp searchResultDoneProtocolOp;
111
112  // The connection that will be used to communicate with the client.
113  @Nullable private final LDAPListenerClientConnection clientConnection;
114
115
116
117  /**
118   * Creates a new instance of this canned response request handler that will
119   * immediately return a "SUCCESS" response to any request that is received.
120   */
121  public CannedResponseRequestHandler()
122  {
123    this(ResultCode.SUCCESS, null, null, null);
124  }
125
126
127
128  /**
129   * Creates a new instance of this canned response request handler that will
130   * immediately return a response with the provided information to any request
131   * that is received.
132   *
133   * @param  resultCode         The result code to use for the responses.  It
134   *                            must not be {@code null}.
135   * @param  matchedDN          The matched DN to use for the responses.  It may
136   *                            be {@code null} if no matched DN should be
137   *                            included.
138   * @param  diagnosticMessage  The diagnostic message to use for the responses.
139   *                            It may be {@code null} if no diagnostic message
140   *                            should be included.
141   * @param  referralURLs       The referral URLs to use for the responses.  It
142   *                            may be empty or {@code null} if no referral URLs
143   *                            should be included.
144   */
145  public CannedResponseRequestHandler(@NotNull final ResultCode resultCode,
146                                      @Nullable final String matchedDN,
147                                      @Nullable final String diagnosticMessage,
148                                      @Nullable final List<String> referralURLs)
149  {
150    this(resultCode, matchedDN, diagnosticMessage, referralURLs, null, null);
151  }
152
153
154
155  /**
156   * Creates a new instance of this canned response request handler that will
157   * immediately return a response with the provided information to any request
158   * that is received.
159   *
160   * @param  resultCode         The result code to use for the responses.  It
161   *                            must not be {@code null}.
162   * @param  matchedDN          The matched DN to use for the responses.  It may
163   *                            be {@code null} if no matched DN should be
164   *                            included.
165   * @param  diagnosticMessage  The diagnostic message to use for the responses.
166   *                            It may be {@code null} if no diagnostic message
167   *                            should be included.
168   * @param  referralURLs       The referral URLs to use for the responses.  It
169   *                            may be empty or {@code null} if no referral URLs
170   *                            should be included.
171   * @param  searchEntries      The set of search result entries that should be
172   *                            returned for every search.  It may be
173   *                            {@code null} or empty if no entries are
174   *                            required.
175   * @param  searchReferences   The set of search result references that should
176   *                            be returned for every search.  It may be
177   *                            {@code null} or empty if no references are
178   *                            required.
179   */
180  public CannedResponseRequestHandler(@NotNull final ResultCode resultCode,
181       @Nullable final String matchedDN,
182       @Nullable final String diagnosticMessage,
183       @Nullable final List<String> referralURLs,
184       @Nullable final Collection<? extends Entry> searchEntries,
185       @Nullable final Collection<SearchResultReference> searchReferences)
186  {
187    Validator.ensureNotNull(resultCode);
188
189    clientConnection = null;
190
191    final int rc = resultCode.intValue();
192    addResponseProtocolOp = new AddResponseProtocolOp(rc, matchedDN,
193         diagnosticMessage, referralURLs);
194    bindResponseProtocolOp = new BindResponseProtocolOp(rc, matchedDN,
195         diagnosticMessage, referralURLs, null);
196    compareResponseProtocolOp = new CompareResponseProtocolOp(rc, matchedDN,
197         diagnosticMessage, referralURLs);
198    deleteResponseProtocolOp = new DeleteResponseProtocolOp(rc, matchedDN,
199         diagnosticMessage, referralURLs);
200    extendedResponseProtocolOp = new ExtendedResponseProtocolOp(rc, matchedDN,
201         diagnosticMessage, referralURLs, null, null);
202    modifyResponseProtocolOp = new ModifyResponseProtocolOp(rc, matchedDN,
203         diagnosticMessage, referralURLs);
204    modifyDNResponseProtocolOp = new ModifyDNResponseProtocolOp(rc, matchedDN,
205         diagnosticMessage, referralURLs);
206    searchResultDoneProtocolOp = new SearchResultDoneProtocolOp(rc, matchedDN,
207         diagnosticMessage, referralURLs);
208
209    if ((searchEntries == null) || searchEntries.isEmpty())
210    {
211      searchEntryProtocolOps = Collections.emptyList();
212    }
213    else
214    {
215      final ArrayList<SearchResultEntryProtocolOp> l =
216           new ArrayList<>(searchEntries.size());
217      for (final Entry e : searchEntries)
218      {
219        l.add(new SearchResultEntryProtocolOp(e));
220      }
221
222      searchEntryProtocolOps = Collections.unmodifiableList(l);
223    }
224
225    if ((searchReferences == null) || searchReferences.isEmpty())
226    {
227      searchReferenceProtocolOps = Collections.emptyList();
228    }
229    else
230    {
231      final ArrayList<SearchResultReferenceProtocolOp> l =
232           new ArrayList<>(searchReferences.size());
233      for (final SearchResultReference r : searchReferences)
234      {
235        l.add(new SearchResultReferenceProtocolOp(r));
236      }
237
238      searchReferenceProtocolOps = Collections.unmodifiableList(l);
239    }
240  }
241
242
243
244  /**
245   * Creates a new instance of this canned response request handler using the
246   * information of the provided handler and the given client connection.
247   *
248   * @param  h  The request handler from which to take the canned responses.
249   * @param  c  The connection to use to communicate with the client.
250   */
251  private CannedResponseRequestHandler(
252               @NotNull final CannedResponseRequestHandler h,
253               @NotNull final LDAPListenerClientConnection c)
254  {
255    addResponseProtocolOp      = h.addResponseProtocolOp;
256    bindResponseProtocolOp     = h.bindResponseProtocolOp;
257    compareResponseProtocolOp  = h.compareResponseProtocolOp;
258    deleteResponseProtocolOp   = h.deleteResponseProtocolOp;
259    extendedResponseProtocolOp = h.extendedResponseProtocolOp;
260    modifyResponseProtocolOp   = h.modifyResponseProtocolOp;
261    modifyDNResponseProtocolOp = h.modifyDNResponseProtocolOp;
262    searchEntryProtocolOps     = h.searchEntryProtocolOps;
263    searchReferenceProtocolOps = h.searchReferenceProtocolOps;
264    searchResultDoneProtocolOp = h.searchResultDoneProtocolOp;
265
266    clientConnection = c;
267  }
268
269
270
271  /**
272   * {@inheritDoc}
273   */
274  @Override()
275  @NotNull()
276  public CannedResponseRequestHandler newInstance(
277              @NotNull final LDAPListenerClientConnection connection)
278         throws LDAPException
279  {
280    return new CannedResponseRequestHandler(this, connection);
281  }
282
283
284
285  /**
286   * {@inheritDoc}
287   */
288  @Override()
289  @NotNull()
290  public LDAPMessage processAddRequest(final int messageID,
291                          @NotNull final AddRequestProtocolOp request,
292                          @NotNull final List<Control> controls)
293  {
294    return new LDAPMessage(messageID, addResponseProtocolOp,
295         Collections.<Control>emptyList());
296  }
297
298
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override()
304  @NotNull()
305  public LDAPMessage processBindRequest(final int messageID,
306                          @NotNull final BindRequestProtocolOp request,
307                          @NotNull final List<Control> controls)
308  {
309    return new LDAPMessage(messageID, bindResponseProtocolOp,
310         Collections.<Control>emptyList());
311  }
312
313
314
315  /**
316   * {@inheritDoc}
317   */
318  @Override()
319  @NotNull()
320  public LDAPMessage processCompareRequest(final int messageID,
321                          @NotNull final CompareRequestProtocolOp request,
322                          @NotNull final List<Control> controls)
323  {
324    return new LDAPMessage(messageID, compareResponseProtocolOp,
325         Collections.<Control>emptyList());
326  }
327
328
329
330  /**
331   * {@inheritDoc}
332   */
333  @Override()
334  @NotNull()
335  public LDAPMessage processDeleteRequest(final int messageID,
336                          @NotNull final DeleteRequestProtocolOp request,
337                          @NotNull final List<Control> controls)
338  {
339    return new LDAPMessage(messageID, deleteResponseProtocolOp,
340         Collections.<Control>emptyList());
341  }
342
343
344
345  /**
346   * {@inheritDoc}
347   */
348  @Override()
349  @NotNull()
350  public LDAPMessage processExtendedRequest(final int messageID,
351                          @NotNull final ExtendedRequestProtocolOp request,
352                          @NotNull final List<Control> controls)
353  {
354    return new LDAPMessage(messageID, extendedResponseProtocolOp,
355         Collections.<Control>emptyList());
356  }
357
358
359
360  /**
361   * {@inheritDoc}
362   */
363  @Override()
364  @NotNull()
365  public LDAPMessage processModifyRequest(final int messageID,
366                          @NotNull final ModifyRequestProtocolOp request,
367                          @NotNull final List<Control> controls)
368  {
369    return new LDAPMessage(messageID, modifyResponseProtocolOp,
370         Collections.<Control>emptyList());
371  }
372
373
374
375  /**
376   * {@inheritDoc}
377   */
378  @Override()
379  @NotNull()
380  public LDAPMessage processModifyDNRequest(final int messageID,
381                          @NotNull final ModifyDNRequestProtocolOp request,
382                          @NotNull final List<Control> controls)
383  {
384    return new LDAPMessage(messageID, modifyDNResponseProtocolOp,
385         Collections.<Control>emptyList());
386  }
387
388
389
390  /**
391   * {@inheritDoc}
392   */
393  @Override()
394  @NotNull()
395  public LDAPMessage processSearchRequest(final int messageID,
396                          @NotNull final SearchRequestProtocolOp request,
397                          @NotNull final List<Control> controls)
398  {
399    for (final SearchResultEntryProtocolOp e : searchEntryProtocolOps)
400    {
401      try
402      {
403        clientConnection.sendSearchResultEntry(messageID, e);
404      }
405      catch (final Exception ex)
406      {
407        Debug.debugException(ex);
408      }
409    }
410
411    for (final SearchResultReferenceProtocolOp r : searchReferenceProtocolOps)
412    {
413      try
414      {
415        clientConnection.sendSearchResultReference(messageID, r);
416      }
417      catch (final Exception ex)
418      {
419        Debug.debugException(ex);
420      }
421    }
422
423    return new LDAPMessage(messageID, searchResultDoneProtocolOp,
424         Collections.<Control>emptyList());
425  }
426}