From: Simon Brandhof Date: Fri, 13 Jul 2012 18:28:40 +0000 (+0200) Subject: SONAR-3633 improve the management of server-side settings X-Git-Tag: 3.2~87 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6cc4d52f9791ad2547111543e41662c7522f89b8;p=sonarqube.git SONAR-3633 improve the management of server-side settings * do not save default resource permissions in a db migration but in a server-side extension * new component to save settings from server-side components. It will have to be used by ruby app later. --- diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java index c21cd08f48e..a496d826fc6 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java @@ -26,6 +26,7 @@ import org.sonar.api.ServerComponent; import org.sonar.core.persistence.MyBatis; import java.util.List; +import java.util.Map; public class PropertiesDao implements BatchComponent, ServerComponent { @@ -89,4 +90,45 @@ public class PropertiesDao implements BatchComponent, ServerComponent { MyBatis.closeQuietly(session); } } + + public void deleteGlobalProperties() { + SqlSession session = mybatis.openSession(); + PropertiesMapper mapper = session.getMapper(PropertiesMapper.class); + try { + mapper.deleteGlobalProperties(); + session.commit(); + + } finally { + MyBatis.closeQuietly(session); + } + } + + public void deleteGlobalProperty(String key) { + SqlSession session = mybatis.openSession(); + PropertiesMapper mapper = session.getMapper(PropertiesMapper.class); + try { + mapper.deleteGlobalProperty(key); + session.commit(); + + } finally { + MyBatis.closeQuietly(session); + } + } + + public void saveGlobalProperties(Map properties) { + SqlSession session = mybatis.openBatchSession(); + PropertiesMapper mapper = session.getMapper(PropertiesMapper.class); + try { + for (Map.Entry entry : properties.entrySet()) { + mapper.deleteGlobalProperty(entry.getKey()); + } + for (Map.Entry entry : properties.entrySet()) { + mapper.insert(new PropertyDto().setKey(entry.getKey()).setValue(entry.getValue())); + } + session.commit(); + + } finally { + MyBatis.closeQuietly(session); + } + } } diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java index 1f75ed36dfb..cb63e4bb227 100644 --- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java @@ -32,5 +32,6 @@ public interface PropertiesMapper { PropertyDto selectByKey(PropertyDto key); void update(PropertyDto property); void insert(PropertyDto property); - + void deleteGlobalProperty(String key); + void deleteGlobalProperties(); } diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 768c4f4dfe6..913e1ff6d9c 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -10,28 +10,6 @@ ALTER TABLE GROUP_ROLES ALTER COLUMN ID RESTART WITH 2; INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 1); INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 2); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (1, 'sonar.role.admin.TRK.defaultGroups', 'sonar-administrators'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (2, 'sonar.role.admin.TRK.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (3, 'sonar.role.user.TRK.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (4, 'sonar.role.user.TRK.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (5, 'sonar.role.codeviewer.TRK.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (6, 'sonar.role.codeviewer.TRK.defaultUsers', ''); - --- COMPATIBILITY WITH OLD VERSIONS OF VIEWS PLUGIN -> see migration 320 -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (7, 'sonar.role.admin.VW.defaultGroups', 'sonar-administrators'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (8, 'sonar.role.admin.VW.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (9, 'sonar.role.user.VW.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (10, 'sonar.role.user.VW.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (11, 'sonar.role.codeviewer.VW.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (12, 'sonar.role.codeviewer.VW.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (13, 'sonar.role.admin.SVW.defaultGroups', 'sonar-administrators'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (14, 'sonar.role.admin.SVW.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (15, 'sonar.role.user.SVW.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (16, 'sonar.role.user.SVW.defaultUsers', ''); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (17, 'sonar.role.codeviewer.SVW.defaultGroups', 'Anyone,sonar-users'); -INSERT INTO PROPERTIES(ID, PROP_KEY, TEXT_VALUE) VALUES (18, 'sonar.role.codeviewer.SVW.defaultUsers', ''); -ALTER TABLE PROPERTIES ALTER COLUMN ID RESTART WITH 19; - INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('2'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('10'); diff --git a/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml b/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml index 768ee357f24..d3d55c0008f 100644 --- a/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml @@ -50,13 +50,13 @@ update properties set text_value = #{value} where id = #{id} - + INSERT INTO properties (prop_key, resource_id, user_id, text_value) VALUES (#{key}, #{resourceId}, #{userId}, #{value}) - + select properties_seq.NEXTVAL from DUAL @@ -64,4 +64,12 @@ VALUES (#{id}, #{key}, #{resourceId}, #{userId}, #{value}) + + delete from properties where prop_key=#{id} and resource_id is null and user_id is null + + + + delete from properties where resource_id is null and user_id is null + + diff --git a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java index 20c39047808..6fef82d9fd7 100644 --- a/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java @@ -19,11 +19,13 @@ */ package org.sonar.core.properties; +import com.google.common.collect.Maps; import org.junit.Before; import org.junit.Test; import org.sonar.core.persistence.AbstractDaoTestCase; import java.util.List; +import java.util.TreeMap; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; @@ -34,12 +36,12 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { private PropertiesDao dao; @Before - public void createDao() throws Exception { + public void createDao() { dao = new PropertiesDao(getMyBatis()); } @Test - public void shouldFindUserIdsForFavouriteResource() throws Exception { + public void shouldFindUserIdsForFavouriteResource() { setupData("shouldFindUserIdsForFavouriteResource"); List userIds = dao.findUserIdsForFavouriteResource(2L); assertThat(userIds.size(), is(2)); @@ -47,7 +49,7 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { } @Test - public void selectGlobalProperties() throws Exception { + public void selectGlobalProperties() { setupData("selectGlobalProperties"); List properties = dao.selectGlobalProperties(); assertThat(properties.size(), is(2)); @@ -62,7 +64,7 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { } @Test - public void selectProjectProperties() throws Exception { + public void selectProjectProperties() { setupData("selectProjectProperties"); List properties = dao.selectProjectProperties("org.struts:struts"); assertThat(properties.size(), is(1)); @@ -73,7 +75,7 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { } @Test - public void setProperty_update() throws Exception { + public void setProperty_update() { setupData("update"); dao.setProperty(new PropertyDto().setKey("global.key").setValue("new_global")); @@ -85,7 +87,7 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { } @Test - public void setProperty_insert() throws Exception { + public void setProperty_insert() { setupData("insert"); dao.setProperty(new PropertyDto().setKey("global.key").setValue("new_global")); @@ -95,6 +97,36 @@ public class PropertiesDaoTest extends AbstractDaoTestCase { checkTables("insert", "properties"); } + @Test + public void deleteGlobalProperties() { + setupData("deleteGlobalProperties"); + + dao.deleteGlobalProperties(); + + checkTables("deleteGlobalProperties", "properties"); + } + + @Test + public void deleteGlobalProperty() { + setupData("deleteGlobalProperty"); + + dao.deleteGlobalProperty("to_be_deleted"); + + checkTables("deleteGlobalProperty", "properties"); + } + + @Test + public void saveGlobalProperties() { + setupData("saveGlobalProperties"); + + TreeMap props = Maps.newTreeMap(); + props.put("to_be_inserted", "inserted"); + props.put("to_be_updated", "updated"); + dao.saveGlobalProperties(props); + + checkTable("saveGlobalProperties", "properties", "prop_key", "text_value", "resource_id", "user_id"); + } + private PropertyDto findById(List properties, int id) { for (PropertyDto property : properties) { if (property.getId() == id) { diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties-result.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties-result.xml new file mode 100644 index 00000000000..a5cfed3378b --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties-result.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties.xml new file mode 100644 index 00000000000..3e5eb87705c --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperties.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty-result.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty-result.xml new file mode 100644 index 00000000000..0428139feb6 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty-result.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty.xml new file mode 100644 index 00000000000..aaf0fd642d3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteGlobalProperty.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties-result.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties-result.xml new file mode 100644 index 00000000000..f12984a944b --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties-result.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties.xml new file mode 100644 index 00000000000..b0fa0be6deb --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/saveGlobalProperties.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java index 2b8423a67d7..5b118ecd4fc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java @@ -210,43 +210,34 @@ public class Settings implements BatchComponent, ServerComponent { } else { newValue += "," + StringUtils.trim(value); } - properties.put(key, newValue); - return this; + return setProperty(key, newValue); } public final Settings setProperty(String key, @Nullable String value) { - if (!clearIfNullValue(key, value)) { + if (value == null) { + properties.remove(key); + doOnRemoveProperty(key); + } else { properties.put(key, StringUtils.trim(value)); + doOnSetProperty(key, value); } return this; } public final Settings setProperty(String key, @Nullable Boolean value) { - if (!clearIfNullValue(key, value)) { - properties.put(key, String.valueOf(value)); - } - return this; + return setProperty(key, String.valueOf(value)); } public final Settings setProperty(String key, @Nullable Integer value) { - if (!clearIfNullValue(key, value)) { - properties.put(key, String.valueOf(value)); - } - return this; + return setProperty(key, String.valueOf(value)); } public final Settings setProperty(String key, @Nullable Long value) { - if (!clearIfNullValue(key, value)) { - properties.put(key, String.valueOf(value)); - } - return this; + return setProperty(key, String.valueOf(value)); } public final Settings setProperty(String key, @Nullable Double value) { - if (!clearIfNullValue(key, value)) { - properties.put(key, String.valueOf(value)); - } - return this; + return setProperty(key, String.valueOf(value)); } public final Settings setProperty(String key, @Nullable Date date) { @@ -276,24 +267,21 @@ public class Settings implements BatchComponent, ServerComponent { } public final Settings setProperties(Map props) { - properties.clear(); + clear(); return addProperties(props); } public final Settings setProperty(String key, @Nullable Date date, boolean includeTime) { - if (!clearIfNullValue(key, date)) { - properties.put(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date)); - } - return this; + return setProperty(key, includeTime ? DateUtils.formatDateTime(date) : DateUtils.formatDate(date)); } public final Settings removeProperty(String key) { - properties.remove(key); - return this; + return setProperty(key, (String) null); } public final Settings clear() { properties.clear(); + doOnClearProperties(); return this; } @@ -308,14 +296,6 @@ public class Settings implements BatchComponent, ServerComponent { return definitions; } - private boolean clearIfNullValue(String key, @Nullable Object value) { - if (value == null) { - properties.remove(key); - return true; - } - return false; - } - /** * Create empty settings. Definition of available properties is loaded from the given annotated class. * This method is usually used by unit tests. @@ -323,4 +303,13 @@ public class Settings implements BatchComponent, ServerComponent { public static Settings createForComponent(Object component) { return new Settings(new PropertyDefinitions(component)); } + + protected void doOnSetProperty(String key, @Nullable String value) { + } + + protected void doOnRemoveProperty(String key) { + } + + protected void doOnClearProperties() { + } } diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/Backup.java b/sonar-server/src/main/java/org/sonar/server/configuration/Backup.java index 8b6583a959f..7e495cf8746 100644 --- a/sonar-server/src/main/java/org/sonar/server/configuration/Backup.java +++ b/sonar-server/src/main/java/org/sonar/server/configuration/Backup.java @@ -31,6 +31,7 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.LoggerFactory; import org.sonar.api.database.DatabaseSession; import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.server.platform.PersistentSettings; import javax.annotation.Nullable; import java.io.IOException; @@ -52,12 +53,12 @@ public class Backup { backupables = new ArrayList(); } - public Backup(DatabaseSession session) { + public Backup(DatabaseSession session, PersistentSettings persistentSettings) { this(); this.session = session; backupables.add(new MetricsBackup(session)); - backupables.add(new PropertiesBackup(session)); + backupables.add(new PropertiesBackup(persistentSettings)); // Note that order is important, because profile can have reference to rule backupables.add(new RulesBackup(session)); backupables.add(new ProfilesBackup(session)); @@ -153,27 +154,27 @@ public class Backup { private XStream getConfiguredXstream() { XStream xStream = new XStream( - new XppDriver() { - @Override - public HierarchicalStreamWriter createWriter(Writer out) { - return new PrettyPrintWriter(out) { - @Override - protected void writeText(QuickWriter writer, @Nullable String text) { - if (text != null) { - writer.write("' so we will - * split all occurrences of this sequence into two CDATA first one would contain ']]' and second '>' - */ - text = StringUtils.replace(text, "]]>", "]]]]>"); - writer.write(text); - writer.write("]]>"); - } + new XppDriver() { + @Override + public HierarchicalStreamWriter createWriter(Writer out) { + return new PrettyPrintWriter(out) { + @Override + protected void writeText(QuickWriter writer, @Nullable String text) { + if (text != null) { + writer.write("' so we will + * split all occurrences of this sequence into two CDATA first one would contain ']]' and second '>' + */ + text = StringUtils.replace(text, "]]>", "]]]]>"); + writer.write(text); + writer.write("]]>"); } - }; - } - }); + } + }; + } + }); xStream.processAnnotations(SonarConfig.class); xStream.addDefaultImplementation(ArrayList.class, Collection.class); diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java b/sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java index d0fb42a0777..359db78cbaf 100644 --- a/sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java +++ b/sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java @@ -19,61 +19,56 @@ */ package org.sonar.server.configuration; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.thoughtworks.xstream.XStream; import org.apache.commons.collections.CollectionUtils; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; -import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.configuration.Property; +import org.sonar.server.platform.PersistentSettings; -import java.util.ArrayList; import java.util.List; +import java.util.Map; public class PropertiesBackup implements Backupable { - private DatabaseSession databaseSession; - private static final String FROM_GLOBAL_PROPERTIES = "from " + Property.class.getSimpleName() + " p WHERE p.resourceId IS NULL and user_id is null"; + private final PersistentSettings persistentSettings; - public PropertiesBackup(DatabaseSession databaseSession) { - this.databaseSession = databaseSession; + public PropertiesBackup(PersistentSettings persistentSettings) { + this.persistentSettings = persistentSettings; } public void exportXml(SonarConfig sonarConfig) { - List xmlProperties = new ArrayList(); + List xmlProperties = Lists.newArrayList(); - List dbProperties = databaseSession.createQuery(FROM_GLOBAL_PROPERTIES).getResultList(); - if (dbProperties != null) { - for (Property dbProperty : dbProperties) { - String propKey = dbProperty.getKey(); - if (!CoreProperties.SERVER_ID.equals(propKey)) { - // "sonar.core.id" must never be restored, it is unique for a server and it created once at the 1rst server startup - xmlProperties.add(new Property(dbProperty.getKey(), dbProperty.getValue())); - } + for (Map.Entry entry : persistentSettings.getProperties().entrySet()) { + // "sonar.core.id" must never be restored, it is unique for a server and it created once at the 1rst server startup + if (!CoreProperties.SERVER_ID.equals(entry.getKey())) { + xmlProperties.add(new Property(entry.getKey(), entry.getValue())); } - sonarConfig.setProperties(xmlProperties); } + sonarConfig.setProperties(xmlProperties); } public void importXml(SonarConfig sonarConfig) { LoggerFactory.getLogger(getClass()).info("Restore properties"); - clearProperties(); + // "sonar.core.id" property should not be cleared, because it is the unique key used to identify the server + // and it is used by the batch to verify that it connects to the same DB as the remote server (see SONAR-3126). + String serverId = persistentSettings.getString(CoreProperties.SERVER_ID); + String serverStartTime = persistentSettings.getString(CoreProperties.SERVER_STARTTIME); + + Map properties = Maps.newHashMap(); if (CollectionUtils.isNotEmpty(sonarConfig.getProperties())) { for (Property xmlProperty : sonarConfig.getProperties()) { - String propKey = xmlProperty.getKey(); - if (!CoreProperties.SERVER_ID.equals(propKey)) { - // "sonar.core.id" must never be restored, it is unique for a server and it created once at the 1rst server startup - databaseSession.save(new Property(propKey, xmlProperty.getValue())); - } + properties.put(xmlProperty.getKey(), xmlProperty.getValue()); } } - databaseSession.commit(); - } - - private void clearProperties() { - // "sonar.core.id" property should not be cleared, because it is the unique key used to identify the server - // and it is used by the batch to verify that it connects to the same DB as the remote server (see SONAR-3126). - databaseSession.createQuery("delete " + FROM_GLOBAL_PROPERTIES + " and prop_key != '" + CoreProperties.SERVER_ID + "'").executeUpdate(); + properties.put(CoreProperties.SERVER_ID, serverId); + properties.put(CoreProperties.SERVER_STARTTIME, serverStartTime); + persistentSettings.deleteProperties(); + persistentSettings.saveProperties(properties); } public void configure(XStream xStream) { diff --git a/sonar-server/src/main/java/org/sonar/server/platform/GlobalSettingsUpdater.java b/sonar-server/src/main/java/org/sonar/server/platform/GlobalSettingsUpdater.java deleted file mode 100644 index d2d65070bce..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/platform/GlobalSettingsUpdater.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.platform; - -import org.apache.commons.configuration.Configuration; -import org.sonar.api.config.GlobalPropertyChangeHandler; - -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; - -/** - * Update cache of global settings (see org.sonar.api.config.Settings) and notify org.sonar.api.config.GlobalPropertyChangeHandler extensions - * - * @since 3.0 - */ -public class GlobalSettingsUpdater { - private ServerSettings settings; - private Configuration deprecatedConf; - private List changeHandlers; - - public GlobalSettingsUpdater(ServerSettings settings, Configuration config, List changeHandlers) { - this.settings = settings; - this.deprecatedConf = config; - this.changeHandlers = changeHandlers; - } - - public GlobalSettingsUpdater(ServerSettings settings, Configuration config) { - this(settings, config, Collections.emptyList()); - } - - public void setProperty(String key, @Nullable String value) { - settings.setProperty(key, value); - deprecatedConf.setProperty(key, value); - - GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create(key, value); - for (GlobalPropertyChangeHandler changeHandler : changeHandlers) { - changeHandler.onChange(change); - } - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/PersistentSettings.java b/sonar-server/src/main/java/org/sonar/server/platform/PersistentSettings.java new file mode 100644 index 00000000000..0b922d9bb25 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/platform/PersistentSettings.java @@ -0,0 +1,86 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.platform; + +import com.google.common.collect.Maps; +import org.sonar.api.ServerComponent; +import org.sonar.api.config.Settings; +import org.sonar.core.properties.PropertiesDao; +import org.sonar.core.properties.PropertyDto; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * @since 3.2 + */ +public class PersistentSettings implements ServerComponent { + private final PropertiesDao propertiesDao; + private final ServerSettings settings; + + public PersistentSettings(PropertiesDao propertiesDao, ServerSettings settings) { + this.propertiesDao = propertiesDao; + this.settings = settings; + } + + public void start() { + Map databaseProperties = Maps.newHashMap(); + for (PropertyDto property : propertiesDao.selectGlobalProperties()) { + databaseProperties.put(property.getKey(), property.getValue()); + } + settings.activateDatabaseSettings(SonarHome.getHome(), databaseProperties); + } + + public PersistentSettings saveProperty(String key, @Nullable String value) { + settings.setProperty(key, value); + propertiesDao.setProperty(new PropertyDto().setKey(key).setValue(value)); + return this; + } + + public PersistentSettings removeProperty(String key) { + settings.removeProperty(key); + propertiesDao.deleteGlobalProperty(key); + return this; + } + + public PersistentSettings deleteProperties() { + settings.clear(); + propertiesDao.deleteGlobalProperties(); + return this; + } + + public PersistentSettings saveProperties(Map properties) { + settings.addProperties(properties); + propertiesDao.saveGlobalProperties(properties); + return this; + } + + public String getString(String key) { + return settings.getString(key); + } + + public Map getProperties() { + return settings.getProperties(); + } + + public Settings getSettings() { + return settings; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index e20fed0c4c0..287f8e2f541 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -78,19 +78,7 @@ import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.qualitymodel.DefaultModelManager; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; -import org.sonar.server.startup.ActivateDefaultProfiles; -import org.sonar.server.startup.DeleteDeprecatedMeasures; -import org.sonar.server.startup.EnableProfiles; -import org.sonar.server.startup.GeneratePluginIndex; -import org.sonar.server.startup.GwtPublisher; -import org.sonar.server.startup.JdbcDriverDeployer; -import org.sonar.server.startup.RegisterMetrics; -import org.sonar.server.startup.RegisterNewDashboards; -import org.sonar.server.startup.RegisterNewFilters; -import org.sonar.server.startup.RegisterProvidedProfiles; -import org.sonar.server.startup.RegisterQualityModels; -import org.sonar.server.startup.RegisterRules; -import org.sonar.server.startup.ServerMetadataPersister; +import org.sonar.server.startup.*; import org.sonar.server.ui.CodeColorizers; import org.sonar.server.ui.JRubyI18n; import org.sonar.server.ui.SecurityRealmFactory; @@ -184,7 +172,7 @@ public final class Platform { private void startCoreComponents() { coreContainer = rootContainer.createChild(); - coreContainer.addSingleton(ServerDatabaseSettingsLoader.class); + coreContainer.addSingleton(PersistentSettings.class); coreContainer.addSingleton(DefaultDatabaseConnector.class); coreContainer.addSingleton(ServerExtensionInstaller.class); coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class); @@ -204,7 +192,6 @@ public final class Platform { servicesContainer.addSingleton(ReviewDatabaseStore.class); servicesContainer.addSingleton(WorkflowEngine.class); - servicesContainer.addSingleton(GlobalSettingsUpdater.class); servicesContainer.addSingleton(HttpDownloader.class); servicesContainer.addSingleton(UpdateCenterClient.class); servicesContainer.addSingleton(UpdateCenterMatrixFactory.class); @@ -267,6 +254,7 @@ public final class Platform { startupContainer.addSingleton(GeneratePluginIndex.class); startupContainer.addSingleton(RegisterNewFilters.class); startupContainer.addSingleton(RegisterNewDashboards.class); + startupContainer.addSingleton(SetDefaultProjectPermissions.class); startupContainer.startComponents(); startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart(); diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerDatabaseSettingsLoader.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerDatabaseSettingsLoader.java deleted file mode 100644 index 0596e81cfcb..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerDatabaseSettingsLoader.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.server.platform; - -import org.sonar.core.properties.PropertiesDao; - -/** - * @since 3.0 - */ -public final class ServerDatabaseSettingsLoader { - - private PropertiesDao propertiesDao; - private ServerSettings settings; - - public ServerDatabaseSettingsLoader(PropertiesDao propertiesDao, ServerSettings settings) { - this.propertiesDao = propertiesDao; - this.settings = settings; - } - - public void start() { - settings.activateDatabaseSettings(propertiesDao); - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerSettings.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerSettings.java index fbcaaaa576e..07f0f4e65b4 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerSettings.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerSettings.java @@ -22,15 +22,17 @@ package org.sonar.server.platform; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.configuration.Configuration; import org.sonar.api.CoreProperties; +import org.sonar.api.ServerComponent; +import org.sonar.api.config.GlobalPropertyChangeHandler; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; import org.sonar.core.config.ConfigurationUtils; -import org.sonar.core.properties.PropertiesDao; -import org.sonar.core.properties.PropertyDto; +import javax.annotation.Nullable; import javax.servlet.ServletContext; import java.io.File; -import java.util.List; +import java.util.Collections; +import java.util.Map; import java.util.Properties; /** @@ -44,50 +46,42 @@ import java.util.Properties; * * @since 2.12 */ -public class ServerSettings extends Settings { +public class ServerSettings extends Settings implements ServerComponent { public static final String DEPLOY_DIR = "sonar.web.deployDir"; - private PropertiesDao propertiesDao; private Configuration deprecatedConfiguration; private File deployDir; + private GlobalPropertyChangeHandler[] changeHandlers; + + public ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, ServletContext servletContext, GlobalPropertyChangeHandler[] changeHandlers) { + this(definitions, deprecatedConfiguration, getDeployDir(servletContext), SonarHome.getHome(), changeHandlers); + } public ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, ServletContext servletContext) { - super(definitions); - this.deprecatedConfiguration = deprecatedConfiguration; - this.deployDir = getDeployDir(servletContext); - load(); + this(definitions, deprecatedConfiguration, servletContext, new GlobalPropertyChangeHandler[0]); } - ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, File deployDir, File sonarHome) { + @VisibleForTesting + ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, File deployDir, File sonarHome, GlobalPropertyChangeHandler[] changeHandlers) { super(definitions); this.deprecatedConfiguration = deprecatedConfiguration; this.deployDir = deployDir; - load(sonarHome); - } - - public ServerSettings activateDatabaseSettings(PropertiesDao dao) { - return activateDatabaseSettings(dao, SonarHome.getHome()); - } - - @VisibleForTesting - ServerSettings activateDatabaseSettings(PropertiesDao dao, File sonarHome) { - this.propertiesDao = dao; - load(sonarHome); - return this; + this.changeHandlers = changeHandlers; + load(sonarHome, Collections.emptyMap()); } - private ServerSettings load() { - return load(SonarHome.getHome()); + public ServerSettings activateDatabaseSettings(File sonarHome, Map databaseProperties) { + return load(sonarHome, databaseProperties); } - private ServerSettings load(File sonarHome) { - clear(); - setProperty(CoreProperties.SONAR_HOME, sonarHome.getAbsolutePath()); - setProperty(DEPLOY_DIR, deployDir.getAbsolutePath()); + private ServerSettings load(File sonarHome, Map databaseSettings) { + properties.clear(); + properties.put(CoreProperties.SONAR_HOME, sonarHome.getAbsolutePath()); + properties.put(DEPLOY_DIR, deployDir.getAbsolutePath()); // order is important : the last override the first - loadDatabaseSettings(); + properties.putAll(databaseSettings); loadPropertiesFile(sonarHome); addEnvironmentVariables(); addSystemProperties(); @@ -98,15 +92,6 @@ public class ServerSettings extends Settings { return this; } - private void loadDatabaseSettings() { - if (propertiesDao != null) { - List dpProps = propertiesDao.selectGlobalProperties(); - for (PropertyDto dbProp : dpProps) { - setProperty(dbProp.getKey(), dbProp.getValue()); - } - } - } - private void loadPropertiesFile(File sonarHome) { File propertiesFile = new File(sonarHome, "conf/sonar.properties"); if (!propertiesFile.isFile() || !propertiesFile.exists()) { @@ -116,7 +101,9 @@ public class ServerSettings extends Settings { try { Properties p = ConfigurationUtils.openProperties(propertiesFile); p = ConfigurationUtils.interpolateEnvVariables(p); - addProperties(p); + for (Map.Entry entry : p.entrySet()) { + properties.put(entry.getKey().toString(), entry.getValue().toString()); + } } catch (Exception e) { throw new IllegalStateException("Fail to load configuration file: " + propertiesFile, e); } @@ -133,4 +120,24 @@ public class ServerSettings extends Settings { } return dir; } + + @Override + protected void doOnSetProperty(String key, @Nullable String value) { + deprecatedConfiguration.setProperty(key, value); + + GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create(key, value); + for (GlobalPropertyChangeHandler changeHandler : changeHandlers) { + changeHandler.onChange(change); + } + } + + @Override + protected void doOnRemoveProperty(String key) { + deprecatedConfiguration.clearProperty(key); + } + + @Override + protected void doOnClearProperties() { + deprecatedConfiguration.clear(); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewDashboards.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewDashboards.java index 5f11a57d670..82943acaa0d 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewDashboards.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewDashboards.java @@ -93,7 +93,7 @@ public final class RegisterNewDashboards { .setOrderIndex(index); activeDashboardDao.insert(activeDashboardDto); - LOG.info("New dashboard '" + dashboardDto.getName() + "' registered"); + LoggerFactory.getLogger(getClass()).info("New dashboard '" + dashboardDto.getName() + "' registered"); } protected DashboardDto register(String name, Dashboard dashboard) { diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewFilters.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewFilters.java index 470a2eeaad5..1a9c7b1d233 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewFilters.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterNewFilters.java @@ -53,7 +53,7 @@ public final class RegisterNewFilters { } public void start() { - TimeProfiler profiler = new TimeProfiler().start("Register filters"); + TimeProfiler profiler = new TimeProfiler(LoggerFactory.getLogger(getClass())).start("Register filters"); for (FilterTemplate template : filterTemplates) { if (shouldRegister(template.getName())) { diff --git a/sonar-server/src/main/java/org/sonar/server/startup/ServerMetadataPersister.java b/sonar-server/src/main/java/org/sonar/server/startup/ServerMetadataPersister.java index 93c3a92e877..52592b35527 100644 --- a/sonar-server/src/main/java/org/sonar/server/startup/ServerMetadataPersister.java +++ b/sonar-server/src/main/java/org/sonar/server/startup/ServerMetadataPersister.java @@ -19,43 +19,29 @@ */ package org.sonar.server.startup; +import com.google.common.collect.ImmutableMap; +import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; -import org.sonar.api.database.DatabaseSession; -import org.sonar.api.database.configuration.Property; import org.sonar.api.platform.Server; +import org.sonar.server.platform.PersistentSettings; import java.text.SimpleDateFormat; -public class ServerMetadataPersister { +public final class ServerMetadataPersister { private final Server server; - private final DatabaseSession session; + private final PersistentSettings persistentSettings; - public ServerMetadataPersister(Server server, DatabaseSession session) { + public ServerMetadataPersister(Server server, PersistentSettings persistentSettings) { this.server = server; - this.session = session; + this.persistentSettings = persistentSettings; } public void start() { - setProperty(CoreProperties.SERVER_ID, server.getId()); - setProperty(CoreProperties.SERVER_VERSION, server.getVersion()); - setProperty(CoreProperties.SERVER_STARTTIME, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(server.getStartedAt())); - session.commit(); - } - - private void setProperty(String key, String value) { - Property prop = session.getSingleResult(Property.class, "key", key); - - if (value == null && prop != null) { - session.removeWithoutFlush(prop); - - } else if (value != null) { - if (prop == null) { - prop = new Property(key, value); - } else { - prop.setValue(value); - } - session.saveWithoutFlush(prop); - } + LoggerFactory.getLogger(getClass()).debug("Persisting server metadata"); + persistentSettings.saveProperties(ImmutableMap.of( + CoreProperties.SERVER_ID, server.getId(), + CoreProperties.SERVER_VERSION, server.getVersion(), + CoreProperties.SERVER_STARTTIME, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(server.getStartedAt()))); } } \ No newline at end of file diff --git a/sonar-server/src/main/java/org/sonar/server/startup/SetDefaultProjectPermissions.java b/sonar-server/src/main/java/org/sonar/server/startup/SetDefaultProjectPermissions.java new file mode 100644 index 00000000000..318a8ff6089 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/startup/SetDefaultProjectPermissions.java @@ -0,0 +1,57 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.startup; + +import com.google.common.collect.Maps; +import org.slf4j.LoggerFactory; +import org.sonar.server.platform.PersistentSettings; + +import java.util.Map; + +/** + * @since 3.2 + */ +public class SetDefaultProjectPermissions { + private final PersistentSettings persistentSettings; + + public SetDefaultProjectPermissions(PersistentSettings persistentSettings) { + this.persistentSettings = persistentSettings; + } + + public void start() { + if (persistentSettings.getSettings().getKeysStartingWith("sonar.role.").isEmpty()) { + LoggerFactory.getLogger(SetDefaultProjectPermissions.class).info("Setting default project permissions"); + Map props = Maps.newHashMap(); + props.put("sonar.role.admin.TRK.defaultGroups", "sonar-administrators"); + props.put("sonar.role.user.TRK.defaultGroups", "Anyone,sonar-users"); + props.put("sonar.role.codeviewer.TRK.defaultGroups", "Anyone,sonar-users"); + + // Support old versions of Views plugin + props.put("sonar.role.admin.VW.defaultGroups", "sonar-administrators"); + props.put("sonar.role.user.VW.defaultGroups", "Anyone,sonar-users"); + props.put("sonar.role.codeviewer.VW.defaultGroups", "Anyone,sonar-users"); + props.put("sonar.role.admin.SVW.defaultGroups", "sonar-administrators"); + props.put("sonar.role.user.SVW.defaultGroups", "Anyone,sonar-users"); + props.put("sonar.role.codeviewer.SVW.defaultGroups", "Anyone,sonar-users"); + + persistentSettings.saveProperties(props); + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index a5eb9095852..cad1ff6d9a7 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -57,10 +57,7 @@ import org.sonar.server.filters.Filter; import org.sonar.server.filters.FilterExecutor; import org.sonar.server.filters.FilterResult; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; -import org.sonar.server.platform.GlobalSettingsUpdater; -import org.sonar.server.platform.NewUserNotifier; -import org.sonar.server.platform.Platform; -import org.sonar.server.platform.ServerIdGenerator; +import org.sonar.server.platform.*; import org.sonar.server.plugins.*; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; @@ -335,7 +332,7 @@ public final class JRubyFacade { } public void setGlobalProperty(String key, @Nullable String value) { - get(GlobalSettingsUpdater.class).setProperty(key, value); + get(ServerSettings.class).setProperty(key, value); } public Settings getSettings() { diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/320_move_default_roles.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/320_move_default_roles.rb index 75ee2d51658..8635e4d3890 100644 --- a/sonar-server/src/main/webapp/WEB-INF/db/migrate/320_move_default_roles.rb +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/320_move_default_roles.rb @@ -31,6 +31,7 @@ class MoveDefaultRoles < ActiveRecord::Migration class User < ActiveRecord::Base end + class UserRole < ActiveRecord::Base end @@ -49,28 +50,11 @@ class MoveDefaultRoles < ActiveRecord::Migration # upgrade from version < 3.2. move_groups move_users - else - create_default_groups('admin', 'TRK', 'sonar-administrators') - create_default_groups('user', 'TRK', 'Anyone,sonar-users') - create_default_groups('codeviewer', 'TRK', 'Anyone,sonar-users') - - # Support old versions of Views plugin - create_default_groups('admin', 'VW', 'sonar-administrators') - create_default_groups('user', 'VW', 'Anyone,sonar-users') - create_default_groups('codeviewer', 'VW', 'Anyone,sonar-users') - create_default_groups('admin', 'SVW', 'sonar-administrators') - create_default_groups('user', 'SVW', 'Anyone,sonar-users') - create_default_groups('codeviewer', 'SVW', 'Anyone,sonar-users') end end private - def self.create_default_groups(role, qualifier, groups) - Property.create(:prop_key => "sonar.role.#{role}.#{qualifier}.defaultGroups", :text_value => groups) - Property.create(:prop_key => "sonar.role.#{role}.#{qualifier}.defaultUsers", :text_value => '') - end - def self.move_groups groups_per_role={} group_roles = GroupRole.find(:all, :conditions => ['resource_id is null and role like ?', 'default-%']) diff --git a/sonar-server/src/test/java/org/sonar/server/configuration/ProfilesBackupTest.java b/sonar-server/src/test/java/org/sonar/server/configuration/ProfilesBackupTest.java index 5cd0cb6b63d..a762d9bcc14 100644 --- a/sonar-server/src/test/java/org/sonar/server/configuration/ProfilesBackupTest.java +++ b/sonar-server/src/test/java/org/sonar/server/configuration/ProfilesBackupTest.java @@ -31,6 +31,7 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleParam; import org.sonar.api.rules.RulePriority; import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.server.platform.PersistentSettings; import org.sonar.test.TestUtils; import java.io.IOException; @@ -44,6 +45,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; public class ProfilesBackupTest extends AbstractDbUnitTestCase { @@ -157,7 +159,7 @@ public class ProfilesBackupTest extends AbstractDbUnitTestCase { */ @Test public void shouldSupportMissingEnabledField() throws IOException { - Backup backup = new Backup(getSession()); + Backup backup = new Backup(getSession(), mock(PersistentSettings.class)); backup.doImportXml(FileUtils.readFileToString(TestUtils.getResource(getClass(), "shouldSupportMissingEnabledField.xml"))); RulesProfile profile = getSession().getSingleResult(RulesProfile.class, "name", "Missing enabled field"); @@ -166,7 +168,7 @@ public class ProfilesBackupTest extends AbstractDbUnitTestCase { @Test public void shouldSupportEnabledField() throws IOException { - Backup backup = new Backup(getSession()); + Backup backup = new Backup(getSession(), mock(PersistentSettings.class)); backup.doImportXml(FileUtils.readFileToString(TestUtils.getResource(getClass(), "shouldSupportEnabledField.xml"))); RulesProfile enabledProfile = getSession().getSingleResult(RulesProfile.class, "name", "Enabled"); diff --git a/sonar-server/src/test/java/org/sonar/server/configuration/PropertiesBackupTest.java b/sonar-server/src/test/java/org/sonar/server/configuration/PropertiesBackupTest.java index 078a39dc41a..5e91f5fe5b7 100644 --- a/sonar-server/src/test/java/org/sonar/server/configuration/PropertiesBackupTest.java +++ b/sonar-server/src/test/java/org/sonar/server/configuration/PropertiesBackupTest.java @@ -19,102 +19,93 @@ */ package org.sonar.server.configuration; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.CharEncoding; +import com.google.common.collect.ImmutableMap; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; import org.junit.Before; import org.junit.Test; +import org.sonar.api.CoreProperties; import org.sonar.api.database.configuration.Property; import org.sonar.jpa.test.AbstractDbUnitTestCase; -import org.sonar.test.TestUtils; +import org.sonar.server.platform.PersistentSettings; import java.util.Arrays; import java.util.Collection; +import java.util.Map; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.startsWith; -import static org.hamcrest.collection.IsCollectionContaining.hasItem; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; public class PropertiesBackupTest extends AbstractDbUnitTestCase { - private SonarConfig sonarConfig; + private PersistentSettings persistentSettings; + private PropertiesBackup backup; @Before public void setup() { - sonarConfig = new SonarConfig(); + persistentSettings = mock(PersistentSettings.class); + backup = new PropertiesBackup(persistentSettings); } @Test - public void shouldExportProperties() { - setupData("shouldExportProperties"); + public void export_properties() { + when(persistentSettings.getProperties()).thenReturn(ImmutableMap.of("key1", "value1", "key2", "value2")); - new PropertiesBackup(getSession()).exportXml(sonarConfig); + SonarConfig config = new SonarConfig(); + backup.exportXml(config); - Property prop1 = new Property("key1", "value1"); - Property prop2 = new Property("key2", "value2"); - Property prop3 = new Property("sonar.core.version", "3.1"); - - assertTrue(CollectionUtils.isEqualCollection(sonarConfig.getProperties(), Arrays.asList(prop1, prop2, prop3))); + assertThat(config.getProperties()).containsOnly(new Property("key1", "value1"), new Property("key2", "value2")); } @Test - public void shouldNotExportPropertiesLinkedToResources() { - setupData("shouldNotExportPropertiesLinkedToResources"); - - new PropertiesBackup(getSession()).exportXml(sonarConfig); + public void do_not_export_server_id() { + when(persistentSettings.getProperties()).thenReturn(ImmutableMap.of(CoreProperties.SERVER_ID, "111")); - Property prop1 = new Property("key1", "value1"); - Property prop2 = new Property("key2", "value2"); + SonarConfig config = new SonarConfig(); + backup.exportXml(config); - assertTrue(CollectionUtils.isEqualCollection(sonarConfig.getProperties(), Arrays.asList(prop1, prop2))); + assertThat(config.getProperties()).isEmpty(); } - @Test - public void shouldExportAnArrayProperty() { - setupData("shouldExportAnArrayProperty"); - - new PropertiesBackup(getSession()).exportXml(sonarConfig); - - assertThat(sonarConfig.getProperties(), hasItem(new Property("key1", "value1,value2,value3"))); - } - - @Test - public void shouldImportProperties() { - setupData("shouldImportProperties"); - - Collection newProperties = Arrays.asList(new Property("key1", "value1"), new Property("key2", "value2"), new Property("key3", "value3")); - sonarConfig.setProperties(newProperties); - - new PropertiesBackup(getSession()).importXml(sonarConfig); - - checkTables("shouldImportProperties", "properties"); - } @Test - public void shouldNotImportSonarCoreIdProperty() { - setupData("shouldNotImportSonarCoreIdProperty"); - - Collection newProperties = Arrays.asList(new Property("sonar.core.id", "11111111")); - sonarConfig.setProperties(newProperties); - - new PropertiesBackup(getSession()).importXml(sonarConfig); - - checkTables("shouldNotImportSonarCoreIdProperty", "properties"); + public void import_backup_of_properties() { + Collection newProperties = Arrays.asList(new Property("key1", "value1"), new Property("key2", "value2")); + SonarConfig config = new SonarConfig(); + config.setProperties(newProperties); + + backup.importXml(config); + + verify(persistentSettings).saveProperties(argThat(new BaseMatcher>() { + public boolean matches(Object o) { + Map map = (Map) o; + return map.get("key1").equals("value1") && map.get("key2").equals("value2"); + } + + public void describeTo(Description description) { + } + })); } @Test - public void shouldImportMultilineProperties() throws Exception { - setupData("shouldImportMultilineProperties"); - - new Backup(getSession()).doImportXml( - FileUtils.readFileToString( - TestUtils.getResource(getClass(), "backup-with-multiline-property.xml"), CharEncoding.UTF_8)); - - Property property = getSession().getSingleResult(Property.class, "key", "sonar.multiline.secured"); - assertThat(property.getValue(), startsWith("ONQwdcwcwwdadalkdmaiQGMqMVnhtAbhxwjjoVkHbWgx")); - assertThat(property.getValue(), endsWith("mmmm")); - + public void do_not_import_server_id() { + // initial server id + when(persistentSettings.getString(CoreProperties.SERVER_ID)).thenReturn("111"); + + Collection newProperties = Arrays.asList(new Property(CoreProperties.SERVER_ID, "999")); + SonarConfig config = new SonarConfig(); + config.setProperties(newProperties); + backup.importXml(config); + + verify(persistentSettings).saveProperties(argThat(new BaseMatcher>() { + public boolean matches(Object o) { + Map map = (Map) o; + return map.get(CoreProperties.SERVER_ID).equals("111"); + } + + public void describeTo(Description description) { + } + })); } } diff --git a/sonar-server/src/test/java/org/sonar/server/platform/ServerSettingsTest.java b/sonar-server/src/test/java/org/sonar/server/platform/ServerSettingsTest.java index 9d79811cb53..2ffff06b75d 100644 --- a/sonar-server/src/test/java/org/sonar/server/platform/ServerSettingsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/platform/ServerSettingsTest.java @@ -19,54 +19,62 @@ */ package org.sonar.server.platform; +import com.google.common.collect.ImmutableMap; import org.apache.commons.configuration.BaseConfiguration; import org.junit.Test; +import org.sonar.api.config.GlobalPropertyChangeHandler; import org.sonar.api.config.PropertyDefinitions; -import org.sonar.core.persistence.AbstractDaoTestCase; -import org.sonar.core.properties.PropertiesDao; import java.io.File; import java.net.URISyntaxException; +import java.util.Map; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.fest.assertions.Assertions.assertThat; -public class ServerSettingsTest extends AbstractDaoTestCase { +public class ServerSettingsTest { private static File home = getHome(); @Test public void shouldLoadPropertiesFile() { - ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home); + ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home, new GlobalPropertyChangeHandler[0]); - assertThat(settings.getString("hello"), is("world")); + assertThat(settings.getString("hello")).isEqualTo("world"); } @Test public void systemPropertiesShouldOverridePropertiesFile() { System.setProperty("ServerSettingsTestEnv", "in_env"); - ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home); + ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home, new GlobalPropertyChangeHandler[0]); - assertThat(settings.getString("ServerSettingsTestEnv"), is("in_env")); + assertThat(settings.getString("ServerSettingsTestEnv")).isEqualTo("in_env"); } @Test(expected = IllegalStateException.class) public void shouldFailIfPropertiesFileNotFound() { File sonarHome = new File("unknown/path"); - new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), sonarHome); + new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), sonarHome, new GlobalPropertyChangeHandler[0]); } @Test - public void shouldActivateDatabaseSettings() { - setupData("db/shared"); + public void activateDatabaseSettings() { + ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home, new GlobalPropertyChangeHandler[0]); - ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home); - settings.activateDatabaseSettings(new PropertiesDao(getMyBatis()), home); + Map databaseProperties = ImmutableMap.of("in_db", "true"); + settings.activateDatabaseSettings(home, databaseProperties); - assertThat(settings.getString("global_only"), is("is_global")); - assertThat(settings.getString("global_and_project"), is("is_global")); - assertThat(settings.getString("project_only"), nullValue()); + assertThat(settings.getString("in_db")).isEqualTo("true"); + } + + @Test + public void file_settings_override_db_settings() { + ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home, new GlobalPropertyChangeHandler[0]); + assertThat(settings.getString("in_file")).isEqualTo("true"); + + Map databaseProperties = ImmutableMap.of("in_file", "false"); + settings.activateDatabaseSettings(home, databaseProperties); + + assertThat(settings.getString("in_file")).isEqualTo("true"); } private static File getHome() { diff --git a/sonar-server/src/test/java/org/sonar/server/startup/ServerMetadataPersisterTest.java b/sonar-server/src/test/java/org/sonar/server/startup/ServerMetadataPersisterTest.java index 7fe0f5fbf7b..ba00c5c61cd 100644 --- a/sonar-server/src/test/java/org/sonar/server/startup/ServerMetadataPersisterTest.java +++ b/sonar-server/src/test/java/org/sonar/server/startup/ServerMetadataPersisterTest.java @@ -19,28 +19,34 @@ */ package org.sonar.server.startup; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.sonar.api.CoreProperties; import org.sonar.api.platform.Server; -import org.sonar.jpa.test.AbstractDbUnitTestCase; +import org.sonar.server.platform.PersistentSettings; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Map; import java.util.TimeZone; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; -public class ServerMetadataPersisterTest extends AbstractDbUnitTestCase { +public class ServerMetadataPersisterTest { private TimeZone initialTimeZone; + private PersistentSettings persistentSettings; @Before public void fixTimeZone() { initialTimeZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + persistentSettings = mock(PersistentSettings.class); } @After @@ -50,41 +56,28 @@ public class ServerMetadataPersisterTest extends AbstractDbUnitTestCase { @Test public void testSaveProperties() throws ParseException { - setupData("testSaveProperties"); - persist(newServer()); - checkTables("testSaveProperties", "properties"); - } - - @Test - public void testUpdateExistingProperties() throws ParseException { - setupData("testUpdateExistingProperties"); - persist(newServer()); - checkTables("testUpdateExistingProperties", "properties"); - } - - @Test - public void testDeleteProperties() throws ParseException { - setupData("testDeleteProperties"); - Server server = mock(Server.class); - when(server.getStartedAt()).thenReturn(new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2010-05-18 17:59"));//this is a mandatory not-null property - persist(server); - checkTables("testDeleteProperties", "properties"); - } - - private void persist(Server server) { - ServerMetadataPersister persister = new ServerMetadataPersister(server, getSession()); - persister.start(); - } - - private Server newServer() throws ParseException { Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2010-05-18 17:59"); Server server = mock(Server.class); when(server.getPermanentServerId()).thenReturn("1abcdef"); when(server.getId()).thenReturn("123"); - when(server.getVersion()).thenReturn("2.2"); + when(server.getVersion()).thenReturn("3.2"); when(server.getStartedAt()).thenReturn(date); + ServerMetadataPersister persister = new ServerMetadataPersister(server, persistentSettings); + persister.start(); - return server; + verify(persistentSettings).saveProperties(argThat(new BaseMatcher>() { + public boolean matches(Object o) { + Map map = (Map) o; + return map.get(CoreProperties.SERVER_ID).equals("123") + && map.get(CoreProperties.SERVER_VERSION).equals("3.2") + && map.get(CoreProperties.SERVER_STARTTIME).equals("2010-05-18T17:59:00+0000") + && map.size() == 3; + } + public void describeTo(Description description) { + } + })); } + + } diff --git a/sonar-server/src/test/java/org/sonar/server/startup/SetDefaultProjectPermissionsTest.java b/sonar-server/src/test/java/org/sonar/server/startup/SetDefaultProjectPermissionsTest.java new file mode 100644 index 00000000000..0f7ef2329f1 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/startup/SetDefaultProjectPermissionsTest.java @@ -0,0 +1,63 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.server.startup; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.server.platform.PersistentSettings; + +import java.util.Map; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; + +public class SetDefaultProjectPermissionsTest { + @Test + public void set_default_permissions_if_none() { + PersistentSettings persistentSettings = mock(PersistentSettings.class); + Settings settings = new Settings(); + when(persistentSettings.getSettings()).thenReturn(settings); + + new SetDefaultProjectPermissions(persistentSettings).start(); + + verify(persistentSettings).saveProperties(argThat(new BaseMatcher>() { + public boolean matches(Object o) { + Map map = (Map) o; + return map.size() == 9 && map.get("sonar.role.admin.TRK.defaultGroups").equals("sonar-administrators"); + } + + public void describeTo(Description description) { + } + })); + } + + @Test + public void do_not_set_default_permissions_if_exist() { + PersistentSettings persistentSettings = mock(PersistentSettings.class); + Settings settings = new Settings().setProperty("sonar.role.admin.TRK.defaultGroups", "custom-group"); + when(persistentSettings.getSettings()).thenReturn(settings); + + new SetDefaultProjectPermissions(persistentSettings).start(); + + verify(persistentSettings, never()).saveProperties(any(Map.class)); + } +} diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties b/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties index 016382d7fe1..020777dad4f 100644 --- a/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties +++ b/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties @@ -1,2 +1,3 @@ hello: world +in_file: true ServerSettingsTestEnv: in_file \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/db/shared.xml b/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/db/shared.xml deleted file mode 100644 index 55b972c772f..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/db/shared.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml deleted file mode 100644 index f7ada567379..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml deleted file mode 100644 index ec292a1388d..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties-result.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties-result.xml deleted file mode 100644 index ec292a1388d..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties-result.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties.xml deleted file mode 100644 index a9e2365bf01..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties-result.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties-result.xml deleted file mode 100644 index 365acc0ccd3..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties-result.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties.xml deleted file mode 100644 index 824615a0cc5..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file