001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027
028package org.opends.guitools.controlpanel.ui;
029
030import static org.forgerock.util.Utils.*;
031import static org.opends.messages.AdminToolMessages.*;
032import static org.opends.server.util.CollectionUtils.*;
033import static org.opends.server.util.StaticUtils.*;
034
035import java.awt.Component;
036import java.awt.GridBagConstraints;
037import java.awt.GridBagLayout;
038import java.awt.event.ActionEvent;
039import java.awt.event.ActionListener;
040import java.awt.event.KeyEvent;
041import java.util.ArrayList;
042import java.util.HashMap;
043import java.util.HashSet;
044import java.util.LinkedHashSet;
045import java.util.List;
046import java.util.Map;
047import java.util.Random;
048import java.util.Set;
049
050import javax.swing.Box;
051import javax.swing.JButton;
052import javax.swing.JLabel;
053import javax.swing.JMenu;
054import javax.swing.JMenuBar;
055import javax.swing.JMenuItem;
056import javax.swing.JPanel;
057import javax.swing.JScrollPane;
058import javax.swing.JTable;
059import javax.swing.JTextArea;
060import javax.swing.ListSelectionModel;
061import javax.swing.SwingUtilities;
062import javax.swing.event.ListSelectionEvent;
063import javax.swing.event.ListSelectionListener;
064
065import org.forgerock.i18n.LocalizableMessage;
066import org.forgerock.i18n.slf4j.LocalizedLogger;
067import org.forgerock.opendj.ldap.ByteString;
068import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
069import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
070import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
071import org.opends.guitools.controlpanel.datamodel.TaskTableModel;
072import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
073import org.opends.guitools.controlpanel.task.CancelTaskTask;
074import org.opends.guitools.controlpanel.task.Task;
075import org.opends.guitools.controlpanel.ui.renderer.TaskCellRenderer;
076import org.opends.guitools.controlpanel.util.ConfigFromFile;
077import org.opends.guitools.controlpanel.util.Utilities;
078import org.opends.server.core.DirectoryServer;
079import org.opends.server.tools.tasks.TaskEntry;
080import org.opends.server.types.Attribute;
081import org.opends.server.types.AttributeBuilder;
082import org.opends.server.types.AttributeType;
083import org.opends.server.types.DN;
084import org.opends.server.types.Entry;
085import org.opends.server.types.ObjectClass;
086import org.opends.server.types.OpenDsException;
087
088/**
089 * The panel displaying the list of scheduled tasks.
090 *
091 */
092public class ManageTasksPanel extends StatusGenericPanel
093{
094  private static final long serialVersionUID = -8034784684412532193L;
095
096  private JLabel lNoTasksFound;
097
098  /**
099   * Remove task button.
100   */
101  private JButton cancelTask;
102  /**
103   * The scroll that contains the list of tasks (actually is a table).
104   */
105  private JScrollPane tableScroll;
106  /**
107   * The table of tasks.
108   */
109  private JTable taskTable;
110
111  /**
112   * The model of the table.
113   */
114  private TaskTableModel tableModel;
115
116  private ManageTasksMenuBar menuBar;
117
118  private MonitoringAttributesViewPanel<LocalizableMessage> operationViewPanel;
119  private GenericDialog operationViewDlg;
120
121  private JPanel detailsPanel;
122  private JLabel noDetailsLabel;
123  /** The panel containing all the labels and values of the details. */
124  private JPanel detailsSubpanel;
125  private JLabel logsLabel;
126  private JScrollPane logsScroll;
127  private JTextArea logs;
128  private JLabel noLogsLabel;
129
130  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
131
132  /** Default constructor. */
133  public ManageTasksPanel()
134  {
135    super();
136    createLayout();
137  }
138
139  /** {@inheritDoc} */
140  public LocalizableMessage getTitle()
141  {
142    return INFO_CTRL_PANEL_TASK_TO_SCHEDULE_LIST_TITLE.get();
143  }
144
145  /** {@inheritDoc} */
146  public boolean requiresScroll()
147  {
148    return false;
149  }
150
151  /** {@inheritDoc} */
152  public GenericDialog.ButtonType getButtonType()
153  {
154    return GenericDialog.ButtonType.CLOSE;
155  }
156
157  /** {@inheritDoc} */
158  public void okClicked()
159  {
160    // Nothing to do, it only contains a close button.
161  }
162
163  /** {@inheritDoc} */
164  @Override
165  public JMenuBar getMenuBar()
166  {
167    if (menuBar == null)
168    {
169      menuBar = new ManageTasksMenuBar(getInfo());
170    }
171    return menuBar;
172  }
173
174  /** {@inheritDoc} */
175  public Component getPreferredFocusComponent()
176  {
177    return taskTable;
178  }
179
180  /**
181   * Returns the selected cancelable tasks in the list.
182   * @param onlyCancelable add only the cancelable tasks.
183   * @return the selected cancelable tasks in the list.
184   */
185  private List<TaskEntry> getSelectedTasks(boolean onlyCancelable)
186  {
187    ArrayList<TaskEntry> tasks = new ArrayList<>();
188    int[] rows = taskTable.getSelectedRows();
189    for (int row : rows)
190    {
191      if (row != -1)
192      {
193        TaskEntry task = tableModel.get(row);
194        if (!onlyCancelable || task.isCancelable())
195        {
196          tasks.add(task);
197        }
198      }
199    }
200    return tasks;
201  }
202
203  /**
204   * Creates the components and lays them in the panel.
205   * @param gbc the grid bag constraints to be used.
206   */
207  private void createLayout()
208  {
209    GridBagConstraints gbc = new GridBagConstraints();
210    gbc.anchor = GridBagConstraints.WEST;
211    gbc.gridx = 0;
212    gbc.gridy = 0;
213    gbc.gridwidth = 2;
214    addErrorPane(gbc);
215
216    gbc.weightx = 0.0;
217    gbc.gridy ++;
218    gbc.anchor = GridBagConstraints.WEST;
219    gbc.weightx = 0.0;
220    gbc.fill = GridBagConstraints.NONE;
221    gbc.gridwidth = 2;
222    gbc.insets.left = 0;
223    gbc.gridx = 0;
224    gbc.gridy = 0;
225    lNoTasksFound = Utilities.createDefaultLabel(
226        INFO_CTRL_PANEL_NO_TASKS_FOUND.get());
227    gbc.gridy ++;
228    gbc.anchor = GridBagConstraints.CENTER;
229    gbc.gridheight = 2;
230    add(lNoTasksFound, gbc);
231    lNoTasksFound.setVisible(false);
232
233    gbc.gridwidth = 1;
234    gbc.weightx = 1.0;
235    gbc.weighty = 1.0;
236    gbc.fill = GridBagConstraints.BOTH;
237    gbc.insets.top = 10;
238    gbc.anchor = GridBagConstraints.NORTHWEST;
239    // Done to provide a good size to the table.
240    tableModel = new TaskTableModel()
241    {
242      private static final long serialVersionUID = 55555512319230987L;
243
244      /**
245       * Updates the table model contents and sorts its contents depending on
246       * the sort options set by the user.
247       */
248      public void forceResort()
249      {
250        Set<String> selectedIds = getSelectedIds();
251        super.forceResort();
252        setSelectedIds(selectedIds);
253      }
254    };
255    tableModel.setData(createDummyTaskList());
256    taskTable =
257      Utilities.createSortableTable(tableModel, new TaskCellRenderer());
258    taskTable.getSelectionModel().setSelectionMode(
259        ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
260    tableScroll = Utilities.createScrollPane(taskTable);
261    add(tableScroll, gbc);
262    updateTableSizes();
263    int height = taskTable.getPreferredScrollableViewportSize().height;
264    add(Box.createVerticalStrut(height), gbc);
265
266    gbc.gridx = 1;
267    gbc.gridheight = 1;
268    gbc.anchor = GridBagConstraints.EAST;
269    gbc.fill = GridBagConstraints.NONE;
270    gbc.weightx = 0.0;
271    gbc.weighty = 0.0;
272    cancelTask = Utilities.createButton(
273        INFO_CTRL_PANEL_CANCEL_TASK_BUTTON_LABEL.get());
274    cancelTask.setOpaque(false);
275    gbc.insets.left = 10;
276    add(cancelTask, gbc);
277
278    gbc.gridy ++;
279    gbc.weighty = 1.0;
280    gbc.fill = GridBagConstraints.VERTICAL;
281    add(Box.createVerticalGlue(), gbc);
282    cancelTask.addActionListener(new ActionListener()
283    {
284      /** {@inheritDoc} */
285      public void actionPerformed(ActionEvent ev)
286      {
287        cancelTaskClicked();
288      }
289    });
290
291    gbc.gridy ++;
292    gbc.gridx = 0;
293    gbc.gridwidth = 2;
294    gbc.weightx = 0.0;
295    gbc.weighty = 0.0;
296    gbc.fill = GridBagConstraints.HORIZONTAL;
297    gbc.anchor = GridBagConstraints.NORTHWEST;
298    gbc.insets.top = 15;
299    gbc.insets.left = 0;
300    logsLabel = Utilities.createDefaultLabel(
301        INFO_CTRL_PANEL_TASK_LOG_LABEL.get());
302    logsLabel.setFont(ColorAndFontConstants.titleFont);
303    add(logsLabel, gbc);
304
305    logs = Utilities.createNonEditableTextArea(LocalizableMessage.EMPTY, 5, 50);
306    logs.setFont(ColorAndFontConstants.defaultFont);
307    gbc.fill = GridBagConstraints.BOTH;
308    gbc.weightx = 1.0;
309    gbc.weighty = 0.7;
310    gbc.gridy ++;
311    gbc.insets.top = 5;
312    logsScroll = Utilities.createScrollPane(logs);
313    add(logsScroll, gbc);
314    height = logs.getPreferredSize().height;
315    add(Box.createVerticalStrut(height), gbc);
316    logsScroll.setVisible(false);
317
318    gbc.anchor = GridBagConstraints.CENTER;
319    gbc.fill = GridBagConstraints.NONE;
320    gbc.weightx = 1.0;
321    gbc.weighty = 1.0;
322    noLogsLabel =
323      Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_TASK_SELECTED.get());
324    add(noLogsLabel, gbc);
325
326    gbc.fill = GridBagConstraints.BOTH;
327    gbc.weightx = 1.0;
328    gbc.weighty = 0.8;
329    gbc.gridy ++;
330    gbc.insets.left = 0;
331    gbc.insets.top = 15;
332    createDetailsPanel();
333    add(detailsPanel, gbc);
334
335    ListSelectionListener listener = new ListSelectionListener()
336    {
337      /** {@inheritDoc} */
338      public void valueChanged(ListSelectionEvent ev)
339      {
340        tableSelected();
341      }
342    };
343    taskTable.getSelectionModel().addListSelectionListener(listener);
344    listener.valueChanged(null);
345  }
346
347  /**
348   * Creates the details panel.
349   *
350   */
351  private void createDetailsPanel()
352  {
353    detailsPanel = new JPanel(new GridBagLayout());
354    detailsPanel.setOpaque(false);
355
356    GridBagConstraints gbc = new GridBagConstraints();
357    gbc.gridx = 1;
358    gbc.gridy = 1;
359    gbc.anchor = GridBagConstraints.NORTHWEST;
360    JLabel label = Utilities.createDefaultLabel(
361        INFO_CTRL_PANEL_TASK_SPECIFIC_DETAILS.get());
362    label.setFont(ColorAndFontConstants.titleFont);
363    detailsPanel.add(label, gbc);
364    gbc.gridy ++;
365    gbc.anchor = GridBagConstraints.CENTER;
366    gbc.fill = GridBagConstraints.NONE;
367    gbc.weightx = 1.0;
368    gbc.weighty = 1.0;
369    noDetailsLabel =
370      Utilities.createDefaultLabel(INFO_CTRL_PANEL_NO_TASK_SELECTED.get());
371    gbc.gridwidth = 2;
372    detailsPanel.add(noDetailsLabel, gbc);
373
374    detailsSubpanel = new JPanel(new GridBagLayout());
375    detailsSubpanel.setOpaque(false);
376    gbc.anchor = GridBagConstraints.NORTHWEST;
377    gbc.fill = GridBagConstraints.BOTH;
378    detailsPanel.add(Utilities.createBorderLessScrollBar(detailsSubpanel), gbc);
379
380    detailsPanel.add(
381        Box.createVerticalStrut(logs.getPreferredSize().height), gbc);
382  }
383
384  /**
385   * Method called when the table is selected.
386   *
387   */
388  private void tableSelected()
389  {
390    List<TaskEntry> tasks = getSelectedTasks(true);
391    cancelTask.setEnabled(!tasks.isEmpty());
392
393    detailsSubpanel.removeAll();
394
395    tasks = getSelectedTasks(false);
396
397    boolean displayContents = false;
398    if (tasks.isEmpty())
399    {
400      noDetailsLabel.setText(INFO_CTRL_PANEL_NO_TASK_SELECTED.get().toString());
401      logsScroll.setVisible(false);
402      noLogsLabel.setText(INFO_CTRL_PANEL_NO_TASK_SELECTED.get().toString());
403      noLogsLabel.setVisible(true);
404    }
405    else if (tasks.size() > 1)
406    {
407      noDetailsLabel.setText(
408          INFO_CTRL_PANEL_MULTIPLE_TASKS_SELECTED.get().toString());
409      logsScroll.setVisible(false);
410      noLogsLabel.setText(
411          INFO_CTRL_PANEL_MULTIPLE_TASKS_SELECTED.get().toString());
412      noLogsLabel.setVisible(true);
413    }
414    else
415    {
416      TaskEntry taskEntry = tasks.iterator().next();
417      Map<LocalizableMessage,List<String>> taskSpecificAttrs =
418        taskEntry.getTaskSpecificAttributeValuePairs();
419      List<LocalizableMessage> lastLogMessages = taskEntry.getLogMessages();
420      if (!lastLogMessages.isEmpty())
421      {
422        StringBuilder sb = new StringBuilder();
423        for (LocalizableMessage msg : lastLogMessages)
424        {
425          if (sb.length() != 0)
426          {
427            sb.append("\n");
428          }
429          sb.append(msg);
430        }
431        logs.setText(sb.toString());
432      }
433      else
434      {
435        logs.setText("");
436      }
437      logsScroll.setVisible(true);
438      noLogsLabel.setVisible(false);
439
440      if (taskSpecificAttrs.isEmpty())
441      {
442        noDetailsLabel.setText(
443            INFO_CTRL_PANEL_NO_TASK_SPECIFIC_DETAILS.get().toString());
444      }
445      else
446      {
447        displayContents = true;
448        GridBagConstraints gbc = new GridBagConstraints();
449        gbc.gridy = 0;
450        gbc.fill = GridBagConstraints.NONE;
451        gbc.anchor = GridBagConstraints.NORTHWEST;
452        gbc.insets.top = 10;
453        for (LocalizableMessage label : taskSpecificAttrs.keySet())
454        {
455          List<String> values = taskSpecificAttrs.get(label);
456          gbc.gridx = 0;
457          gbc.insets.left = 10;
458          gbc.insets.right = 0;
459          detailsSubpanel.add(Utilities.createPrimaryLabel(
460              INFO_CTRL_PANEL_OPERATION_NAME_AS_LABEL.get(label)),
461              gbc);
462
463          gbc.gridx = 1;
464          gbc.insets.right = 10;
465
466          String s = joinAsString("\n", values);
467          detailsSubpanel.add(
468              Utilities.makeHtmlPane(s, ColorAndFontConstants.defaultFont),
469              gbc);
470
471          gbc.gridy ++;
472        }
473        gbc.gridx = 0;
474        gbc.gridwidth = 2;
475        gbc.weightx = 1.0;
476        gbc.weighty = 1.0;
477        gbc.fill = GridBagConstraints.BOTH;
478        detailsSubpanel.add(Box.createGlue(), gbc);
479      }
480    }
481    noDetailsLabel.setVisible(!displayContents);
482    revalidate();
483    repaint();
484  }
485
486  /**
487   * Creates a list with task descriptors.  This is done simply to have a good
488   * initial size for the table.
489   * @return a list with bogus task descriptors.
490   */
491  private Set<TaskEntry> createRandomTasksList()
492  {
493    Set<TaskEntry> list = new HashSet<>();
494    Random r = new Random();
495    int numberTasks = r.nextInt(10);
496    for (int i= 0; i<numberTasks; i++)
497    {
498      CustomSearchResult csr =
499        new CustomSearchResult("cn=mytask"+i+",cn=tasks");
500      String p = "ds-task-";
501      String[] attrNames =
502      {
503          p + "id",
504          p + "class-name",
505          p + "state",
506          p + "scheduled-start-time",
507          p + "actual-start-time",
508          p + "completion-time",
509          p + "dependency-id",
510          p + "failed-dependency-action",
511          p + "log-message",
512          p + "notify-on-error",
513          p + "notify-on-completion",
514          p + "ds-recurring-task-schedule"
515      };
516      String[] values =
517      {
518          "ID",
519          "TheClassName",
520          "TheState",
521          "Schedule Start Time",
522          "Actual Start Time",
523          "Completion Time",
524          "Dependency ID",
525          "Failed Dependency Action",
526          "Log LocalizableMessage.                              Should be pretty long"+
527          "Log LocalizableMessage.                              Should be pretty long"+
528          "Log LocalizableMessage.                              Should be pretty long"+
529          "Log LocalizableMessage.                              Should be pretty long"+
530          "Log LocalizableMessage.                              Should be pretty long",
531          "Notify On Error",
532          "Notify On Completion",
533          "Recurring Task Schedule"
534      };
535      for (int j=0; j < attrNames.length; j++)
536      {
537        Object o = values[j] + r.nextInt();
538        csr.set(attrNames[j], newArrayList(o));
539      }
540      try
541      {
542        Entry entry = getEntry(csr);
543        TaskEntry task = new TaskEntry(entry);
544        list.add(task);
545      }
546      catch (Throwable t)
547      {
548        logger.error(LocalizableMessage.raw("Error getting entry '"+csr.getDN()+"': "+t, t));
549      }
550    }
551    return list;
552  }
553
554  /**
555   * Creates a list with task descriptors.  This is done simply to have a good
556   * initial size for the table.
557   * @return a list with bogus task descriptors.
558   */
559  private Set<TaskEntry> createDummyTaskList()
560  {
561    Set<TaskEntry> list = new HashSet<>();
562    for (int i= 0; i<10; i++)
563    {
564      CustomSearchResult csr =
565        new CustomSearchResult("cn=mytask"+i+",cn=tasks");
566      String p = "ds-task-";
567      String[] attrNames =
568      {
569          p + "id",
570          p + "class-name",
571          p + "state",
572          p + "scheduled-start-time",
573          p + "actual-start-time",
574          p + "completion-time",
575          p + "dependency-id",
576          p + "failed-dependency-action",
577          p + "log-message",
578          p + "notify-on-error",
579          p + "notify-on-completion",
580          p + "ds-recurring-task-schedule"
581      };
582      String[] values =
583      {
584          "A very 29-backup - Sun Mar 29 00:00:00 MET 2009",
585          "A long task type",
586          "A very long task status",
587          "Schedule Start Time",
588          "Actual Start Time",
589          "Completion Time",
590          "Dependency ID",
591          "Failed Dependency Action",
592          "Log LocalizableMessage.                              Should be pretty long\n"+
593          "Log LocalizableMessage.                              Should be pretty long\n"+
594          "Log LocalizableMessage.                              Should be pretty long\n"+
595          "Log LocalizableMessage.                              Should be pretty long\n"+
596          "Log LocalizableMessage.                              Should be pretty long\n",
597          "Notify On Error",
598          "Notify On Completion",
599          "Recurring Task Schedule"
600      };
601      for (int j=0; j < attrNames.length; j++)
602      {
603        Object o = values[j];
604        csr.set(attrNames[j], newArrayList(o));
605      }
606      try
607      {
608        Entry entry = getEntry(csr);
609        TaskEntry task = new TaskEntry(entry);
610        list.add(task);
611      }
612      catch (Throwable t)
613      {
614        logger.error(LocalizableMessage.raw("Error getting entry '"+csr.getDN()+"': "+t, t));
615      }
616    }
617    return list;
618  }
619
620  private void cancelTaskClicked()
621  {
622    ArrayList<LocalizableMessage> errors = new ArrayList<>();
623    ProgressDialog dlg = new ProgressDialog(
624        Utilities.createFrame(),
625        Utilities.getParentDialog(this),
626        INFO_CTRL_PANEL_CANCEL_TASK_TITLE.get(), getInfo());
627    List<TaskEntry> tasks = getSelectedTasks(true);
628    CancelTaskTask newTask = new CancelTaskTask(getInfo(), dlg, tasks);
629    for (Task task : getInfo().getTasks())
630    {
631      task.canLaunch(newTask, errors);
632    }
633    if (errors.isEmpty())
634    {
635      boolean confirmed = displayConfirmationDialog(
636          INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
637          INFO_CTRL_PANEL_CANCEL_TASK_MSG.get());
638      if (confirmed)
639      {
640        launchOperation(newTask,
641            INFO_CTRL_PANEL_CANCELING_TASK_SUMMARY.get(),
642            INFO_CTRL_PANEL_CANCELING_TASK_COMPLETE.get(),
643            INFO_CTRL_PANEL_CANCELING_TASK_SUCCESSFUL.get(),
644            ERR_CTRL_PANEL_CANCELING_TASK_ERROR_SUMMARY.get(),
645            ERR_CTRL_PANEL_CANCELING_TASK_ERROR_DETAILS.get(),
646            null,
647            dlg);
648        dlg.setVisible(true);
649      }
650    }
651  }
652
653
654
655  /**
656   * Gets the Entry object equivalent to the provided CustomSearchResult.
657   * The method assumes that the schema in DirectoryServer has been initialized.
658   * @param csr the search result.
659   * @return the Entry object equivalent to the provided CustomSearchResult.
660   * @throws OpenDsException if there is an error parsing the DN or retrieving
661   * the attributes definition and objectclasses in the schema of the server.
662   * TODO: move somewhere better.
663   */
664  public static Entry getEntry(CustomSearchResult csr) throws OpenDsException
665  {
666    DN dn = DN.valueOf(csr.getDN());
667    Map<ObjectClass,String> objectClasses = new HashMap<>();
668    Map<AttributeType,List<Attribute>> userAttributes = new HashMap<>();
669    Map<AttributeType,List<Attribute>> operationalAttributes = new HashMap<>();
670
671    for (String wholeName : csr.getAttributeNames())
672    {
673      final Attribute attribute = parseAttrDescription(wholeName);
674      final String attrName = attribute.getName();
675      final String lowerName = toLowerCase(attrName);
676
677      // See if this is an objectclass or an attribute.  Then get the
678      // corresponding definition and add the value to the appropriate hash.
679      if (lowerName.equals("objectclass"))
680      {
681        for (Object value : csr.getAttributeValues(attrName))
682        {
683          String ocName = value.toString().trim();
684          String lowerOCName = toLowerCase(ocName);
685
686          ObjectClass objectClass =
687            DirectoryServer.getObjectClass(lowerOCName);
688          if (objectClass == null)
689          {
690            objectClass = DirectoryServer.getDefaultObjectClass(ocName);
691          }
692
693          objectClasses.put(objectClass, ocName);
694        }
695      }
696      else
697      {
698        AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(lowerName, attrName);
699        AttributeBuilder builder = new AttributeBuilder(attribute, true);
700        for (Object value : csr.getAttributeValues(attrName))
701        {
702          ByteString bs;
703          if (value instanceof byte[])
704          {
705            bs = ByteString.wrap((byte[])value);
706          }
707          else
708          {
709            bs = ByteString.valueOfUtf8(value.toString());
710          }
711          builder.add(bs);
712        }
713
714        List<Attribute> attrList = builder.toAttributeList();
715        if (attrType.isOperational())
716        {
717          operationalAttributes.put(attrType, attrList);
718        }
719        else
720        {
721          userAttributes.put(attrType, attrList);
722        }
723      }
724    }
725
726    return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
727  }
728
729  /**
730   * Parse an AttributeDescription (an attribute type name and its
731   * options).
732   * TODO: make this method in LDIFReader public.
733   *
734   * @param attrDescr
735   *          The attribute description to be parsed.
736   * @return A new attribute with no values, representing the
737   *         attribute type and its options.
738   */
739  private static Attribute parseAttrDescription(String attrDescr)
740  {
741    AttributeBuilder builder;
742    int semicolonPos = attrDescr.indexOf(';');
743    if (semicolonPos > 0)
744    {
745      builder = new AttributeBuilder(attrDescr.substring(0, semicolonPos));
746      int nextPos = attrDescr.indexOf(';', semicolonPos + 1);
747      while (nextPos > 0)
748      {
749        String option = attrDescr.substring(semicolonPos + 1, nextPos);
750        if (option.length() > 0)
751        {
752          builder.setOption(option);
753          semicolonPos = nextPos;
754          nextPos = attrDescr.indexOf(';', semicolonPos + 1);
755        }
756      }
757
758      String option = attrDescr.substring(semicolonPos + 1);
759      if (option.length() > 0)
760      {
761        builder.setOption(option);
762      }
763    }
764    else
765    {
766      builder = new AttributeBuilder(attrDescr);
767    }
768
769    if(builder.getAttributeType().getSyntax().isBEREncodingRequired())
770    {
771      //resetting doesn't hurt and returns false.
772      builder.setOption("binary");
773    }
774
775    return builder.toAttribute();
776  }
777
778  /**
779   * The main method to test this panel.
780   * @param args the arguments.
781   */
782  public static void main(String[] args)
783  {
784    // This is a hack to initialize configuration
785    new ConfigFromFile();
786    final ManageTasksPanel p = new ManageTasksPanel();
787    Thread t = new Thread(new Runnable()
788    {
789      public void run()
790      {
791        try
792        {
793          // To let the dialog to be displayed
794          Thread.sleep(5000);
795        }
796        catch (Throwable t)
797        {
798          t.printStackTrace();
799        }
800        while (p.isVisible())
801        {
802          try
803          {
804            SwingUtilities.invokeLater(new Runnable(){
805              public void run()
806              {
807                Set<TaskEntry> tasks = p.createRandomTasksList();
808                p.tableModel.setData(tasks);
809                boolean visible = p.tableModel.getRowCount() > 0;
810                if (visible)
811                {
812                  p.updateTableSizes();
813                }
814                p.tableModel.fireTableDataChanged();
815                p.lNoTasksFound.setVisible(!visible);
816                p.tableScroll.setVisible(visible);
817                p.cancelTask.setVisible(visible);
818              }
819            });
820            Thread.sleep(5000);
821          }
822          catch (Exception ex)
823          {
824            ex.printStackTrace();
825          }
826        }
827      }
828    });
829    t.start();
830
831
832    SwingUtilities.invokeLater(new Runnable(){
833      public void run()
834      {
835        GenericDialog dlg = new GenericDialog(Utilities.createFrame(), p);
836        dlg.setModal(true);
837        dlg.pack();
838        dlg.setVisible(true);
839      }
840    });
841    t = null;
842  }
843
844  /**
845   * Displays a dialog allowing the user to select which operations to display.
846   *
847   */
848  private void operationViewClicked()
849  {
850    if (operationViewDlg == null)
851    {
852      operationViewPanel = MonitoringAttributesViewPanel.createMessageInstance(
853          tableModel.getAllAttributes());
854      operationViewDlg = new GenericDialog(Utilities.getFrame(this),
855          operationViewPanel);
856      Utilities.centerGoldenMean(operationViewDlg,
857          Utilities.getParentDialog(this));
858      operationViewDlg.setModal(true);
859    }
860    operationViewPanel.setSelectedAttributes(
861        tableModel.getDisplayedAttributes());
862    operationViewDlg.setVisible(true);
863    if (!operationViewPanel.isCanceled())
864    {
865      LinkedHashSet<LocalizableMessage> displayedAttributes =
866        operationViewPanel.getAttributes();
867      setAttributesToDisplay(displayedAttributes);
868      updateTableSizes();
869    }
870  }
871
872  /** {@inheritDoc} */
873  public void configurationChanged(ConfigurationChangeEvent ev)
874  {
875    updateErrorPaneIfServerRunningAndAuthRequired(ev.getNewDescriptor(),
876        INFO_CTRL_PANEL_SCHEDULED_TASK_LIST_REQUIRES_SERVER_RUNNING.get(),
877        INFO_CTRL_PANEL_SCHEDULED_TASK_LIST_AUTHENTICATION.get());
878    ServerDescriptor server = ev.getNewDescriptor();
879    final Set<TaskEntry> tasks = server.getTaskEntries();
880    if (haveChanged(tasks))
881    {
882      SwingUtilities.invokeLater(new Runnable()
883      {
884        /** {@inheritDoc} */
885        public void run()
886        {
887          Set<String> selectedIds = getSelectedIds();
888          tableModel.setData(tasks);
889          boolean visible = tableModel.getRowCount() > 0;
890          if (visible)
891          {
892            updateTableSizes();
893            setSelectedIds(selectedIds);
894          }
895          else
896          {
897            logsLabel.setVisible(false);
898            logsScroll.setVisible(false);
899          }
900          tableModel.fireTableDataChanged();
901          lNoTasksFound.setVisible(!visible &&
902              !errorPane.isVisible());
903          tableScroll.setVisible(visible);
904          cancelTask.setVisible(visible);
905          detailsPanel.setVisible(visible);
906        }
907      });
908    }
909  }
910
911  private boolean haveChanged(final Set<TaskEntry> tasks)
912  {
913    if (tableModel.getRowCount() != tasks.size())
914    {
915      return true;
916    }
917    for (int i=0; i<tableModel.getRowCount(); i++)
918    {
919      if (!tasks.contains(tableModel.get(i)))
920      {
921        return true;
922      }
923    }
924    return false;
925  }
926
927  private void updateTableSizes()
928  {
929    Utilities.updateTableSizes(taskTable, 5);
930    Utilities.updateScrollMode(tableScroll, taskTable);
931  }
932
933  private void setAttributesToDisplay(LinkedHashSet<LocalizableMessage> attributes)
934  {
935    Set<String> selectedIds = getSelectedIds();
936    tableModel.setAttributes(attributes);
937    tableModel.forceDataStructureChange();
938    setSelectedIds(selectedIds);
939  }
940
941  /**
942   * The specific menu bar of this panel.
943   *
944   */
945  class ManageTasksMenuBar extends MainMenuBar
946  {
947    private static final long serialVersionUID = 5051878116443370L;
948
949    /**
950     * Constructor.
951     * @param info the control panel info.
952     */
953    public ManageTasksMenuBar(ControlPanelInfo info)
954    {
955      super(info);
956    }
957
958    /** {@inheritDoc} */
959    @Override
960    protected void addMenus()
961    {
962      add(createViewMenuBar());
963      add(createHelpMenuBar());
964    }
965
966    /**
967     * Creates the view menu bar.
968     * @return the view menu bar.
969     */
970    @Override
971    protected JMenu createViewMenuBar()
972    {
973      JMenu menu = Utilities.createMenu(
974          INFO_CTRL_PANEL_CONNECTION_HANDLER_VIEW_MENU.get(),
975          INFO_CTRL_PANEL_CONNECTION_HANDLER_VIEW_MENU_DESCRIPTION.get());
976      menu.setMnemonic(KeyEvent.VK_V);
977      final JMenuItem viewOperations = Utilities.createMenuItem(
978          INFO_CTRL_PANEL_TASK_ATTRIBUTES_VIEW.get());
979      menu.add(viewOperations);
980      viewOperations.addActionListener(new ActionListener()
981      {
982        public void actionPerformed(ActionEvent ev)
983        {
984          operationViewClicked();
985        }
986      });
987      return menu;
988    }
989  }
990
991  private Set<String> getSelectedIds()
992  {
993    Set<String> selectedIds = new HashSet<>();
994    int[] indexes = taskTable.getSelectedRows();
995    if (indexes != null)
996    {
997      for (int index : indexes)
998      {
999        TaskEntry taskEntry = tableModel.get(index);
1000        selectedIds.add(taskEntry.getId());
1001      }
1002    }
1003    return selectedIds;
1004  }
1005
1006  private void setSelectedIds(Set<String> ids)
1007  {
1008    taskTable.getSelectionModel().clearSelection();
1009    for (int i=0; i<tableModel.getRowCount(); i++)
1010    {
1011      TaskEntry taskEntry = tableModel.get(i);
1012      if (ids.contains(taskEntry.getId()))
1013      {
1014        taskTable.getSelectionModel().addSelectionInterval(i, i);
1015      }
1016    }
1017  }
1018}