001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.server.database; 021 022 import org.apache.commons.io.IOUtils; 023 import org.apache.commons.lang.StringUtils; 024 import org.apache.derby.drda.NetworkServerControl; 025 import org.sonar.api.CoreProperties; 026 import org.sonar.api.config.Settings; 027 import org.sonar.api.database.DatabaseProperties; 028 import org.sonar.api.utils.Logs; 029 import org.sonar.api.utils.SonarException; 030 import org.sonar.server.platform.ServerStartException; 031 032 import java.io.File; 033 import java.io.FileOutputStream; 034 import java.io.IOException; 035 import java.io.PrintWriter; 036 import java.net.InetAddress; 037 import java.util.Properties; 038 039 public class EmbeddedDatabase { 040 041 private static final String DEFAULT_USER = "sonar"; 042 private static final String DEFAULT_PWD = "sonar"; 043 044 private NetworkServerControl serverControl = null; 045 046 private File dbHome; 047 private Properties dbProps; 048 private PrintWriter dbLog; 049 050 public EmbeddedDatabase(Settings settings) { 051 this.dbHome = getDataDirectory(settings); 052 this.dbProps = getDefaultProperties(settings); 053 } 054 055 public EmbeddedDatabase(File dbHome, Properties dbProps) { 056 this.dbHome = dbHome; 057 this.dbProps = dbProps; 058 } 059 060 public File getDataDir() { 061 return dbHome; 062 } 063 064 protected File getDataDirectory(Settings settings) { 065 String dirName = settings.getString(DatabaseProperties.PROP_EMBEDDED_DATA_DIR); 066 if (StringUtils.isBlank(dirName)) { 067 File sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME)); 068 if (!sonarHome.isDirectory() || !sonarHome.exists()) { 069 throw new ServerStartException("Sonar home directory does not exist"); 070 } 071 return new File(sonarHome, "data"); 072 } 073 return new File(dirName); 074 } 075 076 public void setDbLog(PrintWriter dbLog) { 077 this.dbLog = dbLog; 078 } 079 080 public void start() { 081 if (dbHome.exists() && !dbHome.isDirectory()) { 082 throw new SonarException("Database home " + dbHome.getPath() + " is not a directory"); 083 } 084 if (!dbHome.exists()) { 085 dbHome.mkdirs(); 086 } 087 System.setProperty("derby.system.home", dbHome.getPath()); 088 089 saveDerbyPropertiesFile(); 090 startListening(); 091 Logs.INFO.info("Embedded database started. Data stored in: " + dbHome.getAbsolutePath()); 092 } 093 094 private void startListening() { 095 try { 096 int port = Integer.parseInt(dbProps.getProperty("derby.drda.portNumber")); 097 String host = dbProps.getProperty("derby.drda.host"); 098 serverControl = new NetworkServerControl(InetAddress.getByName(host), port, DEFAULT_USER, DEFAULT_PWD); 099 Logs.INFO.info("Starting embedded database on port " + port); 100 serverControl.start(dbLog); 101 ensureServerIsUp(); 102 } catch (Exception e) { 103 throw new SonarException(e); 104 } 105 } 106 107 private void saveDerbyPropertiesFile() { 108 FileOutputStream output = null; 109 try { 110 File derbyProps = new File(dbHome.getPath() + "/derby.properties"); 111 output = new FileOutputStream(derbyProps); 112 dbProps.store(output, "GENERATED FILE, DO NOT EDIT ME UNLESS YOU WANT TO LOOSE YOUR TIME ;O)"); 113 114 } catch (IOException e) { 115 throw new SonarException(e); 116 117 } finally { 118 IOUtils.closeQuietly(output); 119 } 120 } 121 122 public void stop() { 123 if (serverControl != null) { 124 try { 125 serverControl.shutdown(); 126 ensureServerIsDown(); 127 serverControl = null; 128 Logs.INFO.info("Embedded database stopped"); 129 130 } catch (Exception e) { 131 throw new SonarException(e); 132 } 133 } 134 } 135 136 private void ensureServerIsUp() { 137 for (int retry = 0; retry < 100; retry++) { 138 try { 139 serverControl.ping(); 140 return; 141 142 } catch (Exception ex) { 143 sleep(300); 144 } 145 } 146 throw new SonarException("Embedded database does not respond to ping requests"); 147 } 148 149 private void ensureServerIsDown() { 150 for (int retry = 0; retry < 100; retry++) { 151 try { 152 serverControl.ping(); 153 sleep(300); 154 155 } catch (SonarException se) { 156 throw se; 157 158 } catch (Exception e) { 159 // normal case: the database does not respond to ping 160 return; 161 } 162 } 163 throw new SonarException("Fail to stop embedded database"); 164 } 165 166 167 private void sleep(long time) { 168 try { 169 Thread.sleep(time); 170 } catch (InterruptedException e) { 171 throw new SonarException("Fail to ping embedded database", e); 172 } 173 } 174 175 public static Properties getDefaultProperties(Settings settings) { 176 Properties props = new Properties(); 177 props.setProperty("derby.drda.startNetworkServer", "true"); 178 props.setProperty("derby.drda.host", StringUtils.defaultIfBlank(settings.getString("sonar.derby.drda.host"), "localhost")); 179 props.setProperty("derby.drda.portNumber", StringUtils.defaultIfBlank(settings.getString("sonar.derby.drda.portNumber"), "1527")); 180 props.setProperty("derby.drda.maxThreads", StringUtils.defaultIfBlank(settings.getString("sonar.derby.drda.maxThreads"), "20")); 181 props.setProperty("derby.drda.minThreads", StringUtils.defaultIfBlank(settings.getString("sonar.derby.drda.minThreads"), "2")); 182 props.setProperty("derby.drda.logConnections", StringUtils.defaultIfBlank(settings.getString("sonar.derby.drda.logConnections"), "false")); 183 props.setProperty("derby.stream.error.logSeverityLevel", StringUtils.defaultIfBlank(settings.getString("sonar.derby.stream.error.logSeverityLevel"), "20000")); 184 props.setProperty("derby.connection.requireAuthentication", "true"); 185 props.setProperty("derby.user." + DEFAULT_USER, DEFAULT_PWD); 186 return props; 187 } 188 189 }