package jp.co.worksap.workspace.database.db2; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.Charset; import java.util.List; import javax.annotation.CheckReturnValue; import jp.co.worksap.workspace.common.OperatingSystem; import jp.co.worksap.workspace.common.PathWalker; import jp.co.worksap.workspace.common.PipingDaemon; import jp.co.worksap.workspace.common.UnArchiver; import jp.co.worksap.workspace.common.download.AuthenticationInfoProvider; import jp.co.worksap.workspace.common.download.Downloader; import lombok.extern.slf4j.Slf4j; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.io.Files; import com.google.common.io.Resources; @Slf4j public class DB2Installer { private static final Joiner COMMAND_JOINER = Joiner.on(' '); private final File db2cmd; public DB2Installer() throws IOException { OperatingSystem os = OperatingSystem.create(); PathWalker pathWalker = new PathWalker(new PathWalker.PathFindStrategy()); Optional<File> command = pathWalker.findOnPath(os.appendExtensionTo("db2cmd")); if (command.isPresent()) { db2cmd = command.get(); } else { // no command found, we use default path db2cmd = new File("C:/Program Files/IBM/SQLLIB/BIN/db2cmd.exe"); assert !db2cmd.exists(); } } public void install(DB2Configuration configuration, AuthenticationInfoProvider infoProvider) { try { if (!db2cmd.exists()) { URL downloadUrl = configuration.getUrlToDownload(); File downloadedFile = File.createTempFile("db2", ".download"); Downloader downloader = Downloader.createFor(downloadUrl, infoProvider); downloader.download(downloadUrl, downloadedFile); unpack(downloadedFile); setup(configuration, downloadedFile.getParentFile()); } configure(configuration); } catch (IOException e) { throw new IllegalStateException(e); } } private void unpack(File downloadedFile) { try { log.info("Unpacking installer..."); new UnArchiver().extract(downloadedFile, downloadedFile.getParentFile()); } finally { log.info("Unpacking completed..."); } } private void setup(DB2Configuration configuration, File location) { try { // TODO OS Dependent String setupPath = location.getAbsolutePath() + "/WSER/image/setup.exe"; File rspFile = File.createTempFile("db2Config", ".rsp"); Resources.copy(DB2Installer.class.getResource("db2.rsp"), Files.asByteSink(rspFile).openStream()); FileWriter rspFileWriter = new FileWriter(rspFile, true); rspFileWriter.write("\nDB2.USERNAME=" + configuration.getUsername()); rspFileWriter.write("\nDB2.PASSWORD=" + configuration.getPassword()); rspFileWriter.write("\nDAS_USERNAME=" + configuration.getUsername()); rspFileWriter.write("\nDAS_PASSWORD=" + configuration.getPassword()); rspFileWriter.flush(); ProcessBuilder builder = new ProcessBuilder(setupPath, "/u", rspFile.getAbsolutePath()); log.info("execute command ({}) to install db2", builder.command()); Process process = builder.start(); try { recordStdoutOf(process); recordStderrOf(process); process.getOutputStream().close(); int statusCode = process.waitFor(); if (statusCode != 0) { throw new IllegalArgumentException("Failed to install db2, status code is " + statusCode); } } catch (InterruptedException e) { throw new IllegalStateException(e); } finally { rspFileWriter.close(); process.destroy(); } } catch (IOException e) { throw new IllegalStateException(e); } } private void configure(DB2Configuration configuration) { catalogNodes(configuration.getNodes()); catalogDatabases(configuration.getDatabases()); } private void catalogDatabases(List<Database> databases) { boolean error = false; StringBuilder errorMessage = new StringBuilder("Failed to catalog database "); for (Database database : databases) { if (databaseExists(database)) { Process process = buildProcess("UNCATALOG DATABASE " + database.getAlias()); try { recordStdoutOf(process); recordStderrOf(process); process.getOutputStream().close(); int statusCode = process.waitFor(); if (statusCode != 0) { error = true; errorMessage.append(database.getAlias() + ", "); } } catch (InterruptedException | IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); } } Process process = buildProcess(database.catalogCommand()); try { recordStdoutOf(process); recordStderrOf(process); process.getOutputStream().close(); int statusCode = process.waitFor(); if (statusCode != 0) { error = true; errorMessage.append(database.getAlias() + ", "); } } catch (InterruptedException | IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); log.info(database.getAlias() + " : Done!!"); } } if (error) { throw new IllegalArgumentException(errorMessage.toString()); } } private void catalogNodes(List<Node> nodes) { boolean error = false; StringBuilder errorMessage = new StringBuilder("Failed to catalog node "); for (Node node : nodes) { if (nodeExists(node)) { Process process = buildProcess("UNCATALOG NODE " + node.getNode()); try { recordStdoutOf(process); recordStderrOf(process); process.getOutputStream().close(); int statusCode = process.waitFor(); if (statusCode != 0) { error = true; errorMessage.append(node.getNode() + ", "); } } catch (InterruptedException | IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); } } Process process = buildProcess(node.catalogCommand()); try { recordStdoutOf(process); recordStderrOf(process); process.getOutputStream().close(); int statusCode = process.waitFor(); if (statusCode != 0) { error = true; errorMessage.append(node.getNode() + ", "); } } catch (InterruptedException | IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); log.info(node.getNode() + " : Done!!"); } } if (error) { throw new IllegalArgumentException(errorMessage.toString()); } } @CheckReturnValue @VisibleForTesting boolean nodeExists(Node node) { Process process = buildProcess("LIST NODE DIRECTORY"); try { process.getOutputStream().close(); // TODO choose correct charset dynamically BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.forName("SHIFT-JIS"))); String line; while ((line = bufferedReader.readLine()) != null) { line = line.toLowerCase(); if ((line.contains("node name") || line.contains("ノード名")) && line.contains(node.getNode().toLowerCase())) { return true; } } int statusCode = process.waitFor(); if (statusCode != 0) { return false; } } catch (InterruptedException | IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); } return false; } @CheckReturnValue @VisibleForTesting boolean databaseExists(Database database) { Process process = buildProcess("LIST DATABASE DIRECTORY"); try { process.getOutputStream().close(); // TODO choose correct charset dynamically BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.forName("SHIFT-JIS"))); String line; while ((line = bufferedReader.readLine()) != null) { line = line.toLowerCase(); if ((line.contains("database alias") || line.contains("データベース別名")) && line.contains(database.getAlias().toLowerCase())) { return true; } } int statusCode = process.waitFor(); if (statusCode != 0) { return false; } } catch (InterruptedException e) { throw new IllegalStateException(e); } catch (IOException e) { throw new IllegalStateException(e); } finally { process.destroy(); } return false; } private Process buildProcess(String process) { try { List<String> command = Lists.newArrayList(db2cmd.getAbsolutePath(), "/c", "/w", "/i", "db2", process); ProcessBuilder builder = new ProcessBuilder(command); log.info("execute command ({}) to configure db2", COMMAND_JOINER.join(command)); return builder.start(); } catch (IOException e) { throw new IllegalStateException(e); } } private void recordStdoutOf(final Process process) throws IOException { Thread daemon = PipingDaemon.createThread(process.getInputStream(), "DB2 Install", "stdout"); daemon.start(); } private void recordStderrOf(final Process process) throws IOException { Thread daemon = PipingDaemon.createThread(process.getErrorStream(), "DB2 Install", "stderr"); daemon.start(); } }