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