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): Emmanuel Cecchet.
019: * Contributor(s): ______________________.
020: */package org.continuent.sequoia.controller.backup.backupers;
021:
022: import java.io.File;
023: import java.util.ArrayList;
024: import java.util.Date;
025: import java.util.Iterator;
026:
027: import org.continuent.sequoia.common.exceptions.BackupException;
028: import org.continuent.sequoia.controller.backend.DatabaseBackend;
029: import org.continuent.sequoia.controller.backup.Backuper;
030:
031: /**
032: * This class defines a ScriptBackuper, which performs all backup functions by
033: * invoking a generic wrapper script conventionally named 'backupmgr'. The
034: * following options are accepted in the backuper definition: <p/>
035: * <ul>
036: * <li>bindir -- Directory containing backup script.</li>
037: * <li>dumpFormat -- Dump format returned by getDumpFormat()</li>
038: * <li>options -- Options to be passed into wrapper using --flags option</li>
039: * <li>scriptname -- Wrapper script name if other than backupmgr.</li>
040: * <li>urlDecoderClassname -- Class used to decode host, port, and database
041: * from URL</li>
042: * </ul>
043: * <p/> The wrapper script semantics are described briefly below.
044: * <code><pre>NAME
045: * backupmgr
046: * SYNTAX
047: * backupmgr --op {backup | restore | delete | test}
048: * --host hostname --port port --database dbname --login dblogin
049: * --password dbpass --dumpname name --dumppath dirpath --flags options
050: * --tables tables-to-backup
051: * DESCRIPTION
052: * Perform backup, restore, and log deletion operations. The operations
053: * are defined using the --op argument, which is always supplied.
054: *
055: * backup - Perform a backup
056: * restore - Restore a backup
057: * delete - Delete a backup
058: *
059: * The backupmgr script must encapsulate fully all operations, including
060: * if necessary dropping and recreating databases or other administrative
061: * functions associated with supported operations.
062: *
063: * RETURN CODE
064: * Scripts must return 0 if successful. Any other value is considered
065: * to indicate backup failure. In addition, the script is considered to
066: * have failed if it writes to stderr. Stderr from underlying processes
067: * must therefore be suppressed or redirected if it does not represent
068: * an error.
069: * </pre></code> For additional invocation details please refer to method
070: * implementations. Note: For now, we default to dump server semantics provided
071: * by AbstractBackuper.
072: *
073: * @author <a href="mailto:robert.hodges@continuent.com">Robert Hodges</a>
074: * @versi2on 1.0
075: * @see AbstractBackuper
076: */
077: public class ScriptBackuper extends AbstractBackuper {
078: // Options used by this backuper.
079: String bindir = null;
080: String urlDecoderClassname = "org.continuent.sequoia.controller.backup.backupers.BasicUrlParser";
081: String scriptName = "backupmgr";
082: String flags = null;
083: String dumpFormat = "User defined";
084:
085: // Executor for native commands.
086: NativeCommandExec nativeCommandExec = new NativeCommandExec();
087:
088: /**
089: * Returns the dump format.
090: *
091: * @see org.continuent.sequoia.controller.backup.Backuper#getDumpFormat()
092: */
093: public String getDumpFormat() {
094: return dumpFormat;
095: }
096:
097: /**
098: * Stores options of interest to this backuper.
099: *
100: * @see Backuper#setOptions(java.lang.String)
101: */
102: public void setOptions(String options) {
103: super .setOptions(options);
104: bindir = applyOption(bindir, "bindir");
105: urlDecoderClassname = applyOption(urlDecoderClassname,
106: "urlDecoderClassname");
107: scriptName = applyOption(scriptName, "scriptName");
108: flags = applyOption(flags, "flags");
109: dumpFormat = applyOption(dumpFormat, "dumpFormat");
110: }
111:
112: // Returns option from map if the option has been set.
113: private String applyOption(String defaultValue, String optionName) {
114: String optionValue = (String) optionsMap.get(optionName);
115: return (optionValue == null) ? defaultValue : optionValue;
116: }
117:
118: /**
119: * Invokes a backup operation on the backup management script.
120: *
121: * @see org.continuent.sequoia.controller.backup.Backuper#backup(org.continuent.sequoia.controller.backend.DatabaseBackend,
122: * java.lang.String, java.lang.String, java.lang.String,
123: * java.lang.String, java.util.ArrayList)
124: */
125: public Date backup(DatabaseBackend backend, String login,
126: String password, String dumpName, String path,
127: ArrayList tables) throws BackupException {
128: // Generate and execute the command.
129: String cmd = getBackupRestoreCommand("backup", backend, login,
130: password, dumpName, path, tables);
131: if (!nativeCommandExec.safelyExecNativeCommand(cmd, null, null,
132: 0, getIgnoreStdErrOutput())) {
133: throw new BackupException(
134: "Backup operation failed with errors; see log");
135: }
136:
137: return new Date();
138: }
139:
140: /**
141: * Invokes a restore operation on the backup management script.
142: *
143: * @see org.continuent.sequoia.controller.backup.Backuper#restore(org.continuent.sequoia.controller.backend.DatabaseBackend,
144: * java.lang.String, java.lang.String, java.lang.String,
145: * java.lang.String, java.util.ArrayList)
146: */
147: public void restore(DatabaseBackend backend, String login,
148: String password, String dumpName, String path,
149: ArrayList tables) throws BackupException {
150: // Generate and execute the command.
151: String cmd = getBackupRestoreCommand("restore", backend, login,
152: password, dumpName, path, tables);
153: if (!nativeCommandExec.safelyExecNativeCommand(cmd, null, null,
154: 0, getIgnoreStdErrOutput())) {
155: throw new BackupException(
156: "Restore operation failed with errors; see log");
157: }
158: }
159:
160: /**
161: * Invokes a restore operation on the backup management script.
162: *
163: * @see org.continuent.sequoia.controller.backup.Backuper#deleteDump(java.lang.String,
164: * java.lang.String)
165: */
166: public void deleteDump(String path, String dumpName)
167: throws BackupException {
168: String backupCmd = getBackupCommand();
169: String cmd = backupCmd + " --op delete --dumpName '" + dumpName
170: + "' --dumpPath '" + path;
171: if (!nativeCommandExec.safelyExecNativeCommand(cmd, null, null,
172: 0, getIgnoreStdErrOutput())) {
173: throw new BackupException(
174: "Dump delete operation failed with errors; see log");
175: }
176: }
177:
178: // Returns an instantiated URL parser. We do this at the time of a backup
179: // operation so that we can pass a nice error message back to the caller.
180: private JdbcUrlParser getJdbcParser() throws BackupException {
181: if (urlDecoderClassname == null) {
182: return new BasicUrlParser();
183: } else {
184: try {
185: JdbcUrlParser parser = (JdbcUrlParser) Class.forName(
186: urlDecoderClassname).newInstance();
187: return parser;
188: } catch (ClassNotFoundException e) {
189: throw new BackupException(
190: "Unable to find URL decoder class: "
191: + urlDecoderClassname);
192: } catch (InstantiationException e) {
193: throw new BackupException(
194: "Unable to instantiate URL decoder class: "
195: + urlDecoderClassname);
196: } catch (IllegalAccessException e) {
197: throw new BackupException(
198: "Unable to access URL decoder class or its default constructor: "
199: + urlDecoderClassname);
200: } catch (Throwable t) {
201: throw new BackupException(
202: "Unexpected error while instantiating URL decoder class; see log",
203: t);
204: }
205: }
206: }
207:
208: // Returns the command string for a backup or restore command with full
209: // options.
210: private String getBackupRestoreCommand(String operation,
211: DatabaseBackend backend, String login, String password,
212: String dumpName, String path, ArrayList tables)
213: throws BackupException {
214: // Parse the backend URL.
215: JdbcUrlParser urlParser = getJdbcParser();
216: urlParser.setUrl(backend.getURL());
217:
218: // Create the table list, if any.
219: String tableList = null;
220: if (tables != null) {
221: Iterator iter = tables.iterator();
222: while (iter.hasNext()) {
223: String tableName = (String) iter.next();
224: if (tableList == null)
225: tableList = tableName;
226: else
227: tableList += "," + tableName;
228: }
229: }
230:
231: // Construct the command.
232: String backupCmd = getBackupCommand();
233: String cmd = backupCmd + " --op " + operation + " --host "
234: + urlParser.getHost() + " --port "
235: + urlParser.getPort() + " --database "
236: + urlParser.getDbName() + " --login " + login
237: + " --password " + password + " --dumpName " + dumpName
238: + " --dumpPath " + path;
239: if (tableList != null) {
240: cmd += " --tables " + tableList;
241: }
242: if (flags != null) {
243: cmd += " --flags " + flags;
244: }
245:
246: return cmd;
247: }
248:
249: // Returns the backup command as a path.
250: private String getBackupCommand() {
251: if (bindir == null)
252: return scriptName;
253: else
254: return bindir + File.separatorChar + scriptName;
255: }
256: }
|