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