KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > backup > backupers > PostgreSQLSplitPlainTextBackuper


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2005 Emic Networks
4  * Contact: sequoia@continuent.org
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Initial developer(s): Emmanuel Cecchet.
19  * Contributor(s): Dylan Hansen, Mathieu Peltier, Olivier Fambon.
20  *
21  */

22
23 package org.continuent.sequoia.controller.backup.backupers;
24
25 import java.io.File JavaDoc;
26 import java.io.FilenameFilter JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Date JavaDoc;
30
31 import org.continuent.sequoia.common.exceptions.BackupException;
32 import org.continuent.sequoia.common.log.Trace;
33 import org.continuent.sequoia.controller.backend.DatabaseBackend;
34 import org.continuent.sequoia.controller.backup.BackupManager;
35 import org.continuent.sequoia.controller.backup.DumpTransferInfo;
36
37 /**
38  * This class defines a Backuper for PostgreSQL databases. This backuper makes
39  * dumps in a plain-text format.
40  * *****************************************************************************
41  * This class backups PostgreSQL database usig pg_dump. backup command from
42  * console executes the pg_dump with split, and example pg_dump dbName -h
43  * 127.0.0.1 -p 5432 -U postgres | split -a 4 -b 10m - /tmp/dumpFile which does
44  * spliting ONE single dump file to splitted files. Size of the splitted files
45  * are determined by virtual xml file in options for Backuper, and example
46  * <Backuper backuperName="splitter"
47  * className="org.continuent.sequoia.controller.backup.backupers.PostgreSQLSplitPlainTextBackuper"
48  * options="zip=true,splitSize=10m"/> default size is 1000m Restore command
49  * restores the DB from splitted files, and example cat /tmp/dumpFile* | psql
50  * dbName -h 127.0.0.1 -p 5432 -U postgres restore checks the existing of dump
51  * file for first suffix dumpFileaaaa (suffix is set to 4) Contributer also
52  * added two methods and one varibale to AbstractPostgreSQLBackuper class
53  * protected String[] makeSplitCommand(String command, PostgreSQLUrlInfo info,
54  * String options, String login) protected String[]
55  * makeSplitCommandWithAuthentication(String command, PostgreSQLUrlInfo info,
56  * String options, String login, String password, boolean isPsql) static
57  * protected String SPLIT_SIZE = "1000m"; Contributor for this class: Gurkan
58  * Ozfidan
59  * *****************************************************************************
60  * <p>
61  * Supported URLs are:
62  * <ul>
63  * <li>jdbc:postgresql://host:port/dbname?param1=foo,param2=bar</li>
64  * <li>jdbc:postgresql://host/dbname?param1=foo,param2=bar</li>
65  * <li>jdbc:postgresql:dbname?param1=foo,param2=bar</li>
66  * </ul>
67  *
68  * @author <a HREF="mailto:emmanuel.cecchet@emicnetworks.com">Emmanuel Cecchet</a>
69  * @author <a HREF="mailto:dhansen@h2st.com">Dylan Hansen</a>
70  * @author <a HREF="mailto:mathieu.peltier@emicnetworks.com">Mathieu Peltier</a>
71  * @author <a HREF="mailto:olivier.fambon@emicnetworks.com">Olivier Fambon</a>
72  * @version 1.0
73  */

74 public class PostgreSQLSplitPlainTextBackuper
75     extends AbstractPostgreSQLBackuper
76 {
77   // Logger
78
static Trace logger = Trace
79                                              .getLogger(PostgreSQLSplitPlainTextBackuper.class
80                                                  .getName());
81
82   /**
83    * The dump format for this (family of) backuper.
84    */

85   public static final String JavaDoc DUMP_FORMAT = "PostgreSQL Split Plain Text Dump";
86
87   /**
88    * @see org.continuent.sequoia.controller.backup.Backuper#getDumpFormat()
89    */

90   public String JavaDoc getDumpFormat()
91   {
92     return DUMP_FORMAT;
93   }
94
95   /**
96    * Backups the DB using pg_dump/split commands; It creates splitted dump files
97    * instead of one single(huge) dump file
98    *
99    * @see org.continuent.sequoia.controller.backup.Backuper#backup(DatabaseBackend,
100    * String, String, String, String, ArrayList)
101    */

102   public Date JavaDoc backup(DatabaseBackend backend, String JavaDoc login, String JavaDoc password,
103       String JavaDoc dumpName, String JavaDoc path, ArrayList JavaDoc tables) throws BackupException
104   {
105     // Parse the URL for the connection information
106
String JavaDoc url = backend.getURL();
107     PostgreSQLUrlInfo info = new AbstractPostgreSQLBackuper.PostgreSQLUrlInfo(
108         url);
109
110     if (logger.isDebugEnabled())
111       logger.debug("Backing up database '" + info.getDbName() + "' on host '"
112           + info.getHost() + ":" + info.getPort() + "'");
113
114     try
115     {
116
117       // Create the path, if it does not already exist
118
File JavaDoc pathDir = new File JavaDoc(path);
119       if (!pathDir.exists())
120       {
121         pathDir.mkdirs();
122         pathDir.mkdir();
123       }
124
125       String JavaDoc fullPath = getDumpPhysicalPath(path, dumpName);
126
127       int exitValue = -1;
128       if (useAuthentication)
129       {
130         if (logger.isDebugEnabled())
131           logger.debug("Performing backup using authentication");
132
133         String JavaDoc[] gCmd = makeSplitCommandWithAuthentication("pg_dump", info,
134             " | split -a 4 -b 10m - " + fullPath, login, password, false);
135
136         exitValue = executeNativeCommand(gCmd);
137       }
138       else
139       {
140         String JavaDoc[] gCmdArray = makeSplitCommand("pg_dump", info,
141             " | split -a 4 -b " + splitSize + " - " + fullPath, login);
142
143         exitValue = executeNativeCommand(gCmdArray);
144       }
145
146       if (exitValue != 0)
147       {
148         printErrors();
149         throw new BackupException(
150             "pg_dump execution did not complete successfully!");
151       }
152
153     }
154     catch (Exception JavaDoc e)
155     {
156       String JavaDoc msg = "Error while performing backup";
157       logger.error(msg, e);
158       throw new BackupException(msg, e);
159     }
160
161     return new Date JavaDoc(System.currentTimeMillis());
162   }
163
164   /**
165    * Restores the DB using cat/psql commands; It restores the DB using splitted
166    * dump files.
167    *
168    * @see org.continuent.sequoia.controller.backup.Backuper#restore(DatabaseBackend,
169    * String, String, String, String, ArrayList)
170    */

171   public void restore(DatabaseBackend backend, String JavaDoc login, String JavaDoc password,
172       String JavaDoc dumpName, String JavaDoc path, ArrayList JavaDoc tables) throws BackupException
173   {
174     // Parse the URL for the connection information
175
String JavaDoc url = backend.getURL();
176     PostgreSQLUrlInfo info = new AbstractPostgreSQLBackuper.PostgreSQLUrlInfo(
177         url);
178
179     if (logger.isDebugEnabled())
180       logger.debug("Restoring database '" + info.getDbName() + "' on host '"
181           + info.getHost() + ":" + info.getPort() + "'");
182
183     // Check to see if the given path + dumpName exists
184
String JavaDoc fullPath = getDumpPhysicalPath(path, dumpName);
185
186     File JavaDoc dump = new File JavaDoc(fullPath + "aaaa");// checking only first split file,
187
// suffix length is 4
188
if (!dump.exists())
189       throw new BackupException("Backup '" + fullPath + "' does not exist!");
190
191     try
192     {
193       if (useAuthentication)
194       {
195         if (logger.isInfoEnabled())
196           logger.info("Performing database operations using authentication");
197
198         // Drop the database if it already exists
199
if (logger.isDebugEnabled())
200           logger.debug("Dropping database '" + info.getDbName() + "'");
201
202         String JavaDoc[] dropCmd = makeCommandWithAuthentication("dropdb", info, "",
203             login, password, false);
204         if (executeNativeCommand(dropCmd) != 0)
205         {
206           printErrors();
207           throw new BackupException(
208               "dropdb execution did not complete successfully!");
209         }
210
211         // Re-create the database, use the specified encoding if provided
212
if (logger.isDebugEnabled())
213           logger.debug("Re-creating '" + info.getDbName() + "'");
214
215         String JavaDoc[] createCmd = makeCommandWithAuthentication("createdb", info,
216             encoding != null ? "--encoding=" + encoding + " " : "", login,
217             password, false);
218         if (executeNativeCommand(createCmd) != 0)
219         {
220           printErrors();
221           throw new BackupException(
222               "createdb execution did not complete successfully!");
223         }
224
225         // Run a pre-restore script, if specified
226
if (preRestoreScript != null)
227         {
228           if (logger.isDebugEnabled())
229             logger.debug("Running pre-restore script '" + preRestoreScript
230                 + "' on '" + info.getDbName() + "'");
231
232           String JavaDoc[] preRestoreCmd = makeCommandWithAuthentication("psql", info,
233               " -f " + preRestoreScript, login, password, true);
234
235           if (executeNativeCommand(preRestoreCmd) != 0)
236           {
237             printErrors();
238             throw new BackupException(
239                 "psql execution did not complete successfully!");
240           }
241         }
242
243         // Use the psql command to rebuild the database
244
if (logger.isDebugEnabled())
245           logger.debug("Rebuilding '" + info.getDbName() + "' from dump '"
246               + dumpName + "'");
247
248         String JavaDoc[] replayCmd = makeSplitCommandWithAuthentication("cat" + " "
249             + fullPath + "* | psql", info, "", login, password, false);
250
251         if (executeNativeCommand(replayCmd) != 0)
252         {
253           printErrors();
254           throw new BackupException(
255               "pg_restore execution did not complete successfully!");
256         }
257
258         // Run a post-restore script, if specified
259
if (postRestoreScript != null)
260         {
261           if (logger.isDebugEnabled())
262             logger.debug("Running post-restore script '" + postRestoreScript
263                 + "' on '" + info.getDbName() + "'");
264
265           String JavaDoc[] postRestoreCmd = makeCommandWithAuthentication("psql", info,
266               " -f " + postRestoreScript, login, password, true);
267
268           if (executeNativeCommand(postRestoreCmd) != 0)
269           {
270             printErrors();
271             throw new BackupException(
272                 "psql execution did not complete successfully!");
273           }
274         }
275       }
276       else
277       // No authentication
278
{
279         // Drop the database if it already exists
280
if (logger.isDebugEnabled())
281           logger.debug("Dropping database '" + info.getDbName() + "'");
282
283         String JavaDoc dropCmd = makeCommand("dropdb", info, "", login);
284         if (executeNativeCommand(dropCmd) != 0)
285         {
286           printErrors();
287           throw new BackupException(
288               "dropdb execution did not complete successfully!");
289         }
290
291         // Re-create the database, use the specified encoding if provided
292
if (logger.isDebugEnabled())
293           logger.debug("Re-creating '" + info.getDbName() + "'");
294
295         String JavaDoc createCmd = makeCommand("createdb", info, encoding != null
296             ? "--encoding=" + encoding + " "
297             : "", login);
298         if (executeNativeCommand(createCmd) != 0)
299         {
300           printErrors();
301           throw new BackupException(
302               "createdb execution did not complete successfully!");
303         }
304
305         // Run a pre-restore script, if specified
306
if (preRestoreScript != null)
307         {
308           if (logger.isDebugEnabled())
309             logger.debug("Running pre-restore script '" + preRestoreScript
310                 + "' on '" + info.getDbName() + "'");
311
312           String JavaDoc preRestoreCmd = makeCommand("psql", info, " -f "
313               + preRestoreScript, login);
314           if (executeNativeCommand(preRestoreCmd) != 0)
315           {
316             printErrors();
317             throw new BackupException(
318                 "psql execution did not complete successfully!");
319           }
320         }
321
322         // Use the psql command to rebuild the database
323
if (logger.isDebugEnabled())
324           logger.debug("Rebuilding '" + info.getDbName() + "' from dump '"
325               + dumpName + "'");
326
327         String JavaDoc[] cmdArray = makeSplitCommand("cat" + " " + fullPath
328             + "* | psql", info, "", login);
329
330         if (executeNativeCommand(cmdArray) != 0)
331         {
332           printErrors();
333           throw new BackupException(
334               "psql execution did not complete successfully!");
335         }
336
337         // Run a post-restore script, if specified
338
if (postRestoreScript != null)
339         {
340           if (logger.isDebugEnabled())
341             logger.debug("Running post-restore script '" + postRestoreScript
342                 + "' on '" + info.getDbName() + "'");
343
344           String JavaDoc postRestoreCmd = makeCommand("psql", info, " -f "
345               + postRestoreScript, login);
346           if (executeNativeCommand(postRestoreCmd) != 0)
347           {
348             printErrors();
349             throw new BackupException(
350                 "psql execution did not complete successfully!");
351           }
352         }
353
354       }
355     }
356     catch (Exception JavaDoc e)
357     {
358       String JavaDoc msg = "Error while performing backup";
359       logger.error(msg, e);
360       throw new BackupException(msg, e);
361     }
362   }
363
364   /**
365    * @see org.continuent.sequoia.controller.backup.Backuper#fetchDump(org.continuent.sequoia.controller.backup.DumpTransferInfo,
366    * java.lang.String, java.lang.String)
367    */

368   public void fetchDump(DumpTransferInfo dumpTransferInfo, String JavaDoc path,
369       String JavaDoc dumpName) throws BackupException, IOException JavaDoc
370   {
371     BackupManager.fetchDumpFile(dumpTransferInfo, path, dumpName + ".sql");
372   }
373
374   /**
375    * @see org.continuent.sequoia.controller.backup.Backuper#deleteDump(java.lang.String,
376    * java.lang.String)
377    */

378   public void deleteDump(String JavaDoc path, String JavaDoc dumpName) throws BackupException
379   {
380     File JavaDoc dir = new File JavaDoc(path);
381
382     // Only list files that start with the given dump name
383
String JavaDoc[] files = dir.list(new DumpNameFilter(dumpName));
384
385     for (int i = 0; i < files.length; i++)
386     {
387       File JavaDoc toRemove = new File JavaDoc(getDumpPhysicalPath(path, files[i]));
388       if (logger.isDebugEnabled())
389         logger.debug("Deleting dump " + toRemove);
390       toRemove.delete();
391     }
392   }
393
394   /**
395    * Private class that filters files based on beginning of file name. This
396    * should be equal to the dump file(s) we want to delete.
397    *
398    * @author Dylan Hansen
399    */

400   class DumpNameFilter implements FilenameFilter JavaDoc
401   {
402     private String JavaDoc dumpName;
403
404     /**
405      * Creates a new instance of DumpNameFilter
406      *
407      * @param dumpName Dump name for set of file(s)
408      */

409     public DumpNameFilter(String JavaDoc dumpName)
410     {
411       this.dumpName = dumpName;
412     }
413
414     /**
415      * Does this file match the filter?
416      *
417      * @param dir Directory of file, not used
418      * @param theDump Name of current dump we're checking
419      */

420     public boolean accept(File JavaDoc dir, String JavaDoc theDump)
421     {
422       return (theDump.startsWith(dumpName) && theDump.length() == dumpName
423           .length() + 4);
424     }
425   }
426
427 }
428
Popular Tags