Source Code Cross Referenced for MySQLBackuper.java in  » Database-JDBC-Connection-Pool » sequoia-2.10.9 » org » continuent » sequoia » controller » backup » backupers » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database JDBC Connection Pool » sequoia 2.10.9 » org.continuent.sequoia.controller.backup.backupers 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /**
002:         * Sequoia: Database clustering technology.
003:         * Copyright (C) 2006 Continuent Inc.
004:         * Contact: sequoia@continuent.org
005:         * 
006:         * Licensed under the Apache License, Version 2.0 (the "License");
007:         * you may not use this file except in compliance with the License.
008:         * You may obtain a copy of the License at
009:         * 
010:         * http://www.apache.org/licenses/LICENSE-2.0
011:         * 
012:         * Unless required by applicable law or agreed to in writing, software
013:         * distributed under the License is distributed on an "AS IS" BASIS,
014:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015:         * See the License for the specific language governing permissions and
016:         * limitations under the License. 
017:         *
018:         * Initial developer(s): Mykola Paliyenko.
019:         * Contributor(s): Emmanuel Cecchet, Stephane Giron
020:         */package org.continuent.sequoia.controller.backup.backupers;
021:
022:        import java.io.BufferedReader;
023:        import java.io.BufferedWriter;
024:        import java.io.File;
025:        import java.io.FileInputStream;
026:        import java.io.FileOutputStream;
027:        import java.io.FileWriter;
028:        import java.io.IOException;
029:        import java.io.InputStreamReader;
030:        import java.sql.Connection;
031:        import java.sql.ResultSet;
032:        import java.sql.SQLException;
033:        import java.util.ArrayList;
034:        import java.util.Date;
035:        import java.util.Iterator;
036:        import java.util.regex.Matcher;
037:        import java.util.regex.Pattern;
038:
039:        import org.continuent.sequoia.common.exceptions.BackupException;
040:        import org.continuent.sequoia.common.exceptions.UnreachableBackendException;
041:        import org.continuent.sequoia.common.log.Trace;
042:        import org.continuent.sequoia.controller.backend.DatabaseBackend;
043:        import org.continuent.sequoia.controller.connection.PooledConnection;
044:        import org.continuent.sequoia.controller.connection.SimpleConnectionManager;
045:
046:        /**
047:         * MySQL backuper inspired from the PostgreSQL backuper.
048:         * <p>
049:         * Options for this backuper are:
050:         * 
051:         * <pre>
052:         * - bindir: path to mysqldump binary
053:         * - dumpServer: address to bind the dump server
054:         * </pre>
055:         * 
056:         * @author <a href="mailto:mpaliyenko@gmail.com">Mykola Paliyenko</a>
057:         * @author <a href="mailto:emmanuel.cecchet@continuent.com">Emmanuel Cecchet</a>
058:         * @author <a href="mailto:stephane.giron@continuent.com">Stephane Giron</a>
059:         */
060:        public class MySQLBackuper extends AbstractBackuper {
061:            private static final String DEFAULT_MYSQL_PORT = "3306";
062:
063:            private static final String DEFAULT_MYSQL_HOST = "localhost";
064:
065:            static Trace logger = Trace
066:                    .getLogger(MySQLBackuper.class.getName());
067:            /** end user logger */
068:            static Trace endUserLogger = Trace
069:                    .getLogger("org.continuent.sequoia.enduser");
070:
071:            /** CommandExec instance for running native commands. * */
072:            protected NativeCommandExec nativeCmdExec = new NativeCommandExec();
073:
074:            /**
075:             * Creates a new <code>MySQLBackuper</code> object
076:             */
077:            public MySQLBackuper() {
078:            }
079:
080:            /**
081:             * @see org.continuent.sequoia.controller.backup.Backuper#getDumpFormat()
082:             */
083:            public String getDumpFormat() {
084:                return "MySQL raw dump";
085:            }
086:
087:            /**
088:             * @see org.continuent.sequoia.controller.backup.Backuper#backup(org.continuent.sequoia.controller.backend.DatabaseBackend,
089:             *      java.lang.String, java.lang.String, java.lang.String,
090:             *      java.lang.String, java.util.ArrayList)
091:             */
092:            public Date backup(DatabaseBackend backend, String login,
093:                    String password, String dumpName, String path,
094:                    ArrayList tables) throws BackupException {
095:                String url = backend.getURL();
096:                if (!url.startsWith("jdbc:mysql:")) {
097:                    throw new BackupException("Unsupported db url " + url);
098:                }
099:                MySQLUrlInfo info = new MySQLUrlInfo(url);
100:
101:                try {
102:                    File pathDir = new File(path);
103:                    if (!pathDir.exists()) {
104:                        pathDir.mkdirs();
105:                        pathDir.mkdir();
106:                    }
107:                    String dumpPath = getDumpPhysicalPath(path, dumpName);
108:                    if (logger.isDebugEnabled()) {
109:                        logger.debug("Dumping " + backend.getURL() + " in "
110:                                + dumpPath);
111:                    }
112:
113:                    String executablePath = (optionsMap.containsKey("bindir") ? (String) optionsMap
114:                            .get("bindir")
115:                            + File.separator
116:                            : "")
117:                            + "mysqldump";
118:
119:                    int exitValue = safelyExecuteNativeCommand(executablePath
120:                            + getBackupStoredProceduresOption(executablePath)
121:                            + " -h " + info.getHost() + " --port="
122:                            + info.getPort() + " -u" + login + " --password="
123:                            + password + " " + info.getDbName(), dumpPath, true);
124:
125:                    if (exitValue != 0) {
126:                        printErrors();
127:                        throw new BackupException(
128:                                "mysqldump execution did not complete successfully!");
129:                    }
130:                    performPostBackup(backend, login, password, dumpPath);
131:                } catch (Exception e) {
132:                    String msg = "Error while performing backup";
133:                    logger.error(msg, e);
134:                    throw new BackupException(msg, e);
135:                }
136:
137:                return new Date();
138:            }
139:
140:            /**
141:             * Perform all post backup operations that are required :
142:             * <ul>
143:             * <li>save auto-generated keys values</li>
144:             * </ul>
145:             * 
146:             * @param backend backend that is used for the backup
147:             * @param login login that is used
148:             * @param password password that is used
149:             * @param dumpPath path and file where the backup file is generated
150:             * @throws SQLException if an exception occurs when querying the database
151:             * @throws UnreachableBackendException if a connection cannot be taken on this
152:             *           backend
153:             * @throws IOException if a problem occurs when writing into the backup file
154:             */
155:            private void performPostBackup(DatabaseBackend backend,
156:                    String login, String password, String dumpPath)
157:                    throws SQLException, UnreachableBackendException,
158:                    IOException {
159:                BufferedWriter out = null;
160:
161:                // If there are any auto-generated keys on tables, the backup will not
162:                // contain these. We need to save it at the end of the dump to be able to
163:                // restart the "auto_generated" column at the right value.
164:                SimpleConnectionManager simpleConnectionManager = new SimpleConnectionManager(
165:                        backend.getURL(), backend.getName(), login, password,
166:                        backend.getDriverPath(), backend.getDriverClassName());
167:                try {
168:                    simpleConnectionManager.initializeConnections();
169:                    PooledConnection pooledConn = simpleConnectionManager
170:                            .getConnection();
171:                    Connection conn = pooledConn.getConnection();
172:
173:                    ResultSet rs = conn.createStatement().executeQuery(
174:                            "show table status");
175:                    while (rs.next()) {
176:                        long lastAutoIncrement = rs.getLong("Auto_increment");
177:                        if (lastAutoIncrement > 1) {
178:                            if (out == null)
179:                                out = new BufferedWriter(new FileWriter(
180:                                        dumpPath, true));
181:
182:                            out.write("ALTER TABLE `"
183:                                    + rs.getString("Name")
184:                                            .replaceAll("`", "``")
185:                                    + "` AUTO_INCREMENT=" + lastAutoIncrement
186:                                    + ";");
187:
188:                            out.newLine();
189:                        }
190:
191:                    }
192:                    if (out != null) {
193:                        out.flush();
194:                        out.close();
195:                    }
196:                    rs.close();
197:                    conn.close();
198:                } finally {
199:                    simpleConnectionManager.finalizeConnections();
200:                }
201:            }
202:
203:            /**
204:             * Returns the option to supply to mysql so that it backups stored-procedures
205:             * and functions in the dump. This can be either "" (mysql versions prior to
206:             * 5.0.13 do not know about stored procedures) or " --routines" (mysql 5.0.13
207:             * and above).
208:             * 
209:             * @param executablePath the path to the mysql utility used for making dumps
210:             * @return the option to supply to mysql so that it backups strored-procedures
211:             *         and functions in the dump
212:             * @throws IOException in case of error when communicating with the
213:             *           sub-process.
214:             * @throws IllegalArgumentException if no version information can be found in
215:             *           'executablePath'
216:             */
217:            private static String getBackupStoredProceduresOption(
218:                    String executablePath) throws IOException,
219:                    IllegalArgumentException {
220:                int majorVersion = Integer
221:                        .parseInt(getMajorVersion(executablePath));
222:                if (majorVersion < 5)
223:                    return "";
224:                else if (majorVersion == 5) {
225:                    String minorVersionString = getMinorVersion(executablePath);
226:                    float minorVersion = Float.parseFloat(minorVersionString);
227:                    if (minorVersion < 1) {
228:                        // --routines supported from v5.0.13 upwards
229:                        // Need to check for extra value (i.e. 0.9 < 0.13)
230:                        String extraVersionString;
231:                        try {
232:                            extraVersionString = minorVersionString
233:                                    .substring(minorVersionString.indexOf('.') + 1);
234:                        } catch (IndexOutOfBoundsException e) { // No minor version number
235:                            return "";
236:                        }
237:                        float extraVersion = Float
238:                                .parseFloat(extraVersionString);
239:                        if (extraVersion < 13)
240:                            return "";
241:                    }
242:                }
243:
244:                return " --routines";
245:            }
246:
247:            /**
248:             * Returns the major version number for specified mysql native utility.
249:             * 
250:             * @param executablePath the path to the mysql native utility
251:             * @return the major version number for specified mysql native utility. *
252:             * @throws IOException in case of error when communicating with the
253:             *           sub-process.
254:             * @throws IllegalArgumentException if no version information can be found in
255:             *           'executablePath'
256:             */
257:            private static String getMajorVersion(String executablePath)
258:                    throws IOException, IllegalArgumentException {
259:                Process p = Runtime.getRuntime().exec(
260:                        executablePath + " --version");
261:                BufferedReader pout = new BufferedReader(new InputStreamReader(
262:                        p.getInputStream()));
263:
264:                String versionString = pout.readLine();
265:                // sample version string:
266:                // "/usr/bin/mysql Ver 14.12 Distrib 5.0.18, for pc-linux-gnu (i686) using
267:                // readline 5.0"
268:                Pattern regex = Pattern
269:                        .compile("mysql.*Ver ([0-9.]*) Distrib ([0-9])\\.([0-9.]*)");
270:                Matcher m = regex.matcher(versionString);
271:                if (!m.find())
272:                    throw new IllegalArgumentException(
273:                            "Can not find version information for "
274:                                    + executablePath);
275:
276:                return m.group(2);
277:            }
278:
279:            /**
280:             * Returns the minor version number for specified mysql native utility.
281:             * 
282:             * @param executablePath the path to the mysql native utility
283:             * @return the minor version number for specified mysql native utility. *
284:             * @throws IOException in case of error when communicating with the
285:             *           sub-process.
286:             * @throws IllegalArgumentException if no version information can be found in
287:             *           'executablePath'
288:             */
289:            private static String getMinorVersion(String executablePath)
290:                    throws IOException, IllegalArgumentException {
291:                Process p = Runtime.getRuntime().exec(
292:                        executablePath + " --version");
293:                BufferedReader pout = new BufferedReader(new InputStreamReader(
294:                        p.getInputStream()));
295:
296:                String versionString = pout.readLine();
297:                // sample version string:
298:                // "/usr/bin/mysql Ver 14.12 Distrib 5.0.18, for pc-linux-gnu (i686) using
299:                // readline 5.0"
300:                Pattern regex = Pattern
301:                        .compile("mysql.*Ver ([0-9.]*) Distrib ([0-9])\\.([0-9.]*)");
302:                Matcher m = regex.matcher(versionString);
303:                if (!m.find())
304:                    throw new IllegalArgumentException(
305:                            "Can not find version information for "
306:                                    + executablePath);
307:
308:                return m.group(3);
309:            }
310:
311:            /**
312:             * @see org.continuent.sequoia.controller.backup.Backuper#restore(org.continuent.sequoia.controller.backend.DatabaseBackend,
313:             *      java.lang.String, java.lang.String, java.lang.String,
314:             *      java.lang.String, java.util.ArrayList)
315:             */
316:            public void restore(DatabaseBackend backend, String login,
317:                    String password, String dumpName, String path,
318:                    ArrayList tables) throws BackupException {
319:                String url = backend.getURL();
320:                if (!url.startsWith("jdbc:mysql:")) {
321:                    throw new BackupException("Unsupported db url " + url);
322:                }
323:                MySQLUrlInfo info = new MySQLUrlInfo(url);
324:                try {
325:                    File pathDir = new File(path);
326:                    if (!pathDir.exists()) {
327:                        pathDir.mkdirs();
328:                        pathDir.mkdir();
329:                    }
330:                    String dumpPath = getDumpPhysicalPath(path, dumpName);
331:                    if (logger.isDebugEnabled()) {
332:                        logger.debug("Restoring " + backend.getURL() + " from "
333:                                + dumpPath);
334:                    }
335:
336:                    String executablePath = (optionsMap.containsKey("bindir") ? (String) optionsMap
337:                            .get("bindir")
338:                            + File.separator
339:                            : "");
340:
341:                    // Drop the database if it already exists
342:                    if (logger.isDebugEnabled())
343:                        logger.debug("Dropping database '" + info.getDbName()
344:                                + "'");
345:
346:                    String mysqladminExecutablePath = executablePath
347:                            + "mysqladmin";
348:                    if (executeNativeCommand(mysqladminExecutablePath + " -h "
349:                            + info.getHost() + " --port=" + info.getPort()
350:                            + " -f -u" + login + " --password=" + password
351:                            + " drop " + info.getDbName()) != 0) {
352:                        // Errors can happen there, e.g. if the database does not exist yet.
353:                        // Just log them, and carry-on...
354:                        printErrors();
355:                    }
356:
357:                    // Create database
358:                    if (logger.isDebugEnabled())
359:                        logger.debug("Creating database '" + info.getDbName()
360:                                + "'");
361:
362:                    if (executeNativeCommand(mysqladminExecutablePath + " -h "
363:                            + info.getHost() + " --port=" + info.getPort()
364:                            + " -f -u" + login + " --password=" + password
365:                            + " create " + info.getDbName()) != 0) {
366:                        // Errors can happen there, e.g. if the database does not exist yet.
367:                        // Just log them, and carry-on...
368:                        printErrors();
369:                        throw new BackupException("Failed to create database '"
370:                                + info.getDbName() + "'");
371:                    }
372:
373:                    // Load dump
374:                    String mysqlExecutablePath = executablePath + "mysql";
375:                    int exitValue = safelyExecuteNativeCommand(
376:                            mysqlExecutablePath + " -h " + info.getHost()
377:                                    + " --port=" + info.getPort() + " -u"
378:                                    + login + " --password=" + password + " "
379:                                    + info.getDbName(), dumpPath, false);
380:
381:                    if (exitValue != 0) {
382:                        printErrors();
383:                        throw new BackupException(
384:                                "mysql execution did not complete successfully!");
385:                    }
386:                } catch (Exception e) {
387:                    String msg = "Error while performing restore";
388:                    logger.error(msg, e);
389:                    throw new BackupException(msg, e);
390:                }
391:            }
392:
393:            /**
394:             * @see org.continuent.sequoia.controller.backup.Backuper#deleteDump(java.lang.String,
395:             *      java.lang.String)
396:             */
397:            public void deleteDump(String path, String dumpName)
398:                    throws BackupException {
399:                File toRemove = new File(getDumpPhysicalPath(path, dumpName));
400:                if (logger.isDebugEnabled())
401:                    logger.debug("Deleting compressed dump " + toRemove);
402:                toRemove.delete();
403:            }
404:
405:            /**
406:             * Get the dump physical path from its logical name
407:             * 
408:             * @param path the path where the dump is stored
409:             * @param dumpName dump logical name
410:             * @return path to zip file
411:             */
412:            private String getDumpPhysicalPath(String path, String dumpName) {
413:                return path + File.separator + dumpName;
414:            }
415:
416:            /**
417:             * Allow to parse MySQL URL.
418:             */
419:            protected class MySQLUrlInfo {
420:                private boolean isLocal;
421:
422:                private String host;
423:
424:                private String port;
425:
426:                private String dbName;
427:
428:                // Used to parse url
429:                private Pattern pattern = Pattern
430:                        .compile("jdbc:mysql:((//([a-zA-Z0-9_\\-.]+|\\[[a-fA-F0-9:]+])((:(\\d+))|))/|)([a-zA-Z][a-zA-Z0-9_\\-]*)(\\?.*)?");
431:
432:                Matcher matcher;
433:
434:                /**
435:                 * Creates a new <code>MySQLUrlInfo</code> object, used to parse the
436:                 * postgresql jdbc options. If host and/or port aren't specified, will
437:                 * default to localhost:3306. Note that database name must be specified.
438:                 * 
439:                 * @param url the MySQL JDBC url to parse
440:                 */
441:                public MySQLUrlInfo(String url) {
442:                    matcher = pattern.matcher(url);
443:
444:                    if (matcher.matches()) {
445:                        if (matcher.group(3) != null)
446:                            host = matcher.group(3);
447:                        else
448:                            host = DEFAULT_MYSQL_HOST;
449:
450:                        if (matcher.group(6) != null)
451:                            port = matcher.group(6);
452:                        else
453:                            port = DEFAULT_MYSQL_PORT;
454:
455:                        dbName = matcher.group(7);
456:                    }
457:                }
458:
459:                /**
460:                 * Gets the HostParameters of this postgresql jdbc url as a String that can
461:                 * be used to pass into cmd line/shell calls.
462:                 * 
463:                 * @return a string that can be used to pass into a cmd line/shell call.
464:                 */
465:                public String getHostParametersString() {
466:                    if (isLocal) {
467:                        return "";
468:                    } else {
469:                        return "-h " + host + " --port=" + port;
470:                    }
471:                }
472:
473:                /**
474:                 * Gets the database name part of this postgresql jdbc url.
475:                 * 
476:                 * @return the database name part of this postgresql jdbc url.
477:                 */
478:                public String getDbName() {
479:                    return dbName;
480:                }
481:
482:                /**
483:                 * Gets the host part of this postgresql jdbc url.
484:                 * 
485:                 * @return the host part of this postgresql jdbc url.
486:                 */
487:                public String getHost() {
488:                    return host;
489:                }
490:
491:                /**
492:                 * Gets the port part of this postgresql jdbc url.
493:                 * 
494:                 * @return the port part of this postgresql jdbc url.
495:                 */
496:                public String getPort() {
497:                    return port;
498:                }
499:
500:                /**
501:                 * Checks whether this postgresql jdbc url refers to a local db or not, i.e.
502:                 * has no host specified, e.g. jdbc:postgresql:myDb.
503:                 * 
504:                 * @return true if this postgresql jdbc url has no host specified, i.e.
505:                 *         refers to a local db.
506:                 */
507:                public boolean isLocal() {
508:                    return isLocal;
509:                }
510:
511:            }
512:
513:            /**
514:             * Executes a native operating system command. Output of these commands is
515:             * captured and logged.
516:             * 
517:             * @param command String of command to execute
518:             * @return 0 if successful, any number otherwise
519:             * @throws IOException
520:             * @throws InterruptedException
521:             */
522:            protected int executeNativeCommand(String command)
523:                    throws IOException, InterruptedException {
524:                return nativeCmdExec.executeNativeCommand(command, null, null,
525:                        0, getIgnoreStdErrOutput());
526:            }
527:
528:            /**
529:             * Executes a native operating system dump or restore command. Output of these
530:             * commands is carefully captured and logged.
531:             * 
532:             * @param command String of command to execute
533:             * @param dumpPath path to the dump file (either source or dest, depending on
534:             *          specified 'backup' parameter.
535:             * @param backup specifies whether we are doing a backup (true) or a restore
536:             *          (false). This sets the semantics of the dumpPath parameter to
537:             *          resp. dest or source file name.
538:             * @return 0 if successful, 1 otherwise
539:             */
540:            protected int safelyExecuteNativeCommand(String command,
541:                    String dumpPath, boolean backup) throws IOException {
542:                if (backup)
543:                    return nativeCmdExec.safelyExecNativeCommand(command, null,
544:                            new FileOutputStream(dumpPath), 0,
545:                            getIgnoreStdErrOutput()) ? 0 : 1;
546:                else {
547:                    FileInputStream dumpStream = new FileInputStream(dumpPath);
548:                    return nativeCmdExec.safelyExecNativeCommand(command,
549:                            new NativeCommandInputSource(dumpStream), null, 0,
550:                            getIgnoreStdErrOutput()) ? 0 : 1;
551:                }
552:            }
553:
554:            protected void printErrors() {
555:                ArrayList errors = nativeCmdExec.getStderr();
556:                Iterator it = errors.iterator();
557:                while (it.hasNext()) {
558:                    String msg = (String) it.next();
559:                    logger.info(msg);
560:                    endUserLogger.error(msg);
561:                }
562:            }
563:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.