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 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.awt.Component;
032import java.awt.GridBagConstraints;
033import java.awt.GridBagLayout;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.HashMap;
040import java.util.HashSet;
041import java.util.LinkedHashSet;
042import java.util.Map;
043import java.util.Set;
044import java.util.SortedSet;
045import java.util.TreeSet;
046
047import javax.swing.Box;
048import javax.swing.DefaultListModel;
049import javax.swing.JButton;
050import javax.swing.JLabel;
051import javax.swing.JList;
052import javax.swing.JPanel;
053import javax.swing.SwingUtilities;
054import javax.swing.event.ListSelectionEvent;
055import javax.swing.event.ListSelectionListener;
056
057import org.forgerock.i18n.LocalizableMessage;
058import org.forgerock.i18n.LocalizableMessageBuilder;
059import org.opends.guitools.controlpanel.datamodel.BackendDescriptor;
060import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor;
061import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
062import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
063import org.opends.guitools.controlpanel.task.DeleteBaseDNAndBackendTask;
064import org.opends.guitools.controlpanel.task.Task;
065import org.opends.guitools.controlpanel.ui.renderer.CustomListCellRenderer;
066import org.opends.guitools.controlpanel.util.Utilities;
067import org.opends.server.types.DN;
068
069/**
070 * The panel displayed when the user clicks on 'Delete Base DN...' in the
071 * browse entries dialog.
072 */
073public class DeleteBaseDNPanel extends StatusGenericPanel
074{
075  private static final long serialVersionUID = 2182662824496761087L;
076
077  /** The list containing the base DNs. */
078  protected JList list;
079  /** Label indicating that no element was found. */
080  protected JLabel lNoElementsFound;
081  /** The main panel. */
082  protected JPanel mainPanel;
083
084  /** Default constructor. */
085  public DeleteBaseDNPanel()
086  {
087    super();
088    createLayout();
089  }
090
091  /** {@inheritDoc} */
092  public LocalizableMessage getTitle()
093  {
094    return INFO_CTRL_PANEL_DELETE_BASE_DN_TITLE.get();
095  }
096
097  /** {@inheritDoc} */
098  public Component getPreferredFocusComponent()
099  {
100    return list;
101  }
102
103  /** {@inheritDoc} */
104  public boolean requiresScroll()
105  {
106    return false;
107  }
108
109  /**
110   * Returns the no backend found label.
111   * @return the no backend found label.
112   */
113  protected LocalizableMessage getNoElementsFoundLabel()
114  {
115    return INFO_CTRL_PANEL_NO_BASE_DNS_FOUND_LABEL.get();
116  }
117
118  /**
119   * Returns the list label.
120   * @return the list label.
121   */
122  protected LocalizableMessage getListLabel()
123  {
124    return INFO_CTRL_PANEL_SELECT_BASE_DNS_TO_DELETE.get();
125  }
126
127  /**
128   * Updates the list of base DNs.
129   * @param newElements the base DNs to be used to update the list.
130   */
131  protected void updateList(final Collection<?> newElements)
132  {
133    final DefaultListModel model = (DefaultListModel)list.getModel();
134    boolean changed = newElements.size() != model.getSize();
135    if (!changed)
136    {
137      int i = 0;
138      for (Object newElement : newElements)
139      {
140        changed = !newElement.equals(model.getElementAt(i));
141        if (changed)
142        {
143          break;
144        }
145        i++;
146      }
147    }
148    if (changed)
149    {
150      SwingUtilities.invokeLater(new Runnable()
151      {
152        /** {@inheritDoc} */
153        public void run()
154        {
155          @SuppressWarnings("deprecation")
156          Object[] s = list.getSelectedValues();
157          Set<Object> selected = new HashSet<>();
158          if (s != null)
159          {
160            Collections.addAll(selected, s);
161          }
162          final DefaultListModel model = (DefaultListModel)list.getModel();
163          model.clear();
164          SortedSet<Integer> indices = new TreeSet<>();
165          int i = 0;
166          for (Object newElement : newElements)
167          {
168            model.addElement(newElement);
169            if (selected.contains(newElement))
170            {
171              indices.add(i);
172            }
173            i ++;
174          }
175          if (!selected.isEmpty())
176          {
177            list.setSelectedIndices(toIntArray(indices));
178          }
179          checkVisibility();
180        }
181
182        private int[] toIntArray(Set<Integer> indices)
183        {
184          int[] result = new int[indices.size()];
185          int i = 0;
186          for (Integer index : indices)
187          {
188            result[i] = index;
189            i++;
190          }
191          return result;
192        }
193      });
194    }
195  }
196
197  private void checkVisibility()
198  {
199    mainPanel.setVisible(list.getModel().getSize() > 0);
200    lNoElementsFound.setVisible(list.getModel().getSize() == 0);
201  }
202
203  /** Creates the layout of the panel (but the contents are not populated here). */
204  private void createLayout()
205  {
206    GridBagConstraints gbc = new GridBagConstraints();
207
208    gbc.gridx = 0;
209    gbc.gridy = 0;
210    gbc.gridwidth = 1;
211    addErrorPane(gbc);
212
213    mainPanel = new JPanel(new GridBagLayout());
214    mainPanel.setOpaque(false);
215    gbc.gridy ++;
216    gbc.weightx = 1.0;
217    gbc.weighty = 1.0;
218    gbc.fill = GridBagConstraints.BOTH;
219    add(mainPanel, gbc);
220
221    gbc.anchor = GridBagConstraints.CENTER;
222    gbc.fill = GridBagConstraints.NONE;
223    lNoElementsFound = Utilities.createPrimaryLabel(getNoElementsFoundLabel());
224    add(lNoElementsFound, gbc);
225    lNoElementsFound.setVisible(false);
226
227    gbc.gridy = 0;
228    gbc.anchor = GridBagConstraints.WEST;
229    gbc.weightx = 0.0;
230    gbc.gridwidth = 2;
231    gbc.weightx = 0.0;
232    gbc.weighty = 0.0;
233    gbc.fill = GridBagConstraints.NONE;
234    JLabel lBaseDNs =
235      Utilities.createPrimaryLabel(getListLabel());
236    mainPanel.add(lBaseDNs, gbc);
237    gbc.insets.top = 5;
238    list = new JList(new DefaultListModel());
239    list.setCellRenderer(new CustomListCellRenderer(list));
240    list.setVisibleRowCount(15);
241    gbc.gridy ++;
242    gbc.gridheight = 3;
243    gbc.gridwidth = 1;
244    gbc.weightx = 1.0;
245    gbc.weighty = 1.0;
246    gbc.fill = GridBagConstraints.BOTH;
247    mainPanel.add(Utilities.createScrollPane(list), gbc);
248
249    JButton selectAllButton = Utilities.createButton(
250        INFO_CTRL_PANEL_SELECT_ALL_BUTTON.get());
251    selectAllButton.addActionListener(new ActionListener()
252    {
253      /** {@inheritDoc} */
254      public void actionPerformed(ActionEvent ev)
255      {
256        int[] indices = new int[list.getModel().getSize()];
257        for (int i=0 ; i<indices.length; i++)
258        {
259          indices[i] = i;
260        }
261        list.setSelectedIndices(indices);
262      }
263    });
264    gbc.gridx ++;
265    gbc.gridheight = 1;
266    gbc.fill = GridBagConstraints.HORIZONTAL;
267    gbc.insets.left = 5;
268    gbc.weightx = 0.0;
269    gbc.weighty = 0.0;
270    mainPanel.add(selectAllButton, gbc);
271
272    gbc.gridy ++;
273    JButton unselectAllButton = Utilities.createButton(
274        INFO_CTRL_PANEL_CLEAR_SELECTION_BUTTON.get());
275    unselectAllButton.addActionListener(new ActionListener()
276    {
277      /** {@inheritDoc} */
278      public void actionPerformed(ActionEvent ev)
279      {
280        list.clearSelection();
281      }
282    });
283    mainPanel.add(unselectAllButton, gbc);
284
285    list.addListSelectionListener(new ListSelectionListener()
286    {
287      /** {@inheritDoc} */
288      public void valueChanged(ListSelectionEvent ev)
289      {
290        checkOKButtonEnable();
291      }
292    });
293
294    gbc.gridy ++;
295    gbc.fill = GridBagConstraints.VERTICAL;
296    gbc.insets.top = 0;
297    gbc.weighty = 1.0;
298    mainPanel.add(Box.createVerticalGlue(), gbc);
299  }
300
301  /** {@inheritDoc} */
302  public void toBeDisplayed(boolean visible)
303  {
304    if (visible)
305    {
306      list.clearSelection();
307      checkVisibility();
308    }
309  }
310
311  /** {@inheritDoc} */
312  protected void checkOKButtonEnable()
313  {
314    setEnabledOK(!list.isSelectionEmpty() && mainPanel.isVisible());
315  }
316
317  /** {@inheritDoc} */
318  public void configurationChanged(ConfigurationChangeEvent ev)
319  {
320    ServerDescriptor desc = ev.getNewDescriptor();
321    final SortedSet<DN> newElements = new TreeSet<>();
322    for (BackendDescriptor backend : desc.getBackends())
323    {
324      if (!backend.isConfigBackend())
325      {
326        for (BaseDNDescriptor baseDN : backend.getBaseDns())
327        {
328          newElements.add(baseDN.getDn());
329        }
330      }
331    }
332    updateList(newElements);
333    updateErrorPaneAndOKButtonIfAuthRequired(desc,
334        isLocal() ?
335            INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_FOR_BASE_DN_DELETE.get() :
336      INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(desc.getHostname()));
337  }
338
339  /** {@inheritDoc} */
340  public void okClicked()
341  {
342    final LinkedHashSet<LocalizableMessage> errors = new LinkedHashSet<>();
343    ProgressDialog progressDialog = new ProgressDialog(
344        Utilities.createFrame(),
345        Utilities.getParentDialog(this), getTitle(), getInfo());
346    @SuppressWarnings("deprecation")
347    Object[] dns = list.getSelectedValues();
348    ArrayList<BaseDNDescriptor> baseDNsToDelete = new ArrayList<>();
349    for (Object o : dns)
350    {
351      DN dn = (DN)o;
352      BaseDNDescriptor baseDN = findBaseDN(dn);
353      if (baseDN != null)
354      {
355        baseDNsToDelete.add(baseDN);
356      }
357    }
358    DeleteBaseDNAndBackendTask newTask = new DeleteBaseDNAndBackendTask(
359        getInfo(), progressDialog, new HashSet<BackendDescriptor>(),
360        baseDNsToDelete);
361    for (Task task : getInfo().getTasks())
362    {
363      task.canLaunch(newTask, errors);
364    }
365    if (errors.isEmpty())
366    {
367      LocalizableMessage confirmationMessage = getConfirmationMessage(baseDNsToDelete);
368      if (displayConfirmationDialog(
369          INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(),
370          confirmationMessage))
371      {
372        launchOperation(newTask,
373            INFO_CTRL_PANEL_DELETING_BASE_DNS_SUMMARY.get(),
374            INFO_CTRL_PANEL_DELETING_BASE_DNS_COMPLETE.get(),
375            INFO_CTRL_PANEL_DELETING_BASE_DNS_SUCCESSFUL.get(),
376            ERR_CTRL_PANEL_DELETING_BASE_DNS_ERROR_SUMMARY.get(),
377            ERR_CTRL_PANEL_DELETING_BASE_DNS_ERROR_DETAILS.get(),
378            null,
379            progressDialog);
380        progressDialog.setVisible(true);
381        Utilities.getParentDialog(this).setVisible(false);
382      }
383    }
384    if (!errors.isEmpty())
385    {
386      displayErrorDialog(errors);
387    }
388  }
389
390  private BaseDNDescriptor findBaseDN(DN dn)
391  {
392    for (BackendDescriptor backend : getInfo().getServerDescriptor().getBackends())
393    {
394      for (BaseDNDescriptor baseDN : backend.getBaseDns())
395      {
396        if (baseDN.getDn().equals(dn))
397        {
398          return baseDN;
399        }
400      }
401    }
402    return null;
403  }
404
405  private LocalizableMessage getConfirmationMessage(
406      Collection<BaseDNDescriptor> baseDNsToDelete)
407  {
408    LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
409    Map<String, Set<BaseDNDescriptor>> hmBackends = new HashMap<>();
410    for (BaseDNDescriptor baseDN : baseDNsToDelete)
411    {
412      String backendID = baseDN.getBackend().getBackendID();
413      Set<BaseDNDescriptor> set = hmBackends.get(backendID);
414      if (set == null)
415      {
416        set = new HashSet<>();
417        hmBackends.put(backendID, set);
418      }
419      set.add(baseDN);
420    }
421    ArrayList<String> indirectBackendsToDelete = new ArrayList<>();
422    for (Set<BaseDNDescriptor> set : hmBackends.values())
423    {
424      BackendDescriptor backend = set.iterator().next().getBackend();
425      if (set.size() == backend.getBaseDns().size())
426      {
427        // All of the suffixes must be deleted.
428        indirectBackendsToDelete.add(backend.getBackendID());
429      }
430    }
431    mb.append(INFO_CTRL_PANEL_CONFIRMATION_DELETE_BASE_DNS_DETAILS.get());
432    for (BaseDNDescriptor baseDN : baseDNsToDelete)
433    {
434      mb.append("<br> - ").append(baseDN.getDn());
435    }
436    if (!indirectBackendsToDelete.isEmpty())
437    {
438      mb.append("<br><br>");
439      mb.append(
440          INFO_CTRL_PANEL_CONFIRMATION_DELETE_BASE_DNS_INDIRECT_DETAILS.get());
441      for (String backendID : indirectBackendsToDelete)
442      {
443        mb.append("<br> - ").append(backendID);
444      }
445    }
446    mb.append("<br><br>");
447    mb.append(INFO_CTRL_PANEL_DO_YOU_WANT_TO_CONTINUE.get());
448    return mb.toMessage();
449  }
450}