001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.core;
018
019import static org.opends.messages.ConfigMessages.*;
020import static org.opends.messages.PluginMessages.*;
021import static org.opends.server.util.StaticUtils.*;
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.LinkedHashSet;
027import java.util.List;
028import java.util.Set;
029import java.util.StringTokenizer;
030import java.util.concurrent.ConcurrentHashMap;
031import java.util.concurrent.locks.ReentrantLock;
032
033import org.forgerock.i18n.LocalizableMessage;
034import org.forgerock.i18n.LocalizableMessageDescriptor.Arg4;
035import org.forgerock.i18n.LocalizableMessageDescriptor.Arg5;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.forgerock.opendj.config.server.ConfigChangeResult;
038import org.forgerock.opendj.config.server.ConfigException;
039import org.forgerock.opendj.ldap.DN;
040import org.forgerock.opendj.ldap.ResultCode;
041import org.forgerock.util.Utils;
042import org.forgerock.opendj.config.ClassPropertyDefinition;
043import org.forgerock.opendj.config.server.ConfigurationAddListener;
044import org.forgerock.opendj.config.server.ConfigurationChangeListener;
045import org.forgerock.opendj.config.server.ConfigurationDeleteListener;
046import org.forgerock.opendj.server.config.meta.PluginCfgDefn;
047import org.forgerock.opendj.server.config.server.PluginCfg;
048import org.forgerock.opendj.server.config.server.PluginRootCfg;
049import org.opends.server.api.ClientConnection;
050import org.opends.server.api.plugin.DirectoryServerPlugin;
051import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
052import org.opends.server.api.plugin.PluginResult;
053import org.opends.server.api.plugin.PluginType;
054import org.opends.server.types.CanceledOperationException;
055import org.opends.server.types.DisconnectReason;
056import org.opends.server.types.Entry;
057import org.opends.server.types.InitializationException;
058import org.opends.server.types.IntermediateResponse;
059import org.opends.server.types.LDIFExportConfig;
060import org.opends.server.types.LDIFImportConfig;
061import org.opends.server.types.Modification;
062import org.opends.server.types.Operation;
063import org.opends.server.types.SearchResultEntry;
064import org.opends.server.types.SearchResultReference;
065import org.opends.server.types.operation.PluginOperation;
066import org.opends.server.types.operation.PostOperationAbandonOperation;
067import org.opends.server.types.operation.PostOperationAddOperation;
068import org.opends.server.types.operation.PostOperationBindOperation;
069import org.opends.server.types.operation.PostOperationCompareOperation;
070import org.opends.server.types.operation.PostOperationDeleteOperation;
071import org.opends.server.types.operation.PostOperationExtendedOperation;
072import org.opends.server.types.operation.PostOperationModifyDNOperation;
073import org.opends.server.types.operation.PostOperationModifyOperation;
074import org.opends.server.types.operation.PostOperationSearchOperation;
075import org.opends.server.types.operation.PostOperationUnbindOperation;
076import org.opends.server.types.operation.PostResponseAddOperation;
077import org.opends.server.types.operation.PostResponseBindOperation;
078import org.opends.server.types.operation.PostResponseCompareOperation;
079import org.opends.server.types.operation.PostResponseDeleteOperation;
080import org.opends.server.types.operation.PostResponseExtendedOperation;
081import org.opends.server.types.operation.PostResponseModifyDNOperation;
082import org.opends.server.types.operation.PostResponseModifyOperation;
083import org.opends.server.types.operation.PostResponseSearchOperation;
084import org.opends.server.types.operation.PostSynchronizationAddOperation;
085import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
086import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
087import org.opends.server.types.operation.PostSynchronizationModifyOperation;
088import org.opends.server.types.operation.PreOperationAddOperation;
089import org.opends.server.types.operation.PreOperationBindOperation;
090import org.opends.server.types.operation.PreOperationCompareOperation;
091import org.opends.server.types.operation.PreOperationDeleteOperation;
092import org.opends.server.types.operation.PreOperationExtendedOperation;
093import org.opends.server.types.operation.PreOperationModifyDNOperation;
094import org.opends.server.types.operation.PreOperationModifyOperation;
095import org.opends.server.types.operation.PreOperationOperation;
096import org.opends.server.types.operation.PreOperationSearchOperation;
097import org.opends.server.types.operation.PreParseAbandonOperation;
098import org.opends.server.types.operation.PreParseAddOperation;
099import org.opends.server.types.operation.PreParseBindOperation;
100import org.opends.server.types.operation.PreParseCompareOperation;
101import org.opends.server.types.operation.PreParseDeleteOperation;
102import org.opends.server.types.operation.PreParseExtendedOperation;
103import org.opends.server.types.operation.PreParseModifyDNOperation;
104import org.opends.server.types.operation.PreParseModifyOperation;
105import org.opends.server.types.operation.PreParseOperation;
106import org.opends.server.types.operation.PreParseSearchOperation;
107import org.opends.server.types.operation.PreParseUnbindOperation;
108import org.opends.server.types.operation.SearchEntrySearchOperation;
109import org.opends.server.types.operation.SearchReferenceSearchOperation;
110import org.opends.server.types.operation.SubordinateModifyDNOperation;
111
112/**
113 * This class defines a utility that will be used to manage the configuration
114 * for the set of plugins defined in the Directory Server.  It will perform the
115 * necessary initialization of those plugins when the server is first started,
116 * and then will manage any changes to them while the server is running.  It
117 * also provides methods for invoking all the plugins of a given type.
118 */
119public class PluginConfigManager
120       implements ConfigurationAddListener<PluginCfg>,
121                  ConfigurationDeleteListener<PluginCfg>,
122                  ConfigurationChangeListener<PluginCfg>
123{
124  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
125
126  // Arrays for holding the plugins of each type.
127  private DirectoryServerPlugin[] startupPlugins;
128  private DirectoryServerPlugin[] shutdownPlugins;
129  private DirectoryServerPlugin[] postConnectPlugins;
130  private DirectoryServerPlugin[] postDisconnectPlugins;
131  private DirectoryServerPlugin[] ldifImportPlugins;
132  private DirectoryServerPlugin[] ldifImportEndPlugins;
133  private DirectoryServerPlugin[] ldifImportBeginPlugins;
134  private DirectoryServerPlugin[] ldifExportPlugins;
135  private DirectoryServerPlugin[] preParseAbandonPlugins;
136  private DirectoryServerPlugin[] preParseAddPlugins;
137  private DirectoryServerPlugin[] preParseBindPlugins;
138  private DirectoryServerPlugin[] preParseComparePlugins;
139  private DirectoryServerPlugin[] preParseDeletePlugins;
140  private DirectoryServerPlugin[] preParseExtendedPlugins;
141  private DirectoryServerPlugin[] preParseModifyPlugins;
142  private DirectoryServerPlugin[] preParseModifyDNPlugins;
143  private DirectoryServerPlugin[] preParseSearchPlugins;
144  private DirectoryServerPlugin[] preParseUnbindPlugins;
145  private DirectoryServerPlugin[] preOperationAddPlugins;
146  private DirectoryServerPlugin[] preOperationBindPlugins;
147  private DirectoryServerPlugin[] preOperationComparePlugins;
148  private DirectoryServerPlugin[] preOperationDeletePlugins;
149  private DirectoryServerPlugin[] preOperationExtendedPlugins;
150  private DirectoryServerPlugin[] preOperationModifyPlugins;
151  private DirectoryServerPlugin[] preOperationModifyDNPlugins;
152  private DirectoryServerPlugin[] preOperationSearchPlugins;
153  private DirectoryServerPlugin[] postOperationAbandonPlugins;
154  private DirectoryServerPlugin[] postOperationAddPlugins;
155  private DirectoryServerPlugin[] postOperationBindPlugins;
156  private DirectoryServerPlugin[] postOperationComparePlugins;
157  private DirectoryServerPlugin[] postOperationDeletePlugins;
158  private DirectoryServerPlugin[] postOperationExtendedPlugins;
159  private DirectoryServerPlugin[] postOperationModifyPlugins;
160  private DirectoryServerPlugin[] postOperationModifyDNPlugins;
161  private DirectoryServerPlugin[] postOperationSearchPlugins;
162  private DirectoryServerPlugin[] postOperationUnbindPlugins;
163  private DirectoryServerPlugin[] postResponseAddPlugins;
164  private DirectoryServerPlugin[] postResponseBindPlugins;
165  private DirectoryServerPlugin[] postResponseComparePlugins;
166  private DirectoryServerPlugin[] postResponseDeletePlugins;
167  private DirectoryServerPlugin[] postResponseExtendedPlugins;
168  private DirectoryServerPlugin[] postResponseModifyPlugins;
169  private DirectoryServerPlugin[] postResponseModifyDNPlugins;
170  private DirectoryServerPlugin[] postResponseSearchPlugins;
171  private DirectoryServerPlugin[] postSynchronizationAddPlugins;
172  private DirectoryServerPlugin[] postSynchronizationDeletePlugins;
173  private DirectoryServerPlugin[] postSynchronizationModifyPlugins;
174  private DirectoryServerPlugin[] postSynchronizationModifyDNPlugins;
175  private DirectoryServerPlugin[] searchResultEntryPlugins;
176  private DirectoryServerPlugin[] searchResultReferencePlugins;
177  private DirectoryServerPlugin[] subordinateModifyDNPlugins;
178  private DirectoryServerPlugin[] subordinateDeletePlugins;
179  private DirectoryServerPlugin[] intermediateResponsePlugins;
180
181  /** The mapping between the DN of a plugin entry and the plugin instance loaded from that entry. */
182  private ConcurrentHashMap<DN,
183               DirectoryServerPlugin<? extends PluginCfg>>
184                    registeredPlugins;
185
186  /**
187   * The mapping between an operation and a set of post operation plugins
188   * it should skip. This pairs up pre and post operation plugin processing
189   * such that only plugins that successfully execute its pre op plugin will
190   * have its post op plugin executed on a per operation basis. If an
191   * operation is not registered on this list then all all pre op plugins
192   * executed successfully for this operation so all post op plugins should
193   * execute.
194   */
195  private ConcurrentHashMap<PluginOperation, ArrayList<DirectoryServerPlugin>>
196      skippedPreOperationPlugins;
197
198  /** The plugin root configuration read at server startup. */
199  private PluginRootCfg pluginRootConfig;
200
201  /** The lock that will provide threadsafe access to the sets of registered plugins. */
202  private ReentrantLock pluginLock;
203
204  private final ServerContext serverContext;
205
206  /**
207   * Creates a new instance of this plugin config manager.
208   *
209   * @param serverContext
210   *          The server context.
211   */
212  public PluginConfigManager(ServerContext serverContext)
213  {
214    this.serverContext = serverContext;
215    pluginLock = new ReentrantLock();
216
217    startupPlugins                     = new DirectoryServerPlugin[0];
218    shutdownPlugins                    = new DirectoryServerPlugin[0];
219    postConnectPlugins                 = new DirectoryServerPlugin[0];
220    postDisconnectPlugins              = new DirectoryServerPlugin[0];
221    ldifImportPlugins                  = new DirectoryServerPlugin[0];
222    ldifImportEndPlugins               = new DirectoryServerPlugin[0];
223    ldifImportBeginPlugins             = new DirectoryServerPlugin[0];
224    ldifExportPlugins                  = new DirectoryServerPlugin[0];
225    preParseAbandonPlugins             = new DirectoryServerPlugin[0];
226    preParseAddPlugins                 = new DirectoryServerPlugin[0];
227    preParseBindPlugins                = new DirectoryServerPlugin[0];
228    preParseComparePlugins             = new DirectoryServerPlugin[0];
229    preParseDeletePlugins              = new DirectoryServerPlugin[0];
230    preParseExtendedPlugins            = new DirectoryServerPlugin[0];
231    preParseModifyPlugins              = new DirectoryServerPlugin[0];
232    preParseModifyDNPlugins            = new DirectoryServerPlugin[0];
233    preParseSearchPlugins              = new DirectoryServerPlugin[0];
234    preParseUnbindPlugins              = new DirectoryServerPlugin[0];
235    preOperationAddPlugins             = new DirectoryServerPlugin[0];
236    preOperationBindPlugins            = new DirectoryServerPlugin[0];
237    preOperationComparePlugins         = new DirectoryServerPlugin[0];
238    preOperationDeletePlugins          = new DirectoryServerPlugin[0];
239    preOperationExtendedPlugins        = new DirectoryServerPlugin[0];
240    preOperationModifyPlugins          = new DirectoryServerPlugin[0];
241    preOperationModifyDNPlugins        = new DirectoryServerPlugin[0];
242    preOperationSearchPlugins          = new DirectoryServerPlugin[0];
243    postOperationAbandonPlugins        = new DirectoryServerPlugin[0];
244    postOperationAddPlugins            = new DirectoryServerPlugin[0];
245    postOperationBindPlugins           = new DirectoryServerPlugin[0];
246    postOperationComparePlugins        = new DirectoryServerPlugin[0];
247    postOperationDeletePlugins         = new DirectoryServerPlugin[0];
248    postOperationExtendedPlugins       = new DirectoryServerPlugin[0];
249    postOperationModifyPlugins         = new DirectoryServerPlugin[0];
250    postOperationModifyDNPlugins       = new DirectoryServerPlugin[0];
251    postOperationSearchPlugins         = new DirectoryServerPlugin[0];
252    postOperationUnbindPlugins         = new DirectoryServerPlugin[0];
253    postResponseAddPlugins             = new DirectoryServerPlugin[0];
254    postResponseBindPlugins            = new DirectoryServerPlugin[0];
255    postResponseComparePlugins         = new DirectoryServerPlugin[0];
256    postResponseDeletePlugins          = new DirectoryServerPlugin[0];
257    postResponseExtendedPlugins        = new DirectoryServerPlugin[0];
258    postResponseModifyPlugins          = new DirectoryServerPlugin[0];
259    postResponseModifyDNPlugins        = new DirectoryServerPlugin[0];
260    postResponseSearchPlugins          = new DirectoryServerPlugin[0];
261    postSynchronizationAddPlugins      = new DirectoryServerPlugin[0];
262    postSynchronizationDeletePlugins   = new DirectoryServerPlugin[0];
263    postSynchronizationModifyPlugins   = new DirectoryServerPlugin[0];
264    postSynchronizationModifyDNPlugins = new DirectoryServerPlugin[0];
265    searchResultEntryPlugins           = new DirectoryServerPlugin[0];
266    searchResultReferencePlugins       = new DirectoryServerPlugin[0];
267    subordinateModifyDNPlugins         = new DirectoryServerPlugin[0];
268    subordinateDeletePlugins           = new DirectoryServerPlugin[0];
269    intermediateResponsePlugins        = new DirectoryServerPlugin[0];
270    registeredPlugins                  = new ConcurrentHashMap<>();
271    skippedPreOperationPlugins = new ConcurrentHashMap<>();
272  }
273
274  /**
275   * Initializes this plugin configuration manager. This should only be called
276   * at Directory Server startup and before user plugins are loaded.
277   *
278   * @throws ConfigException
279   *           If a critical configuration problem prevents the plugin
280   *           initialization from succeeding.
281   */
282  public void initializePluginConfigManager() throws ConfigException
283  {
284    registeredPlugins.clear();
285
286    pluginRootConfig = serverContext.getRootConfig().getPluginRoot();
287    pluginRootConfig.addPluginAddListener(this);
288    pluginRootConfig.addPluginDeleteListener(this);
289  }
290
291  /**
292   * Initializes any plugins defined in the directory server
293   * configuration. This should only be called at Directory Server
294   * startup and after this plugin configuration manager has been
295   * initialized.
296   *
297   * @param pluginTypes
298   *          The set of plugin types for the plugins to initialize, or
299   *          <CODE>null</CODE> to initialize all types of plugins
300   *          defined in the server configuration. In general, this
301   *          should only be non-null for cases in which the server is
302   *          running in a special mode that only uses a minimal set of
303   *          plugins (e.g., LDIF import or export).
304   * @throws ConfigException
305   *           If a critical configuration problem prevents the plugin
306   *           initialization from succeeding.
307   * @throws InitializationException
308   *           If a problem occurs while initializing the plugins that
309   *           is not related to the server configuration.
310   */
311  public void initializeUserPlugins(Set<PluginType> pluginTypes)
312         throws ConfigException, InitializationException
313  {
314    //Initialize the user plugins.
315    for (String pluginName : pluginRootConfig.listPlugins())
316    {
317      PluginCfg pluginConfiguration = pluginRootConfig.getPlugin(pluginName);
318      pluginConfiguration.addChangeListener(this);
319
320      if (! pluginConfiguration.isEnabled())
321      {
322        continue;
323      }
324
325      // Create a set of plugin types for the plugin.
326      HashSet<PluginType> initTypes = new HashSet<>();
327      for (PluginCfgDefn.PluginType pluginType : pluginConfiguration.getPluginType())
328      {
329        PluginType t = getPluginType(pluginType);
330        if (pluginTypes == null || pluginTypes.contains(t))
331        {
332          initTypes.add(t);
333        }
334      }
335
336      if (initTypes.isEmpty())
337      {
338        continue;
339      }
340
341      try
342      {
343        DirectoryServerPlugin<? extends PluginCfg> plugin =
344             loadPlugin(pluginConfiguration.getJavaClass(), initTypes,
345                        pluginConfiguration, true);
346        registerPlugin(plugin, pluginConfiguration.dn(), initTypes);
347      }
348      catch (InitializationException ie)
349      {
350        logger.error(ie.getMessageObject());
351        continue;
352      }
353    }
354  }
355
356  /**
357   * Loads the specified class, instantiates it as a plugin, and optionally
358   * initializes that plugin.
359   *
360   * @param  className      The fully-qualified name of the plugin class to
361   *                        load, instantiate, and initialize.
362   * @param  pluginTypes    The set of plugin types for the plugins to
363   *                        initialize, or {@code null} to initialize all types
364   *                        of plugins defined in the server configuration.  In
365   *                        general, this should only be non-null for cases in
366   *                        which the server is running in a special mode that
367   *                        only uses a minimal set of plugins (e.g., LDIF
368   *                        import or export).
369   * @param  configuration  The configuration to use to initialize the plugin.
370   *                        It must not be {@code null}.
371   * @param  initialize     Indicates whether the plugin instance should be
372   *                        initialized.
373   *
374   * @return  The possibly initialized plugin.
375   *
376   * @throws  InitializationException  If a problem occurred while attempting to
377   *                                   initialize the plugin.
378   */
379  private <T extends PluginCfg> DirectoryServerPlugin<T>
380               loadPlugin(String className, Set<PluginType> pluginTypes,
381                          T configuration, boolean initialize)
382          throws InitializationException
383  {
384    try
385    {
386      PluginCfgDefn definition =
387           PluginCfgDefn.getInstance();
388      ClassPropertyDefinition propertyDefinition =
389           definition.getJavaClassPropertyDefinition();
390      Class<? extends DirectoryServerPlugin> pluginClass =
391           propertyDefinition.loadClass(className, DirectoryServerPlugin.class);
392      DirectoryServerPlugin<T> plugin = pluginClass.newInstance();
393
394      if (initialize)
395      {
396        plugin.initializeInternal(serverContext, configuration.dn(), pluginTypes,
397            configuration.isInvokeForInternalOperations());
398        plugin.initializePlugin(pluginTypes, configuration);
399      }
400      else
401      {
402        List<LocalizableMessage> unacceptableReasons = new ArrayList<>();
403        if (!plugin.isConfigurationAcceptable(configuration, unacceptableReasons))
404        {
405          String buffer = Utils.joinAsString(".  ", unacceptableReasons);
406          throw new InitializationException(
407              ERR_CONFIG_PLUGIN_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), buffer));
408        }
409      }
410
411      return plugin;
412    }
413    catch (Exception e)
414    {
415      LocalizableMessage message = ERR_CONFIG_PLUGIN_CANNOT_INITIALIZE.
416          get(className, configuration.dn(), stackTraceToSingleLineString(e));
417      throw new InitializationException(message, e);
418    }
419  }
420
421  /**
422   * Gets the OpenDS plugin type object that corresponds to the configuration
423   * counterpart.
424   *
425   * @param  configPluginType  The configuration plugin type for which to
426   *                           retrieve the OpenDS plugin type.
427   */
428  private PluginType getPluginType(PluginCfgDefn.PluginType
429                                        configPluginType)
430  {
431    switch (configPluginType)
432    {
433      case STARTUP:                return PluginType.STARTUP;
434      case SHUTDOWN:               return PluginType.SHUTDOWN;
435      case POSTCONNECT:            return PluginType.POST_CONNECT;
436      case POSTDISCONNECT:         return PluginType.POST_DISCONNECT;
437      case LDIFIMPORT:             return PluginType.LDIF_IMPORT;
438      case LDIFIMPORTEND:          return PluginType.LDIF_IMPORT_END;
439      case LDIFIMPORTBEGIN:        return PluginType.LDIF_IMPORT_BEGIN;
440      case LDIFEXPORT:             return PluginType.LDIF_EXPORT;
441      case PREPARSEABANDON:        return PluginType.PRE_PARSE_ABANDON;
442      case PREPARSEADD:            return PluginType.PRE_PARSE_ADD;
443      case PREPARSEBIND:           return PluginType.PRE_PARSE_BIND;
444      case PREPARSECOMPARE:        return PluginType.PRE_PARSE_COMPARE;
445      case PREPARSEDELETE:         return PluginType.PRE_PARSE_DELETE;
446      case PREPARSEEXTENDED:       return PluginType.PRE_PARSE_EXTENDED;
447      case PREPARSEMODIFY:         return PluginType.PRE_PARSE_MODIFY;
448      case PREPARSEMODIFYDN:       return PluginType.PRE_PARSE_MODIFY_DN;
449      case PREPARSESEARCH:         return PluginType.PRE_PARSE_SEARCH;
450      case PREPARSEUNBIND:         return PluginType.PRE_PARSE_UNBIND;
451      case PREOPERATIONADD:        return PluginType.PRE_OPERATION_ADD;
452      case PREOPERATIONBIND:       return PluginType.PRE_OPERATION_BIND;
453      case PREOPERATIONCOMPARE:    return PluginType.PRE_OPERATION_COMPARE;
454      case PREOPERATIONDELETE:     return PluginType.PRE_OPERATION_DELETE;
455      case PREOPERATIONEXTENDED:   return PluginType.PRE_OPERATION_EXTENDED;
456      case PREOPERATIONMODIFY:     return PluginType.PRE_OPERATION_MODIFY;
457      case PREOPERATIONMODIFYDN:   return PluginType.PRE_OPERATION_MODIFY_DN;
458      case PREOPERATIONSEARCH:     return PluginType.PRE_OPERATION_SEARCH;
459      case POSTOPERATIONABANDON:   return PluginType.POST_OPERATION_ABANDON;
460      case POSTOPERATIONADD:       return PluginType.POST_OPERATION_ADD;
461      case POSTOPERATIONBIND:      return PluginType.POST_OPERATION_BIND;
462      case POSTOPERATIONCOMPARE:   return PluginType.POST_OPERATION_COMPARE;
463      case POSTOPERATIONDELETE:    return PluginType.POST_OPERATION_DELETE;
464      case POSTOPERATIONEXTENDED:  return PluginType.POST_OPERATION_EXTENDED;
465      case POSTOPERATIONMODIFY:    return PluginType.POST_OPERATION_MODIFY;
466      case POSTOPERATIONMODIFYDN:  return PluginType.POST_OPERATION_MODIFY_DN;
467      case POSTOPERATIONSEARCH:    return PluginType.POST_OPERATION_SEARCH;
468      case POSTOPERATIONUNBIND:    return PluginType.POST_OPERATION_UNBIND;
469      case POSTRESPONSEADD:        return PluginType.POST_RESPONSE_ADD;
470      case POSTRESPONSEBIND:       return PluginType.POST_RESPONSE_BIND;
471      case POSTRESPONSECOMPARE:    return PluginType.POST_RESPONSE_COMPARE;
472      case POSTRESPONSEDELETE:     return PluginType.POST_RESPONSE_DELETE;
473      case POSTRESPONSEEXTENDED:   return PluginType.POST_RESPONSE_EXTENDED;
474      case POSTRESPONSEMODIFY:     return PluginType.POST_RESPONSE_MODIFY;
475      case POSTRESPONSEMODIFYDN:   return PluginType.POST_RESPONSE_MODIFY_DN;
476      case POSTRESPONSESEARCH:     return PluginType.POST_RESPONSE_SEARCH;
477      case SEARCHRESULTENTRY:      return PluginType.SEARCH_RESULT_ENTRY;
478      case SEARCHRESULTREFERENCE:  return PluginType.SEARCH_RESULT_REFERENCE;
479      case SUBORDINATEMODIFYDN:    return PluginType.SUBORDINATE_MODIFY_DN;
480      case SUBORDINATEDELETE:      return PluginType.SUBORDINATE_DELETE;
481      case INTERMEDIATERESPONSE:   return PluginType.INTERMEDIATE_RESPONSE;
482      case POSTSYNCHRONIZATIONADD:
483                return PluginType.POST_SYNCHRONIZATION_ADD;
484      case POSTSYNCHRONIZATIONDELETE:
485                return PluginType.POST_SYNCHRONIZATION_DELETE;
486      case POSTSYNCHRONIZATIONMODIFY:
487                return PluginType.POST_SYNCHRONIZATION_MODIFY;
488      case POSTSYNCHRONIZATIONMODIFYDN:
489                return PluginType.POST_SYNCHRONIZATION_MODIFY_DN;
490      default:                     return null;
491    }
492  }
493
494  /** Finalizes all plugins that are registered with the Directory Server. */
495  public void finalizePlugins()
496  {
497    pluginLock.lock();
498
499    try
500    {
501      for (DirectoryServerPlugin<? extends PluginCfg> plugin : registeredPlugins.values())
502      {
503        try
504        {
505          plugin.finalizePlugin();
506        }
507        catch (Exception e)
508        {
509          logger.traceException(e);
510        }
511      }
512
513      registeredPlugins.clear();
514    }
515    finally
516    {
517      pluginLock.unlock();
518    }
519  }
520
521  /**
522   * Retrieves the set of plugins that have been registered with the Directory
523   * Server.
524   *
525   * @return  The set of plugins that have been registered with the Directory
526   *          Server.
527   */
528  public ConcurrentHashMap<DN,
529              DirectoryServerPlugin<? extends PluginCfg>>
530                   getRegisteredPlugins()
531  {
532    return registeredPlugins;
533  }
534
535  /**
536   * Retrieves the plugin with the specified configuration entry DN.
537   *
538   * @param  pluginDN  The DN of the configuration entry for the plugin to
539   *                   retrieve.
540   *
541   * @return  The requested plugin, or <CODE>null</CODE> if there is no such
542   *          plugin.
543   */
544  public DirectoryServerPlugin getRegisteredPlugin(DN pluginDN)
545  {
546    return registeredPlugins.get(pluginDN);
547  }
548
549  /**
550   * Registers the provided internal plugin with this plugin config
551   * manager and ensures that it will be invoked in the specified ways.
552   *
553   * @param plugin
554   *          The internal plugin to register with the server. The
555   *          plugin must specify a configuration entry which is
556   *          guaranteed to be unique.
557   */
558  void registerInternalPlugin(InternalDirectoryServerPlugin plugin)
559  {
560    pluginLock.lock();
561    try
562    {
563      registerPlugin0(plugin, plugin.getPluginTypes());
564    }
565    finally
566    {
567      pluginLock.unlock();
568    }
569  }
570
571  /**
572   * Register a plugin in the appropriate tables.
573   *
574   * @param plugin
575   *          The plugin to register with the server.
576   * @param pluginTypes
577   *          The plugin types that will be used to control the points
578   *          at which the provided plugin is invoked.
579   */
580  private void registerPlugin0(
581      DirectoryServerPlugin<? extends PluginCfg> plugin,
582      Set<PluginType> pluginTypes)
583  {
584    for (PluginType t : pluginTypes)
585    {
586      switch (t)
587      {
588      case STARTUP:
589        startupPlugins =
590            addPlugin(startupPlugins, plugin, t, pluginRootConfig
591                .getPluginOrderStartup());
592        break;
593      case SHUTDOWN:
594        shutdownPlugins =
595            addPlugin(shutdownPlugins, plugin, t, pluginRootConfig
596                .getPluginOrderShutdown());
597        break;
598      case POST_CONNECT:
599        postConnectPlugins =
600            addPlugin(postConnectPlugins, plugin, t, pluginRootConfig
601                .getPluginOrderPostConnect());
602        break;
603      case POST_DISCONNECT:
604        postDisconnectPlugins =
605            addPlugin(postDisconnectPlugins, plugin, t,
606                pluginRootConfig.getPluginOrderPostDisconnect());
607        break;
608      case LDIF_IMPORT:
609        ldifImportPlugins =
610            addPlugin(ldifImportPlugins, plugin, t, pluginRootConfig
611                .getPluginOrderLDIFImport());
612        break;
613      case LDIF_IMPORT_END:
614        ldifImportEndPlugins =
615            addPlugin(ldifImportEndPlugins, plugin, t, pluginRootConfig
616                .getPluginOrderLDIFImportEnd());
617        break;
618      case LDIF_IMPORT_BEGIN:
619        ldifImportBeginPlugins =
620            addPlugin(ldifImportBeginPlugins, plugin, t,
621                pluginRootConfig.getPluginOrderLDIFImportBegin());
622        break;
623      case LDIF_EXPORT:
624        ldifExportPlugins =
625            addPlugin(ldifExportPlugins, plugin, t, pluginRootConfig
626                .getPluginOrderLDIFExport());
627        break;
628      case PRE_PARSE_ABANDON:
629        preParseAbandonPlugins =
630            addPlugin(preParseAbandonPlugins, plugin, t,
631                pluginRootConfig.getPluginOrderPreParseAbandon());
632        break;
633      case PRE_PARSE_ADD:
634        preParseAddPlugins =
635            addPlugin(preParseAddPlugins, plugin, t, pluginRootConfig
636                .getPluginOrderPreParseAdd());
637        break;
638      case PRE_PARSE_BIND:
639        preParseBindPlugins =
640            addPlugin(preParseBindPlugins, plugin, t, pluginRootConfig
641                .getPluginOrderPreParseBind());
642        break;
643      case PRE_PARSE_COMPARE:
644        preParseComparePlugins =
645            addPlugin(preParseComparePlugins, plugin, t,
646                pluginRootConfig.getPluginOrderPreParseCompare());
647        break;
648      case PRE_PARSE_DELETE:
649        preParseDeletePlugins =
650            addPlugin(preParseDeletePlugins, plugin, t,
651                pluginRootConfig.getPluginOrderPreParseDelete());
652        break;
653      case PRE_PARSE_EXTENDED:
654        preParseExtendedPlugins =
655            addPlugin(preParseExtendedPlugins, plugin, t,
656                pluginRootConfig.getPluginOrderPreParseExtended());
657        break;
658      case PRE_PARSE_MODIFY:
659        preParseModifyPlugins =
660            addPlugin(preParseModifyPlugins, plugin, t,
661                pluginRootConfig.getPluginOrderPreParseModify());
662        break;
663      case PRE_PARSE_MODIFY_DN:
664        preParseModifyDNPlugins =
665            addPlugin(preParseModifyDNPlugins, plugin, t,
666                pluginRootConfig.getPluginOrderPreParseModifyDN());
667        break;
668      case PRE_PARSE_SEARCH:
669        preParseSearchPlugins =
670            addPlugin(preParseSearchPlugins, plugin, t,
671                pluginRootConfig.getPluginOrderPreParseSearch());
672        break;
673      case PRE_PARSE_UNBIND:
674        preParseUnbindPlugins =
675            addPlugin(preParseUnbindPlugins, plugin, t,
676                pluginRootConfig.getPluginOrderPreParseUnbind());
677        break;
678      case PRE_OPERATION_ADD:
679        preOperationAddPlugins =
680            addPlugin(preOperationAddPlugins, plugin, t,
681                pluginRootConfig.getPluginOrderPreOperationAdd());
682        break;
683      case PRE_OPERATION_BIND:
684        preOperationBindPlugins =
685            addPlugin(preOperationBindPlugins, plugin, t,
686                pluginRootConfig.getPluginOrderPreOperationBind());
687        break;
688      case PRE_OPERATION_COMPARE:
689        preOperationComparePlugins =
690            addPlugin(preOperationComparePlugins, plugin, t,
691                pluginRootConfig.getPluginOrderPreOperationCompare());
692        break;
693      case PRE_OPERATION_DELETE:
694        preOperationDeletePlugins =
695            addPlugin(preOperationDeletePlugins, plugin, t,
696                pluginRootConfig.getPluginOrderPreOperationDelete());
697        break;
698      case PRE_OPERATION_EXTENDED:
699        preOperationExtendedPlugins =
700            addPlugin(preOperationExtendedPlugins, plugin, t,
701                pluginRootConfig.getPluginOrderPreOperationExtended());
702        break;
703      case PRE_OPERATION_MODIFY:
704        preOperationModifyPlugins =
705            addPlugin(preOperationModifyPlugins, plugin, t,
706                pluginRootConfig.getPluginOrderPreOperationModify());
707        break;
708      case PRE_OPERATION_MODIFY_DN:
709        preOperationModifyDNPlugins =
710            addPlugin(preOperationModifyDNPlugins, plugin, t,
711                pluginRootConfig.getPluginOrderPreOperationModifyDN());
712        break;
713      case PRE_OPERATION_SEARCH:
714        preOperationSearchPlugins =
715            addPlugin(preOperationSearchPlugins, plugin, t,
716                pluginRootConfig.getPluginOrderPreOperationSearch());
717        break;
718      case POST_OPERATION_ABANDON:
719        postOperationAbandonPlugins =
720            addPlugin(postOperationAbandonPlugins, plugin, t,
721                pluginRootConfig.getPluginOrderPostOperationAbandon());
722        break;
723      case POST_OPERATION_ADD:
724        postOperationAddPlugins =
725            addPlugin(postOperationAddPlugins, plugin, t,
726                pluginRootConfig.getPluginOrderPostOperationAdd());
727        break;
728      case POST_OPERATION_BIND:
729        postOperationBindPlugins =
730            addPlugin(postOperationBindPlugins, plugin, t,
731                pluginRootConfig.getPluginOrderPostOperationBind());
732        break;
733      case POST_OPERATION_COMPARE:
734        postOperationComparePlugins =
735            addPlugin(postOperationComparePlugins, plugin, t,
736                pluginRootConfig.getPluginOrderPostOperationCompare());
737        break;
738      case POST_OPERATION_DELETE:
739        postOperationDeletePlugins =
740            addPlugin(postOperationDeletePlugins, plugin, t,
741                pluginRootConfig.getPluginOrderPostOperationDelete());
742        break;
743      case POST_OPERATION_EXTENDED:
744        postOperationExtendedPlugins =
745            addPlugin(postOperationExtendedPlugins, plugin, t,
746                pluginRootConfig.getPluginOrderPostOperationExtended());
747        break;
748      case POST_OPERATION_MODIFY:
749        postOperationModifyPlugins =
750            addPlugin(postOperationModifyPlugins, plugin, t,
751                pluginRootConfig.getPluginOrderPostOperationModify());
752        break;
753      case POST_OPERATION_MODIFY_DN:
754        postOperationModifyDNPlugins =
755            addPlugin(postOperationModifyDNPlugins, plugin, t,
756                pluginRootConfig.getPluginOrderPostOperationModifyDN());
757        break;
758      case POST_OPERATION_SEARCH:
759        postOperationSearchPlugins =
760            addPlugin(postOperationSearchPlugins, plugin, t,
761                pluginRootConfig.getPluginOrderPostOperationSearch());
762        break;
763      case POST_OPERATION_UNBIND:
764        postOperationUnbindPlugins =
765            addPlugin(postOperationUnbindPlugins, plugin, t,
766                pluginRootConfig.getPluginOrderPostOperationUnbind());
767        break;
768      case POST_RESPONSE_ADD:
769        postResponseAddPlugins =
770            addPlugin(postResponseAddPlugins, plugin, t,
771                pluginRootConfig.getPluginOrderPostResponseAdd());
772        break;
773      case POST_RESPONSE_BIND:
774        postResponseBindPlugins =
775            addPlugin(postResponseBindPlugins, plugin, t,
776                pluginRootConfig.getPluginOrderPostResponseBind());
777        break;
778      case POST_RESPONSE_COMPARE:
779        postResponseComparePlugins =
780            addPlugin(postResponseComparePlugins, plugin, t,
781                pluginRootConfig.getPluginOrderPostResponseCompare());
782        break;
783      case POST_RESPONSE_DELETE:
784        postResponseDeletePlugins =
785            addPlugin(postResponseDeletePlugins, plugin, t,
786                pluginRootConfig.getPluginOrderPostResponseDelete());
787        break;
788      case POST_RESPONSE_EXTENDED:
789        postResponseExtendedPlugins =
790            addPlugin(postResponseExtendedPlugins, plugin, t,
791                pluginRootConfig.getPluginOrderPostResponseExtended());
792        break;
793      case POST_RESPONSE_MODIFY:
794        postResponseModifyPlugins =
795            addPlugin(postResponseModifyPlugins, plugin, t,
796                pluginRootConfig.getPluginOrderPostResponseModify());
797        break;
798      case POST_RESPONSE_MODIFY_DN:
799        postResponseModifyDNPlugins =
800            addPlugin(postResponseModifyDNPlugins, plugin, t,
801                pluginRootConfig.getPluginOrderPostResponseModifyDN());
802        break;
803      case POST_RESPONSE_SEARCH:
804        postResponseSearchPlugins =
805            addPlugin(postResponseSearchPlugins, plugin, t,
806                pluginRootConfig.getPluginOrderPostResponseSearch());
807        break;
808      case POST_SYNCHRONIZATION_ADD:
809        postSynchronizationAddPlugins =
810            addPlugin(postSynchronizationAddPlugins, plugin, t,
811                pluginRootConfig.getPluginOrderPostSynchronizationAdd());
812        break;
813      case POST_SYNCHRONIZATION_DELETE:
814        postSynchronizationDeletePlugins =
815            addPlugin(postSynchronizationDeletePlugins, plugin, t,
816                pluginRootConfig
817                    .getPluginOrderPostSynchronizationDelete());
818        break;
819      case POST_SYNCHRONIZATION_MODIFY:
820        postSynchronizationModifyPlugins =
821            addPlugin(postSynchronizationModifyPlugins, plugin, t,
822                pluginRootConfig
823                    .getPluginOrderPostSynchronizationModify());
824        break;
825      case POST_SYNCHRONIZATION_MODIFY_DN:
826        postSynchronizationModifyDNPlugins =
827            addPlugin(postSynchronizationModifyDNPlugins, plugin, t,
828                pluginRootConfig
829                    .getPluginOrderPostSynchronizationModifyDN());
830        break;
831      case SEARCH_RESULT_ENTRY:
832        searchResultEntryPlugins =
833            addPlugin(searchResultEntryPlugins, plugin, t,
834                pluginRootConfig.getPluginOrderSearchResultEntry());
835        break;
836      case SEARCH_RESULT_REFERENCE:
837        searchResultReferencePlugins =
838            addPlugin(searchResultReferencePlugins, plugin, t,
839                pluginRootConfig.getPluginOrderSearchResultReference());
840        break;
841      case SUBORDINATE_MODIFY_DN:
842        subordinateModifyDNPlugins =
843            addPlugin(subordinateModifyDNPlugins, plugin, t,
844                pluginRootConfig.getPluginOrderSubordinateModifyDN());
845        break;
846      case SUBORDINATE_DELETE:
847        subordinateDeletePlugins =
848            addPlugin(subordinateDeletePlugins, plugin, t,
849                pluginRootConfig.getPluginOrderSubordinateDelete());
850        break;
851      case INTERMEDIATE_RESPONSE:
852        intermediateResponsePlugins =
853            addPlugin(intermediateResponsePlugins, plugin, t,
854                pluginRootConfig.getPluginOrderIntermediateResponse());
855        break;
856      default:
857      }
858    }
859  }
860
861  /**
862   * Registers the provided plugin with this plugin config manager and
863   * ensures that it will be invoked in the specified ways.
864   *
865   * @param plugin
866   *          The plugin to register with the server.
867   * @param pluginEntryDN
868   *          The DN of the configuration entry for the provided plugin.
869   * @param pluginTypes
870   *          The plugin types that will be used to control the points
871   *          at which the provided plugin is invoked.
872   */
873  private void registerPlugin(
874      DirectoryServerPlugin<? extends PluginCfg> plugin,
875      DN pluginEntryDN, Set<PluginType> pluginTypes)
876  {
877    pluginLock.lock();
878    try
879    {
880      registeredPlugins.put(pluginEntryDN, plugin);
881      registerPlugin0(plugin, pluginTypes);
882    }
883    finally
884    {
885      pluginLock.unlock();
886    }
887  }
888
889  /**
890   * Adds the provided plugin to the given array.  The provided array will not
891   * itself be modified, but rather a new array will be created with one
892   * additional element.  The provided plugin will be the last element in the
893   * new array.
894   * <BR><BR>
895   * Note that the only use of this method outside of this class should be for
896   * testing purposes.
897   *
898   * @param  pluginArray  The array containing the existing set of plugins.
899   * @param  plugin       The plugin to be added to the array.
900   * @param  pluginType   The plugin type for the plugin being registered.
901   * @param  pluginOrder  A string that represents the order in which plugins of
902   *                      this type should be invoked, or {@code null} if the
903   *                      order is not considered important.
904   *
905   * @return  The new array containing the new set of plugins.
906   */
907  static DirectoryServerPlugin[] addPlugin(DirectoryServerPlugin[] pluginArray,
908                                           DirectoryServerPlugin plugin,
909                                           PluginType pluginType,
910                                           String pluginOrder)
911  {
912    // If the provided plugin order string is null, empty, or contains only a
913    // wildcard, then simply add the new plugin to the end of the list.
914    // Otherwise, parse the order string and figure out where to put the
915    // provided plugin.
916    if (pluginOrder == null
917        || (pluginOrder = pluginOrder.trim()).length() == 0
918        || pluginOrder.equals("*"))
919    {
920      DirectoryServerPlugin[] newPlugins =
921           new DirectoryServerPlugin[pluginArray.length+1];
922      System.arraycopy(pluginArray, 0, newPlugins, 0, pluginArray.length);
923      newPlugins[pluginArray.length] = plugin;
924
925      return newPlugins;
926    }
927    else
928    {
929      // Parse the plugin order into initial and final plugin names.
930      boolean starFound = false;
931      LinkedHashSet<String> initialPluginNames = new LinkedHashSet<>();
932      LinkedHashSet<String> finalPluginNames   = new LinkedHashSet<>();
933
934      StringTokenizer tokenizer = new StringTokenizer(pluginOrder, ",");
935      while (tokenizer.hasMoreTokens())
936      {
937        String token = tokenizer.nextToken().trim();
938        if (token.length() == 0)
939        {
940          // Only log the warning once per plugin type.  The plugin array will
941          // be empty the first time through, so we can use that to make the
942          // determination.
943          if (pluginArray.length == 0)
944          {
945            logger.warn(WARN_CONFIG_PLUGIN_EMPTY_ELEMENT_IN_ORDER, pluginType.getName());
946          }
947        }
948        else if (token.equals("*"))
949        {
950          if (starFound)
951          {
952            // Only log the warning once per plugin type.  The plugin array will
953            // be empty the first time through, so we can use that to make the
954            // determination.
955            if (pluginArray.length == 0)
956            {
957              logger.warn(WARN_CONFIG_PLUGIN_MULTIPLE_WILDCARDS_IN_ORDER, pluginType.getName());
958            }
959          }
960          else
961          {
962            starFound = true;
963          }
964        }
965        else
966        {
967          String lowerName = toLowerCase(token);
968          if (starFound)
969          {
970            if (initialPluginNames.contains(lowerName) ||
971                finalPluginNames.contains(lowerName))
972            {
973              // Only log the warning once per plugin type.  The plugin array
974              // will be empty the first time through, so we can use that to
975              // make the determination.
976              if (pluginArray.length == 0)
977              {
978                logger.warn(WARN_CONFIG_PLUGIN_LISTED_MULTIPLE_TIMES,
979                    pluginType.getName(), token);
980              }
981            }
982
983            finalPluginNames.add(lowerName);
984          }
985          else
986          {
987            if (initialPluginNames.contains(lowerName))
988            {
989              // Only log the warning once per plugin type.  The plugin array
990              // will be empty the first time through, so we can use that to
991              // make the determination.
992              if (pluginArray.length == 0)
993              {
994                logger.warn(WARN_CONFIG_PLUGIN_LISTED_MULTIPLE_TIMES,
995                    pluginType.getName(), token);
996              }
997            }
998
999            initialPluginNames.add(lowerName);
1000          }
1001        }
1002      }
1003
1004      if (! starFound)
1005      {
1006        // Only log the warning once per plugin type.  The plugin array will be
1007        // empty the first time through, so we can use that to make the
1008        // determination.
1009        if (pluginArray.length == 0)
1010        {
1011          logger.warn(WARN_CONFIG_PLUGIN_ORDER_NO_WILDCARD, pluginType.getName());
1012        }
1013      }
1014
1015      // Parse the array of already registered plugins to sort them accordingly.
1016      HashMap<String,DirectoryServerPlugin> initialPlugins = new HashMap<>(initialPluginNames.size());
1017      HashMap<String,DirectoryServerPlugin> finalPlugins = new HashMap<>(finalPluginNames.size());
1018      ArrayList<DirectoryServerPlugin> otherPlugins = new ArrayList<>();
1019      for (DirectoryServerPlugin p : pluginArray)
1020      {
1021        DN dn = p.getPluginEntryDN();
1022        String lowerName = toLowerCase(dn.rdn().getFirstAVA().getAttributeValue().toString());
1023        if (initialPluginNames.contains(lowerName))
1024        {
1025          initialPlugins.put(lowerName, p);
1026        }
1027        else if (finalPluginNames.contains(lowerName))
1028        {
1029          finalPlugins.put(lowerName, p);
1030        }
1031        else
1032        {
1033          otherPlugins.add(p);
1034        }
1035      }
1036
1037      // Get the name of the provided plugin from its RDN value and put it in
1038      // the correct category.
1039      DN dn = plugin.getPluginEntryDN();
1040      String lowerName = toLowerCase(dn.rdn().getFirstAVA().getAttributeValue().toString());
1041      if (initialPluginNames.contains(lowerName))
1042      {
1043        initialPlugins.put(lowerName, plugin);
1044      }
1045      else if (finalPluginNames.contains(lowerName))
1046      {
1047        finalPlugins.put(lowerName, plugin);
1048      }
1049      else
1050      {
1051        otherPlugins.add(plugin);
1052      }
1053
1054      // Compile a list of all the plugins in the correct order, convert it to
1055      // an array, and return it.
1056      ArrayList<DirectoryServerPlugin> newList = new ArrayList<>(pluginArray.length + 1);
1057      for (String name : initialPluginNames)
1058      {
1059        DirectoryServerPlugin p = initialPlugins.get(name);
1060        if (p != null)
1061        {
1062          newList.add(p);
1063        }
1064      }
1065
1066      newList.addAll(otherPlugins);
1067
1068      for (String name : finalPluginNames)
1069      {
1070        DirectoryServerPlugin p = finalPlugins.get(name);
1071        if (p != null)
1072        {
1073          newList.add(p);
1074        }
1075      }
1076
1077      DirectoryServerPlugin[] newPlugins =
1078           new DirectoryServerPlugin[newList.size()];
1079      newList.toArray(newPlugins);
1080      return newPlugins;
1081    }
1082  }
1083
1084  /**
1085   * Deregisters the provided internal plugin.
1086   *
1087   * @param plugin
1088   *          The internal plugin to deregister from the server.
1089   */
1090  void deregisterInternalPlugin(InternalDirectoryServerPlugin plugin)
1091  {
1092    pluginLock.lock();
1093    try
1094    {
1095      deregisterPlugin0(plugin);
1096      plugin.finalizePlugin();
1097    }
1098    finally
1099    {
1100      pluginLock.unlock();
1101    }
1102  }
1103
1104  /**
1105   * Deregisters the plugin with the provided configuration entry DN.
1106   *
1107   * @param configEntryDN
1108   *          The DN of the configuration entry for the plugin to
1109   *          deregister.
1110   */
1111  private void deregisterPlugin(DN configEntryDN)
1112  {
1113    pluginLock.lock();
1114    DirectoryServerPlugin<? extends PluginCfg> plugin;
1115    try
1116    {
1117      plugin = registeredPlugins.remove(configEntryDN);
1118      if (plugin != null)
1119      {
1120        deregisterPlugin0(plugin);
1121        plugin.finalizePlugin();
1122      }
1123    }
1124    finally
1125    {
1126      pluginLock.unlock();
1127    }
1128  }
1129
1130  /**
1131   * Deregisters the provided plugin.
1132   *
1133   * @param plugin
1134   *          The plugin to deregister from the server.
1135   */
1136  private void deregisterPlugin0(
1137      DirectoryServerPlugin<? extends PluginCfg> plugin)
1138  {
1139    for (PluginType t : plugin.getPluginTypes())
1140    {
1141      switch (t)
1142      {
1143        case STARTUP:
1144          startupPlugins = removePlugin(startupPlugins, plugin);
1145          break;
1146        case SHUTDOWN:
1147          shutdownPlugins = removePlugin(shutdownPlugins, plugin);
1148          break;
1149        case POST_CONNECT:
1150          postConnectPlugins = removePlugin(postConnectPlugins, plugin);
1151          break;
1152        case POST_DISCONNECT:
1153          postDisconnectPlugins = removePlugin(postDisconnectPlugins, plugin);
1154          break;
1155        case LDIF_IMPORT:
1156          ldifImportPlugins = removePlugin(ldifImportPlugins, plugin);
1157          break;
1158        case LDIF_IMPORT_END:
1159          ldifImportEndPlugins = removePlugin(ldifImportEndPlugins, plugin);
1160          break;
1161        case LDIF_IMPORT_BEGIN:
1162          ldifImportBeginPlugins =
1163            removePlugin(ldifImportBeginPlugins, plugin);
1164          break;
1165        case LDIF_EXPORT:
1166          ldifExportPlugins = removePlugin(ldifExportPlugins, plugin);
1167          break;
1168        case PRE_PARSE_ABANDON:
1169          preParseAbandonPlugins = removePlugin(preParseAbandonPlugins,
1170                                                plugin);
1171          break;
1172        case PRE_PARSE_ADD:
1173          preParseAddPlugins = removePlugin(preParseAddPlugins, plugin);
1174          break;
1175        case PRE_PARSE_BIND:
1176          preParseBindPlugins = removePlugin(preParseBindPlugins, plugin);
1177          break;
1178        case PRE_PARSE_COMPARE:
1179          preParseComparePlugins = removePlugin(preParseComparePlugins,
1180                                                plugin);
1181          break;
1182        case PRE_PARSE_DELETE:
1183          preParseDeletePlugins = removePlugin(preParseDeletePlugins, plugin);
1184          break;
1185        case PRE_PARSE_EXTENDED:
1186          preParseExtendedPlugins = removePlugin(preParseExtendedPlugins,
1187                                                 plugin);
1188          break;
1189        case PRE_PARSE_MODIFY:
1190          preParseModifyPlugins = removePlugin(preParseModifyPlugins, plugin);
1191          break;
1192        case PRE_PARSE_MODIFY_DN:
1193          preParseModifyDNPlugins = removePlugin(preParseModifyDNPlugins,
1194                                                 plugin);
1195          break;
1196        case PRE_PARSE_SEARCH:
1197          preParseSearchPlugins = removePlugin(preParseSearchPlugins, plugin);
1198          break;
1199        case PRE_PARSE_UNBIND:
1200          preParseUnbindPlugins = removePlugin(preParseUnbindPlugins, plugin);
1201          break;
1202        case PRE_OPERATION_ADD:
1203          preOperationAddPlugins = removePlugin(preOperationAddPlugins,
1204                                                plugin);
1205          break;
1206        case PRE_OPERATION_BIND:
1207          preOperationBindPlugins = removePlugin(preOperationBindPlugins,
1208                                                 plugin);
1209          break;
1210        case PRE_OPERATION_COMPARE:
1211          preOperationComparePlugins =
1212               removePlugin(preOperationComparePlugins, plugin);
1213          break;
1214        case PRE_OPERATION_DELETE:
1215          preOperationDeletePlugins = removePlugin(preOperationDeletePlugins,
1216                                                   plugin);
1217          break;
1218        case PRE_OPERATION_EXTENDED:
1219          preOperationExtendedPlugins =
1220               removePlugin(preOperationExtendedPlugins, plugin);
1221          break;
1222        case PRE_OPERATION_MODIFY:
1223          preOperationModifyPlugins = removePlugin(preOperationModifyPlugins,
1224                                                   plugin);
1225          break;
1226        case PRE_OPERATION_MODIFY_DN:
1227          preOperationModifyDNPlugins =
1228               removePlugin(preOperationModifyDNPlugins, plugin);
1229          break;
1230        case PRE_OPERATION_SEARCH:
1231          preOperationSearchPlugins = removePlugin(preOperationSearchPlugins,
1232                                                   plugin);
1233          break;
1234        case POST_OPERATION_ABANDON:
1235          postOperationAbandonPlugins =
1236               removePlugin(postOperationAbandonPlugins, plugin);
1237          break;
1238        case POST_OPERATION_ADD:
1239          postOperationAddPlugins = removePlugin(postOperationAddPlugins,
1240                                                 plugin);
1241          break;
1242        case POST_OPERATION_BIND:
1243          postOperationBindPlugins = removePlugin(postOperationBindPlugins,
1244                                                  plugin);
1245          break;
1246        case POST_OPERATION_COMPARE:
1247          postOperationComparePlugins =
1248               removePlugin(postOperationComparePlugins, plugin);
1249          break;
1250        case POST_OPERATION_DELETE:
1251          postOperationDeletePlugins =
1252               removePlugin(postOperationDeletePlugins, plugin);
1253          break;
1254        case POST_OPERATION_EXTENDED:
1255          postOperationExtendedPlugins =
1256               removePlugin(postOperationExtendedPlugins, plugin);
1257          break;
1258        case POST_OPERATION_MODIFY:
1259          postOperationModifyPlugins =
1260               removePlugin(postOperationModifyPlugins, plugin);
1261          break;
1262        case POST_OPERATION_MODIFY_DN:
1263          postOperationModifyDNPlugins =
1264               removePlugin(postOperationModifyDNPlugins, plugin);
1265          break;
1266        case POST_OPERATION_SEARCH:
1267          postOperationSearchPlugins =
1268               removePlugin(postOperationSearchPlugins, plugin);
1269          break;
1270        case POST_OPERATION_UNBIND:
1271          postOperationUnbindPlugins =
1272               removePlugin(postOperationUnbindPlugins, plugin);
1273          break;
1274        case POST_RESPONSE_ADD:
1275          postResponseAddPlugins = removePlugin(postResponseAddPlugins,
1276                                                plugin);
1277          break;
1278        case POST_RESPONSE_BIND:
1279          postResponseBindPlugins = removePlugin(postResponseBindPlugins,
1280                                                 plugin);
1281          break;
1282        case POST_RESPONSE_COMPARE:
1283          postResponseComparePlugins =
1284               removePlugin(postResponseComparePlugins, plugin);
1285          break;
1286        case POST_RESPONSE_DELETE:
1287          postResponseDeletePlugins = removePlugin(postResponseDeletePlugins,
1288                                                   plugin);
1289          break;
1290        case POST_RESPONSE_EXTENDED:
1291          postResponseExtendedPlugins =
1292               removePlugin(postResponseExtendedPlugins, plugin);
1293          break;
1294        case POST_RESPONSE_MODIFY:
1295          postResponseModifyPlugins = removePlugin(postResponseModifyPlugins,
1296                                                   plugin);
1297          break;
1298        case POST_RESPONSE_MODIFY_DN:
1299          postResponseModifyDNPlugins =
1300               removePlugin(postResponseModifyDNPlugins, plugin);
1301          break;
1302        case POST_RESPONSE_SEARCH:
1303          postResponseSearchPlugins = removePlugin(postResponseSearchPlugins,
1304                                                   plugin);
1305          break;
1306        case POST_SYNCHRONIZATION_ADD:
1307          postSynchronizationAddPlugins =
1308               removePlugin(postSynchronizationAddPlugins, plugin);
1309          break;
1310        case POST_SYNCHRONIZATION_DELETE:
1311          postSynchronizationDeletePlugins =
1312               removePlugin(postSynchronizationDeletePlugins, plugin);
1313          break;
1314        case POST_SYNCHRONIZATION_MODIFY:
1315          postSynchronizationModifyPlugins =
1316               removePlugin(postSynchronizationModifyPlugins, plugin);
1317          break;
1318        case POST_SYNCHRONIZATION_MODIFY_DN:
1319          postSynchronizationModifyDNPlugins =
1320               removePlugin(postSynchronizationModifyDNPlugins, plugin);
1321          break;
1322        case SEARCH_RESULT_ENTRY:
1323          searchResultEntryPlugins = removePlugin(searchResultEntryPlugins,
1324                                                  plugin);
1325          break;
1326        case SEARCH_RESULT_REFERENCE:
1327          searchResultReferencePlugins =
1328               removePlugin(searchResultReferencePlugins, plugin);
1329          break;
1330        case SUBORDINATE_MODIFY_DN:
1331          subordinateModifyDNPlugins =
1332               removePlugin(subordinateModifyDNPlugins, plugin);
1333          break;
1334        case SUBORDINATE_DELETE:
1335          subordinateDeletePlugins =
1336               removePlugin(subordinateDeletePlugins, plugin);
1337          break;
1338        case INTERMEDIATE_RESPONSE:
1339          intermediateResponsePlugins =
1340               removePlugin(intermediateResponsePlugins, plugin);
1341          break;
1342        default:
1343      }
1344    }
1345  }
1346
1347  /**
1348   * Removes the provided plugin from the given array.  The provided array will
1349   * not itself be modified, but rather a new array will be created with one
1350   * fewer element (assuming that the specified plugin was found).
1351   *
1352   * @param  pluginArray  The array containing the existing set of plugins.
1353   * @param  plugin       The plugin to be removed from the array.
1354   *
1355   * @return  The new array containing the new set of plugins.
1356   */
1357  private DirectoryServerPlugin[]
1358               removePlugin(DirectoryServerPlugin[] pluginArray,
1359                            DirectoryServerPlugin plugin)
1360  {
1361    int slot   = -1;
1362    int length = pluginArray.length;
1363    for (int i=0; i < length; i++)
1364    {
1365      if (pluginArray[i].getPluginEntryDN().equals(plugin.getPluginEntryDN()))
1366      {
1367        slot = i;
1368        break;
1369      }
1370    }
1371
1372    if (slot < 0)
1373    {
1374      // The plugin wasn't found in the list, so return the same list.
1375      return pluginArray;
1376    }
1377
1378    // If it was the only element in the array, then return an empty array.
1379    if (length == 0)
1380    {
1381      return new DirectoryServerPlugin[0];
1382    }
1383
1384    // Create an array that's one element smaller and copy the remaining "good"
1385    // elements into it.
1386    DirectoryServerPlugin[] newPlugins = new DirectoryServerPlugin[length-1];
1387    if (slot > 0)
1388    {
1389      System.arraycopy(pluginArray, 0, newPlugins, 0, slot);
1390    }
1391
1392    if (slot < length-1)
1393    {
1394      System.arraycopy(pluginArray, slot+1, newPlugins, slot, length-slot-1);
1395    }
1396
1397    return newPlugins;
1398  }
1399
1400  /**
1401   * Invokes the set of startup plugins that have been registered with the
1402   * Directory Server.
1403   *
1404   * @return  The result of processing the startup plugins.
1405   */
1406  public PluginResult.Startup invokeStartupPlugins()
1407  {
1408    PluginResult.Startup result = null;
1409
1410    for (DirectoryServerPlugin p : startupPlugins)
1411    {
1412      try
1413      {
1414        result = p.doStartup();
1415      }
1416      catch (Exception e)
1417      {
1418        logger.traceException(e);
1419
1420        LocalizableMessage message = ERR_PLUGIN_STARTUP_PLUGIN_EXCEPTION.get(
1421                p.getPluginEntryDN(), stackTraceToSingleLineString(e));
1422        return PluginResult.Startup.stopStartup(message);
1423      }
1424
1425      if (result == null)
1426      {
1427        LocalizableMessage message = ERR_PLUGIN_STARTUP_PLUGIN_RETURNED_NULL.get(p.getPluginEntryDN());
1428        logger.error(message);
1429        return PluginResult.Startup.stopStartup(message);
1430      }
1431      else if (! result.continueProcessing())
1432      {
1433        logger.error(ERR_PLUGIN_STARTUP_PLUGIN_FAIL_ABORT,
1434                p.getPluginEntryDN(),
1435                result.getErrorMessage(),
1436                result.getErrorMessage().ordinal());
1437        return result;
1438      }
1439    }
1440
1441    if (result == null)
1442    {
1443      // This should only happen if there were no startup plugins registered,
1444      // which is fine.
1445      result = PluginResult.Startup.continueStartup();
1446    }
1447
1448    return result;
1449  }
1450
1451  /**
1452   * Invokes the set of shutdown plugins that have been configured in the
1453   * Directory Server.
1454   *
1455   * @param  reason  The human-readable reason for the shutdown.
1456   */
1457  public void invokeShutdownPlugins(LocalizableMessage reason)
1458  {
1459    for (DirectoryServerPlugin p : shutdownPlugins)
1460    {
1461      try
1462      {
1463        p.doShutdown(reason);
1464      }
1465      catch (Exception e)
1466      {
1467        logger.traceException(e);
1468        logger.error(ERR_PLUGIN_SHUTDOWN_PLUGIN_EXCEPTION, p.getPluginEntryDN(), stackTraceToSingleLineString(e));
1469      }
1470    }
1471  }
1472
1473  /**
1474   * Invokes the set of post-connect plugins that have been configured in the
1475   * Directory Server.
1476   *
1477   * @param  clientConnection  The client connection that has been established.
1478   *
1479   * @return  The result of processing the post-connect plugins.
1480   */
1481  public PluginResult.PostConnect invokePostConnectPlugins(ClientConnection
1482                                                           clientConnection)
1483  {
1484    PluginResult.PostConnect result = null;
1485
1486    for (DirectoryServerPlugin p : postConnectPlugins)
1487    {
1488      try
1489      {
1490        result = p.doPostConnect(clientConnection);
1491      }
1492      catch (Exception e)
1493      {
1494        logger.traceException(e);
1495
1496        LocalizableMessage message = ERR_PLUGIN_POST_CONNECT_PLUGIN_EXCEPTION.
1497            get(p.getPluginEntryDN(),
1498                clientConnection.getConnectionID(),
1499                clientConnection.getClientAddress(),
1500                stackTraceToSingleLineString(e));
1501        logger.error(message);
1502
1503        return PluginResult.PostConnect.disconnectClient(
1504            DisconnectReason.SERVER_ERROR, true, message);
1505      }
1506
1507      if (result == null)
1508      {
1509        LocalizableMessage message = ERR_PLUGIN_POST_CONNECT_PLUGIN_RETURNED_NULL.
1510            get(p.getPluginEntryDN(), clientConnection.getConnectionID(), clientConnection.getClientAddress());
1511        logger.error(message);
1512
1513        return PluginResult.PostConnect.disconnectClient(
1514            DisconnectReason.SERVER_ERROR, true, message);
1515      }
1516      else if (!result.continuePluginProcessing())
1517      {
1518        return result;
1519      }
1520    }
1521
1522    if (result == null)
1523    {
1524      // This should only happen if there were no post-connect plugins
1525      // registered, which is fine.
1526      result = PluginResult.PostConnect.continueConnectProcessing();
1527    }
1528
1529    return result;
1530  }
1531
1532  /**
1533   * Invokes the set of post-disconnect plugins that have been configured in the
1534   * Directory Server.
1535   *
1536   * @param  clientConnection  The client connection that has been closed.
1537   * @param  disconnectReason  The general reason that the connection was
1538   *                           closed.
1539   * @param  message           A human-readable message that may provide
1540   *                           additional information about the closure.
1541   *
1542   * @return  The result of processing the post-connect plugins.
1543   */
1544  public PluginResult.PostDisconnect invokePostDisconnectPlugins(
1545                                        ClientConnection clientConnection,
1546                                        DisconnectReason disconnectReason,
1547                                        LocalizableMessage message)
1548  {
1549    PluginResult.PostDisconnect result = null;
1550
1551    for (DirectoryServerPlugin p : postDisconnectPlugins)
1552    {
1553      try
1554      {
1555        result = p.doPostDisconnect(clientConnection, disconnectReason,
1556                message);
1557      }
1558      catch (Exception e)
1559      {
1560        logger.traceException(e);
1561        logger.error(ERR_PLUGIN_POST_DISCONNECT_PLUGIN_EXCEPTION,
1562                p.getPluginEntryDN(),
1563                clientConnection.getConnectionID(),
1564                clientConnection.getClientAddress(),
1565                stackTraceToSingleLineString(e));
1566      }
1567
1568      if (result == null)
1569      {
1570        logger.error(ERR_PLUGIN_POST_DISCONNECT_PLUGIN_RETURNED_NULL,
1571                p.getPluginEntryDN(),
1572                clientConnection.getConnectionID(),
1573                clientConnection.getClientAddress());
1574      }
1575      else if (! result.continuePluginProcessing())
1576      {
1577        return result;
1578      }
1579    }
1580
1581    if (result == null)
1582    {
1583      // This should only happen if there were no post-disconnect plugins
1584      // registered, which is fine.
1585      result = PluginResult.PostDisconnect.continueDisconnectProcessing();
1586    }
1587
1588    return result;
1589  }
1590
1591  /**
1592   * Invokes the set of LDIF import plugins that have been configured in the
1593   * Directory Server.
1594   *
1595   * @param  importConfig  The LDIF import configuration used to read the
1596   *                       associated entry.
1597   * @param  entry         The entry that has been read from LDIF.
1598   *
1599   * @return  The result of processing the LDIF import plugins.
1600   */
1601  public PluginResult.ImportLDIF invokeLDIFImportPlugins(
1602      LDIFImportConfig importConfig, Entry entry)
1603  {
1604    PluginResult.ImportLDIF result = null;
1605
1606    for (DirectoryServerPlugin p : ldifImportPlugins)
1607    {
1608      try
1609      {
1610        result = p.doLDIFImport(importConfig, entry);
1611      }
1612      catch (Exception e)
1613      {
1614        logger.traceException(e);
1615
1616        LocalizableMessage message = ERR_PLUGIN_LDIF_IMPORT_PLUGIN_EXCEPTION.get(
1617            p.getPluginEntryDN(), entry.getName(), stackTraceToSingleLineString(e));
1618        logger.error(message);
1619
1620        return PluginResult.ImportLDIF.stopEntryProcessing(message);
1621      }
1622
1623      if (result == null)
1624      {
1625        LocalizableMessage message = ERR_PLUGIN_LDIF_IMPORT_PLUGIN_RETURNED_NULL.
1626            get(p.getPluginEntryDN(), entry.getName());
1627        logger.error(message);
1628
1629        return PluginResult.ImportLDIF.stopEntryProcessing(message);
1630      }
1631      else if (! result.continuePluginProcessing())
1632      {
1633        return result;
1634      }
1635    }
1636
1637    if (result == null)
1638    {
1639      // This should only happen if there were no LDIF import plugins
1640      // registered, which is fine.
1641      result = PluginResult.ImportLDIF.continueEntryProcessing();
1642    }
1643
1644    return result;
1645  }
1646
1647  /**
1648   * Invokes the LDIF import session finalization of LDIF import plugins that
1649   * have been configured in the Directory Server.
1650   *
1651   * @param  importConfig  The LDIF import configuration used for the LDIF
1652   *                       import session.
1653   */
1654  public void invokeLDIFImportEndPlugins(
1655      LDIFImportConfig importConfig)
1656  {
1657    for (DirectoryServerPlugin p : ldifImportEndPlugins)
1658    {
1659      p.doLDIFImportEnd(importConfig);
1660    }
1661  }
1662
1663  /**
1664   * Invokes the LDIF import session initialization of LDIF import plugins that
1665   * have been configured in the Directory Server.
1666   *
1667   * @param  importConfig  The LDIF import configuration used for the LDIF
1668   *                       import session.
1669   */
1670  public void invokeLDIFImportBeginPlugins(
1671      LDIFImportConfig importConfig)
1672  {
1673    for (DirectoryServerPlugin p : ldifImportBeginPlugins)
1674    {
1675      p.doLDIFImportBegin(importConfig);
1676    }
1677  }
1678
1679  /**
1680   * Invokes the set of LDIF export plugins that have been configured in the
1681   * Directory Server.
1682   *
1683   * @param  exportConfig  The LDIF export configuration used to read the
1684   *                       associated entry.
1685   * @param  entry         The entry that has been read from LDIF.
1686   *
1687   * @return  The result of processing the LDIF export plugins.
1688   */
1689  public PluginResult.ImportLDIF invokeLDIFExportPlugins(
1690      LDIFExportConfig exportConfig, Entry entry)
1691  {
1692    PluginResult.ImportLDIF result = null;
1693
1694    for (DirectoryServerPlugin p : ldifExportPlugins)
1695    {
1696      try
1697      {
1698        result = p.doLDIFExport(exportConfig, entry);
1699      }
1700      catch (Exception e)
1701      {
1702        logger.traceException(e);
1703
1704        LocalizableMessage message = ERR_PLUGIN_LDIF_EXPORT_PLUGIN_EXCEPTION.
1705            get(p.getPluginEntryDN(),
1706                entry.getName(),
1707                stackTraceToSingleLineString(e));
1708        logger.error(message);
1709
1710        return PluginResult.ImportLDIF.stopEntryProcessing(message);
1711      }
1712
1713      if (result == null)
1714      {
1715        LocalizableMessage message = ERR_PLUGIN_LDIF_EXPORT_PLUGIN_RETURNED_NULL.
1716            get(p.getPluginEntryDN(), entry.getName());
1717        logger.error(message);
1718
1719        return PluginResult.ImportLDIF.stopEntryProcessing(message);
1720      }
1721      else if (! result.continuePluginProcessing())
1722      {
1723        return result;
1724      }
1725    }
1726
1727    if (result == null)
1728    {
1729      // This should only happen if there were no LDIF export plugins
1730      // registered, which is fine.
1731      result = PluginResult.ImportLDIF.continueEntryProcessing();
1732    }
1733
1734    return result;
1735  }
1736
1737  /**
1738   * Invokes the set of pre-parse abandon plugins that have been configured in
1739   * the Directory Server.
1740   *
1741   * @param  abandonOperation  The abandon operation for which to invoke the
1742   *                           pre-parse plugins.
1743   *
1744   * @return  The result of processing the pre-parse abandon plugins.
1745   */
1746  public PluginResult.PreParse invokePreParseAbandonPlugins(
1747                                   PreParseAbandonOperation abandonOperation)
1748  {
1749    PluginResult.PreParse result = null;
1750
1751    for (DirectoryServerPlugin p : preParseAbandonPlugins)
1752    {
1753      if (isInternalOperation(abandonOperation, p))
1754      {
1755        continue;
1756      }
1757
1758      try
1759      {
1760        result = p.doPreParse(abandonOperation);
1761      }
1762      catch (Exception e)
1763      {
1764        return handlePreParseException(e, abandonOperation, p);
1765      }
1766
1767      if (result == null)
1768      {
1769        return handlePreParseResult(abandonOperation, p);
1770      }
1771      else if (!result.continuePluginProcessing())
1772      {
1773        return result;
1774      }
1775    }
1776
1777    if (result == null)
1778    {
1779      // This should only happen if there were no pre-parse abandon plugins
1780      // registered, which is fine.
1781      result = PluginResult.PreParse.continueOperationProcessing();
1782    }
1783
1784    return result;
1785  }
1786
1787  private PluginResult.PreParse handlePreParseException(
1788      Exception e, PreParseOperation operation, DirectoryServerPlugin plugin)
1789  {
1790    logger.traceException(e);
1791
1792    LocalizableMessage message =
1793        ERR_PLUGIN_PRE_PARSE_PLUGIN_EXCEPTION.get(operation.getOperationType()
1794            .getOperationName(), plugin.getPluginEntryDN(),
1795            operation.getConnectionID(), operation.getOperationID(),
1796            stackTraceToSingleLineString(e));
1797    logger.error(message);
1798
1799    return PluginResult.PreParse.stopProcessing(DirectoryServer
1800        .getServerErrorResultCode(), message);
1801  }
1802
1803  private PluginResult.PreParse handlePreParseResult(
1804      PreParseOperation operation, DirectoryServerPlugin plugin)
1805  {
1806    LocalizableMessage message =
1807        ERR_PLUGIN_PRE_PARSE_PLUGIN_RETURNED_NULL.get(operation
1808            .getOperationType().getOperationName(), plugin
1809            .getPluginEntryDN(), operation.getConnectionID(), operation.getOperationID());
1810    logger.error(message);
1811
1812    return PluginResult.PreParse.stopProcessing(DirectoryServer
1813        .getServerErrorResultCode(), message);
1814  }
1815
1816  /**
1817   * Invokes the set of pre-parse add plugins that have been configured in the
1818   * Directory Server.
1819   *
1820   * @param  addOperation  The add operation for which to invoke the pre-parse
1821   *                       plugins.
1822   *
1823   * @return  The result of processing the pre-parse add plugins.
1824   *
1825   * @throws CanceledOperationException if the operation should be canceled.
1826   */
1827  public PluginResult.PreParse invokePreParseAddPlugins(
1828      PreParseAddOperation addOperation)
1829      throws CanceledOperationException {
1830    PluginResult.PreParse result = null;
1831
1832    for (DirectoryServerPlugin p : preParseAddPlugins)
1833    {
1834      if (isInternalOperation(addOperation, p))
1835      {
1836        continue;
1837      }
1838
1839      try
1840      {
1841        result = p.doPreParse(addOperation);
1842      }
1843      catch (CanceledOperationException coe)
1844      {
1845        throw coe;
1846      }
1847      catch (Exception e)
1848      {
1849        return handlePreParseException(e, addOperation, p);
1850      }
1851
1852      if (result == null)
1853      {
1854        return handlePreParseResult(addOperation, p);
1855      }
1856      else if (!result.continuePluginProcessing())
1857      {
1858        return result;
1859      }
1860    }
1861
1862    if (result == null)
1863    {
1864      // This should only happen if there were no pre-parse add plugins
1865      // registered, which is fine.
1866      result = PluginResult.PreParse.continueOperationProcessing();
1867    }
1868
1869    return result;
1870  }
1871
1872  /**
1873   * Invokes the set of pre-parse bind plugins that have been configured in
1874   * the Directory Server.
1875   *
1876   * @param  bindOperation  The bind operation for which to invoke the pre-parse
1877   *                        plugins.
1878   *
1879   * @return  The result of processing the pre-parse bind plugins.
1880   */
1881  public PluginResult.PreParse invokePreParseBindPlugins(
1882                                   PreParseBindOperation bindOperation)
1883  {
1884    PluginResult.PreParse result = null;
1885
1886    for (DirectoryServerPlugin p : preParseBindPlugins)
1887    {
1888      if (isInternalOperation(bindOperation, p))
1889      {
1890        continue;
1891      }
1892
1893      try
1894      {
1895        result = p.doPreParse(bindOperation);
1896      }
1897      catch (Exception e)
1898      {
1899        return handlePreParseException(e, bindOperation, p);
1900      }
1901
1902      if (result == null)
1903      {
1904        return handlePreParseResult(bindOperation, p);
1905      }
1906      else if (!result.continuePluginProcessing())
1907      {
1908        return result;
1909      }
1910    }
1911
1912    if (result == null)
1913    {
1914      // This should only happen if there were no pre-parse bind plugins
1915      // registered, which is fine.
1916      result = PluginResult.PreParse.continueOperationProcessing();
1917    }
1918
1919    return result;
1920  }
1921
1922  /**
1923   * Invokes the set of pre-parse compare plugins that have been configured in
1924   * the Directory Server.
1925   *
1926   * @param  compareOperation  The compare operation for which to invoke the
1927   *                           pre-parse plugins.
1928   *
1929   * @return  The result of processing the pre-parse compare plugins.
1930   *
1931   * @throws CanceledOperationException if the operation should be canceled.
1932   */
1933  public PluginResult.PreParse invokePreParseComparePlugins(
1934      PreParseCompareOperation compareOperation)
1935      throws CanceledOperationException {
1936    PluginResult.PreParse result = null;
1937
1938    for (DirectoryServerPlugin p : preParseComparePlugins)
1939    {
1940      if (isInternalOperation(compareOperation, p))
1941      {
1942        continue;
1943      }
1944
1945      try
1946      {
1947        result = p.doPreParse(compareOperation);
1948      }
1949      catch (CanceledOperationException coe)
1950      {
1951        throw coe;
1952      }
1953      catch (Exception e)
1954      {
1955        return handlePreParseException(e, compareOperation, p);
1956      }
1957
1958      if (result == null)
1959      {
1960        return handlePreParseResult(compareOperation, p);
1961      }
1962      else if (!result.continuePluginProcessing())
1963      {
1964        return result;
1965      }
1966    }
1967
1968    if (result == null)
1969    {
1970      // This should only happen if there were no pre-parse compare plugins
1971      // registered, which is fine.
1972      result = PluginResult.PreParse.continueOperationProcessing();
1973    }
1974
1975    return result;
1976  }
1977
1978  /**
1979   * Invokes the set of pre-parse delete plugins that have been configured in
1980   * the Directory Server.
1981   *
1982   * @param  deleteOperation  The delete operation for which to invoke the
1983   *                          pre-parse plugins.
1984   *
1985   * @return  The result of processing the pre-parse delete plugins.
1986   *
1987   * @throws CanceledOperationException if the operation should be canceled.
1988   */
1989  public PluginResult.PreParse invokePreParseDeletePlugins(
1990                              PreParseDeleteOperation deleteOperation)
1991      throws CanceledOperationException {
1992    PluginResult.PreParse result = null;
1993
1994    for (DirectoryServerPlugin p : preParseDeletePlugins)
1995    {
1996      if (isInternalOperation(deleteOperation, p))
1997      {
1998        continue;
1999      }
2000
2001      try
2002      {
2003        result = p.doPreParse(deleteOperation);
2004      }
2005      catch (CanceledOperationException coe)
2006      {
2007        throw coe;
2008      }
2009      catch (Exception e)
2010      {
2011        return handlePreParseException(e, deleteOperation, p);
2012      }
2013
2014      if (result == null)
2015      {
2016        return handlePreParseResult(deleteOperation, p);
2017      }
2018      else if (!result.continuePluginProcessing())
2019      {
2020        return result;
2021      }
2022    }
2023
2024    if (result == null)
2025    {
2026      // This should only happen if there were no pre-parse delete plugins
2027      // registered, which is fine.
2028      result = PluginResult.PreParse.continueOperationProcessing();
2029    }
2030
2031    return result;
2032  }
2033
2034  /**
2035   * Invokes the set of pre-parse extended plugins that have been configured in
2036   * the Directory Server.
2037   *
2038   * @param  extendedOperation  The extended operation for which to invoke the
2039   *                            pre-parse plugins.
2040   *
2041   * @return  The result of processing the pre-parse extended plugins.
2042   *
2043   * @throws CanceledOperationException if the operation should be canceled.
2044   */
2045  public PluginResult.PreParse invokePreParseExtendedPlugins(
2046                                   PreParseExtendedOperation extendedOperation)
2047      throws CanceledOperationException {
2048    PluginResult.PreParse result = null;
2049
2050    for (DirectoryServerPlugin p : preParseExtendedPlugins)
2051    {
2052      if (isInternalOperation(extendedOperation, p))
2053      {
2054        continue;
2055      }
2056
2057      try
2058      {
2059        result = p.doPreParse(extendedOperation);
2060      }
2061      catch (CanceledOperationException coe)
2062      {
2063        throw coe;
2064      }
2065      catch (Exception e)
2066      {
2067        return handlePreParseException(e, extendedOperation, p);
2068      }
2069
2070      if (result == null)
2071      {
2072        return handlePreParseResult(extendedOperation, p);
2073      }
2074      else if (!result.continuePluginProcessing())
2075      {
2076        return result;
2077      }
2078    }
2079
2080    if (result == null)
2081    {
2082      // This should only happen if there were no pre-parse extended plugins
2083      // registered, which is fine.
2084      result = PluginResult.PreParse.continueOperationProcessing();
2085    }
2086
2087    return result;
2088  }
2089
2090  /**
2091   * Invokes the set of pre-parse modify plugins that have been configured in
2092   * the Directory Server.
2093   *
2094   * @param  modifyOperation  The modify operation for which to invoke the
2095   *                          pre-parse plugins.
2096   *
2097   * @return  The result of processing the pre-parse modify plugins.
2098   *
2099   * @throws CanceledOperationException if the operation should be canceled.
2100   */
2101  public PluginResult.PreParse invokePreParseModifyPlugins(
2102                                   PreParseModifyOperation modifyOperation)
2103      throws CanceledOperationException {
2104    PluginResult.PreParse result = null;
2105
2106    for (DirectoryServerPlugin p : preParseModifyPlugins)
2107    {
2108      if (isInternalOperation(modifyOperation, p))
2109      {
2110        continue;
2111      }
2112
2113      try
2114      {
2115        result = p.doPreParse(modifyOperation);
2116      }
2117      catch (CanceledOperationException coe)
2118      {
2119        throw coe;
2120      }
2121      catch (Exception e)
2122      {
2123        return handlePreParseException(e, modifyOperation, p);
2124      }
2125
2126      if (result == null)
2127      {
2128        return handlePreParseResult(modifyOperation, p);
2129      }
2130      else if (!result.continuePluginProcessing())
2131      {
2132        return result;
2133      }
2134    }
2135
2136    if (result == null)
2137    {
2138      // This should only happen if there were no pre-parse modify plugins
2139      // registered, which is fine.
2140      result = PluginResult.PreParse.continueOperationProcessing();
2141    }
2142
2143    return result;
2144  }
2145
2146  /**
2147   * Invokes the set of pre-parse modify DN plugins that have been configured in
2148   * the Directory Server.
2149   *
2150   * @param  modifyDNOperation  The modify DN operation for which to invoke the
2151   *                            pre-parse plugins.
2152   *
2153   * @return  The result of processing the pre-parse modify DN plugins.
2154   *
2155   * @throws CanceledOperationException if the operation should be canceled.
2156   */
2157  public PluginResult.PreParse invokePreParseModifyDNPlugins(
2158                                   PreParseModifyDNOperation modifyDNOperation)
2159      throws CanceledOperationException {
2160    PluginResult.PreParse result = null;
2161
2162    for (DirectoryServerPlugin p : preParseModifyDNPlugins)
2163    {
2164      if (isInternalOperation(modifyDNOperation, p))
2165      {
2166        continue;
2167      }
2168
2169      try
2170      {
2171        result = p.doPreParse(modifyDNOperation);
2172      }
2173      catch (CanceledOperationException coe)
2174      {
2175        throw coe;
2176      }
2177      catch (Exception e)
2178      {
2179        return handlePreParseException(e, modifyDNOperation, p);
2180      }
2181
2182      if (result == null)
2183      {
2184        return handlePreParseResult(modifyDNOperation, p);
2185      }
2186      else if (!result.continuePluginProcessing())
2187      {
2188        return result;
2189      }
2190    }
2191
2192    if (result == null)
2193    {
2194      // This should only happen if there were no pre-parse modify DN plugins
2195      // registered, which is fine.
2196      result = PluginResult.PreParse.continueOperationProcessing();
2197    }
2198
2199    return result;
2200  }
2201
2202  /**
2203   * Invokes the set of pre-parse search plugins that have been configured in
2204   * the Directory Server.
2205   *
2206   * @param  searchOperation  The search operation for which to invoke the
2207   *                          pre-parse plugins.
2208   *
2209   * @return  The result of processing the pre-parse search plugins.
2210   *
2211   * @throws CanceledOperationException if the operation should be canceled.
2212   */
2213  public PluginResult.PreParse invokePreParseSearchPlugins(
2214                                   PreParseSearchOperation searchOperation)
2215      throws CanceledOperationException {
2216    PluginResult.PreParse result = null;
2217
2218    for (DirectoryServerPlugin p : preParseSearchPlugins)
2219    {
2220      if (isInternalOperation(searchOperation, p))
2221      {
2222        continue;
2223      }
2224
2225      try
2226      {
2227        result = p.doPreParse(searchOperation);
2228      }
2229      catch (CanceledOperationException coe)
2230      {
2231        throw coe;
2232      }
2233      catch (Exception e)
2234      {
2235        return handlePreParseException(e, searchOperation, p);
2236      }
2237
2238      if (result == null)
2239      {
2240        return handlePreParseResult(searchOperation, p);
2241      }
2242      else if (!result.continuePluginProcessing())
2243      {
2244        return result;
2245      }
2246    }
2247
2248    if (result == null)
2249    {
2250      // This should only happen if there were no pre-parse search plugins
2251      // registered, which is fine.
2252      result = PluginResult.PreParse.continueOperationProcessing();
2253    }
2254
2255    return result;
2256  }
2257
2258  /**
2259   * Invokes the set of pre-parse unbind plugins that have been configured in
2260   * the Directory Server.
2261   *
2262   * @param  unbindOperation  The unbind operation for which to invoke the
2263   *                          pre-parse plugins.
2264   *
2265   * @return  The result of processing the pre-parse unbind plugins.
2266   */
2267  public PluginResult.PreParse invokePreParseUnbindPlugins(
2268                                   PreParseUnbindOperation unbindOperation)
2269  {
2270    PluginResult.PreParse result = null;
2271
2272    for (DirectoryServerPlugin p : preParseUnbindPlugins)
2273    {
2274      if (isInternalOperation(unbindOperation, p))
2275      {
2276        continue;
2277      }
2278
2279      try
2280      {
2281        result = p.doPreParse(unbindOperation);
2282      }
2283      catch (Exception e)
2284      {
2285        return handlePreParseException(e, unbindOperation, p);
2286      }
2287
2288      if (result == null)
2289      {
2290        return handlePreParseResult(unbindOperation, p);
2291      }
2292      else if (!result.continuePluginProcessing())
2293      {
2294        return result;
2295      }
2296    }
2297
2298    if (result == null)
2299    {
2300      // This should only happen if there were no pre-parse unbind plugins
2301      // registered, which is fine.
2302      result = PluginResult.PreParse.continueOperationProcessing();
2303    }
2304
2305    return result;
2306  }
2307
2308  /**
2309   * Invokes the set of pre-operation add plugins that have been configured in
2310   * the Directory Server.
2311   *
2312   * @param  addOperation  The add operation for which to invoke the
2313   *                       pre-operation plugins.
2314   *
2315   * @return  The result of processing the pre-operation add plugins.
2316   *
2317   * @throws CanceledOperationException if the operation should be canceled.
2318   */
2319  public PluginResult.PreOperation invokePreOperationAddPlugins(
2320                                       PreOperationAddOperation addOperation)
2321      throws CanceledOperationException {
2322    PluginResult.PreOperation result = null;
2323
2324    for (int i = 0; i < preOperationAddPlugins.length; i++)
2325    {
2326      DirectoryServerPlugin p = preOperationAddPlugins[i];
2327      if (isInternalOperation(addOperation, p))
2328      {
2329        continue;
2330      }
2331
2332      try
2333      {
2334        result = p.doPreOperation(addOperation);
2335      }
2336      catch (CanceledOperationException coe)
2337      {
2338        throw coe;
2339      }
2340      catch (Exception e)
2341      {
2342        return handlePreOperationException(e, i, preOperationAddPlugins,
2343            addOperation, p);
2344      }
2345
2346      if (result == null)
2347      {
2348        return handlePreOperationResult(addOperation, i, preOperationAddPlugins,
2349            p);
2350      }
2351      else if (!result.continuePluginProcessing())
2352      {
2353        registerSkippedPreOperationPlugins(i, preOperationAddPlugins,
2354            addOperation);
2355        return result;
2356      }
2357    }
2358
2359    if (result == null)
2360    {
2361      // This should only happen if there were no pre-operation add plugins
2362      // registered, which is fine.
2363      result = PluginResult.PreOperation.continueOperationProcessing();
2364    }
2365
2366    return result;
2367  }
2368
2369  /**
2370   * Invokes the set of pre-operation bind plugins that have been configured in
2371   * the Directory Server.
2372   *
2373   * @param  bindOperation  The bind operation for which to invoke the
2374   *                        pre-operation plugins.
2375   *
2376   * @return  The result of processing the pre-operation bind plugins.
2377   */
2378  public PluginResult.PreOperation invokePreOperationBindPlugins(
2379                                       PreOperationBindOperation bindOperation)
2380  {
2381    PluginResult.PreOperation result = null;
2382
2383    for (int i = 0; i < preOperationBindPlugins.length; i++)
2384    {
2385      DirectoryServerPlugin p = preOperationBindPlugins[i];
2386      if (isInternalOperation(bindOperation, p))
2387      {
2388        continue;
2389      }
2390
2391      try
2392      {
2393        result = p.doPreOperation(bindOperation);
2394      }
2395      catch (Exception e)
2396      {
2397        return handlePreOperationException(e, i, preOperationBindPlugins,
2398            bindOperation, p);
2399      }
2400
2401      if (result == null)
2402      {
2403        return handlePreOperationResult(bindOperation, i,
2404            preOperationBindPlugins, p);
2405      }
2406      else if (!result.continuePluginProcessing())
2407      {
2408        registerSkippedPreOperationPlugins(i, preOperationBindPlugins,
2409            bindOperation);
2410
2411        return result;
2412      }
2413    }
2414
2415    if (result == null)
2416    {
2417      // This should only happen if there were no pre-operation add plugins
2418      // registered, which is fine.
2419      result = PluginResult.PreOperation.continueOperationProcessing();
2420    }
2421
2422    return result;
2423  }
2424
2425  /**
2426   * Invokes the set of pre-operation compare plugins that have been configured
2427   * in the Directory Server.
2428   *
2429   * @param  compareOperation  The compare operation for which to invoke the
2430   *                           pre-operation plugins.
2431   *
2432   * @return  The result of processing the pre-operation compare plugins.
2433   *
2434   * @throws CanceledOperationException if the operation should be canceled.
2435   */
2436  public PluginResult.PreOperation invokePreOperationComparePlugins(
2437     PreOperationCompareOperation compareOperation)
2438      throws CanceledOperationException {
2439    PluginResult.PreOperation result = null;
2440
2441    for (int i = 0; i < preOperationComparePlugins.length; i++)
2442    {
2443      DirectoryServerPlugin p = preOperationComparePlugins[i];
2444      if (isInternalOperation(compareOperation, p))
2445      {
2446        continue;
2447      }
2448
2449      try
2450      {
2451        result = p.doPreOperation(compareOperation);
2452      }
2453      catch (CanceledOperationException coe)
2454      {
2455        throw coe;
2456      }
2457      catch (Exception e)
2458      {
2459        return handlePreOperationException(e, i, preOperationComparePlugins,
2460            compareOperation, p);
2461      }
2462
2463      if (result == null)
2464      {
2465        return handlePreOperationResult(compareOperation, i,
2466            preOperationComparePlugins, p);
2467      }
2468      else if (!result.continuePluginProcessing())
2469      {
2470        return result;
2471      }
2472    }
2473
2474    if (result == null)
2475    {
2476      // This should only happen if there were no pre-operation add plugins
2477      // registered, which is fine.
2478      result = PluginResult.PreOperation.continueOperationProcessing();
2479    }
2480
2481    return result;
2482  }
2483
2484  /**
2485   * Invokes the set of pre-operation delete plugins that have been configured
2486   * in the Directory Server.
2487   *
2488   * @param  deleteOperation  The delete operation for which to invoke the
2489   *                          pre-operation plugins.
2490   *
2491   * @return  The result of processing the pre-operation delete plugins.
2492   *
2493   * @throws CanceledOperationException if the operation should be canceled.
2494   */
2495  public PluginResult.PreOperation invokePreOperationDeletePlugins(
2496                                  PreOperationDeleteOperation deleteOperation)
2497      throws CanceledOperationException {
2498    PluginResult.PreOperation result = null;
2499
2500    for (int i = 0; i < preOperationDeletePlugins.length; i++)
2501    {
2502      DirectoryServerPlugin p = preOperationDeletePlugins[i];
2503      if (isInternalOperation(deleteOperation, p))
2504      {
2505        continue;
2506      }
2507
2508      try
2509      {
2510        result = p.doPreOperation(deleteOperation);
2511      }
2512      catch (CanceledOperationException coe)
2513      {
2514        throw coe;
2515      }
2516      catch (Exception e)
2517      {
2518        return handlePreOperationException(e, i, preOperationDeletePlugins,
2519            deleteOperation, p);
2520      }
2521
2522      if (result == null)
2523      {
2524        return handlePreOperationResult(deleteOperation, i,
2525            preOperationDeletePlugins, p);
2526      }
2527      else if (!result.continuePluginProcessing())
2528      {
2529        registerSkippedPreOperationPlugins(i, preOperationDeletePlugins,
2530            deleteOperation);
2531
2532        return result;
2533      }
2534    }
2535
2536    if (result == null)
2537    {
2538      // This should only happen if there were no pre-operation add plugins
2539      // registered, which is fine.
2540      result = PluginResult.PreOperation.continueOperationProcessing();
2541    }
2542
2543    return result;
2544  }
2545
2546  private PluginResult.PreOperation handlePreOperationException(Exception e,
2547      int i, DirectoryServerPlugin[] plugins, PreOperationOperation operation,
2548      DirectoryServerPlugin plugin)
2549  {
2550    logger.traceException(e);
2551
2552    LocalizableMessage message =
2553        ERR_PLUGIN_PRE_OPERATION_PLUGIN_EXCEPTION.get(operation
2554            .getOperationType().getOperationName(), plugin
2555            .getPluginEntryDN(), operation.getConnectionID(), operation
2556            .getOperationID(), stackTraceToSingleLineString(e));
2557    logger.error(message);
2558
2559    registerSkippedPreOperationPlugins(i, plugins, operation);
2560
2561    return PluginResult.PreOperation.stopProcessing(DirectoryServer
2562        .getServerErrorResultCode(), message);
2563  }
2564
2565  private PluginResult.PreOperation handlePreOperationResult(
2566      PreOperationOperation operation, int i, DirectoryServerPlugin[] plugins,
2567      DirectoryServerPlugin plugin)
2568  {
2569    LocalizableMessage message =
2570        ERR_PLUGIN_PRE_OPERATION_PLUGIN_RETURNED_NULL.get(operation
2571            .getOperationType().getOperationName(), plugin
2572            .getPluginEntryDN(), operation.getConnectionID(), operation
2573            .getOperationID());
2574    logger.error(message);
2575
2576    registerSkippedPreOperationPlugins(i, plugins, operation);
2577
2578    return PluginResult.PreOperation.stopProcessing(DirectoryServer
2579        .getServerErrorResultCode(), message);
2580  }
2581
2582  /**
2583   * Invokes the set of pre-operation extended plugins that have been configured
2584   * in the Directory Server.
2585   *
2586   * @param  extendedOperation  The extended operation for which to invoke the
2587   *                            pre-operation plugins.
2588   *
2589   * @return  The result of processing the pre-operation extended plugins.
2590   *
2591   * @throws CanceledOperationException if the operation should be canceled.
2592   */
2593  public PluginResult.PreOperation invokePreOperationExtendedPlugins(
2594                               PreOperationExtendedOperation extendedOperation)
2595      throws CanceledOperationException {
2596    PluginResult.PreOperation result = null;
2597
2598    for (int i = 0; i < preOperationExtendedPlugins.length; i++)
2599    {
2600      DirectoryServerPlugin p = preOperationExtendedPlugins[i];
2601      if (isInternalOperation(extendedOperation, p))
2602      {
2603        registerSkippedPreOperationPlugin(p, extendedOperation);
2604        continue;
2605      }
2606
2607      try
2608      {
2609        result = p.doPreOperation(extendedOperation);
2610      }
2611      catch (CanceledOperationException coe)
2612      {
2613        throw coe;
2614      }
2615      catch (Exception e)
2616      {
2617        return handlePreOperationException(e, i, preOperationExtendedPlugins,
2618            extendedOperation, p);
2619      }
2620
2621      if (result == null)
2622      {
2623        return handlePreOperationResult(extendedOperation, i,
2624            preOperationExtendedPlugins, p);
2625      }
2626      else if (!result.continuePluginProcessing())
2627      {
2628        registerSkippedPreOperationPlugins(i, preOperationExtendedPlugins,
2629            extendedOperation);
2630
2631        return result;
2632      }
2633    }
2634
2635    if (result == null)
2636    {
2637      // This should only happen if there were no pre-operation add plugins
2638      // registered, which is fine.
2639      result = PluginResult.PreOperation.continueOperationProcessing();
2640    }
2641
2642    return result;
2643  }
2644
2645  /**
2646   * Invokes the set of pre-operation modify plugins that have been configured
2647   * in the Directory Server.
2648   *
2649   * @param  modifyOperation  The modify operation for which to invoke the
2650   *                          pre-operation plugins.
2651   *
2652   * @return  The result of processing the pre-operation modify plugins.
2653   *
2654   * @throws CanceledOperationException if the operation should be canceled.
2655   */
2656  public PluginResult.PreOperation invokePreOperationModifyPlugins(
2657                                  PreOperationModifyOperation modifyOperation)
2658      throws CanceledOperationException {
2659    PluginResult.PreOperation result = null;
2660
2661    for (int i = 0; i < preOperationModifyPlugins.length; i++)
2662    {
2663      DirectoryServerPlugin p = preOperationModifyPlugins[i];
2664      if (isInternalOperation(modifyOperation, p))
2665      {
2666        continue;
2667      }
2668
2669      try
2670      {
2671        result = p.doPreOperation(modifyOperation);
2672      }
2673      catch (CanceledOperationException coe)
2674      {
2675        throw coe;
2676      }
2677      catch (Exception e)
2678      {
2679        return handlePreOperationException(e, i, preOperationModifyPlugins,
2680            modifyOperation, p);
2681      }
2682
2683      if (result == null)
2684      {
2685        return handlePreOperationResult(modifyOperation, i,
2686            preOperationModifyPlugins, p);
2687      }
2688      else if (!result.continuePluginProcessing())
2689      {
2690        registerSkippedPreOperationPlugins(i, preOperationModifyPlugins,
2691            modifyOperation);
2692
2693        return result;
2694      }
2695    }
2696
2697    if (result == null)
2698    {
2699      // This should only happen if there were no pre-operation add plugins
2700      // registered, which is fine.
2701      result = PluginResult.PreOperation.continueOperationProcessing();
2702    }
2703
2704    return result;
2705  }
2706
2707  /**
2708   * Invokes the set of pre-operation modify DN plugins that have been
2709   * configured in the Directory Server.
2710   *
2711   * @param  modifyDNOperation  The modify DN operation for which to invoke the
2712   *                            pre-operation plugins.
2713   *
2714   * @return  The result of processing the pre-operation modify DN plugins.
2715   *
2716   * @throws CanceledOperationException if the operation should be canceled.
2717   */
2718  public PluginResult.PreOperation invokePreOperationModifyDNPlugins(
2719                              PreOperationModifyDNOperation modifyDNOperation)
2720      throws CanceledOperationException {
2721    PluginResult.PreOperation result = null;
2722
2723    for (int i = 0; i < preOperationModifyDNPlugins.length; i++)
2724    {
2725      DirectoryServerPlugin p = preOperationModifyDNPlugins[i];
2726      if (isInternalOperation(modifyDNOperation, p))
2727      {
2728        continue;
2729      }
2730
2731      try
2732      {
2733        result = p.doPreOperation(modifyDNOperation);
2734      }
2735      catch (CanceledOperationException coe)
2736      {
2737        throw coe;
2738      }
2739      catch (Exception e)
2740      {
2741        return handlePreOperationException(e, i, preOperationModifyDNPlugins,
2742            modifyDNOperation, p);
2743      }
2744
2745      if (result == null)
2746      {
2747        return handlePreOperationResult(modifyDNOperation, i,
2748            preOperationModifyDNPlugins, p);
2749      }
2750      else if (!result.continuePluginProcessing())
2751      {
2752        registerSkippedPreOperationPlugins(i, preOperationModifyDNPlugins,
2753            modifyDNOperation);
2754
2755        return result;
2756      }
2757    }
2758
2759    if (result == null)
2760    {
2761      // This should only happen if there were no pre-operation add plugins
2762      // registered, which is fine.
2763      result = PluginResult.PreOperation.continueOperationProcessing();
2764    }
2765
2766    return result;
2767  }
2768
2769  /**
2770   * Invokes the set of pre-operation search plugins that have been configured
2771   * in the Directory Server.
2772   *
2773   * @param  searchOperation  The search operation for which to invoke the
2774   *                          pre-operation plugins.
2775   *
2776   * @return  The result of processing the pre-operation search plugins.
2777   *
2778   * @throws CanceledOperationException if the operation should be canceled.
2779   */
2780  public PluginResult.PreOperation invokePreOperationSearchPlugins(
2781                                  PreOperationSearchOperation searchOperation)
2782      throws CanceledOperationException {
2783    PluginResult.PreOperation result = null;
2784
2785    for (int i = 0; i < preOperationSearchPlugins.length; i++)
2786    {
2787      DirectoryServerPlugin p = preOperationSearchPlugins[i];
2788      if (isInternalOperation(searchOperation, p))
2789      {
2790        continue;
2791      }
2792
2793      try
2794      {
2795        result = p.doPreOperation(searchOperation);
2796      }
2797      catch (CanceledOperationException coe)
2798      {
2799        throw coe;
2800      }
2801      catch (Exception e)
2802      {
2803        return handlePreOperationException(e, i, preOperationSearchPlugins,
2804            searchOperation, p);
2805      }
2806
2807      if (result == null)
2808      {
2809        return handlePreOperationResult(searchOperation, i,
2810            preOperationSearchPlugins, p);
2811      }
2812      else if (!result.continuePluginProcessing())
2813      {
2814        registerSkippedPreOperationPlugins(i, preOperationSearchPlugins,
2815             searchOperation);
2816
2817        return result;
2818      }
2819    }
2820
2821    if (result == null)
2822    {
2823      // This should only happen if there were no pre-operation add plugins
2824      // registered, which is fine.
2825      result = PluginResult.PreOperation.continueOperationProcessing();
2826    }
2827
2828    return result;
2829  }
2830
2831  /**
2832   * Invokes the set of post-operation abandon plugins that have been configured
2833   * in the Directory Server.
2834   *
2835   * @param  abandonOperation  The abandon operation for which to invoke the
2836   *                           post-operation plugins.
2837   *
2838   * @return  The result of processing the post-operation abandon plugins.
2839   */
2840  public PluginResult.PostOperation invokePostOperationAbandonPlugins(
2841                              PostOperationAbandonOperation abandonOperation)
2842  {
2843    PluginResult.PostOperation result = null;
2844    PluginResult.PostOperation finalResult = null;
2845
2846    for (DirectoryServerPlugin p : postOperationAbandonPlugins)
2847    {
2848      if (isInternalOperation(abandonOperation, p))
2849      {
2850        continue;
2851      }
2852
2853      try
2854      {
2855        result = p.doPostOperation(abandonOperation);
2856      }
2857      catch (Exception e)
2858      {
2859        logException(abandonOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
2860      }
2861
2862      if (result == null)
2863      {
2864        logNullResult(abandonOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
2865      }
2866      else if (!result.continueProcessing())
2867      {
2868        // This plugin requested operation processing to stop. However, we
2869        // still have to invoke all the post op plugins that successfully
2870        // invoked its pre op plugins. We will just take this plugin's
2871        // results as the final result.
2872        finalResult = result;
2873      }
2874    }
2875
2876    if (result == null)
2877    {
2878      // This should only happen if there were no post-operation add plugins
2879      // registered, which is fine.
2880      finalResult = PluginResult.PostOperation.continueOperationProcessing();
2881    }
2882    else if(finalResult == null)
2883    {
2884      // None of the plugins requested processing to stop so all results
2885      // have equal priority. Just return the last one.
2886      finalResult = result;
2887    }
2888
2889    return finalResult;
2890  }
2891
2892  /**
2893   * Invokes the set of post-operation add plugins that have been configured in
2894   * the Directory Server.
2895   *
2896   * @param  addOperation  The add operation for which to invoke the
2897   *                       post-operation plugins.
2898   *
2899   * @return  The result of processing the post-operation add plugins.
2900   */
2901  public PluginResult.PostOperation invokePostOperationAddPlugins(
2902                                        PostOperationAddOperation addOperation)
2903  {
2904    PluginResult.PostOperation result = null;
2905    PluginResult.PostOperation finalResult = null;
2906
2907    ArrayList<DirectoryServerPlugin> skippedPlugins =
2908        skippedPreOperationPlugins.remove(addOperation);
2909
2910    for (DirectoryServerPlugin p : postOperationAddPlugins)
2911    {
2912      if (isInternalOperation(addOperation, p)
2913          || isSkipped(skippedPlugins, p))
2914      {
2915        continue;
2916      }
2917
2918      try
2919      {
2920        result = p.doPostOperation(addOperation);
2921      }
2922      catch (Exception e)
2923      {
2924        logException(addOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
2925      }
2926
2927      if (result == null)
2928      {
2929        logNullResult(addOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
2930      }
2931      else if (!result.continueProcessing())
2932      {
2933        // This plugin requested operation processing to stop. However, we
2934        // still have to invoke all the post op plugins that successfully
2935        // invoked its pre op plugins. We will just take this plugin's
2936        // results as the final result.
2937        finalResult = result;
2938      }
2939    }
2940
2941    if (result == null)
2942    {
2943      // This should only happen if there were no post-operation add plugins
2944      // registered, which is fine.
2945      finalResult = PluginResult.PostOperation.continueOperationProcessing();
2946    }
2947    else if(finalResult == null)
2948    {
2949      // None of the plugins requested processing to stop so all results
2950      // have equal priority. Just return the last one.
2951      finalResult = result;
2952    }
2953
2954    return finalResult;
2955  }
2956
2957  /**
2958   * Invokes the set of post-operation bind plugins that have been configured
2959   * in the Directory Server.
2960   *
2961   * @param  bindOperation  The bind operation for which to invoke the
2962   *                        post-operation plugins.
2963   *
2964   * @return  The result of processing the post-operation bind plugins.
2965   */
2966  public PluginResult.PostOperation invokePostOperationBindPlugins(
2967                                   PostOperationBindOperation bindOperation)
2968  {
2969    PluginResult.PostOperation result = null;
2970    PluginResult.PostOperation finalResult = null;
2971
2972    ArrayList<DirectoryServerPlugin> skippedPlugins =
2973        skippedPreOperationPlugins.remove(bindOperation);
2974
2975    for (DirectoryServerPlugin p : postOperationBindPlugins)
2976    {
2977      if (isInternalOperation(bindOperation, p)
2978          || isSkipped(skippedPlugins, p))
2979      {
2980        continue;
2981      }
2982
2983      try
2984      {
2985        result = p.doPostOperation(bindOperation);
2986      }
2987      catch (Exception e)
2988      {
2989        logException(bindOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
2990      }
2991
2992      if (result == null)
2993      {
2994        logNullResult(bindOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
2995      }
2996      else if (!result.continueProcessing())
2997      {
2998        // This plugin requested operation processing to stop. However, we
2999        // still have to invoke all the post op plugins that successfully
3000        // invoked its pre op plugins. We will just take this plugin's
3001        // results as the final result.
3002        finalResult = result;
3003      }
3004    }
3005
3006    if (result == null)
3007    {
3008      // This should only happen if there were no post-operation add plugins
3009      // registered, which is fine.
3010      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3011    }
3012    else if(finalResult == null)
3013    {
3014      // None of the plugins requested processing to stop so all results
3015      // have equal priority. Just return the last one.
3016      finalResult = result;
3017    }
3018
3019    return finalResult;
3020  }
3021
3022  /**
3023   * Invokes the set of post-operation compare plugins that have been configured
3024   * in the Directory Server.
3025   *
3026   * @param  compareOperation  The compare operation for which to invoke the
3027   *                           post-operation plugins.
3028   *
3029   * @return  The result of processing the post-operation compare plugins.
3030   */
3031  public PluginResult.PostOperation invokePostOperationComparePlugins(
3032      PostOperationCompareOperation compareOperation)
3033  {
3034    PluginResult.PostOperation result = null;
3035    PluginResult.PostOperation finalResult = null;
3036
3037    ArrayList<DirectoryServerPlugin> skippedPlugins =
3038        skippedPreOperationPlugins.remove(compareOperation);
3039
3040    for (DirectoryServerPlugin p : postOperationComparePlugins)
3041    {
3042      if (isInternalOperation(compareOperation, p)
3043          || isSkipped(skippedPlugins, p))
3044      {
3045        continue;
3046      }
3047
3048      try
3049      {
3050        result = p.doPostOperation(compareOperation);
3051      }
3052      catch (Exception e)
3053      {
3054        logException(compareOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3055      }
3056
3057      if (result == null)
3058      {
3059        logNullResult(compareOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3060      }
3061      else if (!result.continueProcessing())
3062      {
3063        // This plugin requested operation processing to stop. However, we
3064        // still have to invoke all the post op plugins that successfully
3065        // invoked its pre op plugins. We will just take this plugin's
3066        // results as the final result.
3067        finalResult = result;
3068      }
3069    }
3070
3071    if (result == null)
3072    {
3073      // This should only happen if there were no post-operation add plugins
3074      // registered, which is fine.
3075      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3076    }
3077    else if(finalResult == null)
3078    {
3079      // None of the plugins requested processing to stop so all results
3080      // have equal priority. Just return the last one.
3081      finalResult = result;
3082    }
3083
3084    return finalResult;
3085  }
3086
3087  private boolean isInternalOperation(PluginOperation op, DirectoryServerPlugin p)
3088  {
3089    return op.isInternalOperation() && !p.invokeForInternalOperations();
3090  }
3091
3092  private boolean isSkipped(ArrayList<DirectoryServerPlugin> skippedPlugins, DirectoryServerPlugin p)
3093  {
3094    return skippedPlugins != null && skippedPlugins.contains(p);
3095  }
3096
3097  /**
3098   * Invokes the set of post-operation delete plugins that have been configured
3099   * in the Directory Server.
3100   *
3101   * @param  deleteOperation  The delete operation for which to invoke the
3102   *                          post-operation plugins.
3103   *
3104   * @return  The result of processing the post-operation delete plugins.
3105   */
3106  public PluginResult.PostOperation invokePostOperationDeletePlugins(
3107                                   PostOperationDeleteOperation deleteOperation)
3108  {
3109    PluginResult.PostOperation result = null;
3110    PluginResult.PostOperation finalResult = null;
3111
3112    ArrayList<DirectoryServerPlugin> skippedPlugins =
3113        skippedPreOperationPlugins.remove(deleteOperation);
3114
3115    for (DirectoryServerPlugin p : postOperationDeletePlugins)
3116    {
3117      if (isInternalOperation(deleteOperation, p)
3118          || isSkipped(skippedPlugins, p))
3119      {
3120        continue;
3121      }
3122
3123      try
3124      {
3125        result = p.doPostOperation(deleteOperation);
3126      }
3127      catch (Exception e)
3128      {
3129        logException(deleteOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3130      }
3131
3132      if (result == null)
3133      {
3134        logNullResult(deleteOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3135      }
3136      else if (!result.continueProcessing())
3137      {
3138        // This plugin requested operation processing to stop. However, we
3139        // still have to invoke all the post op plugins that successfully
3140        // invoked its pre op plugins. We will just take this plugin's
3141        // results as the final result.
3142        finalResult = result;
3143      }
3144    }
3145
3146    if (result == null)
3147    {
3148      // This should only happen if there were no post-operation add plugins
3149      // registered, which is fine.
3150      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3151    }
3152    else if(finalResult == null)
3153    {
3154      // None of the plugins requested processing to stop so all results
3155      // have equal priority. Just return the last one.
3156      finalResult = result;
3157    }
3158
3159    return finalResult;
3160  }
3161
3162  /**
3163   * Invokes the set of post-operation extended plugins that have been
3164   * configured in the Directory Server.
3165   *
3166   * @param  extendedOperation  The extended operation for which to invoke the
3167   *                            post-operation plugins.
3168   *
3169   * @return  The result of processing the post-operation extended plugins.
3170   */
3171  public PluginResult.PostOperation invokePostOperationExtendedPlugins(
3172                             PostOperationExtendedOperation extendedOperation)
3173  {
3174    PluginResult.PostOperation result = null;
3175    PluginResult.PostOperation finalResult = null;
3176
3177    ArrayList<DirectoryServerPlugin> skippedPlugins =
3178        skippedPreOperationPlugins.remove(extendedOperation);
3179
3180    for (DirectoryServerPlugin p : postOperationExtendedPlugins)
3181    {
3182      if (isInternalOperation(extendedOperation, p)
3183          || isSkipped(skippedPlugins, p))
3184      {
3185        continue;
3186      }
3187
3188      try
3189      {
3190        result = p.doPostOperation(extendedOperation);
3191      }
3192      catch (Exception e)
3193      {
3194        logException(extendedOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3195      }
3196
3197      if (result == null)
3198      {
3199        logNullResult(extendedOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3200      }
3201      else if (!result.continueProcessing())
3202      {
3203        // This plugin requested operation processing to stop. However, we
3204        // still have to invoke all the post op plugins that successfully
3205        // invoked its pre op plugins. We will just take this plugin's
3206        // results as the final result.
3207        finalResult = result;
3208      }
3209    }
3210
3211    if (result == null)
3212    {
3213      // This should only happen if there were no post-operation add plugins
3214      // registered, which is fine.
3215      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3216    }
3217    else if(finalResult == null)
3218    {
3219      // None of the plugins requested processing to stop so all results
3220      // have equal priority. Just return the last one.
3221      finalResult = result;
3222    }
3223
3224    return finalResult;
3225  }
3226
3227  /**
3228   * Invokes the set of post-operation modify plugins that have been configured
3229   * in the Directory Server.
3230   *
3231   * @param  modifyOperation  The modify operation for which to invoke the
3232   *                          post-operation plugins.
3233   *
3234   * @return  The result of processing the post-operation modify plugins.
3235   */
3236  public PluginResult.PostOperation invokePostOperationModifyPlugins(
3237                                   PostOperationModifyOperation modifyOperation)
3238  {
3239    PluginResult.PostOperation result = null;
3240    PluginResult.PostOperation finalResult = null;
3241
3242    ArrayList<DirectoryServerPlugin> skippedPlugins =
3243        skippedPreOperationPlugins.remove(modifyOperation);
3244
3245    for (DirectoryServerPlugin p : postOperationModifyPlugins)
3246    {
3247      if (isInternalOperation(modifyOperation, p)
3248          || isSkipped(skippedPlugins, p))
3249      {
3250        continue;
3251      }
3252
3253      try
3254      {
3255        result = p.doPostOperation(modifyOperation);
3256      }
3257      catch (Exception e)
3258      {
3259        logException(modifyOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3260      }
3261
3262      if (result == null)
3263      {
3264        logNullResult(modifyOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3265      }
3266      else if (!result.continueProcessing())
3267      {
3268        // This plugin requested operation processing to stop. However, we
3269        // still have to invoke all the post op plugins that successfully
3270        // invoked its pre op plugins. We will just take this plugin's
3271        // results as the final result.
3272        finalResult = result;
3273      }
3274    }
3275
3276    if (result == null)
3277    {
3278      // This should only happen if there were no post-operation add plugins
3279      // registered, which is fine.
3280      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3281    }
3282    else if(finalResult == null)
3283    {
3284      // None of the plugins requested processing to stop so all results
3285      // have equal priority. Just return the last one.
3286      finalResult = result;
3287    }
3288    return finalResult;
3289  }
3290
3291  /**
3292   * Invokes the set of post-operation modify DN plugins that have been
3293   * configured in the Directory Server.
3294   *
3295   * @param  modifyDNOperation  The modify DN operation for which to invoke the
3296   *                            post-operation plugins.
3297   *
3298   * @return  The result of processing the post-operation modify DN plugins.
3299   */
3300  public PluginResult.PostOperation invokePostOperationModifyDNPlugins(
3301                             PostOperationModifyDNOperation modifyDNOperation)
3302  {
3303    PluginResult.PostOperation result = null;
3304    PluginResult.PostOperation finalResult = null;
3305
3306    ArrayList<DirectoryServerPlugin> skippedPlugins =
3307        skippedPreOperationPlugins.remove(modifyDNOperation);
3308
3309    for (DirectoryServerPlugin p : postOperationModifyDNPlugins)
3310    {
3311      if (isInternalOperation(modifyDNOperation, p)
3312          || isSkipped(skippedPlugins, p))
3313      {
3314        continue;
3315      }
3316
3317      try
3318      {
3319        result = p.doPostOperation(modifyDNOperation);
3320      }
3321      catch (Exception e)
3322      {
3323        logException(modifyDNOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3324      }
3325
3326      if (result == null)
3327      {
3328        logNullResult(modifyDNOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3329      }
3330      else if (!result.continueProcessing())
3331      {
3332        // This plugin requested operation processing to stop. However, we
3333        // still have to invoke all the post op plugins that successfully
3334        // invoked its pre op plugins. We will just take this plugin's
3335        // results as the final result.
3336        finalResult = result;
3337      }
3338    }
3339
3340    if (result == null)
3341    {
3342      // This should only happen if there were no post-operation add plugins
3343      // registered, which is fine.
3344      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3345    }
3346    else if(finalResult == null)
3347    {
3348      // None of the plugins requested processing to stop so all results
3349      // have equal priority. Just return the last one.
3350      finalResult = result;
3351    }
3352
3353    return finalResult;
3354  }
3355
3356  /**
3357   * Invokes the set of post-operation search plugins that have been configured
3358   * in the Directory Server.
3359   *
3360   * @param  searchOperation  The search operation for which to invoke the
3361   *                          post-operation plugins.
3362   *
3363   * @return  The result of processing the post-operation search plugins.
3364   */
3365  public PluginResult.PostOperation invokePostOperationSearchPlugins(
3366                                   PostOperationSearchOperation searchOperation)
3367  {
3368    PluginResult.PostOperation result = null;
3369    PluginResult.PostOperation finalResult = null;
3370
3371    ArrayList<DirectoryServerPlugin> skippedPlugins =
3372        skippedPreOperationPlugins.remove(searchOperation);
3373
3374    for (DirectoryServerPlugin p : postOperationSearchPlugins)
3375    {
3376      if (isInternalOperation(searchOperation, p)
3377          || isSkipped(skippedPlugins, p))
3378      {
3379        continue;
3380      }
3381
3382      try
3383      {
3384        result = p.doPostOperation(searchOperation);
3385      }
3386      catch (Exception e)
3387      {
3388        logException(searchOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3389      }
3390
3391      if (result == null)
3392      {
3393        logNullResult(searchOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3394      }
3395      else if (!result.continueProcessing())
3396      {
3397        // This plugin requested operation processing to stop. However, we
3398        // still have to invoke all the post op plugins that successfully
3399        // invoked its pre op plugins. We will just take this plugin's
3400        // results as the final result.
3401        finalResult = result;
3402      }
3403    }
3404
3405    if (result == null)
3406    {
3407      // This should only happen if there were no post-operation add plugins
3408      // registered, which is fine.
3409      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3410    }
3411    else if(finalResult == null)
3412    {
3413      // None of the plugins requested processing to stop so all results
3414      // have equal priority. Just return the last one.
3415      finalResult = result;
3416    }
3417
3418    return finalResult;
3419  }
3420
3421  /**
3422   * Invokes the set of post-operation unbind plugins that have been configured
3423   * in the Directory Server.
3424   *
3425   * @param  unbindOperation  The unbind operation for which to invoke the
3426   *                          post-operation plugins.
3427   *
3428   * @return  The result of processing the post-operation unbind plugins.
3429   */
3430  public PluginResult.PostOperation invokePostOperationUnbindPlugins(
3431                                 PostOperationUnbindOperation unbindOperation)
3432  {
3433    PluginResult.PostOperation result = null;
3434    PluginResult.PostOperation finalResult = null;
3435
3436    ArrayList<DirectoryServerPlugin> skippedPlugins =
3437        skippedPreOperationPlugins.remove(unbindOperation);
3438
3439    for (DirectoryServerPlugin p : postOperationUnbindPlugins)
3440    {
3441      if (isInternalOperation(unbindOperation, p)
3442          || isSkipped(skippedPlugins, p))
3443      {
3444        continue;
3445      }
3446
3447      try
3448      {
3449        result = p.doPostOperation(unbindOperation);
3450      }
3451      catch (Exception e)
3452      {
3453        logException(unbindOperation, p, e, ERR_PLUGIN_POST_OPERATION_PLUGIN_EXCEPTION);
3454      }
3455
3456      if (result == null)
3457      {
3458        logNullResult(unbindOperation, p, ERR_PLUGIN_POST_OPERATION_PLUGIN_RETURNED_NULL);
3459      }
3460      else if (!result.continueProcessing())
3461      {
3462        // This plugin requested operation processing to stop. However, we
3463        // still have to invoke all the post op plugins that successfully
3464        // invoked its pre op plugins. We will just take this plugin's
3465        // results as the final result.
3466        finalResult = result;
3467      }
3468    }
3469
3470    if (result == null)
3471    {
3472      // This should only happen if there were no post-operation add plugins
3473      // registered, which is fine.
3474      finalResult = PluginResult.PostOperation.continueOperationProcessing();
3475    }
3476    else if(finalResult == null)
3477    {
3478      // None of the plugins requested processing to stop so all results
3479      // have equal priority. Just return the last one.
3480      finalResult = result;
3481    }
3482
3483    return finalResult;
3484  }
3485
3486  /**
3487   * Invokes the set of post-response add plugins that have been configured in
3488   * the Directory Server.
3489   *
3490   * @param  addOperation  The add operation for which to invoke the
3491   *                       post-response plugins.
3492   *
3493   * @return  The result of processing the post-response add plugins.
3494   */
3495  public PluginResult.PostResponse invokePostResponseAddPlugins(
3496                                       PostResponseAddOperation addOperation)
3497  {
3498    PluginResult.PostResponse result = null;
3499
3500    for (DirectoryServerPlugin p : postResponseAddPlugins)
3501    {
3502      if (isInternalOperation(addOperation, p))
3503      {
3504        continue;
3505      }
3506
3507      try
3508      {
3509        result = p.doPostResponse(addOperation);
3510      }
3511      catch (Exception e)
3512      {
3513        logException(addOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3514      }
3515
3516      if (result == null)
3517      {
3518        logNullResult(addOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3519      }
3520      else if (!result.continuePluginProcessing())
3521      {
3522        return result;
3523      }
3524    }
3525
3526    if (result == null)
3527    {
3528      // This should only happen if there were no post-response add plugins
3529      // registered, which is fine.
3530      result = PluginResult.PostResponse.continueOperationProcessing();
3531    }
3532
3533    return result;
3534  }
3535
3536  /**
3537   * Invokes the set of post-response bind plugins that have been configured in
3538   * the Directory Server.
3539   *
3540   * @param  bindOperation  The bind operation for which to invoke the
3541   *                        post-response plugins.
3542   *
3543   * @return  The result of processing the post-response bind plugins.
3544   */
3545  public PluginResult.PostResponse invokePostResponseBindPlugins(
3546                                       PostResponseBindOperation bindOperation)
3547  {
3548    PluginResult.PostResponse result = null;
3549
3550    for (DirectoryServerPlugin p : postResponseBindPlugins)
3551    {
3552      if (isInternalOperation(bindOperation, p))
3553      {
3554        continue;
3555      }
3556
3557      try
3558      {
3559        result = p.doPostResponse(bindOperation);
3560      }
3561      catch (Exception e)
3562      {
3563        logException(bindOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3564      }
3565
3566      if (result == null)
3567      {
3568        logNullResult(bindOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3569      }
3570      else if (!result.continuePluginProcessing())
3571      {
3572        return result;
3573      }
3574    }
3575
3576    if (result == null)
3577    {
3578      // This should only happen if there were no post-response add plugins
3579      // registered, which is fine.
3580      result = PluginResult.PostResponse.continueOperationProcessing();
3581    }
3582
3583    return result;
3584  }
3585
3586  /**
3587   * Invokes the set of post-response compare plugins that have been configured
3588   * in the Directory Server.
3589   *
3590   * @param  compareOperation  The compare operation for which to invoke the
3591   *                           post-response plugins.
3592   *
3593   * @return  The result of processing the post-response compare plugins.
3594   */
3595  public PluginResult.PostResponse invokePostResponseComparePlugins(
3596      PostResponseCompareOperation compareOperation)
3597  {
3598    PluginResult.PostResponse result = null;
3599
3600    for (DirectoryServerPlugin p : postResponseComparePlugins)
3601    {
3602      if (isInternalOperation(compareOperation, p))
3603      {
3604        continue;
3605      }
3606
3607      try
3608      {
3609        result = p.doPostResponse(compareOperation);
3610      }
3611      catch (Exception e)
3612      {
3613        logException(compareOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3614      }
3615
3616      if (result == null)
3617      {
3618        logNullResult(compareOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3619      }
3620      else if (!result.continuePluginProcessing())
3621      {
3622        return result;
3623      }
3624    }
3625
3626    if (result == null)
3627    {
3628      // This should only happen if there were no post-response add plugins
3629      // registered, which is fine.
3630      result = PluginResult.PostResponse.continueOperationProcessing();
3631    }
3632
3633    return result;
3634  }
3635
3636  /**
3637   * Invokes the set of post-response delete plugins that have been configured
3638   * in the Directory Server.
3639   *
3640   * @param  deleteOperation  The delete operation for which to invoke the
3641   *                          post-response plugins.
3642   *
3643   * @return  The result of processing the post-response delete plugins.
3644   */
3645  public PluginResult.PostResponse invokePostResponseDeletePlugins(
3646                          PostResponseDeleteOperation deleteOperation)
3647  {
3648    PluginResult.PostResponse result = null;
3649
3650    for (DirectoryServerPlugin p : postResponseDeletePlugins)
3651    {
3652      if (isInternalOperation(deleteOperation, p))
3653      {
3654        continue;
3655      }
3656
3657      try
3658      {
3659        result = p.doPostResponse(deleteOperation);
3660      }
3661      catch (Exception e)
3662      {
3663        logException(deleteOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3664      }
3665
3666      if (result == null)
3667      {
3668        logNullResult(deleteOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3669      }
3670      else if (!result.continuePluginProcessing())
3671      {
3672        return result;
3673      }
3674    }
3675
3676    if (result == null)
3677    {
3678      // This should only happen if there were no post-response add plugins
3679      // registered, which is fine.
3680      result = PluginResult.PostResponse.continueOperationProcessing();
3681    }
3682    return result;
3683  }
3684
3685  /**
3686   * Invokes the set of post-response extended plugins that have been configured
3687   * in the Directory Server.
3688   *
3689   * @param  extendedOperation  The extended operation for which to invoke the
3690   *                            post-response plugins.
3691   *
3692   * @return  The result of processing the post-response extended plugins.
3693   */
3694  public PluginResult.PostResponse invokePostResponseExtendedPlugins(
3695                              PostResponseExtendedOperation extendedOperation)
3696  {
3697    PluginResult.PostResponse result = null;
3698
3699    for (DirectoryServerPlugin p : postResponseExtendedPlugins)
3700    {
3701      if (isInternalOperation(extendedOperation, p))
3702      {
3703        continue;
3704      }
3705
3706      try
3707      {
3708        result = p.doPostResponse(extendedOperation);
3709      }
3710      catch (Exception e)
3711      {
3712        logException(extendedOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3713      }
3714
3715      if (result == null)
3716      {
3717        logNullResult(extendedOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3718      }
3719      else if (!result.continuePluginProcessing())
3720      {
3721        return result;
3722      }
3723    }
3724
3725    if (result == null)
3726    {
3727      // This should only happen if there were no post-response add plugins
3728      // registered, which is fine.
3729      result = PluginResult.PostResponse.continueOperationProcessing();
3730    }
3731
3732    return result;
3733  }
3734
3735  /**
3736   * Invokes the set of post-response modify plugins that have been configured
3737   * in the Directory Server.
3738   *
3739   * @param  modifyOperation  The modify operation for which to invoke the
3740   *                          post-response plugins.
3741   *
3742   * @return  The result of processing the post-response modify plugins.
3743   */
3744  public PluginResult.PostResponse invokePostResponseModifyPlugins(
3745                                  PostResponseModifyOperation modifyOperation)
3746  {
3747    PluginResult.PostResponse result = null;
3748
3749    for (DirectoryServerPlugin p : postResponseModifyPlugins)
3750    {
3751      if (isInternalOperation(modifyOperation, p))
3752      {
3753        continue;
3754      }
3755
3756      try
3757      {
3758        result = p.doPostResponse(modifyOperation);
3759      }
3760      catch (Exception e)
3761      {
3762        logException(modifyOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3763      }
3764
3765      if (result == null)
3766      {
3767        logNullResult(modifyOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3768      }
3769      else if (!result.continuePluginProcessing())
3770      {
3771        return result;
3772      }
3773    }
3774
3775    if (result == null)
3776    {
3777      // This should only happen if there were no post-response add plugins
3778      // registered, which is fine.
3779      result = PluginResult.PostResponse.continueOperationProcessing();
3780    }
3781
3782    return result;
3783  }
3784
3785  /**
3786   * Invokes the set of post-response modify DN plugins that have been
3787   * configured in the Directory Server.
3788   *
3789   * @param  modifyDNOperation  The modify DN operation for which to invoke the
3790   *                            post-response plugins.
3791   *
3792   * @return  The result of processing the post-response modify DN plugins.
3793   */
3794  public PluginResult.PostResponse invokePostResponseModifyDNPlugins(
3795                               PostResponseModifyDNOperation modifyDNOperation)
3796  {
3797    PluginResult.PostResponse result = null;
3798
3799    for (DirectoryServerPlugin p : postResponseModifyDNPlugins)
3800    {
3801      if (isInternalOperation(modifyDNOperation, p))
3802      {
3803        continue;
3804      }
3805
3806      try
3807      {
3808        result = p.doPostResponse(modifyDNOperation);
3809      }
3810      catch (Exception e)
3811      {
3812        logException(modifyDNOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3813      }
3814
3815      if (result == null)
3816      {
3817        logNullResult(modifyDNOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3818      }
3819      else if (!result.continuePluginProcessing())
3820      {
3821        return result;
3822      }
3823    }
3824
3825    if (result == null)
3826    {
3827      // This should only happen if there were no post-response add plugins
3828      // registered, which is fine.
3829      result = PluginResult.PostResponse.continueOperationProcessing();
3830    }
3831
3832    return result;
3833  }
3834
3835  /**
3836   * Invokes the set of post-response search plugins that have been configured
3837   * in the Directory Server.
3838   *
3839   * @param  searchOperation  The search operation for which to invoke the
3840   *                          post-response plugins.
3841   *
3842   * @return  The result of processing the post-response search plugins.
3843   */
3844  public PluginResult.PostResponse invokePostResponseSearchPlugins(
3845                                  PostResponseSearchOperation searchOperation)
3846  {
3847    PluginResult.PostResponse result = null;
3848
3849    for (DirectoryServerPlugin p : postResponseSearchPlugins)
3850    {
3851      if (isInternalOperation(searchOperation, p))
3852      {
3853        continue;
3854      }
3855
3856      try
3857      {
3858        result = p.doPostResponse(searchOperation);
3859      }
3860      catch (Exception e)
3861      {
3862        logException(searchOperation, p, e, ERR_PLUGIN_POST_RESPONSE_PLUGIN_EXCEPTION);
3863      }
3864
3865      if (result == null)
3866      {
3867        logNullResult(searchOperation, p, ERR_PLUGIN_POST_RESPONSE_PLUGIN_RETURNED_NULL);
3868      }
3869      else if (!result.continuePluginProcessing())
3870      {
3871        return result;
3872      }
3873    }
3874
3875    if (result == null)
3876    {
3877      // This should only happen if there were no post-response add plugins
3878      // registered, which is fine.
3879      result = PluginResult.PostResponse.continueOperationProcessing();
3880    }
3881
3882    return result;
3883  }
3884
3885  private void logException(PluginOperation op, DirectoryServerPlugin p, Exception e,
3886      Arg5<Object, Object, Number, Number, Object> errorMsg)
3887  {
3888    logger.traceException(e);
3889    logger.error(errorMsg,
3890            op.getOperationType().getOperationName(),
3891            p.getPluginEntryDN(),
3892            op.getConnectionID(), op.getOperationID(),
3893            stackTraceToSingleLineString(e));
3894  }
3895
3896  private void logNullResult(PluginOperation op, DirectoryServerPlugin p,
3897      Arg4<Object, Object, Number, Number> nullResultMsg)
3898  {
3899    logger.error(nullResultMsg,
3900            op.getOperationType().getOperationName(),
3901            p.getPluginEntryDN(),
3902            op.getConnectionID(), op.getOperationID());
3903  }
3904
3905  /**
3906   * Invokes the set of post-synchronization add plugins that have been
3907   * configured in the Directory Server.
3908   *
3909   * @param  addOperation  The add operation for which to invoke the
3910   *                       post-synchronization plugins.
3911   */
3912  public void invokePostSynchronizationAddPlugins(
3913                   PostSynchronizationAddOperation addOperation)
3914  {
3915    for (DirectoryServerPlugin p : postSynchronizationAddPlugins)
3916    {
3917      try
3918      {
3919        p.doPostSynchronization(addOperation);
3920      }
3921      catch (Exception e)
3922      {
3923        logException(addOperation, p, e, ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION);
3924      }
3925    }
3926  }
3927
3928  /**
3929   * Invokes the set of post-synchronization delete plugins that have been
3930   * configured in the Directory Server.
3931   *
3932   * @param  deleteOperation  The delete operation for which to invoke the
3933   *                          post-synchronization plugins.
3934   */
3935  public void invokePostSynchronizationDeletePlugins(
3936                   PostSynchronizationDeleteOperation deleteOperation)
3937  {
3938    for (DirectoryServerPlugin p : postSynchronizationDeletePlugins)
3939    {
3940      try
3941      {
3942        p.doPostSynchronization(deleteOperation);
3943      }
3944      catch (Exception e)
3945      {
3946        logException(deleteOperation, p, e, ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION);
3947      }
3948    }
3949  }
3950
3951  /**
3952   * Invokes the set of post-synchronization modify plugins that have been
3953   * configured in the Directory Server.
3954   *
3955   * @param  modifyOperation  The modify operation for which to invoke the
3956   *                          post-synchronization plugins.
3957   */
3958  public void invokePostSynchronizationModifyPlugins(
3959                   PostSynchronizationModifyOperation modifyOperation)
3960  {
3961    for (DirectoryServerPlugin p : postSynchronizationModifyPlugins)
3962    {
3963      try
3964      {
3965        p.doPostSynchronization(modifyOperation);
3966      }
3967      catch (Exception e)
3968      {
3969        logException(modifyOperation, p, e, ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION);
3970      }
3971    }
3972  }
3973
3974  /**
3975   * Invokes the set of post-synchronization modify DN plugins that have been
3976   * configured in the Directory Server.
3977   *
3978   * @param  modifyDNOperation  The modify DN operation for which to invoke the
3979   *                            post-synchronization plugins.
3980   */
3981  public void invokePostSynchronizationModifyDNPlugins(
3982                   PostSynchronizationModifyDNOperation modifyDNOperation)
3983  {
3984    for (DirectoryServerPlugin p : postSynchronizationModifyDNPlugins)
3985    {
3986      try
3987      {
3988        p.doPostSynchronization(modifyDNOperation);
3989      }
3990      catch (Exception e)
3991      {
3992        logException(modifyDNOperation, p, e, ERR_PLUGIN_POST_SYNCHRONIZATION_PLUGIN_EXCEPTION);
3993      }
3994    }
3995  }
3996
3997  /**
3998   * Invokes the set of search result entry plugins that have been configured
3999   * in the Directory Server.
4000   *
4001   * @param  searchOperation  The search operation for which to invoke the
4002   *                          search result entry plugins.
4003   * @param  searchEntry      The search result entry to be processed.
4004   *
4005   * @return  The result of processing the search result entry plugins.
4006   */
4007  public PluginResult.IntermediateResponse invokeSearchResultEntryPlugins(
4008      SearchEntrySearchOperation searchOperation,
4009      SearchResultEntry searchEntry)
4010  {
4011    PluginResult.IntermediateResponse result = null;
4012
4013    for (DirectoryServerPlugin p : searchResultEntryPlugins)
4014    {
4015      if (isInternalOperation(searchOperation, p))
4016      {
4017        continue;
4018      }
4019
4020      try
4021      {
4022        result = p.processSearchEntry(searchOperation, searchEntry);
4023      }
4024      catch (Exception e)
4025      {
4026        logger.traceException(e);
4027
4028        LocalizableMessage message = ERR_PLUGIN_SEARCH_ENTRY_PLUGIN_EXCEPTION.
4029            get(p.getPluginEntryDN(),
4030                searchOperation.getConnectionID(),
4031                searchOperation.getOperationID(),
4032                searchEntry.getName(),
4033                stackTraceToSingleLineString(e));
4034        logger.error(message);
4035
4036        return PluginResult.IntermediateResponse.stopProcessing(false,
4037            DirectoryServer.getServerErrorResultCode(), message);
4038      }
4039
4040      if (result == null)
4041      {
4042        LocalizableMessage message = ERR_PLUGIN_SEARCH_ENTRY_PLUGIN_RETURNED_NULL.
4043            get(p.getPluginEntryDN(),
4044                searchOperation.getConnectionID(),
4045                searchOperation.getOperationID(),
4046                searchEntry.getName());
4047        logger.error(message);
4048
4049        return PluginResult.IntermediateResponse.stopProcessing(false,
4050            DirectoryServer.getServerErrorResultCode(), message);
4051      }
4052      else if (! result.continuePluginProcessing())
4053      {
4054        return result;
4055      }
4056    }
4057
4058    if (result == null)
4059    {
4060      // This should only happen if there were no search result entry plugins
4061      // registered, which is fine.
4062      result =
4063          PluginResult.IntermediateResponse.continueOperationProcessing(true);
4064    }
4065
4066    return result;
4067  }
4068
4069  /**
4070   * Invokes the set of search result reference plugins that have been
4071   * configured in the Directory Server.
4072   *
4073   * @param  searchOperation  The search operation for which to invoke the
4074   *                          search result reference plugins.
4075   * @param  searchReference  The search result reference to be processed.
4076   *
4077   * @return  The result of processing the search result reference plugins.
4078   */
4079  public PluginResult.IntermediateResponse invokeSearchResultReferencePlugins(
4080      SearchReferenceSearchOperation searchOperation,
4081      SearchResultReference searchReference)
4082  {
4083    PluginResult.IntermediateResponse result = null;
4084
4085    for (DirectoryServerPlugin p : searchResultReferencePlugins)
4086    {
4087      if (isInternalOperation(searchOperation, p))
4088      {
4089        continue;
4090      }
4091
4092      try
4093      {
4094        result = p.processSearchReference(searchOperation, searchReference);
4095      }
4096      catch (Exception e)
4097      {
4098        logger.traceException(e);
4099
4100        LocalizableMessage message = ERR_PLUGIN_SEARCH_REFERENCE_PLUGIN_EXCEPTION.
4101            get(p.getPluginEntryDN(),
4102                searchOperation.getConnectionID(),
4103                searchOperation.getOperationID(),
4104                searchReference.getReferralURLString(),
4105                stackTraceToSingleLineString(e));
4106        logger.error(message);
4107
4108        return PluginResult.IntermediateResponse.stopProcessing(false,
4109            DirectoryServer.getServerErrorResultCode(), message);
4110      }
4111
4112      if (result == null)
4113      {
4114        LocalizableMessage message = ERR_PLUGIN_SEARCH_REFERENCE_PLUGIN_RETURNED_NULL.
4115            get(p.getPluginEntryDN(),
4116                searchOperation.getConnectionID(),
4117                searchOperation.getOperationID(),
4118                searchReference.getReferralURLString());
4119        logger.error(message);
4120
4121        return PluginResult.IntermediateResponse.stopProcessing(false,
4122            DirectoryServer.getServerErrorResultCode(), message);
4123      }
4124      else if (! result.continuePluginProcessing())
4125      {
4126        return result;
4127      }
4128    }
4129
4130    if (result == null)
4131    {
4132      // This should only happen if there were no search result reference
4133      // plugins registered, which is fine.
4134      result =
4135          PluginResult.IntermediateResponse.continueOperationProcessing(true);
4136    }
4137
4138    return result;
4139  }
4140
4141  /**
4142   * Invokes the set of subordinate modify DN plugins that have been configured
4143   * in the Directory Server.
4144   *
4145   * @param  modifyDNOperation  The modify DN operation with which the
4146   *                            subordinate entry is associated.
4147   * @param  oldEntry           The subordinate entry prior to the move/rename
4148   *                            operation.
4149   * @param  newEntry           The subordinate entry after the move/rename
4150   *                            operation.
4151   * @param  modifications      A list into which any modifications made to the
4152   *                            target entry should be placed.
4153   *
4154   * @return  The result of processing the subordinate modify DN plugins.
4155   */
4156  public PluginResult.SubordinateModifyDN invokeSubordinateModifyDNPlugins(
4157              SubordinateModifyDNOperation modifyDNOperation, Entry oldEntry,
4158              Entry newEntry, List<Modification> modifications)
4159  {
4160    PluginResult.SubordinateModifyDN result = null;
4161
4162    for (DirectoryServerPlugin p : subordinateModifyDNPlugins)
4163    {
4164      if (isInternalOperation(modifyDNOperation, p))
4165      {
4166        continue;
4167      }
4168
4169      try
4170      {
4171        result = p.processSubordinateModifyDN(modifyDNOperation, oldEntry,
4172                                               newEntry, modifications);
4173      }
4174      catch (Exception e)
4175      {
4176        logger.traceException(e);
4177
4178        LocalizableMessage message =
4179            ERR_PLUGIN_SUBORDINATE_MODIFY_DN_PLUGIN_EXCEPTION.get(
4180                p.getPluginEntryDN(),
4181                modifyDNOperation.getConnectionID(),
4182                modifyDNOperation.getOperationID(),
4183                stackTraceToSingleLineString(e));
4184        logger.error(message);
4185
4186        return PluginResult.SubordinateModifyDN.stopProcessing(
4187            DirectoryServer.getServerErrorResultCode(), message);
4188      }
4189
4190      if (result == null)
4191      {
4192        LocalizableMessage message =
4193            ERR_PLUGIN_SUBORDINATE_MODIFY_DN_PLUGIN_RETURNED_NULL.get(
4194                        p.getPluginEntryDN(),
4195                        modifyDNOperation.getConnectionID(),
4196                        modifyDNOperation.getOperationID());
4197        logger.error(message);
4198
4199        return PluginResult.SubordinateModifyDN.stopProcessing(
4200            DirectoryServer.getServerErrorResultCode(), message);
4201      }
4202      else if (! result.continuePluginProcessing())
4203      {
4204        return result;
4205      }
4206    }
4207
4208    if (result == null)
4209    {
4210      // This should only happen if there were no subordinate modify DN plugins
4211      // registered, which is fine.
4212      result = PluginResult.SubordinateModifyDN.continueOperationProcessing();
4213    }
4214
4215    return result;
4216  }
4217
4218  /**
4219   * Invokes the set of subordinate delete plugins that have been configured
4220   * in the Directory Server.
4221   *
4222   * @param  deleteOperation  The delete operation with which the
4223   *                          subordinate entry is associated.
4224   * @param  entry            The subordinate entry being deleted.
4225   *
4226   * @return The result of processing the subordinate delete plugins.
4227   */
4228  public PluginResult.SubordinateDelete invokeSubordinateDeletePlugins(
4229              DeleteOperation deleteOperation, Entry entry)
4230  {
4231    PluginResult.SubordinateDelete result = null;
4232
4233    for (DirectoryServerPlugin p : subordinateDeletePlugins)
4234    {
4235      if (deleteOperation.isInternalOperation() && !p.invokeForInternalOperations())
4236      {
4237        continue;
4238      }
4239
4240      try
4241      {
4242        result = p.processSubordinateDelete(deleteOperation, entry);
4243      }
4244      catch (Exception e)
4245      {
4246        logger.traceException(e);
4247
4248        LocalizableMessage message =
4249            ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_EXCEPTION.get(
4250                p.getPluginEntryDN(),
4251                deleteOperation.getConnectionID(),
4252                deleteOperation.getOperationID(),
4253                stackTraceToSingleLineString(e));
4254        logger.error(message);
4255
4256        return PluginResult.SubordinateDelete.stopProcessing(
4257            DirectoryServer.getServerErrorResultCode(), message);
4258      }
4259
4260      if (result == null)
4261      {
4262        LocalizableMessage message =
4263            ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_RETURNED_NULL.get(
4264                        p.getPluginEntryDN(),
4265                        deleteOperation.getConnectionID(),
4266                        deleteOperation.getOperationID());
4267        logger.error(message);
4268
4269        return PluginResult.SubordinateDelete.stopProcessing(
4270            DirectoryServer.getServerErrorResultCode(), message);
4271      }
4272      else if (! result.continuePluginProcessing())
4273      {
4274        return result;
4275      }
4276    }
4277
4278    if (result == null)
4279    {
4280      // This should only happen if there were no subordinate modify DN plugins
4281      // registered, which is fine.
4282      result = PluginResult.SubordinateDelete.continueOperationProcessing();
4283    }
4284
4285    return result;
4286  }
4287
4288  /**
4289   * Invokes the set of intermediate response plugins that have been configured
4290   * in the Directory Server.
4291   *
4292   * @param  intermediateResponse  The intermediate response for which to invoke
4293   *                               the intermediate response plugins.
4294   *
4295   * @return  The result of processing the intermediate response plugins.
4296   */
4297  public PluginResult.IntermediateResponse
4298              invokeIntermediateResponsePlugins(
4299                   IntermediateResponse intermediateResponse)
4300  {
4301    PluginResult.IntermediateResponse result = null;
4302    Operation operation = intermediateResponse.getOperation();
4303
4304    for (DirectoryServerPlugin p : intermediateResponsePlugins)
4305    {
4306      try
4307      {
4308        result = p.processIntermediateResponse(intermediateResponse);
4309      }
4310      catch (Exception e)
4311      {
4312        logger.traceException(e);
4313
4314        LocalizableMessage message = ERR_PLUGIN_INTERMEDIATE_RESPONSE_PLUGIN_EXCEPTION.
4315            get(p.getPluginEntryDN(),
4316                operation.getConnectionID(), operation.getOperationID(),
4317                stackTraceToSingleLineString(e));
4318        logger.error(message);
4319
4320        return PluginResult.IntermediateResponse.stopProcessing
4321            (false, DirectoryServer.getServerErrorResultCode(), message);
4322      }
4323
4324      if (result == null)
4325      {
4326        LocalizableMessage message = ERR_PLUGIN_INTERMEDIATE_RESPONSE_PLUGIN_RETURNED_NULL.
4327            get(p.getPluginEntryDN(),
4328                operation.getConnectionID(), operation.getOperationID());
4329        logger.error(message);
4330
4331        return PluginResult.IntermediateResponse.stopProcessing
4332            (false, DirectoryServer.getServerErrorResultCode(), message);
4333      }
4334      else if (! result.continuePluginProcessing())
4335      {
4336        return result;
4337      }
4338    }
4339
4340    if (result == null)
4341    {
4342      // This should only happen if there were no intermediate response plugins
4343      // registered, which is fine.WARN
4344
4345      result =
4346          PluginResult.IntermediateResponse.continueOperationProcessing(true);
4347    }
4348
4349    return result;
4350  }
4351
4352  @Override
4353  public boolean isConfigurationAddAcceptable(PluginCfg configuration,
4354                                              List<LocalizableMessage> unacceptableReasons)
4355  {
4356    if (configuration.isEnabled())
4357    {
4358      HashSet<PluginType> pluginTypes = getPluginTypes(configuration);
4359
4360      // Get the name of the class and make sure we can instantiate it as a plugin.
4361      String className = configuration.getJavaClass();
4362      try
4363      {
4364        loadPlugin(className, pluginTypes, configuration, false);
4365      }
4366      catch (InitializationException ie)
4367      {
4368        unacceptableReasons.add(ie.getMessageObject());
4369        return false;
4370      }
4371    }
4372
4373    // If we've gotten here, then it's fine.
4374    return true;
4375  }
4376
4377  @Override
4378  public ConfigChangeResult applyConfigurationAdd(
4379                                 PluginCfg configuration)
4380  {
4381    final ConfigChangeResult ccr = new ConfigChangeResult();
4382
4383    configuration.addChangeListener(this);
4384
4385    if (! configuration.isEnabled())
4386    {
4387      return ccr;
4388    }
4389
4390    HashSet<PluginType> pluginTypes = getPluginTypes(configuration);
4391
4392    // Get the name of the class and make sure we can instantiate it as a plugin.
4393    DirectoryServerPlugin<? extends PluginCfg> plugin = null;
4394    String className = configuration.getJavaClass();
4395    try
4396    {
4397      plugin = loadPlugin(className, pluginTypes, configuration, true);
4398    }
4399    catch (InitializationException ie)
4400    {
4401      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
4402      ccr.addMessage(ie.getMessageObject());
4403    }
4404
4405    if (ccr.getResultCode() == ResultCode.SUCCESS)
4406    {
4407      registerPlugin(plugin, configuration.dn(), pluginTypes);
4408    }
4409
4410    return ccr;
4411  }
4412
4413  @Override
4414  public boolean isConfigurationDeleteAcceptable(
4415                      PluginCfg configuration,
4416                      List<LocalizableMessage> unacceptableReasons)
4417  {
4418    // We will always allow plugins to be removed.
4419    return true;
4420  }
4421
4422  @Override
4423  public ConfigChangeResult applyConfigurationDelete(
4424                                 PluginCfg configuration)
4425  {
4426    final ConfigChangeResult ccr = new ConfigChangeResult();
4427
4428    deregisterPlugin(configuration.dn());
4429
4430    return ccr;
4431  }
4432
4433  @Override
4434  public boolean isConfigurationChangeAcceptable(
4435                      PluginCfg configuration,
4436                      List<LocalizableMessage> unacceptableReasons)
4437  {
4438    if (configuration.isEnabled())
4439    {
4440      HashSet<PluginType> pluginTypes = getPluginTypes(configuration);
4441
4442      // Get the name of the class and make sure we can instantiate it as a plugin.
4443      String className = configuration.getJavaClass();
4444      try
4445      {
4446        loadPlugin(className, pluginTypes, configuration, false);
4447      }
4448      catch (InitializationException ie)
4449      {
4450        unacceptableReasons.add(ie.getMessageObject());
4451        return false;
4452      }
4453    }
4454
4455    // If we've gotten here, then it's fine.
4456    return true;
4457  }
4458
4459  @Override
4460  public ConfigChangeResult applyConfigurationChange(
4461                                 PluginCfg configuration)
4462  {
4463    final ConfigChangeResult ccr = new ConfigChangeResult();
4464
4465    // Get the existing plugin if it's already enabled.
4466    DirectoryServerPlugin existingPlugin =
4467         registeredPlugins.get(configuration.dn());
4468
4469    // If the new configuration has the plugin disabled, then deregister it if
4470    // it is enabled, or do nothing if it's already disabled.
4471    if (! configuration.isEnabled())
4472    {
4473      if (existingPlugin != null)
4474      {
4475        deregisterPlugin(configuration.dn());
4476      }
4477
4478      return ccr;
4479    }
4480
4481    // Get the class for the identity mapper.  If the mapper is already enabled,
4482    // then we shouldn't do anything with it although if the class has changed
4483    // then we'll at least need to indicate that administrative action is
4484    // required.  If the mapper is disabled, then instantiate the class and
4485    // initialize and register it as an identity mapper.  Also, update the
4486    // plugin to indicate whether it should be invoked for internal operations.
4487    String className = configuration.getJavaClass();
4488    if (existingPlugin != null)
4489    {
4490      if (! className.equals(existingPlugin.getClass().getName()))
4491      {
4492        ccr.setAdminActionRequired(true);
4493      }
4494
4495      existingPlugin.setInvokeForInternalOperations(
4496                          configuration.isInvokeForInternalOperations());
4497
4498      return ccr;
4499    }
4500
4501    // Create a set of plugin types for the plugin.
4502    HashSet<PluginType> pluginTypes = getPluginTypes(configuration);
4503
4504    DirectoryServerPlugin<? extends PluginCfg> plugin = null;
4505    try
4506    {
4507      plugin = loadPlugin(className, pluginTypes, configuration, true);
4508    }
4509    catch (InitializationException ie)
4510    {
4511      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
4512      ccr.addMessage(ie.getMessageObject());
4513    }
4514
4515    if (ccr.getResultCode() == ResultCode.SUCCESS)
4516    {
4517      registerPlugin(plugin, configuration.dn(), pluginTypes);
4518    }
4519
4520    return ccr;
4521  }
4522
4523  private HashSet<PluginType> getPluginTypes(PluginCfg configuration)
4524  {
4525    HashSet<PluginType> pluginTypes = new HashSet<>();
4526    for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
4527    {
4528      pluginTypes.add(getPluginType(pluginType));
4529    }
4530    return pluginTypes;
4531  }
4532
4533  private void registerSkippedPreOperationPlugins(int i,
4534                                                DirectoryServerPlugin[] plugins,
4535                                                 PluginOperation operation)
4536  {
4537    ArrayList<DirectoryServerPlugin> skippedPlugins = new ArrayList<>(plugins.length - i);
4538    for(int j = i; j < plugins.length; j++)
4539    {
4540      skippedPlugins.add(plugins[j]);
4541    }
4542    skippedPreOperationPlugins.put(operation, skippedPlugins);
4543  }
4544
4545  private void registerSkippedPreOperationPlugin(DirectoryServerPlugin plugin,
4546                                                 PluginOperation operation)
4547  {
4548    ArrayList<DirectoryServerPlugin> existingList =
4549        skippedPreOperationPlugins.get(operation);
4550    if(existingList == null)
4551    {
4552      existingList = new ArrayList<>();
4553    }
4554    existingList.add(plugin);
4555    skippedPreOperationPlugins.put(operation, existingList);
4556  }
4557}