KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2006 Continuent Inc.
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): Adam Fletcher, Mykola Paliyenko.
19  * Contributor(s): Emmanuel Cecchet, Stephane Giron
20  */

21
22 package org.continuent.sequoia.controller.backup.backupers;
23
24 import java.io.File JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.sql.Connection JavaDoc;
28 import java.sql.DriverManager JavaDoc;
29 import java.sql.Statement JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Date JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.regex.Matcher JavaDoc;
34 import java.util.regex.Pattern JavaDoc;
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 /**
41  * MSSQL backuper inspired from the PostgreSQL backuper. <br>
42  * This backuper takes the following options: <br>
43  * urlHeader: expected URL header for the backend. Default is
44  * "jdbc:jtds:sqlserver:" <br>
45  * driverClassName: driver class name to load. Default is the driver class
46  * defined for the targeted backend. If you need to use a specific driver for
47  * backup/restore operation, you can force it here.
48  * <p>
49  * To use it, edit the virtual database config's <Backup> node:
50  *
51  * <pre>
52  * <Backup>
53  * <Backuper backuperName="MSSQLServer" className="org.continuent.sequoia.controller.backup.backupers.MSSQLBackuper" options=""/>
54  * <Backuper backuperName="MSSQLServerWithjTDSDriver" className="org.continuent.sequoia.controller.backup.backupers.MSSQLBackuper" options="urlHeader=jdbc:jtds:sqlserver:,driverClassName=net.sourceforge.jtds.jdbc.Driver"/>
55  * <Backuper backuperName="MSSQLServerWithMSDriver" className="org.continuent.sequoia.controller.backup.backupers.MSSQLBackuper" options="urlHeader=jdbc:microsoft:sqlserver:,driverClassName=com.microsoft.jdbc.sqlserver.SQLServerDriver"/>
56  * </Backup>
57  *</pre>
58  *
59  * Then in the console to take the backups:<br>
60  * backup mybackend database.dump MSSQLServer \\<fileserver>\<share>
61  *
62  * @author <a HREF="mailto:adamf@powersteeringsoftware.com">Adam Fletcher</a>
63  * @author <a HREF="mailto:mpaliyenko@gmail.com">Mykola Paliyenko</a>
64  * @author <a HREF="mailto:emmanuel.cecchet@continuent.com">Emmanuel Cecchet</a>
65  * @author <a HREF="mailto:stephane.giron@continuent.com">Stephane Giron</a>
66  */

67 public class MSSQLBackuper extends AbstractBackuper
68 {
69
70   private static final String JavaDoc DEFAULT_MSSQL_PORT = "1433";
71   private static final String JavaDoc DEFAULT_MSSQL_HOST = "localhost";
72   private static final String JavaDoc DEFAULT_JDBC_URL = "jdbc:jtds:sqlserver:";
73
74   private static final Object JavaDoc URL_OPTION = "urlHeader";
75   private static final Object JavaDoc DRIVER_CLASS_NAME_OPTION = "driverClassName";
76
77   static Trace logger = Trace
78                                                            .getLogger(MSSQLBackuper.class
79                                                                .getName());
80
81   private ArrayList JavaDoc errors;
82
83   /**
84    * Creates a new <code>MSSQLBackuper</code> object
85    */

86   public MSSQLBackuper()
87   {
88     errors = new ArrayList JavaDoc();
89   }
90
91   /**
92    * @see org.continuent.sequoia.controller.backup.Backuper#getDumpFormat()
93    */

94   public String JavaDoc getDumpFormat()
95   {
96     return "MSSQL raw dump";
97   }
98
99   /**
100    * @see org.continuent.sequoia.controller.backup.Backuper#backup(org.continuent.sequoia.controller.backend.DatabaseBackend,
101    * java.lang.String, java.lang.String, java.lang.String,
102    * java.lang.String, java.util.ArrayList)
103    */

104   public Date JavaDoc backup(DatabaseBackend backend, String JavaDoc login, String JavaDoc password,
105       String JavaDoc dumpName, String JavaDoc path, ArrayList JavaDoc tables) throws BackupException
106   {
107     String JavaDoc url = backend.getURL();
108
109     String JavaDoc expectedUrl = (String JavaDoc) optionsMap.get(URL_OPTION);
110     if (expectedUrl == null)
111       expectedUrl = DEFAULT_JDBC_URL;
112
113     if (!url.startsWith(expectedUrl))
114     {
115       throw new BackupException("Unsupported db url " + url);
116     }
117     MSSQLUrlInfo info = new MSSQLUrlInfo(url);
118     Connection JavaDoc con;
119
120     try
121     {
122       String JavaDoc driverClassName = (String JavaDoc) optionsMap
123           .get(DRIVER_CLASS_NAME_OPTION);
124       if (driverClassName == null)
125         driverClassName = backend.getDriverClassName();
126
127       // Load the driver and connect to the database
128
Class.forName(driverClassName);
129       con = DriverManager.getConnection(url + ";user=" + login + ";password="
130           + password);
131     }
132     catch (Exception JavaDoc e)
133     {
134       String JavaDoc msg = "Error while performing backup during creation of connection";
135       logger.error(msg, e);
136       throw new BackupException(msg, e);
137     }
138
139     try
140     {
141       File JavaDoc pathDir = new File JavaDoc(path);
142       if (!pathDir.exists())
143       {
144         pathDir.mkdirs();
145         pathDir.mkdir();
146       }
147
148       /*
149        * What should be done here: 1) verify the path is a UNC path 2) connect
150        * to the server via JDBC 3) issue the 'BACKUP <dbname> TO DISK='<path>'
151        * <path> must be UNC connect to the configured backend with JDBC
152        */

153       // String dumpPath = path + File.separator + dumpName;
154
// we don't use File.seperator here because we are using UNC paths
155
String JavaDoc dumpPath = path + "\\" + dumpName;
156
157       if (logger.isDebugEnabled())
158       {
159         logger.debug("Dumping " + backend.getURL() + " in " + dumpPath);
160       }
161
162       Statement JavaDoc stmt = con.createStatement();
163       String JavaDoc sqlStatement = "BACKUP DATABASE " + info.getDbName()
164           + " TO DISK = '" + dumpPath + "'";
165
166       logger.debug("sql statement for backup: " + sqlStatement);
167
168       boolean backupResult = stmt.execute(sqlStatement);
169
170       if (backupResult)
171       {
172         String JavaDoc msg = "BACKUP returned false";
173         logger.error(msg);
174         throw new BackupException(msg);
175       }
176     }
177     catch (Exception JavaDoc e)
178     {
179       String JavaDoc msg = "Error while performing backup";
180       logger.error(msg, e);
181       throw new BackupException(msg, e);
182     }
183     finally
184     {
185       try
186       {
187         con.close();
188       }
189       catch (Exception JavaDoc e)
190       {
191         String JavaDoc msg = "Error while performing backup during close connection";
192         logger.error(msg, e);
193         throw new BackupException(msg, e);
194       }
195     }
196     return new Date JavaDoc();
197   }
198
199   /**
200    * @see org.continuent.sequoia.controller.backup.Backuper#restore(org.continuent.sequoia.controller.backend.DatabaseBackend,
201    * java.lang.String, java.lang.String, java.lang.String,
202    * java.lang.String, java.util.ArrayList)
203    */

204   public void restore(DatabaseBackend backend, String JavaDoc login, String JavaDoc password,
205       String JavaDoc dumpName, String JavaDoc path, ArrayList JavaDoc tables) throws BackupException
206   {
207     String JavaDoc url = backend.getURL();
208
209     String JavaDoc expectedUrl = (String JavaDoc) optionsMap.get(URL_OPTION);
210
211     if (expectedUrl == null)
212       expectedUrl = DEFAULT_JDBC_URL;
213
214     if (!url.startsWith(expectedUrl))
215     {
216       throw new BackupException("Unsupported db url " + url);
217     }
218
219     MSSQLUrlInfo info = new MSSQLUrlInfo(url);
220     Connection JavaDoc con;
221     try
222     {
223       String JavaDoc driverClassName = (String JavaDoc) optionsMap
224           .get(DRIVER_CLASS_NAME_OPTION);
225       if (driverClassName == null)
226         driverClassName = backend.getDriverClassName();
227
228       // Load the driver and connect to the database
229
Class.forName(driverClassName);
230
231       // NOTE: you have to connect to the master database to restore,
232
// because if you connect to the current DB you are USING the db
233
// and therefore LOCKING the db.
234

235       con = DriverManager.getConnection(expectedUrl + "//" + info.getHost()
236           + ":" + info.getPort() + "/master;user=" + login + ";password="
237           + password);
238     }
239     catch (Exception JavaDoc e)
240     {
241       String JavaDoc msg = "Error while performing restore during creation of connection";
242       logger.error(msg, e);
243       throw new BackupException(msg, e);
244     }
245
246     try
247     {
248       File JavaDoc pathDir = new File JavaDoc(path);
249       if (!pathDir.exists())
250       {
251         pathDir.mkdirs();
252         pathDir.mkdir();
253       }
254       String JavaDoc dumpPath = path + File.separator + dumpName;
255       if (logger.isDebugEnabled())
256       {
257         logger.debug("Restoring " + backend.getURL() + " from " + dumpPath);
258       }
259
260       Statement JavaDoc stmt = con.createStatement();
261
262       String JavaDoc sqlAtatement = "RESTORE DATABASE " + info.getDbName()
263           + " FROM DISK = '" + dumpPath + "'";
264
265       boolean backupResult = stmt.execute(sqlAtatement);
266
267       if (backupResult)
268       {
269         String JavaDoc msg = "restore returned false";
270         logger.error(msg);
271         throw new BackupException(msg);
272       }
273     }
274     catch (Exception JavaDoc e)
275     {
276       String JavaDoc msg = "Error while performing restore";
277       logger.error(msg, e);
278       throw new BackupException(msg, e);
279     }
280     finally
281     {
282       try
283       {
284         con.close();
285       }
286       catch (Exception JavaDoc e)
287       {
288         String JavaDoc msg = "Error while performing restore during close connection";
289         logger.error(msg, e);
290         throw new BackupException(msg, e);
291       }
292     }
293   }
294
295   String JavaDoc getProcessOutput(Process JavaDoc process) throws IOException JavaDoc
296   {
297     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
298
299     InputStream JavaDoc pis = process.getInputStream();
300     InputStream JavaDoc pes = process.getErrorStream();
301
302     int c = pis.read();
303     while (c != -1)
304     {
305       sb.append(new Character JavaDoc((char) c));
306       c = pis.read();
307     }
308     c = pes.read();
309     while (c != -1)
310     {
311       sb.append(new Character JavaDoc((char) c));
312       c = pes.read();
313     }
314
315     return sb.toString();
316   }
317
318   /**
319    * @see org.continuent.sequoia.controller.backup.Backuper#deleteDump(java.lang.String,
320    * java.lang.String)
321    */

322   public void deleteDump(String JavaDoc path, String JavaDoc dumpName) throws BackupException
323   {
324     File JavaDoc toRemove = new File JavaDoc(getDumpPhysicalPath(path, dumpName));
325     if (logger.isDebugEnabled())
326       logger.debug("Deleting compressed dump " + toRemove);
327     toRemove.delete();
328   }
329
330   /**
331    * Get the dump physical path from its logical name
332    *
333    * @param path the path where the dump is stored
334    * @param dumpName dump logical name
335    * @return path to zip file
336    */

337   private String JavaDoc getDumpPhysicalPath(String JavaDoc path, String JavaDoc dumpName)
338   {
339     return path + File.separator + dumpName;
340   }
341
342   /**
343    * Allow to parse PostgreSQL URL.
344    */

345   protected class MSSQLUrlInfo
346   {
347     private boolean isLocal;
348
349     private String JavaDoc host;
350
351     private String JavaDoc port;
352
353     private String JavaDoc dbName;
354
355     /**
356      * Creates a new <code>MSSQLUrlInfo</code> object, used to parse the MSSQL
357      * JDBC options. If host and/or port aren't specified, will default to
358      * localhost:1433. Note that database name must be specified.
359      *
360      * @param url the MSSQL JDBC url to parse
361      */

362     public MSSQLUrlInfo(String JavaDoc url)
363     {
364       String JavaDoc expectedUrl = (String JavaDoc) optionsMap.get(URL_OPTION);
365
366       if (expectedUrl == null)
367         expectedUrl = DEFAULT_JDBC_URL;
368
369       // Used to parse url
370
Pattern JavaDoc pattern = Pattern
371           .compile(expectedUrl
372               + "((//([a-zA-Z0-9_\\-.]+|\\[[a-fA-F0-9:]+])((:(\\d+))|))/|)([a-zA-Z][a-zA-Z0-9_\\-]*)(\\?.*)?");
373
374       Matcher JavaDoc matcher;
375
376       matcher = pattern.matcher(url);
377
378       if (matcher.matches())
379       {
380         if (matcher.group(3) != null)
381           host = matcher.group(3);
382         else
383           host = DEFAULT_MSSQL_HOST;
384
385         if (matcher.group(6) != null)
386           port = matcher.group(6);
387         else
388           port = DEFAULT_MSSQL_PORT;
389
390         dbName = matcher.group(7);
391       }
392     }
393
394     /**
395      * Gets the HostParameters of this postgresql jdbc url as a String that can
396      * be used to pass into cmd line/shell calls.
397      *
398      * @return a string that can be used to pass into a cmd line/shell call.
399      */

400     public String JavaDoc getHostParametersString()
401     {
402       logger.debug("getHostParamertsString host: " + host + " port: " + port);
403
404       if (isLocal)
405       {
406         return "//localhost";
407       }
408       else
409       {
410         return "//" + host + ":" + port;
411       }
412     }
413
414     /**
415      * Gets the database name part of this postgresql jdbc url.
416      *
417      * @return the database name part of this postgresql jdbc url.
418      */

419     public String JavaDoc getDbName()
420     {
421       return dbName;
422     }
423
424     /**
425      * Gets the host part of this postgresql jdbc url.
426      *
427      * @return the host part of this postgresql jdbc url.
428      */

429     public String JavaDoc getHost()
430     {
431       return host;
432     }
433
434     /**
435      * Gets the port part of this postgresql jdbc url.
436      *
437      * @return the port part of this postgresql jdbc url.
438      */

439     public String JavaDoc getPort()
440     {
441       return port;
442     }
443
444     /**
445      * Checks whether this postgresql jdbc url refers to a local db or not, i.e.
446      * has no host specified, e.g. jdbc:postgresql:myDb.
447      *
448      * @return true if this postgresql jdbc url has no host specified, i.e.
449      * refers to a local db.
450      */

451     public boolean isLocal()
452     {
453       return isLocal;
454     }
455
456   }
457
458   protected void printErrors()
459   {
460     Iterator JavaDoc it = errors.iterator();
461     while (it.hasNext())
462     {
463       logger.info(it.next());
464     }
465   }
466 }
467
Popular Tags