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}