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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.quicksetup.installer.ui;
028
029import static org.forgerock.util.Utils.*;
030import static org.opends.messages.QuickSetupMessages.*;
031
032import java.awt.Component;
033import java.awt.GridBagConstraints;
034import java.awt.GridBagLayout;
035import java.awt.Insets;
036import java.util.Comparator;
037import java.util.HashMap;
038import java.util.HashSet;
039import java.util.LinkedHashSet;
040import java.util.Map;
041import java.util.Map.Entry;
042import java.util.Set;
043import java.util.TreeSet;
044
045import javax.swing.Box;
046import javax.swing.JCheckBox;
047import javax.swing.JComboBox;
048import javax.swing.JEditorPane;
049import javax.swing.JLabel;
050import javax.swing.JPanel;
051import javax.swing.JScrollPane;
052import javax.swing.JSeparator;
053import javax.swing.SwingConstants;
054
055import org.forgerock.i18n.LocalizableMessage;
056import org.opends.admin.ads.ADSContext;
057import org.opends.admin.ads.ReplicaDescriptor;
058import org.opends.admin.ads.SuffixDescriptor;
059import org.opends.quicksetup.Constants;
060import org.opends.quicksetup.UserData;
061import org.opends.quicksetup.installer.AuthenticationData;
062import org.opends.quicksetup.installer.SuffixesToReplicateOptions;
063import org.opends.quicksetup.ui.FieldName;
064import org.opends.quicksetup.ui.GuiApplication;
065import org.opends.quicksetup.ui.QuickSetupStepPanel;
066import org.opends.quicksetup.ui.UIFactory;
067import org.opends.quicksetup.ui.UIFactory.IconType;
068import org.opends.quicksetup.util.Utils;
069import org.opends.server.config.ConfigConstants;
070import org.opends.server.tools.BackendTypeHelper;
071import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter;
072
073/**
074 * This class is used to provide a data model for the list of suffixes that we
075 * have to replicate on the new server.
076 */
077public class SuffixesToReplicatePanel extends QuickSetupStepPanel implements Comparator<SuffixDescriptor>
078{
079  private static final long serialVersionUID = -8051367953737385327L;
080
081  private static final Insets SUFFIXES_TO_REPLICATE_INSETS = new Insets(4, 4, 4, 4);
082
083  private final Set<SuffixDescriptor> orderedSuffixes = new TreeSet<>(this);
084  private final Map<String, JCheckBox> hmCheckBoxes = new HashMap<>();
085  private final Map<String, JComboBox<BackendTypeUIAdapter>> backendTypeComboBoxes = new HashMap<>();
086  /**
087   * The display of the server the user provided in the replication options
088   * panel.
089   */
090  private String serverToConnectDisplay;
091
092  private JLabel noSuffixLabel;
093  private Component labelGlue;
094  private JPanel checkBoxPanel;
095  private JScrollPane scroll;
096
097  /**
098   * Constructor of the panel.
099   *
100   * @param application
101   *          Application represented by this panel and used to initialize the
102   *          fields of the panel.
103   */
104  public SuffixesToReplicatePanel(GuiApplication application)
105  {
106    super(application);
107    createComponents();
108  }
109
110  @Override
111  public Object getFieldValue(FieldName fieldName)
112  {
113    if (fieldName == FieldName.SUFFIXES_TO_REPLICATE_OPTIONS)
114    {
115      return SuffixesToReplicateOptions.Type.REPLICATE_WITH_EXISTING_SUFFIXES;
116    }
117    else if (fieldName == FieldName.SUFFIXES_TO_REPLICATE)
118    {
119      return getSelectedSuffixes();
120    }
121    else if (fieldName == FieldName.SUFFIXES_TO_REPLICATE_BACKEND_TYPE)
122    {
123      return getSelectedSuffixBackendTypes();
124    }
125
126    return null;
127  }
128
129  private Set<SuffixDescriptor> getSelectedSuffixes()
130  {
131    Set<SuffixDescriptor> suffixes = new HashSet<>();
132    for (SuffixDescriptor suffix : orderedSuffixes)
133    {
134      if (hmCheckBoxes.get(suffix.getId()).isSelected())
135      {
136        suffixes.add(suffix);
137      }
138    }
139    return suffixes;
140  }
141
142  private Map<String, BackendTypeUIAdapter> getSelectedSuffixBackendTypes()
143  {
144    final Map<String, BackendTypeUIAdapter> backendTypes = new HashMap<>();
145    for (SuffixDescriptor suffix : getSelectedSuffixes())
146    {
147      final String backendName = suffix.getReplicas().iterator().next().getBackendName();
148      backendTypes.put(backendName, (BackendTypeUIAdapter) backendTypeComboBoxes.get(backendName).getSelectedItem());
149    }
150    return backendTypes;
151  }
152
153  @Override
154  public int compare(SuffixDescriptor desc1, SuffixDescriptor desc2)
155  {
156    int result = compareSuffixDN(desc1, desc2);
157    if (result == 0)
158    {
159      result = compareSuffixStrings(desc1, desc2);
160    }
161    return result;
162  }
163
164  @Override
165  protected Component createInputPanel()
166  {
167    JPanel panel = new JPanel(new GridBagLayout());
168    panel.setOpaque(false);
169
170    GridBagConstraints gbc = new GridBagConstraints();
171    gbc.weightx = 1.0;
172    gbc.anchor = GridBagConstraints.NORTHWEST;
173    gbc.fill = GridBagConstraints.HORIZONTAL;
174    gbc.gridwidth = GridBagConstraints.REMAINDER;
175    gbc.insets = UIFactory.getEmptyInsets();
176    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
177    gbc.insets.left = UIFactory.LEFT_INSET_BACKGROUND;
178
179    // Add the checkboxes
180    checkBoxPanel = new JPanel(new GridBagLayout());
181    checkBoxPanel.setOpaque(false);
182    gbc.insets.top = 0;
183    gbc.anchor = GridBagConstraints.NORTH;
184    gbc.weighty = 1.0;
185    gbc.fill = GridBagConstraints.BOTH;
186    scroll = UIFactory.createBorderLessScrollBar(checkBoxPanel);
187    panel.add(scroll, gbc);
188
189    gbc.insets.left = UIFactory.LEFT_INSET_SUBPANEL_SUBORDINATE;
190    gbc.weighty = 0.0;
191    gbc.fill = GridBagConstraints.HORIZONTAL;
192    gbc.insets.top = UIFactory.TOP_INSET_SECONDARY_FIELD;
193    gbc.anchor = GridBagConstraints.NORTHEAST;
194    panel.add(noSuffixLabel, gbc);
195    noSuffixLabel.setVisible(false);
196
197    labelGlue = Box.createVerticalGlue();
198    gbc.fill = GridBagConstraints.VERTICAL;
199    gbc.weighty = 1.0;
200    panel.add(labelGlue, gbc);
201    labelGlue.setVisible(false);
202
203    return panel;
204  }
205
206  @Override
207  protected boolean requiresScroll()
208  {
209    return false;
210  }
211
212  @Override
213  protected LocalizableMessage getInstructions()
214  {
215    return INFO_SUFFIXES_TO_REPLICATE_PANEL_INSTRUCTIONS.get();
216  }
217
218  @Override
219  protected LocalizableMessage getTitle()
220  {
221    return INFO_SUFFIXES_TO_REPLICATE_PANEL_TITLE.get();
222  }
223
224  @Override
225  public void beginDisplay(UserData data)
226  {
227    Set<SuffixDescriptor> array = orderSuffixes(data.getSuffixesToReplicateOptions().getAvailableSuffixes());
228    AuthenticationData authData = data.getReplicationOptions().getAuthenticationData();
229    String newServerDisplay;
230    newServerDisplay = authData != null ? authData.getHostName() + ":" + authData.getPort() : "";
231
232    if (!array.equals(orderedSuffixes) || !newServerDisplay.equals(serverToConnectDisplay))
233    {
234      serverToConnectDisplay = newServerDisplay;
235      Map<String, Boolean> hmOldValues = new HashMap<>();
236      for (String id : hmCheckBoxes.keySet())
237      {
238        hmOldValues.put(id, hmCheckBoxes.get(id).isSelected());
239      }
240      orderedSuffixes.clear();
241      for (SuffixDescriptor suffix : array)
242      {
243        if (!Utils.areDnsEqual(suffix.getDN(), ADSContext.getAdministrationSuffixDN())
244            && !Utils.areDnsEqual(suffix.getDN(), Constants.SCHEMA_DN)
245            && !Utils.areDnsEqual(suffix.getDN(), Constants.REPLICATION_CHANGES_DN))
246        {
247          orderedSuffixes.add(suffix);
248        }
249      }
250      hmCheckBoxes.clear();
251      for (SuffixDescriptor suffix : orderedSuffixes)
252      {
253        JCheckBox cb = UIFactory.makeJCheckBox(LocalizableMessage.raw(suffix.getDN()),
254                INFO_SUFFIXES_TO_REPLICATE_DN_TOOLTIP.get(), UIFactory.TextStyle.SECONDARY_FIELD_VALID);
255        cb.setOpaque(false);
256        Boolean v = hmOldValues.get(suffix.getId());
257        if (v != null)
258        {
259          cb.setSelected(v);
260        }
261        hmCheckBoxes.put(suffix.getId(), cb);
262      }
263      populateCheckBoxPanel();
264    }
265
266    boolean display = !orderedSuffixes.isEmpty();
267    noSuffixLabel.setVisible(!display);
268    labelGlue.setVisible(!display);
269    scroll.setVisible(display);
270  }
271
272  /** Creates the components of this panel. */
273  private void createComponents()
274  {
275    noSuffixLabel = UIFactory.makeJLabel(
276        UIFactory.IconType.NO_ICON, INFO_SUFFIX_LIST_EMPTY.get(), UIFactory.TextStyle.SECONDARY_FIELD_VALID);
277  }
278
279  private void populateCheckBoxPanel()
280  {
281    checkBoxPanel.removeAll();
282    final GridBagConstraints gbc = new GridBagConstraints();
283    gbc.fill = GridBagConstraints.BOTH;
284    gbc.insets = SUFFIXES_TO_REPLICATE_INSETS;
285    gbc.gridy = 0;
286
287    final Map<String, Set<SuffixDescriptor>> backendToSuffixes = getSuffixesForBackends();
288    for (Map.Entry<String, Set<SuffixDescriptor>> backendData : backendToSuffixes.entrySet())
289    {
290      gbc.anchor = GridBagConstraints.LINE_START;
291      gbc.gridwidth = 1;
292      gbc.gridheight = 1;
293      for (SuffixDescriptor suffix : backendData.getValue())
294      {
295        gbc.gridx = 0;
296        final JCheckBox cb = hmCheckBoxes.get(suffix.getId());
297        checkBoxPanel.add(cb, gbc);
298        printReplicaTooltipButton(suffix, gbc);
299        gbc.gridy++;
300      }
301      printBackendInformations(backendData, gbc);
302      printSeparatorLine(gbc);
303    }
304    gbc.weighty = 1.0;
305    gbc.insets = UIFactory.getEmptyInsets();
306    gbc.fill = GridBagConstraints.VERTICAL;
307    checkBoxPanel.add(Box.createVerticalGlue(), gbc);
308  }
309
310  private Map<String, Set<SuffixDescriptor>> getSuffixesForBackends()
311  {
312    final Map<String, Set<SuffixDescriptor>> backendToSuffixes = new HashMap<>();
313    for (SuffixDescriptor suffix : orderedSuffixes)
314    {
315      final String backendName = suffix.getReplicas().iterator().next().getBackendName();
316      if (!backendToSuffixes.containsKey(backendName))
317      {
318        backendToSuffixes.put(backendName, new LinkedHashSet<SuffixDescriptor>());
319      }
320      backendToSuffixes.get(backendName).add(suffix);
321    }
322
323    return backendToSuffixes;
324  }
325
326  private void printReplicaTooltipButton(SuffixDescriptor suffix, GridBagConstraints gbc)
327  {
328    gbc.gridx++;
329    String imageDesc = "<html>";
330    for (ReplicaDescriptor replica : suffix.getReplicas())
331    {
332      imageDesc += getServerDisplay(replica) + "<br>";
333    }
334    final int entriesNb = suffix.getReplicas().iterator().next().getEntries();
335    final LocalizableMessage entriesNbToPrint = getNumberOfEntriesMsg(entriesNb);
336    imageDesc += entriesNbToPrint + "</html>";
337
338    final JLabel helpReplicasTooltip = new JLabel();
339    helpReplicasTooltip.setIcon(UIFactory.getImageIcon(IconType.HELP_MEDIUM));
340    helpReplicasTooltip.setToolTipText(imageDesc);
341    UIFactory.setTextStyle(helpReplicasTooltip, UIFactory.TextStyle.SECONDARY_FIELD_VALID);
342    checkBoxPanel.add(helpReplicasTooltip, gbc);
343  }
344
345  private LocalizableMessage getNumberOfEntriesMsg(int nEntries)
346  {
347    if (nEntries > 0)
348    {
349      return INFO_SUFFIX_LIST_REPLICA_DISPLAY_ENTRIES.get(nEntries);
350    }
351    else if (nEntries == 0)
352    {
353      return INFO_SUFFIX_LIST_REPLICA_DISPLAY_NO_ENTRIES.get();
354    }
355    else
356    {
357      return INFO_SUFFIX_LIST_REPLICA_DISPLAY_ENTRIES_NOT_AVAILABLE.get();
358    }
359  }
360
361  private void printBackendInformations(Map.Entry<String, Set<SuffixDescriptor>> backendData, GridBagConstraints gbc)
362  {
363    final int nbSuffixForBackend = backendData.getValue().size();
364    gbc.gridy -= nbSuffixForBackend;
365    printBackendNameText(backendData, gbc);
366    printComboBoxForSuffix(backendData.getValue().iterator().next(), gbc);
367    gbc.gridy += nbSuffixForBackend;
368  }
369
370  private void printSeparatorLine(GridBagConstraints gbc)
371  {
372    gbc.gridwidth = gbc.gridx;
373    gbc.gridx = 0;
374    checkBoxPanel.add(new JSeparator(SwingConstants.HORIZONTAL), gbc);
375    gbc.gridy++;
376  }
377
378  private void printBackendNameText(Entry<String, Set<SuffixDescriptor>> backendData, GridBagConstraints gbc)
379  {
380    gbc.gridx++;
381    final JEditorPane backendNameText = UIFactory.makeTextPane(
382        LocalizableMessage.raw(backendData.getKey()), UIFactory.TextStyle.SECONDARY_FIELD_VALID);
383    backendNameText.setToolTipText(INFO_REPLICATED_SUFFIXES_BACKEND_NAME_TOOLTIP.get().toString());
384    gbc.anchor = GridBagConstraints.CENTER;
385    checkBoxPanel.add(backendNameText, gbc);
386  }
387
388  private void printComboBoxForSuffix(SuffixDescriptor suffix, GridBagConstraints gbc)
389  {
390    gbc.gridx++;
391    gbc.anchor = GridBagConstraints.LINE_END;
392    gbc.insets = UIFactory.getEmptyInsets();
393    final ReplicaDescriptor backendData = suffix.getReplicas().iterator().next();
394    final JComboBox<BackendTypeUIAdapter> backendTypeComboBox =
395        new JComboBox<>(new BackendTypeHelper().getBackendTypeUIAdaptors());
396    backendTypeComboBox.setToolTipText(INFO_REPLICATED_SUFFIXES_BACKEND_TYPE_TOOLTIP.get().toString());
397    final Set<String> objectClasses = backendData.getObjectClasses();
398    backendTypeComboBox.setSelectedItem(getBackendTypeFromObjectClasses(objectClasses));
399    backendTypeComboBoxes.put(backendData.getBackendName(), backendTypeComboBox);
400    checkBoxPanel.add(backendTypeComboBox, gbc);
401    gbc.insets = SUFFIXES_TO_REPLICATE_INSETS;
402  }
403
404  /**
405   * Returns the concrete backend type corresponding to the provided object
406   * classes. If the backend is not found, returns the default backend of this
407   * server configuration.
408   *
409   * @param objectClasses
410   *          The set of object class with one should be a concrete backend
411   *          type.
412   * @return The concrete backend type corresponding to object classes or this
413   *         server default one.
414   */
415  private BackendTypeUIAdapter getBackendTypeFromObjectClasses(Set<String> objectClasses)
416  {
417    for (String objectClass : objectClasses)
418    {
419      BackendTypeUIAdapter adapter =
420          BackendTypeHelper.getBackendTypeAdapter(objectClass.replace(ConfigConstants.NAME_PREFIX_CFG, ""));
421      if (adapter != null)
422      {
423        return adapter;
424      }
425    }
426
427    return new BackendTypeHelper().getBackendTypeUIAdaptors()[0];
428  }
429
430  private String getSuffixString(SuffixDescriptor desc)
431  {
432    Set<String> replicaDisplays = new TreeSet<>();
433    for (ReplicaDescriptor rep : desc.getReplicas())
434    {
435      replicaDisplays.add(getServerDisplay(rep));
436    }
437    return joinAsString("\n", replicaDisplays);
438  }
439
440  private String getServerDisplay(ReplicaDescriptor replica)
441  {
442    final boolean isServerToConnect = replica.getServer().getHostPort(false).equalsIgnoreCase(serverToConnectDisplay);
443    return isServerToConnect ? serverToConnectDisplay : replica.getServer().getHostPort(true);
444  }
445
446  private Set<SuffixDescriptor> orderSuffixes(Set<SuffixDescriptor> suffixes)
447  {
448    Set<SuffixDescriptor> ordered = new TreeSet<>(this);
449    ordered.addAll(suffixes);
450
451    return ordered;
452  }
453
454  private int compareSuffixDN(SuffixDescriptor desc1, SuffixDescriptor desc2)
455  {
456    return desc1.getDN().compareTo(desc2.getDN());
457  }
458
459  private int compareSuffixStrings(SuffixDescriptor desc1, SuffixDescriptor desc2)
460  {
461    return getSuffixString(desc1).compareTo(getSuffixString(desc2));
462  }
463
464}