001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 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 }