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