1 21 22 package org.continuent.sequoia.controller.backup.backupers; 23 24 import java.io.BufferedReader ; 25 import java.io.File ; 26 import java.io.FileInputStream ; 27 import java.io.FileOutputStream ; 28 import java.io.IOException ; 29 import java.io.InputStreamReader ; 30 import java.util.ArrayList ; 31 import java.util.Date ; 32 import java.util.Iterator ; 33 import java.util.regex.Matcher ; 34 import java.util.regex.Pattern ; 35 36 import org.continuent.sequoia.common.exceptions.BackupException; 37 import org.continuent.sequoia.common.log.Trace; 38 import org.continuent.sequoia.controller.backend.DatabaseBackend; 39 40 54 public class MySQLBackuper extends AbstractBackuper 55 { 56 private static final String DEFAULT_MYSQL_PORT = "3306"; 57 58 private static final String DEFAULT_MYSQL_HOST = "localhost"; 59 60 static Trace logger = Trace 61 .getLogger(MySQLBackuper.class 62 .getName()); 63 64 static Trace endUserLogger = Trace 65 .getLogger("org.continuent.sequoia.enduser"); 66 67 68 protected NativeCommandExec nativeCmdExec = new NativeCommandExec(); 69 70 73 public MySQLBackuper() 74 { 75 } 76 77 80 public String getDumpFormat() 81 { 82 return "MySQL raw dump"; 83 } 84 85 90 public Date backup(DatabaseBackend backend, String login, String password, 91 String dumpName, String path, ArrayList tables) throws BackupException 92 { 93 String url = backend.getURL(); 94 if (!url.startsWith("jdbc:mysql:")) 95 { 96 throw new BackupException("Unsupported db url " + url); 97 } 98 MySQLUrlInfo info = new MySQLUrlInfo(url); 99 100 try 101 { 102 File pathDir = new File (path); 103 if (!pathDir.exists()) 104 { 105 pathDir.mkdirs(); 106 pathDir.mkdir(); 107 } 108 String dumpPath = getDumpPhysicalPath(path, dumpName); 109 if (logger.isDebugEnabled()) 110 { 111 logger.debug("Dumping " + backend.getURL() + " in " + dumpPath); 112 } 113 114 String executablePath = (optionsMap.containsKey("bindir") 115 ? (String ) optionsMap.get("bindir") + File.separator 116 : "") 117 + "mysqldump"; 118 119 String addDropDBOption = getAddDropDatabaseOption(executablePath); 120 121 int exitValue = safelyExecuteNativeCommand(executablePath 122 + getBackupStoredProceduresOption(executablePath) + addDropDBOption 123 + " -h " + info.getHost() + " --port=" + info.getPort() + " -u" 124 + login + " --password=" + password + " --databases " 125 + info.getDbName(), dumpPath, true); 126 127 if (exitValue != 0) 128 { 129 printErrors(); 130 throw new BackupException( 131 "mysqldump execution did not complete successfully!"); 132 } 133 } 134 catch (Exception e) 135 { 136 String msg = "Error while performing backup"; 137 logger.error(msg, e); 138 throw new BackupException(msg, e); 139 } 140 141 return new Date (); 142 } 143 144 158 private static String getBackupStoredProceduresOption(String executablePath) 159 throws IOException , IllegalArgumentException 160 { 161 int majorVersion = Integer.parseInt(getMajorVersion(executablePath)); 162 if (majorVersion < 5) 163 return ""; 164 else if (majorVersion == 5) 165 { 166 String minorVersionString = getMinorVersion(executablePath); 167 float minorVersion = Float.parseFloat(minorVersionString); 168 if (minorVersion < 1) 169 { 170 String extraVersionString; 173 try 174 { 175 extraVersionString = minorVersionString.substring(minorVersionString 176 .indexOf('.') + 1); 177 } 178 catch (IndexOutOfBoundsException e) 179 { return ""; 181 } 182 float extraVersion = Float.parseFloat(extraVersionString); 183 if (extraVersion < 13) 184 return ""; 185 } 186 } 187 188 return " --routines"; 189 } 190 191 202 private static String getAddDropDatabaseOption(String executablePath) 203 throws IOException , IllegalArgumentException 204 { 205 int majorVersion = Integer.parseInt(getMajorVersion(executablePath)); 206 if (majorVersion < 4) 207 return ""; 208 else if (majorVersion == 4) 209 { 210 String minorVersionString = getMinorVersion(executablePath); 211 float minorVersion = Float.parseFloat(minorVersionString); 212 if (minorVersion < 1) 213 return ""; 214 else if (minorVersion < 2) 215 { 216 String extraVersionString; 219 try 220 { 221 extraVersionString = minorVersionString.substring(minorVersionString 222 .indexOf('.') + 1); 223 } 224 catch (IndexOutOfBoundsException e) 225 { return ""; 227 } 228 float extraVersion = Float.parseFloat(extraVersionString); 229 if (extraVersion < 13) 230 return ""; 231 } 232 } 233 return " --add-drop-database"; 234 } 235 236 246 private static String getMajorVersion(String executablePath) 247 throws IOException , IllegalArgumentException 248 { 249 Process p = Runtime.getRuntime().exec(executablePath + " --version"); 250 BufferedReader pout = new BufferedReader (new InputStreamReader (p 251 .getInputStream())); 252 253 String versionString = pout.readLine(); 254 Pattern regex = Pattern 258 .compile("mysql.*Ver ([0-9.]*) Distrib ([0-9])\\.([0-9.]*)"); 259 Matcher m = regex.matcher(versionString); 260 if (!m.find()) 261 throw new IllegalArgumentException ( 262 "Can not find version information for " + executablePath); 263 264 return m.group(2); 265 } 266 267 277 private static String getMinorVersion(String executablePath) 278 throws IOException , IllegalArgumentException 279 { 280 Process p = Runtime.getRuntime().exec(executablePath + " --version"); 281 BufferedReader pout = new BufferedReader (new InputStreamReader (p 282 .getInputStream())); 283 284 String versionString = pout.readLine(); 285 Pattern regex = Pattern 289 .compile("mysql.*Ver ([0-9.]*) Distrib ([0-9])\\.([0-9.]*)"); 290 Matcher m = regex.matcher(versionString); 291 if (!m.find()) 292 throw new IllegalArgumentException ( 293 "Can not find version information for " + executablePath); 294 295 return m.group(3); 296 } 297 298 303 public void restore(DatabaseBackend backend, String login, String password, 304 String dumpName, String path, ArrayList tables) throws BackupException 305 { 306 String url = backend.getURL(); 307 if (!url.startsWith("jdbc:mysql:")) 308 { 309 throw new BackupException("Unsupported db url " + url); 310 } 311 MySQLUrlInfo info = new MySQLUrlInfo(url); 312 try 313 { 314 File pathDir = new File (path); 315 if (!pathDir.exists()) 316 { 317 pathDir.mkdirs(); 318 pathDir.mkdir(); 319 } 320 String dumpPath = getDumpPhysicalPath(path, dumpName); 321 if (logger.isDebugEnabled()) 322 { 323 logger.debug("Restoring " + backend.getURL() + " from " + dumpPath); 324 } 325 326 String executablePath; 327 executablePath = (optionsMap.containsKey("bindir") ? (String ) optionsMap 328 .get("bindir") 329 + File.separator : "") 330 + "mysql"; 331 332 boolean addDropDatabaseOptionSupported = !"" 333 .equals(getAddDropDatabaseOption(executablePath)); 334 335 if (!addDropDatabaseOptionSupported) 336 { 337 if (logger.isDebugEnabled()) 339 logger.debug("Dropping database '" + info.getDbName() + "'"); 340 341 String mysqladminExecutablePath = (optionsMap.containsKey("bindir") 342 ? (String ) optionsMap.get("bindir") + File.separator 343 : "") 344 + "mysqladmin"; 345 if (executeNativeCommand(mysqladminExecutablePath + " -h " 346 + info.getHost() + " --port=" + info.getPort() + " -f -u" + login 347 + " --password=" + password + " drop " + info.getDbName()) != 0) 348 { 349 printErrors(); 352 } 353 } 354 355 int exitValue = safelyExecuteNativeCommand(executablePath + " -h " 356 + info.getHost() + " --port=" + info.getPort() + " -u" + login 357 + " --password=" + password 358 + (addDropDatabaseOptionSupported ? " " + info.getDbName() : ""), 359 dumpPath, false); 360 361 if (exitValue != 0) 362 { 363 printErrors(); 364 throw new BackupException( 365 "mysql execution did not complete successfully!"); 366 } 367 } 368 catch (Exception e) 369 { 370 String msg = "Error while performing restore"; 371 logger.error(msg, e); 372 throw new BackupException(msg, e); 373 } 374 } 375 376 380 public void deleteDump(String path, String dumpName) throws BackupException 381 { 382 File toRemove = new File (getDumpPhysicalPath(path, dumpName)); 383 if (logger.isDebugEnabled()) 384 logger.debug("Deleting compressed dump " + toRemove); 385 toRemove.delete(); 386 } 387 388 395 private String getDumpPhysicalPath(String path, String dumpName) 396 { 397 return path + File.separator + dumpName; 398 } 399 400 403 protected class MySQLUrlInfo 404 { 405 private boolean isLocal; 406 407 private String host; 408 409 private String port; 410 411 private String dbName; 412 413 private Pattern pattern = Pattern 415 .compile("jdbc:mysql:((//([a-zA-Z0-9_\\-.]+|\\[[a-fA-F0-9:]+])((:(\\d+))|))/|)([a-zA-Z][a-zA-Z0-9_\\-]*)(\\?.*)?"); 416 417 Matcher matcher; 418 419 426 public MySQLUrlInfo(String url) 427 { 428 matcher = pattern.matcher(url); 429 430 if (matcher.matches()) 431 { 432 if (matcher.group(3) != null) 433 host = matcher.group(3); 434 else 435 host = DEFAULT_MYSQL_HOST; 436 437 if (matcher.group(6) != null) 438 port = matcher.group(6); 439 else 440 port = DEFAULT_MYSQL_PORT; 441 442 dbName = matcher.group(7); 443 } 444 } 445 446 452 public String getHostParametersString() 453 { 454 if (isLocal) 455 { 456 return ""; 457 } 458 else 459 { 460 return "-h " + host + " --port=" + port; 461 } 462 } 463 464 469 public String getDbName() 470 { 471 return dbName; 472 } 473 474 479 public String getHost() 480 { 481 return host; 482 } 483 484 489 public String getPort() 490 { 491 return port; 492 } 493 494 501 public boolean isLocal() 502 { 503 return isLocal; 504 } 505 506 } 507 508 517 protected int executeNativeCommand(String command) throws IOException , 518 InterruptedException 519 { 520 return nativeCmdExec.executeNativeCommand(command, null, null, 0, 521 getIgnoreStdErrOutput()); 522 } 523 524 536 protected int safelyExecuteNativeCommand(String command, String dumpPath, 537 boolean backup) throws IOException 538 { 539 if (backup) 540 return nativeCmdExec.safelyExecNativeCommand(command, null, 541 new FileOutputStream (dumpPath), 0, getIgnoreStdErrOutput()) ? 0 : 1; 542 else 543 { 544 FileInputStream dumpStream = new FileInputStream (dumpPath); 545 return nativeCmdExec.safelyExecNativeCommand(command, 546 new NativeCommandInputSource(dumpStream), null, 0, 547 getIgnoreStdErrOutput()) ? 0 : 1; 548 } 549 } 550 551 protected void printErrors() 552 { 553 ArrayList errors = nativeCmdExec.getStderr(); 554 Iterator it = errors.iterator(); 555 while (it.hasNext()) 556 { 557 String msg = (String ) it.next(); 558 logger.info(msg); 559 endUserLogger.error(msg); 560 } 561 } 562 } | Popular Tags |