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}