00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package org.objectweb.cjdbc.controller.backup;
00026
00027 import java.io.BufferedReader;
00028 import java.io.BufferedWriter;
00029 import java.io.File;
00030 import java.io.FileNotFoundException;
00031 import java.io.FileReader;
00032 import java.io.FileWriter;
00033 import java.io.IOException;
00034 import java.io.PrintStream;
00035 import java.util.ArrayList;
00036 import java.util.HashMap;
00037 import java.util.Hashtable;
00038 import java.util.Iterator;
00039 import java.util.Vector;
00040
00041 import org.apache.log4j.Category;
00042 import org.apache.log4j.Priority;
00043 import org.objectweb.cjdbc.common.exceptions.BackupException;
00044 import org.objectweb.cjdbc.common.exceptions.OctopusException;
00045 import org.objectweb.cjdbc.common.i18n.Translate;
00046 import org.objectweb.cjdbc.common.jmx.notifications.CjdbcNotificationList;
00047 import org.objectweb.cjdbc.common.log.Trace;
00048 import org.objectweb.cjdbc.common.shared.BackendState;
00049 import org.objectweb.cjdbc.common.shared.BackupListener;
00050 import org.objectweb.cjdbc.common.sql.schema.DatabaseTable;
00051 import org.objectweb.cjdbc.common.util.LoggingOutputStream;
00052 import org.objectweb.cjdbc.common.util.ZipMe;
00053 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00054 import org.objectweb.cjdbc.controller.connection.AbstractConnectionManager;
00055 import org.webdocwf.util.loader.Loader;
00056 import org.webdocwf.util.loader.generator.LoaderGenerator;
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 public class Octopus extends Thread
00067 {
00068 private static final String OCTOPUS_INCLUDE_HREF = "<include href=\"sql/";
00069 static Trace logger;
00070
00071 static
00072 {
00073 String cjdbcHome = System.getProperty("cjdbc.home");
00074 if (cjdbcHome != null)
00075 System.setProperty("OCTOPUS_HOME", cjdbcHome + File.separator + "lib"
00076 + File.separator + "octopus" + File.separator + "xml");
00077 }
00078 static final String TYPE_CSV = "csv";
00079 static final String COPY_MODE = "copy";
00080 static final String OVERRIDE_MODE = "sync";
00081 static final String GENERATE_DOC = "sql";
00082 static final String OCTOPUS_LOG_FILE = "octopusLog.txt";
00083 boolean cleanOctopus = false;
00084 boolean zipOctopus = true;
00085
00086 static final int ZIP_MODE_CREATE = 0;
00087 static final int ZIP_MODE_EXPAND = 1;
00088
00089 static final int OCTOPUS_MODE_FROM_CSV = 0;
00090 static final int OCTOPUS_MODE_TO_CSV = 1;
00091
00092
00093 public static final int MODE_BACKUP = 0;
00094
00095 public static final int MODE_RECOVERY = 1;
00096
00097 private DatabaseBackend database;
00098
00099 private String user;
00100 private String password;
00101 private String dumpName;
00102 private String octopusDir;
00103 private String csvDir;
00104 private String loaderJobXmlFile;
00105 private ArrayList tables;
00106 private static PrintStream stream;
00107
00108 static String mainDirectory = ".." + File.separator
00109 + "backup";
00110
00111 private boolean backupMode;
00112
00113 private Exception runException;
00114
00115 private BackupListener listener;
00116
00117
00118
00119
00120
00121
00122
00123
00124 public Octopus(DatabaseBackend database, String dumpName)
00125 throws BackupException
00126 {
00127 logger = Trace.getLogger(this.getClass().getName());
00128
00129 this.database = database;
00130 this.dumpName = dumpName;
00131 this.tables = null;
00132
00133
00134 getUserLogin();
00135 setPaths();
00136 }
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147 public Octopus(DatabaseBackend database, String dumpName, boolean backupMode)
00148 throws BackupException
00149 {
00150 this(database, dumpName);
00151 this.backupMode = backupMode;
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 public Octopus(DatabaseBackend database, String dumpName, ArrayList tables,
00164 boolean backupMode) throws BackupException
00165 {
00166 this(database, dumpName, backupMode);
00167 this.tables = tables;
00168 }
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178 public Octopus(DatabaseBackend database, String dumpName, ArrayList tables)
00179 throws BackupException
00180 {
00181 this(database, dumpName);
00182 this.tables = tables;
00183 }
00184
00185
00186
00187
00188
00189
00190
00191 public void backup() throws BackupException, OctopusException
00192 {
00193 sanityCheck();
00194 prepareOctopus(OCTOPUS_MODE_TO_CSV);
00195 launchOctopus();
00196 if (zipOctopus)
00197 zipOctopus(ZIP_MODE_CREATE);
00198 if (cleanOctopus)
00199 cleanUp();
00200 }
00201
00202
00203
00204
00205
00206
00207
00208 public void restore() throws BackupException, OctopusException
00209 {
00210 if (zipOctopus)
00211 zipOctopus(ZIP_MODE_EXPAND);
00212 prepareOctopus(OCTOPUS_MODE_FROM_CSV);
00213 setOctopusLoaderJob();
00214 launchOctopus();
00215 if (cleanOctopus)
00216 cleanUp();
00217 }
00218
00219
00220
00221
00222 public void run()
00223 {
00224 if (backupMode)
00225 try
00226 {
00227 database.setState(BackendState.BACKUPING);
00228 backup();
00229 database.notifyJmx(CjdbcNotificationList.VIRTUALDATABASE_NEW_DUMP_LIST);
00230
00231
00232 database.setState(BackendState.DISABLED);
00233 if (listener != null)
00234 listener.success(database.getName());
00235 }
00236 catch (Exception e)
00237 {
00238 database.setState(BackendState.UNKNOWN);
00239 runException = e;
00240 if (listener != null)
00241 listener.failure(database.getName(), e);
00242 }
00243 else
00244 {
00245 try
00246 {
00247 database.setState(BackendState.RECOVERING);
00248 restore();
00249
00250 database.setLastKnownCheckpoint(dumpName);
00251 database.setState(BackendState.DISABLED);
00252 if (listener != null)
00253 listener.success(database.getName());
00254 }
00255 catch (Exception e1)
00256 {
00257 database.setState(BackendState.UNKNOWN);
00258 runException = e1;
00259 if (listener != null)
00260 listener.failure(database.getName(), e1);
00261 }
00262 }
00263
00264
00265 if (listener != null)
00266 {
00267 synchronized (listener)
00268 {
00269 listener.notifyAll();
00270 }
00271 }
00272 }
00273
00274
00275
00276
00277
00278
00279 public void sanityCheck() throws BackupException
00280 {
00281 boolean notEnabled = false, noPendingRequests = false;
00282 if (database.isReadEnabled() == false)
00283 notEnabled = true;
00284
00285 Vector pending = database.getPendingRequests();
00286 int size = pending.size();
00287 noPendingRequests = (size == 0) ? true : false;
00288 if (logger.isDebugEnabled())
00289 {
00290 if (!noPendingRequests)
00291 {
00292 for (int i = 0; i < size; i++)
00293 {
00294 logger.debug("Pending:" + pending.get(i).toString());
00295 }
00296 }
00297 logger.debug("Pending Requests:" + database.getPendingRequests().size());
00298 logger.debug("Read enabled:" + database.isReadEnabled());
00299 logger.debug("Write enabled:" + database.isWriteEnabled());
00300 }
00301 if (!(notEnabled && noPendingRequests))
00302 throw new BackupException(Translate.get("backend.not.ready.for.backup"));
00303 }
00304
00305
00306
00307
00308
00309
00310
00311 public boolean deleteDir(File dir)
00312 {
00313 if (dir.isDirectory())
00314 {
00315 String[] children = dir.list();
00316 for (int i = 0; i < children.length; i++)
00317 {
00318 boolean success = deleteDir(new File(dir, children[i]));
00319 if (!success)
00320 {
00321 return false;
00322 }
00323 }
00324 }
00325
00326 return dir.delete();
00327 }
00328
00329 final Hashtable getOctopusStrings(DatabaseBackend database)
00330 throws BackupException
00331 {
00332 Hashtable strings = new Hashtable(3);
00333 String type = getDbType(database.getURL());
00334 String octopusPrefixUrl = OctopusConstants.getUrlPrefix(type);
00335 String databaseUrl = database.getURL();
00336
00337 strings.put("octopusType", OctopusConstants.getOctopusType(type));
00338 strings.put("octopusDriver", OctopusConstants.getOctopusDriver(type));
00339 strings.put("octopusUrl", databaseUrl.substring(octopusPrefixUrl.length()));
00340
00341 return strings;
00342 }
00343
00344 final void generateMetadata(String sourceType, String sourceUrl,
00345 String sourceDriver, String sourceUser, String sourcePassword,
00346 String targetType, String targetDriver, String targetUrl,
00347 String targetUser, String targetPassword, boolean backup)
00348 throws OctopusException
00349 {
00350 if (logger.isDebugEnabled())
00351 logger.debug("### Generating metadata ###");
00352 callOctopusLoader(sourceType, sourceUrl, sourceDriver, sourceUser,
00353 sourcePassword, targetType, targetDriver, targetUrl, targetUser,
00354 targetPassword, backup, true);
00355 }
00356
00357
00358
00359
00360 final void prepareOctopus(String sourceType, String sourceUrl,
00361 String sourceDriver, String sourceUser, String sourcePassword,
00362 String targetType, String targetDriver, String targetUrl,
00363 String targetUser, String targetPassword, boolean backup)
00364 throws OctopusException
00365 {
00366 if (logger.isDebugEnabled())
00367 logger.debug("### Generating loader job ###");
00368 callOctopusLoader(sourceType, sourceUrl, sourceDriver, sourceUser,
00369 sourcePassword, targetType, targetDriver, targetUrl, targetUser,
00370 targetPassword, backup, false);
00371 }
00372
00373
00374
00375
00376 final void callOctopusLoader(String sourceType, String sourceUrl,
00377 String sourceDriver, String sourceUser, String sourcePassword,
00378 String targetType, String targetDriver, String targetUrl,
00379 String targetUser, String targetPassword, boolean backup,
00380 boolean generateAllVendors) throws OctopusException
00381 {
00382 try
00383 {
00384 if (logger.isDebugEnabled())
00385 {
00386 logger.debug("Source Type:" + sourceType);
00387 logger.debug("Source Driver:" + sourceDriver);
00388 logger.debug("Source URL :" + sourceUrl);
00389 logger.debug("Source User :" + sourceUser);
00390 logger.debug("Target Type:" + targetType);
00391 logger.debug("Target Driver:" + targetDriver);
00392 logger.debug("Target URL:" + targetUrl);
00393 logger.debug("Target User :" + targetUser);
00394 if (backup)
00395 logger.debug("Backup Mode");
00396 else
00397 logger.debug("Restore Mode");
00398 logger.debug("Generate SQL for all vendors :" + generateAllVendors);
00399 }
00400 LoaderGenerator loader = new LoaderGenerator(sourceType,
00401 sourceUrl,
00402 COPY_MODE,
00403 octopusDir,
00404 sourceDriver,
00405 targetDriver,
00406 targetUrl,
00407 targetType,
00408 sourceUser,
00409 sourcePassword,
00410 targetUser,
00411 targetPassword,
00412 "",
00413 "org.webdoc.util.loader",
00414 "true",
00415 "true",
00416 "true",
00417 "true",
00418 "true",
00419 "true",
00420 String.valueOf(generateAllVendors),
00421
00422
00423
00424
00425 String.valueOf(!generateAllVendors),
00426
00427 "false",
00428 String.valueOf(!generateAllVendors),
00429
00430
00431 String.valueOf(!backup),
00432 null,
00433
00434 null
00435 );
00436 loader.generate();
00437 }
00438 catch (Exception e)
00439 {
00440 throw new OctopusException(e);
00441 }
00442 }
00443
00444 private String[] convertTablesToArray(ArrayList tablesList)
00445 {
00446 int length = tablesList.size();
00447 String[] result = new String[length];
00448 for (int i = 0; i < length; i++)
00449 result[i] = ((DatabaseTable) tablesList.get(i)).getName();
00450 return result;
00451 }
00452
00453
00454
00455
00456
00457
00458 public boolean isCleanOctopus()
00459 {
00460 return cleanOctopus;
00461 }
00462
00463
00464
00465
00466
00467
00468 public boolean isZipOctopus()
00469 {
00470 return zipOctopus;
00471 }
00472
00473
00474
00475
00476
00477
00478 public final String getOctopusDirectory()
00479 {
00480 return mainDirectory;
00481 }
00482
00483
00484
00485
00486
00487
00488 public Exception getRunException()
00489 {
00490 return runException;
00491 }
00492
00493
00494
00495
00496
00497
00498 public final void setOctopusDirectory(String path)
00499 {
00500 mainDirectory = path;
00501 }
00502
00503
00504
00505
00506
00507
00508
00509
00510 public void setOctopusMode(int mode)
00511 {
00512 switch (mode)
00513 {
00514 case MODE_BACKUP :
00515 this.backupMode = true;
00516 break;
00517 case MODE_RECOVERY :
00518 this.backupMode = false;
00519 break;
00520 default :
00521 throw new RuntimeException("Invalid mode " + mode
00522 + " in setOctopusMode");
00523 }
00524 }
00525
00526
00527
00528
00529
00530
00531 public void setCleanOctopus(boolean cleanOctopus)
00532 {
00533 this.cleanOctopus = cleanOctopus;
00534 }
00535
00536
00537
00538
00539
00540
00541 public void setListener(BackupListener listener)
00542 {
00543 this.listener = listener;
00544 }
00545
00546
00547
00548
00549
00550
00551 public void setZipOctopus(boolean zipOctopus)
00552 {
00553 this.zipOctopus = zipOctopus;
00554 }
00555
00556 private void cleanUp()
00557 {
00558 if (logger.isDebugEnabled())
00559 logger.debug("Cleaning up temporary backup files...");
00560 File toRemove = new File(octopusDir);
00561 deleteDir(toRemove);
00562 }
00563
00564 private void getUserLogin()
00565 {
00566
00567 HashMap connectionManagers = database.getConnectionManagers();
00568 Iterator iter = connectionManagers.values().iterator();
00569 AbstractConnectionManager connectionManager = (AbstractConnectionManager) iter
00570 .next();
00571 user = connectionManager.getLogin();
00572 password = connectionManager.getPassword();
00573 if (logger.isDebugEnabled())
00574 logger.debug("User:" + user + ":Password:" + password);
00575 }
00576
00577 private void setPaths() throws BackupException
00578 {
00579
00580 if (stream == null)
00581 stream = new PrintStream(new LoggingOutputStream(Category
00582 .getInstance(this.getClass().getName()), Priority.DEBUG), true);
00583
00584 System.setOut(stream);
00585
00586
00587 octopusDir = mainDirectory + File.separator + dumpName;
00588
00589 File octopusd = new File(octopusDir);
00590 octopusd.mkdirs();
00591 octopusd.mkdir();
00592
00593
00594 csvDir = "csv";
00595 File csvd = new File(octopusDir + File.separator + csvDir);
00596 csvDir = csvd.getAbsolutePath();
00597 csvd.mkdirs();
00598 csvd.mkdir();
00599
00600
00601 loaderJobXmlFile = octopusDir + File.separator + "LoaderJob.olj";
00602
00603 if (logger.isDebugEnabled())
00604 {
00605 logger.debug("=======================================");
00606 logger.debug("Using the following Octopus settings:");
00607 logger.debug("OCTOPUS_DIR=" + octopusDir);
00608 logger.debug("CSV_DIR=" + csvDir);
00609 logger.debug("LOADER_JOB_XML_FILE=" + loaderJobXmlFile);
00610 logger.debug("cleanOctopus=" + cleanOctopus);
00611 logger.debug("zipOctopus=" + cleanOctopus);
00612 logger.debug("OCTOPUS_HOME:" + System.getProperty("OCTOPUS_HOME"));
00613 logger.debug("=======================================");
00614 }
00615
00616 if (!octopusd.exists() || !csvd.exists())
00617 {
00618 throw new BackupException("backup.directory.cannot.be.created");
00619 }
00620 }
00621
00622 private void setOctopusLoaderJob() throws OctopusException, BackupException
00623 {
00624 Hashtable options = getOctopusStrings(database);
00625 String sourceType = (String) options.get("octopusType");
00626 String onErrorContinueEqualFalse = "onErrorContinue=\"false\"";
00627 String onErrorContinueEqualTrue = "onErrorContinue=\"true\"";
00628 BufferedReader br = null;
00629 BufferedWriter bw = null;
00630
00631 try
00632 {
00633 br = new BufferedReader(new FileReader(loaderJobXmlFile));
00634 String line = "";
00635 StringBuffer buffer = new StringBuffer();
00636
00637 while ((line = br.readLine()) != null)
00638 {
00639
00640 int idx = line.indexOf(OCTOPUS_INCLUDE_HREF);
00641 if (idx != -1)
00642 {
00643 idx += OCTOPUS_INCLUDE_HREF.length();
00644
00645 line = line.substring(0, idx - 4) + ".." + File.separator
00646 + octopusDir + File.separator + "SQLForAllVendors"
00647 + File.separator + sourceType + File.separator + "sql"
00648 + File.separator + line.substring(idx);
00649 }
00650
00651
00652 int index7 = line.indexOf(onErrorContinueEqualFalse);
00653 if (index7 != -1)
00654 {
00655 line = line.substring(0, index7) + onErrorContinueEqualTrue
00656 + line.substring(index7 + onErrorContinueEqualFalse.length());
00657 }
00658 buffer.append(line + System.getProperty("line.separator"));
00659 }
00660 br.close();
00661 if (logger.isDebugEnabled())
00662 {
00663 logger.debug("Octopus file updated with success");
00664 }
00665
00666 bw = new BufferedWriter(new FileWriter(loaderJobXmlFile));
00667 bw.write(buffer.toString());
00668 bw.close();
00669 }
00670 catch (FileNotFoundException fie)
00671 {
00672
00673 logger.warn(Translate.get("controller.octopus.loader.job.not.found"));
00674 throw new OctopusException(fie.getMessage());
00675 }
00676 catch (IOException e)
00677 {
00678
00679 logger.warn(Translate.get("controller.octopus.loader.io.problem"));
00680 }
00681 finally
00682 {
00683
00684 if (bw != null)
00685 try
00686 {
00687 bw.close();
00688 }
00689 catch (IOException e1)
00690 {
00691
00692 }
00693 if (br != null)
00694 try
00695 {
00696 br.close();
00697 }
00698 catch (IOException e2)
00699 {
00700 }
00701 }
00702 }
00703
00704
00705
00706
00707
00708
00709 private void launchOctopus() throws OctopusException
00710 {
00711 try
00712 {
00713 Loader myOctopus;
00714 if (tables == null)
00715 {
00716
00717 myOctopus = new Loader(loaderJobXmlFile, Loader.LOGMODE_NORMAL,
00718 "cjdbc", octopusDir, OCTOPUS_LOG_FILE, true, null, null, true,
00719 null, 0, 100);
00720 }
00721 else
00722 {
00723
00724 myOctopus = new Loader(loaderJobXmlFile, Loader.LOGMODE_NORMAL,
00725 "cjdbc", octopusDir, OCTOPUS_LOG_FILE, true, null, null, true,
00726 null, 0, 100, this.convertTablesToArray(tables));
00727
00728 }
00729 try
00730 {
00731 myOctopus.load();
00732 }
00733 catch (Exception e)
00734 {
00735 logger.error("Failed to load octopus", e);
00736 throw new OctopusException(Translate.get(
00737 "controller.octopus.load.failed", e));
00738 }
00739 }
00740
00741
00742 catch (OctopusException oe)
00743 {
00744
00745 throw oe;
00746 }
00747 catch (Exception e)
00748 {
00749 throw new OctopusException(Translate
00750 .get("controller.octopus.instance.failed"));
00751 }
00752 }
00753
00754 private void zipOctopus(int zipMode) throws BackupException
00755 {
00756 try
00757 {
00758 if (logger.isDebugEnabled())
00759 {
00760 logger.debug("Checkpoint:" + dumpName);
00761 logger.debug("CsvDir:" + csvDir);
00762 logger.debug("MainDir:" + mainDirectory);
00763 }
00764 switch (zipMode)
00765 {
00766 case ZIP_MODE_CREATE :
00767 new ZipMe().create(dumpName, octopusDir, mainDirectory);
00768 break;
00769 case ZIP_MODE_EXPAND :
00770 new ZipMe().expand(dumpName, ".", mainDirectory);
00771 break;
00772 default :
00773 throw new BackupException("invalid.zip.mode");
00774 }
00775 }
00776 catch (Exception e)
00777 {
00778 logger.error(e.getMessage(), e);
00779 throw new BackupException(e.getMessage());
00780 }
00781 }
00782
00783 private String getDbType(String url) throws BackupException
00784 {
00785 if (url == null)
00786 throw new BackupException("Invalid null source url");
00787 int index = url.indexOf(':');
00788 int index2 = url.indexOf(':', index + 1);
00789 if (index == -1 || index2 == -1 || index > index2)
00790 throw new BackupException("Invalid source url format");
00791 String type = url.substring(index + 1, index2);
00792 return type;
00793 }
00794
00795 private void prepareOctopus(int octopusMode) throws BackupException,
00796 OctopusException
00797 {
00798
00799 String sourceType = "";
00800 String sourceUrl = "";
00801 String sourceDriver = "";
00802 String sourceUser = "";
00803 String sourcePassword = "";
00804
00805 String targetType = "";
00806 String targetDriver = "";
00807 String targetUrl = "";
00808 String targetUser = "";
00809 String targetPassword = "";
00810
00811 boolean backupMode = true;
00812
00813 Hashtable options = getOctopusStrings(database);
00814
00815
00816 switch (octopusMode)
00817 {
00818 case OCTOPUS_MODE_FROM_CSV :
00819 targetType = (String) options.get("octopusType");
00820 targetUrl = (String) options.get("octopusUrl");
00821 targetDriver = (String) options.get("octopusDriver");
00822 targetUser = user;
00823 targetPassword = password;
00824 sourceType = OctopusConstants.getOctopusType(TYPE_CSV);
00825 sourceDriver = OctopusConstants.getOctopusDriver(TYPE_CSV);
00826 sourceUrl = csvDir;
00827 sourceUser = "";
00828 sourcePassword = "";
00829 backupMode = false;
00830 break;
00831 case OCTOPUS_MODE_TO_CSV :
00832 sourceType = (String) options.get("octopusType");
00833 sourceUrl = (String) options.get("octopusUrl");
00834 sourceDriver = (String) options.get("octopusDriver");
00835 sourceUser = user;
00836 sourcePassword = password;
00837 targetType = OctopusConstants.getOctopusType(TYPE_CSV);
00838 targetDriver = OctopusConstants.getOctopusDriver(TYPE_CSV);
00839 targetUrl = csvDir;
00840 targetUser = "";
00841 targetPassword = "";
00842 backupMode = true;
00843
00844 generateMetadata(sourceType, sourceUrl, sourceDriver, sourceUser,
00845 sourcePassword, targetType, targetDriver, targetUrl, targetUser,
00846 targetPassword, backupMode);
00847 break;
00848 default :
00849 throw new BackupException("invalid.octopus.prepare.type");
00850 }
00851
00852
00853 prepareOctopus(sourceType, sourceUrl, sourceDriver, sourceUser,
00854 sourcePassword, targetType, targetDriver, targetUrl, targetUser,
00855 targetPassword, backupMode);
00856
00857 }
00858
00859 }