001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.unboundidds.tasks;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.Date;
029    import java.util.LinkedHashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    import com.unboundid.ldap.sdk.Attribute;
034    import com.unboundid.ldap.sdk.Entry;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
040    import static com.unboundid.util.Debug.*;
041    
042    
043    
044    /**
045     * <BLOCKQUOTE>
046     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
047     *   LDAP SDK for Java.  It is not available for use in applications that
048     *   include only the Standard Edition of the LDAP SDK, and is not supported for
049     *   use in conjunction with non-UnboundID products.
050     * </BLOCKQUOTE>
051     * This class defines a Directory Server task that can be used to request that
052     * the server terminate a client connection.  The properties that are available
053     * for use with this type of task include:
054     * <UL>
055     *   <LI>The connection ID for the client connection to be terminated.  This
056     *       is required.</LI>
057     *   <LI>A flag that indicates whether the client connection should be notified
058     *       (e.g., using a notice of disconnection unsolicited notification) before
059     *       the connection is actually terminated.</LI>
060     *   <LI>An optional message that may provide a reason for the disconnect.  If
061     *       this is provided, it will appear in the server log, and it may be
062     *       provided to the client if the client is to be notified before the
063     *       connection is closed.</LI>
064     * </UL>
065    
066     */
067    @NotMutable()
068    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
069    public final class DisconnectClientTask
070           extends Task
071    {
072      /**
073       * The fully-qualified name of the Java class that is used for the disconnect
074       * client task.
075       */
076      static final String DISCONNECT_CLIENT_TASK_CLASS =
077           "com.unboundid.directory.server.tasks.DisconnectClientTask";
078    
079    
080    
081      /**
082       * The name of the attribute used to specify the connection ID of the client
083       * connection to terminate.
084       */
085      private static final String ATTR_CONNECTION_ID =
086           "ds-task-disconnect-connection-id";
087    
088    
089    
090      /**
091       * The name of the attribute used to specify the disconnect message to provide
092       * to the server.
093       */
094      private static final String ATTR_DISCONNECT_MESSAGE =
095           "ds-task-disconnect-message";
096    
097    
098    
099      /**
100       * The name of the attribute used to indicate whether to send a notice of
101       * disconnection message to the client before closing the connection.
102       */
103      private static final String ATTR_NOTIFY_CLIENT =
104           "ds-task-disconnect-notify-client";
105    
106    
107    
108      /**
109       * The name of the object class used in disconnect client task entries.
110       */
111      private static final String OC_DISCONNECT_CLIENT_TASK = "ds-task-disconnect";
112    
113    
114    
115      /**
116       * The task property for the connection ID.
117       */
118      private static final TaskProperty PROPERTY_CONNECTION_ID =
119           new TaskProperty(ATTR_CONNECTION_ID,
120                            INFO_DISPLAY_NAME_DISCONNECT_CONN_ID.get(),
121                            INFO_DESCRIPTION_DISCONNECT_CONN_ID.get(), Long.class,
122                            true, false, false);
123    
124    
125    
126      /**
127       * The task property for the disconnect message.
128       */
129      private static final TaskProperty PROPERTY_DISCONNECT_MESSAGE =
130           new TaskProperty(ATTR_DISCONNECT_MESSAGE,
131                            INFO_DISPLAY_NAME_DISCONNECT_MESSAGE.get(),
132                            INFO_DESCRIPTION_DISCONNECT_MESSAGE.get(), String.class,
133                            false, false, false);
134    
135    
136    
137      /**
138       * The task property for the notify client flag.
139       */
140      private static final TaskProperty PROPERTY_NOTIFY_CLIENT =
141           new TaskProperty(ATTR_NOTIFY_CLIENT,
142                            INFO_DISPLAY_NAME_DISCONNECT_NOTIFY.get(),
143                            INFO_DESCRIPTION_DISCONNECT_NOTIFY.get(), Boolean.class,
144                            false, false, false);
145    
146    
147    
148      /**
149       * The serial version UID for this serializable class.
150       */
151      private static final long serialVersionUID = 6870137048384152893L;
152    
153    
154    
155      // Indicates whether to send the client a notice of disconnection.
156      private final boolean notifyClient;
157    
158      // The connection ID of the connection to disconnect.
159      private final long connectionID;
160    
161      // A disconnect message to provide to the server.
162      private final String disconnectMessage;
163    
164    
165    
166      /**
167       * Creates a new uninitialized disconnect client task instance which should
168       * only be used for obtaining general information about this task, including
169       * the task name, description, and supported properties.  Attempts to use a
170       * task created with this constructor for any other reason will likely fail.
171       */
172      public DisconnectClientTask()
173      {
174        notifyClient      = false;
175        connectionID      = -1;
176        disconnectMessage = null;
177      }
178    
179    
180    
181    
182      /**
183       * Creates a new disconnect client task with the provided information.
184       *
185       * @param  taskID             The task ID to use for this task.  If it is
186       *                            {@code null} then a UUID will be generated for
187       *                            use as the task ID.
188       * @param  connectionID       The connection ID of the client connection to
189       *                            terminate.
190       * @param  disconnectMessage  A message to provide to the server to indicate
191       *                            the reason for the disconnect.  It will be
192       *                            included in the server log, and will be provided
193       *                            to the client if a notice of disconnection is to
194       *                            be sent.  It may be {@code null} if no message
195       *                            is to be provided.
196       * @param  notifyClient       Indicates whether to send a notice of
197       *                            disconnection message to the client before
198       *                            terminating the connection.
199       */
200      public DisconnectClientTask(final String taskID, final long connectionID,
201                                  final String disconnectMessage,
202                                  final boolean notifyClient)
203      {
204        this(taskID, connectionID, disconnectMessage, notifyClient, null, null,
205             null, null, null);
206      }
207    
208    
209    
210      /**
211       * Creates a new add disconnect client task with the provided information.
212       *
213       * @param  taskID                  The task ID to use for this task.  If it is
214       *                                 {@code null} then a UUID will be generated
215       *                                 for use as the task ID.
216       * @param  connectionID            The connection ID of the client connection
217       *                                 to terminate.
218       * @param  disconnectMessage       A message to provide to the server to
219       *                                 indicate the reason for the disconnect.  It
220       *                                 will be included in the server log, and
221       *                                 will be provided to the client if a notice
222       *                                 of disconnection is to be sent.  It may be
223       *                                 {@code null} if no message is to be
224       *                                 provided.
225       * @param  notifyClient            Indicates whether to send a notice of
226       *                                 disconnection message to the client before
227       *                                 terminating the connection.
228       * @param  scheduledStartTime      The time that this task should start
229       *                                 running.
230       * @param  dependencyIDs           The list of task IDs that will be required
231       *                                 to complete before this task will be
232       *                                 eligible to start.
233       * @param  failedDependencyAction  Indicates what action should be taken if
234       *                                 any of the dependencies for this task do
235       *                                 not complete successfully.
236       * @param  notifyOnCompletion      The list of e-mail addresses of individuals
237       *                                 that should be notified when this task
238       *                                 completes.
239       * @param  notifyOnError           The list of e-mail addresses of individuals
240       *                                 that should be notified if this task does
241       *                                 not complete successfully.
242       */
243      public DisconnectClientTask(final String taskID, final long connectionID,
244                  final String disconnectMessage, final boolean notifyClient,
245                  final Date scheduledStartTime, final List<String> dependencyIDs,
246                  final FailedDependencyAction failedDependencyAction,
247                  final List<String> notifyOnCompletion,
248                  final List<String> notifyOnError)
249      {
250        super(taskID, DISCONNECT_CLIENT_TASK_CLASS, scheduledStartTime,
251              dependencyIDs, failedDependencyAction, notifyOnCompletion,
252              notifyOnError);
253    
254        this.connectionID      = connectionID;
255        this.disconnectMessage = disconnectMessage;
256        this.notifyClient      = notifyClient;
257      }
258    
259    
260    
261      /**
262       * Creates a new disconnect client task from the provided entry.
263       *
264       * @param  entry  The entry to use to create this disconnect client task.
265       *
266       * @throws  TaskException  If the provided entry cannot be parsed as a
267       *                         disconnect client task entry.
268       */
269      public DisconnectClientTask(final Entry entry)
270             throws TaskException
271      {
272        super(entry);
273    
274    
275        // Get the connection ID.  It must be present.
276        final String idStr = entry.getAttributeValue(ATTR_CONNECTION_ID);
277        if (idStr == null)
278        {
279          throw new TaskException(ERR_DISCONNECT_TASK_NO_CONN_ID.get(
280                                       getTaskEntryDN()));
281        }
282        else
283        {
284          try
285          {
286            connectionID = Long.parseLong(idStr);
287          }
288          catch (Exception e)
289          {
290            debugException(e);
291            throw new TaskException(ERR_DISCONNECT_TASK_CONN_ID_NOT_LONG.get(
292                                         getTaskEntryDN(), idStr),
293                                    e);
294          }
295        }
296    
297    
298        // Get the disconnect message.  It may be absent.
299        disconnectMessage = entry.getAttributeValue(ATTR_DISCONNECT_MESSAGE);
300    
301    
302        // Determine whether to notify the client.  It may be absent.
303        notifyClient = parseBooleanValue(entry, ATTR_NOTIFY_CLIENT, false);
304      }
305    
306    
307    
308      /**
309       * Creates a new disconnect client task from the provided set of task
310       * properties.
311       *
312       * @param  properties  The set of task properties and their corresponding
313       *                     values to use for the task.  It must not be
314       *                     {@code null}.
315       *
316       * @throws  TaskException  If the provided set of properties cannot be used to
317       *                         create a valid disconnect client task.
318       */
319      public DisconnectClientTask(final Map<TaskProperty,List<Object>> properties)
320             throws TaskException
321      {
322        super(DISCONNECT_CLIENT_TASK_CLASS, properties);
323    
324        boolean notify = false;
325        Long    connID = null;
326        String  msg    = null;
327    
328    
329        for (final Map.Entry<TaskProperty,List<Object>> entry :
330             properties.entrySet())
331        {
332          final TaskProperty p = entry.getKey();
333          final String attrName = p.getAttributeName();
334          final List<Object> values = entry.getValue();
335    
336          if (attrName.equalsIgnoreCase(ATTR_CONNECTION_ID))
337          {
338            connID = parseLong(p, values, connID);
339          }
340          else if (attrName.equalsIgnoreCase(ATTR_DISCONNECT_MESSAGE))
341          {
342            msg = parseString(p, values, msg);
343          }
344          else if (attrName.equalsIgnoreCase(ATTR_NOTIFY_CLIENT))
345          {
346            notify = parseBoolean(p, values, notify);
347          }
348        }
349    
350        if (connID == null)
351        {
352          throw new TaskException(ERR_DISCONNECT_TASK_NO_CONN_ID.get(
353                                       getTaskEntryDN()));
354        }
355    
356        connectionID      = connID;
357        disconnectMessage = msg;
358        notifyClient      = notify;
359      }
360    
361    
362    
363      /**
364       * {@inheritDoc}
365       */
366      @Override()
367      public String getTaskName()
368      {
369        return INFO_TASK_NAME_DISCONNECT_CLIENT.get();
370      }
371    
372    
373    
374      /**
375       * {@inheritDoc}
376       */
377      @Override()
378      public String getTaskDescription()
379      {
380        return INFO_TASK_DESCRIPTION_DISCONNECT_CLIENT.get();
381      }
382    
383    
384    
385      /**
386       * Retrieves the connection ID of the client connection to disconnect.
387       *
388       * @return  The connection ID of the client connection to disconnect.
389       */
390      public long getConnectionID()
391      {
392        return connectionID;
393      }
394    
395    
396    
397      /**
398       * Retrieves the disconnect message to provide to the server, and potentially
399       * to the client.
400       *
401       * @return  The disconnect message, or {@code null} if no message is to be
402       *          provided.
403       */
404      public String getDisconnectMessage()
405      {
406        return disconnectMessage;
407      }
408    
409    
410    
411      /**
412       * Indicates whether to send a notice of disconnection message to the client
413       * before terminating the connection.
414       *
415       * @return  {@code true} if the server should send a notice of disconnection
416       *          to the client, or {@code false} if it should terminate the
417       *          connection without warning.
418       */
419      public boolean notifyClient()
420      {
421        return notifyClient;
422      }
423    
424    
425    
426      /**
427       * {@inheritDoc}
428       */
429      @Override()
430      protected List<String> getAdditionalObjectClasses()
431      {
432        return Arrays.asList(OC_DISCONNECT_CLIENT_TASK);
433      }
434    
435    
436    
437      /**
438       * {@inheritDoc}
439       */
440      @Override()
441      protected List<Attribute> getAdditionalAttributes()
442      {
443        final ArrayList<Attribute> attrs = new ArrayList<Attribute>(3);
444    
445        attrs.add(new Attribute(ATTR_CONNECTION_ID, String.valueOf(connectionID)));
446        attrs.add(new Attribute(ATTR_NOTIFY_CLIENT, String.valueOf(notifyClient)));
447    
448        if (disconnectMessage != null)
449        {
450          attrs.add(new Attribute(ATTR_DISCONNECT_MESSAGE, disconnectMessage));
451        }
452    
453        return attrs;
454      }
455    
456    
457    
458      /**
459       * {@inheritDoc}
460       */
461      @Override()
462      public List<TaskProperty> getTaskSpecificProperties()
463      {
464        final List<TaskProperty> propList = Arrays.asList(
465             PROPERTY_CONNECTION_ID,
466             PROPERTY_DISCONNECT_MESSAGE,
467             PROPERTY_NOTIFY_CLIENT);
468    
469        return Collections.unmodifiableList(propList);
470      }
471    
472    
473    
474      /**
475       * {@inheritDoc}
476       */
477      @Override()
478      public Map<TaskProperty,List<Object>> getTaskPropertyValues()
479      {
480        final LinkedHashMap<TaskProperty,List<Object>> props =
481             new LinkedHashMap<TaskProperty,List<Object>>();
482    
483        props.put(PROPERTY_CONNECTION_ID,
484                  Collections.<Object>unmodifiableList(Arrays.asList(
485                       connectionID)));
486    
487        if (disconnectMessage == null)
488        {
489          props.put(PROPERTY_DISCONNECT_MESSAGE, Collections.emptyList());
490        }
491        else
492        {
493          props.put(PROPERTY_DISCONNECT_MESSAGE,
494                    Collections.<Object>unmodifiableList(Arrays.asList(
495                         disconnectMessage)));
496        }
497    
498        props.put(PROPERTY_NOTIFY_CLIENT,
499                  Collections.<Object>unmodifiableList(Arrays.asList(
500                       notifyClient)));
501    
502        props.putAll(super.getTaskPropertyValues());
503        return Collections.unmodifiableMap(props);
504      }
505    }