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