1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 package com.sun.dhcpmgr.common; 27 28 import java.io.IOException; 29 import java.text.MessageFormat; 30 import java.util.ArrayList; 31 32 import com.sun.dhcpmgr.bridge.BridgeException; 33 import com.sun.dhcpmgr.bridge.ExistsException; 34 import com.sun.dhcpmgr.data.Network; 35 import com.sun.dhcpmgr.data.ActionError; 36 import com.sun.dhcpmgr.server.DhcpMgr; 37 38 /** 39 * ExportController contains the logic to export the server's data to 40 * a file for later import on either this server or some other server. 41 * Users of this class must implement the Exporter interface in order 42 * to provide communication. 43 * @see Exporter 44 * @see ImportController 45 */ 46 public class ExportController { 47 Exporter exporter; 48 DhcpMgr server; 49 String user; 50 String file; 51 private boolean allNets = false, allMacros = false, allOptions = false; 52 // Statics used to ensure we never have null array references 53 private static final Network [] emptyNets = new Network[0]; 54 private static final String [] emptyMacros = new String[0]; 55 private static final String [] emptyOptions = new String[0]; 56 Network [] networks = emptyNets; 57 String [] macros = emptyMacros; 58 String [] options = emptyOptions; 59 /* 60 * The following constants are heuristics used to estimate the time 61 * required to complete each step of the export process; they're used to 62 * allow a GUI progress meter to pop up and behave relatively correctly. 63 * We can't afford from a performance point of view to be precise as it 64 * would defeat the purpose of our export architecture, so we try to 65 * make sure the user gets at least some idea of where we are in the 66 * process. The *OPS constants indicate an estimate of how expensive 67 * the delete operations are relative to the export operations. YMMV. 68 */ 69 private static final int OPTION_DELETE_OPS = 2; 70 private static final int MACRO_DELETE_OPS = 2; 71 private static final int NETWORK_DELETE_OPS = 2; 72 private static final int DEFAULT_OPTION_COUNT = 10; 73 private static final int DEFAULT_MACRO_COUNT = 50; 74 private static final int DEFAULT_CLIENTS_PER_NET = 150; 75 76 /** 77 * Construct an ExportController with the given Exporter and a 78 * server-side to use to perform the export. Don't pass in "null" for 79 * either argument; the implementation does not validate these inputs. 80 * @param exporter The exporting object 81 * @param server The server which will do the work for us. 82 */ 83 public ExportController(Exporter exporter, DhcpMgr server) { 84 this.exporter = exporter; 85 this.server = server; 86 } 87 88 /** 89 * Set the name of the user performing the export. This is 90 * recorded in the export file for reference at import. 91 * @param user The name of the user 92 */ 93 public void setUser(String user) { 94 this.user = user; 95 } 96 97 /** 98 * Set the name of the file to which to export. 99 * @param file the full pathname of the file to export into 100 */ 101 public void setFile(String file) { 102 this.file = file; 103 } 104 105 /** 106 * Specify that all networks are to be exported 107 */ 108 public void setAllNetworks() { 109 allNets = true; 110 networks = emptyNets; 111 } 112 113 /** 114 * Specify the networks to be exported. 115 * @param networks An array of Network objects which should be exported 116 */ 117 public void setNetworks(Network [] networks) { 118 allNets = false; 119 // Never allow networks to be null 120 if (networks != null) { 121 this.networks = networks; 122 } else { 123 this.networks = emptyNets; 124 } 125 } 126 127 /** 128 * Specify that all macros should be exported. 129 */ 130 public void setAllMacros() { 131 allMacros = true; 132 macros = emptyMacros; 133 } 134 135 /** 136 * Specify the macros to be exported. 137 * @param macros An array of macro names 138 */ 139 public void setMacros(String [] macros) { 140 allMacros = false; 141 // Never allow macros to be null 142 if (macros != null) { 143 this.macros = macros; 144 } else { 145 this.macros = emptyMacros; 146 } 147 } 148 149 /** 150 * Specify that all options should be exported 151 */ 152 public void setAllOptions() { 153 allOptions = true; 154 options = emptyOptions; 155 } 156 157 /** 158 * Specify the options to be exported. 159 * @param options An array of option names 160 */ 161 public void setOptions(String [] options) { 162 allOptions = false; 163 // Never allow options to be null 164 if (options != null) { 165 this.options = options; 166 } else { 167 this.options = emptyOptions; 168 } 169 } 170 171 /** 172 * Perform the actual export. 173 * @param deleteData True if data should be deleted after a successful 174 * export. 175 * @param overwrite True if file should be forcibly overwritten. An 176 * ExistsException will be thrown if the file exists and overwrite is 177 * false. 178 * @return true if the export succeeded, false on failure. 179 */ 180 public boolean exportData(boolean deleteData, boolean overwrite) 181 throws ExistsException { 182 183 Object ref = null; 184 185 // Value to return; default to false for failure 186 boolean retval = false; 187 188 // Default to deleting the file on any errors 189 boolean deleteFile = true; 190 191 if (allNets) { 192 try { 193 // Load network list 194 setNetworks(server.getNetMgr().getNetworks()); 195 } catch (Exception e) { 196 displayException(e, 197 ResourceStrings.getString("exp_err_loading_networks")); 198 return false; 199 } 200 } 201 202 /* 203 * Number of records in the export file is number of networks, plus 1 204 * for options, plus 1 for macros. 205 */ 206 int recCount = networks.length + 2; 207 208 // Calculate total number of estimated ops for progress 209 int optionOps = allOptions ? DEFAULT_OPTION_COUNT : options.length; 210 int optionDelOps = 0; 211 int macroOps = allMacros ? DEFAULT_MACRO_COUNT : macros.length; 212 int macroDelOps = 0; 213 int netOps = DEFAULT_CLIENTS_PER_NET * networks.length; 214 int netDelOps = 0; 215 int totalOps = optionOps + macroOps + netOps; 216 if (totalOps == 0) { 217 // Nothing to export!!! 218 exporter.displayError(ResourceStrings.getString("exp_err_no_data")); 219 return false; 220 } 221 // If user wants to delete, add to number of ops required 222 if (deleteData) { 223 optionDelOps = optionOps * OPTION_DELETE_OPS; 224 macroDelOps = macroOps * MACRO_DELETE_OPS; 225 netDelOps = netOps * NETWORK_DELETE_OPS; 226 totalOps += optionDelOps + macroDelOps + netDelOps; 227 } 228 229 /* 230 * Open the file; catch IO errors, but if we get an ExistsException we 231 * just let that through to the caller, who's supposed to deal with it 232 * appropriately. 233 */ 234 try { 235 ref = server.openExportFile(file, user, recCount, networks, 236 overwrite); 237 // If lock couldn't be obtained, display error and abort 238 if (ref == null) { 239 String [] args = new String[2]; 240 args[0] = server.getDhcpServiceMgr().getServerName(); 241 args[1] = server.getLockPath(); 242 MessageFormat form = new MessageFormat( 243 ResourceStrings.getString("lock_error")); 244 exporter.displayError(form.format(args)); 245 return false; 246 } 247 } catch (IOException e) { 248 displayException(e, ResourceStrings.getString("exp_err_io")); 249 return false; 250 } 251 252 try { 253 254 // Initialize progress with our expected number of operations 255 exporter.initializeProgress(totalOps); 256 int progress = 0; 257 258 // Now export the options 259 if (optionOps != 0) { 260 // Only update progress if we're actually doing something here 261 exporter.updateProgress(progress, 262 ResourceStrings.getString("exp_exporting_options")); 263 } 264 try { 265 server.exportOptions(ref, allOptions, options); 266 } catch (BridgeException e) { 267 displayException(e, 268 ResourceStrings.getString("exp_err_exporting_options")); 269 throw new InterruptedException(); 270 } 271 progress += optionOps; 272 273 if (macroOps != 0) { 274 // Only update progress if we're actually doing something here 275 exporter.updateProgress(progress, 276 ResourceStrings.getString("exp_exporting_macros")); 277 } 278 279 // Now export the macros 280 try { 281 server.exportMacros(ref, allMacros, macros); 282 } catch (BridgeException e) { 283 displayException(e, 284 ResourceStrings.getString("exp_err_exporting_macros")); 285 throw new InterruptedException(); 286 } 287 progress += macroOps; 288 289 // Set up for progress messages 290 MessageFormat form = new MessageFormat( 291 ResourceStrings.getString("exp_exporting_network")); 292 String [] nets = new String[1]; 293 294 // Now export each network in turn 295 for (int i = 0; i < networks.length; ++i) { 296 // Export the network 297 try { 298 nets[0] = networks[i].toString(); 299 exporter.updateProgress(progress, form.format(nets)); 300 server.exportNetwork(ref, networks[i]); 301 progress += DEFAULT_CLIENTS_PER_NET; 302 } catch (BridgeException e) { 303 MessageFormat fmt = new MessageFormat( 304 ResourceStrings.getString("exp_err_exporting_network")); 305 String [] args = new String [] { nets[0], e.getMessage() }; 306 exporter.displayError(fmt.format(args)); 307 throw new InterruptedException(); 308 } 309 } 310 311 // Success; don't delete the file 312 deleteFile = false; 313 314 // If user wants data deleted, too, then do it now 315 if (deleteData) { 316 ActionError [] optionErrs, macroErrs; 317 318 // Delete options 319 if (optionDelOps != 0) { 320 // Only update progress if something to delete 321 exporter.updateProgress(progress, 322 ResourceStrings.getString("exp_deleting_options")); 323 } 324 if (allOptions) { 325 try { 326 optionErrs = server.getDhcptabMgr().deleteAllOptions(); 327 } catch (BridgeException e) { 328 optionErrs = new ActionError[1]; 329 optionErrs[0] = new ActionError( 330 ResourceStrings.getString("all_options"), e); 331 } 332 } else { 333 optionErrs = server.getDhcptabMgr().deleteOptions(options); 334 } 335 progress += optionDelOps; 336 337 // Delete macros 338 if (macroDelOps != 0) { 339 // Only update progress if something to delete 340 exporter.updateProgress(progress, 341 ResourceStrings.getString("exp_deleting_macros")); 342 } 343 if (allMacros) { 344 try { 345 macroErrs = server.getDhcptabMgr().deleteAllMacros(); 346 } catch (BridgeException e) { 347 macroErrs = new ActionError[1]; 348 macroErrs[0] = new ActionError( 349 ResourceStrings.getString("all_macros"), e); 350 } 351 } else { 352 macroErrs = server.getDhcptabMgr().deleteMacros(macros); 353 } 354 progress += macroDelOps; 355 356 // Delete each network in turn 357 form = new MessageFormat( 358 ResourceStrings.getString("exp_deleting_network")); 359 ArrayList errList = new ArrayList(); 360 for (int i = 0; i < networks.length; ++i) { 361 nets[0] = networks[i].toString(); 362 exporter.updateProgress(progress, form.format(nets)); 363 try { 364 server.getNetMgr().deleteNetwork(nets[0], false); 365 } catch (BridgeException e) { 366 errList.add(new ActionError(nets[0], e)); 367 } 368 progress += DEFAULT_CLIENTS_PER_NET * NETWORK_DELETE_OPS; 369 } 370 371 // This update informs caller we're done 372 exporter.updateProgress(progress, 373 ResourceStrings.getString("export_completed")); 374 // Now display whatever errors happened during delete 375 if (optionErrs != null && optionErrs.length > 0) { 376 exporter.displayErrors( 377 ResourceStrings.getString("exp_err_deleting_options"), 378 ResourceStrings.getString("exp_option"), 379 optionErrs); 380 } 381 382 if (macroErrs != null && macroErrs.length > 0) { 383 exporter.displayErrors( 384 ResourceStrings.getString("exp_err_deleting_macros"), 385 ResourceStrings.getString("exp_macro"), 386 macroErrs); 387 } 388 389 if (!errList.isEmpty()) { 390 exporter.displayErrors( 391 ResourceStrings.getString("exp_err_deleting_networks"), 392 ResourceStrings.getString("exp_network"), 393 (ActionError [])errList.toArray(new ActionError[0])); 394 } 395 } 396 retval = true; 397 } catch (InterruptedException e) { 398 /* 399 * User wanted to cancel, or some serious failure occurred; in the 400 * former case no need to display anything, in the latter it 401 * was already displayed before we got here, so just return. 402 */ 403 retval = false; 404 } catch (Exception e) { 405 // I/O error of some sort. Display it before returning. 406 displayException(e, ResourceStrings.getString("exp_err_io")); 407 retval = false; 408 } finally { 409 // Always close before leaving; display any resulting errors 410 try { 411 server.closeExportFile(ref, deleteFile); 412 } catch (IOException e) { 413 displayException(e, 414 ResourceStrings.getString("exp_err_closing_file")); 415 } 416 } 417 return retval; 418 } 419 420 // Utility method to display an error message for an exception 421 private void displayException(Exception e, String format) { 422 MessageFormat form = new MessageFormat(format); 423 String [] args = new String [] { e.getMessage() }; 424 exporter.displayError(form.format(args)); 425 } 426 }