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