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 }