view src/main/java/nl/cwi/monetdb/util/SQLRestore.java @ 98:c0ce1ea5075f embedded

Some documentation added.
author Pedro Ferreira <pedro.ferreira@monetdbsolutions.com>
date Wed, 11 Jan 2017 17:01:20 +0100 (2017-01-11)
parents 4a93f75c72c9
children 08bc9009d190
line wrap: on
line source
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0.  If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V.
 */

package nl.cwi.monetdb.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

import nl.cwi.monetdb.mcl.connection.MCLException;
import nl.cwi.monetdb.mcl.connection.mapi.MapiConnection;
import nl.cwi.monetdb.mcl.protocol.AbstractProtocol;
import nl.cwi.monetdb.mcl.protocol.ProtocolException;
import nl.cwi.monetdb.mcl.protocol.ServerResponses;

/**
 * Use this class to restore an SQL dump file.
 */
public class SQLRestore {

	private final String _host;
	private final int _port;
	private final String _user;
	private final String _password;
	private final String _dbName;

	public SQLRestore(String host, int port, String user, String password, String dbName) throws IOException {
		if (host == null || user == null || password == null || dbName == null)
			throw new NullPointerException();
		_host = host;
		_port = port;
		_user = user;
		_password = password;
		_dbName = dbName;
	}

	private class ServerResponseReader implements Runnable {
		private final MapiConnection _is;
		private final AtomicBoolean _errorState = new AtomicBoolean(false);
		private String _errorMessage = null;

		ServerResponseReader(MapiConnection is) {
			_is = is;
		}

		public void run() {
			AbstractProtocol protocol = _is.getProtocol();
			int next;
			String line;
			try {
				while (true) {
					protocol.waitUntilPrompt();
					next = protocol.getCurrentServerResponse();
					line = protocol.getRemainingStringLine(0);
					if (line == null)
						break;
					switch (next) {
						case ServerResponses.ERROR:
							_errorMessage = line;
							_errorState.set(true);
							return;
						default:
							// do nothing...
					}
				}
			} catch (IOException e) {
				_errorMessage = e.getMessage();
				_errorState.set(true);
			} finally {
				_is.close();
			}
		}

		/**
		 * @return whether the server has responded with an error. Any
		 *         error is regarded as fatal.
		 */
		public boolean inErrorState() {
			return _errorState.get();
		}

		/**
		 * @return the error message if inErrorState() is true. Behaviour is
		 *		not defined if called before inErrorState is true.
		 */
		public String getErrorMessage() {
			return _errorMessage;
		}
	}

	/**
	 * Restores a given SQL dump to the database.
	 *
	 * @param source
	 * @throws IOException
	 */
	public void restore(File source) throws IOException {
		MapiConnection server = new MapiConnection(null,null, "sql", false, true, _host, _port, _dbName);
		try {
			server.connect(_user, _password);

			ServerResponseReader srr = new ServerResponseReader(server);
			Thread responseReaderThread = new Thread(srr);
			responseReaderThread.start();
			try {
				// FIXME: we assume here that the dump is in system's default encoding
				BufferedReader sourceData = new BufferedReader(new FileReader(source));
				try {
					AbstractProtocol protocol = server.getProtocol();
					protocol.writeNextQuery(null, "s", null); // signal that a new statement (or series of) is coming
					while(!srr.inErrorState()) {
						char[] buf = new char[4096];
						int result = sourceData.read(buf);
						if (result < 0)
							break;
						protocol.writeNextQuery(null, new String(buf, 0, result), null);
					}
				} finally {
					sourceData.close();
				}
			} finally {
				try {
					responseReaderThread.join();
				} catch (InterruptedException e) {
					throw new IOException(e.getMessage());
				}

				// if the server signalled an error, we should respect it...
				if (srr.inErrorState()) {
					throw new IOException(srr.getErrorMessage());
				}
			}
		} catch (MCLException | ProtocolException e) {
			throw new IOException(e.getMessage());
		} finally {
			server.close();
		}
	}

	public void close() {
		// do nothing at the moment...
	}

	public static void main(String[] args) throws IOException {
		if (args.length != 6) {
			System.err.println("USAGE: java " + SQLRestore.class.getName() +
					" <host> <port> <user> <password> <dbname> <dumpfile>");
			System.exit(1);
		}

		// parse arguments
		String host = args[0];
		int port = Integer.parseInt(args[1]); // FIXME: catch NumberFormatException
		String user = args[2];
		String password = args[3];
		String dbName = args[4];
		File dumpFile = new File(args[5]);

		// check arguments
		if (!dumpFile.isFile() || !dumpFile.canRead()) {
			System.err.println("Cannot read: " + dumpFile);
			System.exit(1);
		}

		SQLRestore md = new SQLRestore(host, port, user, password, dbName);
		try {
			System.out.println("Start restoring " + dumpFile);
			long duration = -System.currentTimeMillis();
			md.restore(dumpFile);
			duration += System.currentTimeMillis();
			System.out.println("Restoring took: " + duration + "ms");
		} finally {
			md.close();
		}
	}
}