diff options
15 files changed, 226 insertions, 36 deletions
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProject.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProject.java index 4f2d637658b..d0db106f056 100644 --- a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProject.java +++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProject.java @@ -42,7 +42,7 @@ public class NewViolationsOnMyFavouriteProject extends NotificationDispatcher { @Override public void dispatch(Notification notification, Context context) { if (StringUtils.equals(notification.getType(), "new-violations")) { - Integer projectId = Integer.parseInt(notification.getFieldValue("projectId")); + Long projectId = Long.parseLong(notification.getFieldValue("projectId")); List<String> userLogins = propertiesDao.findUserIdsForFavouriteResource(projectId); for (String userLogin : userLogins) { context.addUser(userLogin); diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProjectTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProjectTest.java index ed2de225074..5a4b1adce3a 100644 --- a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProjectTest.java +++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/newviolations/NewViolationsOnMyFavouriteProjectTest.java @@ -49,7 +49,7 @@ public class NewViolationsOnMyFavouriteProjectTest { public void shouldDispatchToUsersWhoHaveFlaggedProjectAsFavourite() { NotificationDispatcher.Context context = mock(NotificationDispatcher.Context.class); PropertiesDao propertiesDao = mock(PropertiesDao.class); - when(propertiesDao.findUserIdsForFavouriteResource(34)).thenReturn(Lists.newArrayList("user1", "user2")); + when(propertiesDao.findUserIdsForFavouriteResource(34L)).thenReturn(Lists.newArrayList("user1", "user2")); NewViolationsOnMyFavouriteProject dispatcher = new NewViolationsOnMyFavouriteProject(propertiesDao); Notification notification = new Notification("new-violations").setFieldValue("projectId", "34"); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/GlobalPropertyChangeHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/GlobalPropertyChangeHandler.java new file mode 100644 index 00000000000..f7a3a7bb607 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/GlobalPropertyChangeHandler.java @@ -0,0 +1,68 @@ +/* + * 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.api.config; + +import org.sonar.api.ServerExtension; + +import javax.annotation.Nullable; + +/** + * Observe changes of global properties done from web application. It does not support : + * <ul> + * <li>changes done by end-users from the page "Project Settings"</li> + * <li>changes done programmatically on the component org.sonar.api.config.Settings</li> + * </ul> + * + * @since 2.15 + */ +public abstract class GlobalPropertyChangeHandler implements ServerExtension { + + public static final class PropertyChange { + private String key; + private String newValue; + + private PropertyChange(String key, @Nullable String newValue) { + this.key = key; + this.newValue = newValue; + } + + public static PropertyChange create(String key, @Nullable String newValue) { + return new PropertyChange(key, newValue); + } + + public String getKey() { + return key; + } + + public String getNewValue() { + return newValue; + } + + @Override + public String toString() { + return String.format("[key=%s, newValue=%s]", key, newValue); + } + } + + /** + * This method gets called when a property is changed. + */ + public abstract void onChange(PropertyChange change); +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java index e1007096245..83e56623f1b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java @@ -19,7 +19,6 @@ */ package org.sonar.api.config; -import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; @@ -95,8 +94,8 @@ public final class PropertyDefinition { return fix; } - @VisibleForTesting - PropertyDefinition(PropertyType type, String[] options) { + private PropertyDefinition(String key, PropertyType type, String[] options) { + this.key = key; this.type = type; this.options = options; } @@ -105,6 +104,10 @@ public final class PropertyDefinition { return new PropertyDefinition(annotation); } + public static PropertyDefinition create(String key, PropertyType type, String[] options) { + return new PropertyDefinition(key, type, options); + } + public Result validate(@Nullable String value) { // TODO REFACTORING REQUIRED HERE Result result = Result.SUCCESS; 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 9bea5640ff6..e507831f57d 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 @@ -31,9 +31,12 @@ import javax.annotation.Nullable; import java.util.*; /** - * Project Settings on batch side, Global Settings on server side. - * <p/> - * Replace the deprecated component org.apache.commons.configuration.Configuration + * Project Settings on batch side, Global Settings on server side. This component does not access to database, so + * property changed via setter methods are not persisted. + * + * <p> + * This component replaces the deprecated org.apache.commons.configuration.Configuration + * </p> * * @since 2.12 */ diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/GlobalPropertyChangeHandlerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/GlobalPropertyChangeHandlerTest.java new file mode 100644 index 00000000000..b80d25978b3 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/GlobalPropertyChangeHandlerTest.java @@ -0,0 +1,48 @@ +/* + * 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.api.config; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; + +public class GlobalPropertyChangeHandlerTest { + @Test + public void propertyChangeToString() { + GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create("favourite.java.version", "1.5"); + assertThat(change.toString(), is("[key=favourite.java.version, newValue=1.5]")); + } + + @Test + public void propertyChangeGetters() { + GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create("favourite.java.version", "1.5"); + assertThat(change.getKey(), is("favourite.java.version")); + assertThat(change.getNewValue(), is("1.5")); + } + + @Test + public void nullNewValue() { + GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create("favourite.java.version", null); + assertThat(change.getNewValue(), nullValue()); + assertThat(change.toString(), is("[key=favourite.java.version, newValue=null]")); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java index 00557bdc231..035a48c5707 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java @@ -88,7 +88,7 @@ public class PropertyDefinitionTest { @Test public void validate_string() { - PropertyDefinition def = new PropertyDefinition(PropertyType.STRING, new String[0]); + PropertyDefinition def = PropertyDefinition.create("foo", PropertyType.STRING, new String[0]); assertThat(def.validate(null).isValid(), is(true)); assertThat(def.validate("").isValid(), is(true)); @@ -98,7 +98,7 @@ public class PropertyDefinitionTest { @Test public void validate_boolean() { - PropertyDefinition def = new PropertyDefinition(PropertyType.BOOLEAN, new String[0]); + PropertyDefinition def = PropertyDefinition.create("foo", PropertyType.BOOLEAN, new String[0]); assertThat(def.validate(null).isValid(), is(true)); assertThat(def.validate("").isValid(), is(true)); @@ -112,7 +112,7 @@ public class PropertyDefinitionTest { @Test public void validate_integer() { - PropertyDefinition def = new PropertyDefinition(PropertyType.INTEGER, new String[0]); + PropertyDefinition def = PropertyDefinition.create("foo", PropertyType.INTEGER, new String[0]); assertThat(def.validate(null).isValid(), is(true)); assertThat(def.validate("").isValid(), is(true)); @@ -125,7 +125,7 @@ public class PropertyDefinitionTest { @Test public void validate_float() { - PropertyDefinition def = new PropertyDefinition(PropertyType.FLOAT, new String[0]); + PropertyDefinition def = PropertyDefinition.create("foo", PropertyType.FLOAT, new String[0]); assertThat(def.validate(null).isValid(), is(true)); assertThat(def.validate("").isValid(), is(true)); @@ -139,7 +139,7 @@ public class PropertyDefinitionTest { @Test public void validate_single_select_list() { - PropertyDefinition def = new PropertyDefinition(PropertyType.SINGLE_SELECT_LIST, new String[]{"de", "en"}); + PropertyDefinition def = PropertyDefinition.create("foo", PropertyType.SINGLE_SELECT_LIST, new String[]{"de", "en"}); assertThat(def.validate(null).isValid(), is(true)); assertThat(def.validate("").isValid(), is(true)); 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 new file mode 100644 index 00000000000..83d6e146f4e --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/platform/GlobalSettingsUpdater.java @@ -0,0 +1,58 @@ +/* + * 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 2.15 + */ +public class GlobalSettingsUpdater { + private ServerSettings settings; + private Configuration deprecatedConf; + private List<GlobalPropertyChangeHandler> changeHandlers; + + public GlobalSettingsUpdater(ServerSettings settings, Configuration config, List<GlobalPropertyChangeHandler> changeHandlers) { + this.settings = settings; + this.deprecatedConf = config; + this.changeHandlers = changeHandlers; + } + + public GlobalSettingsUpdater(ServerSettings settings, Configuration config) { + this(settings, config, Collections.<GlobalPropertyChangeHandler>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/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index e698c426cd7..52ec833c377 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 @@ -159,7 +159,6 @@ public final class Platform { coreContainer = rootContainer.createChild(); coreContainer.addSingleton(ServerDatabaseSettingsLoader.class); coreContainer.addSingleton(DefaultDatabaseConnector.class); - coreContainer.addSingleton(ServerExtensionInstaller.class); coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class); coreContainer.addPicoAdapter(new DatabaseSessionProvider()); @@ -174,6 +173,7 @@ public final class Platform { ServerExtensionInstaller extensionRegistrar = servicesContainer.getComponentByType(ServerExtensionInstaller.class); extensionRegistrar.registerExtensions(servicesContainer); + servicesContainer.addSingleton(GlobalSettingsUpdater.class); servicesContainer.addSingleton(HttpDownloader.class); servicesContainer.addSingleton(UpdateCenterClient.class); servicesContainer.addSingleton(UpdateCenterMatrixFactory.class); 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 index 2f1d651d72b..b0eb1eef2af 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerDatabaseSettingsLoader.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerDatabaseSettingsLoader.java @@ -36,6 +36,5 @@ public final class ServerDatabaseSettingsLoader { public void start() { settings.activateDatabaseSettings(propertiesDao); - settings.load(); } } 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 edca83e0953..fbcaaaa576e 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 @@ -19,6 +19,7 @@ */ 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.config.PropertyDefinitions; @@ -66,15 +67,21 @@ public class ServerSettings extends Settings { } public ServerSettings activateDatabaseSettings(PropertiesDao dao) { + return activateDatabaseSettings(dao, SonarHome.getHome()); + } + + @VisibleForTesting + ServerSettings activateDatabaseSettings(PropertiesDao dao, File sonarHome) { this.propertiesDao = dao; + load(sonarHome); return this; } - public ServerSettings load() { + private ServerSettings load() { return load(SonarHome.getHome()); } - ServerSettings load(File sonarHome) { + private ServerSettings load(File sonarHome) { clear(); setProperty(CoreProperties.SONAR_HOME, sonarHome.getAbsolutePath()); setProperty(DEPLOY_DIR, deployDir.getAbsolutePath()); 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 f7e153ac0fa..8c34f40739d 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 @@ -50,6 +50,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.Platform; import org.sonar.server.platform.ServerIdGenerator; import org.sonar.server.platform.ServerSettings; @@ -58,6 +59,7 @@ import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.updatecenter.common.Version; +import javax.annotation.Nullable; import java.net.InetAddress; import java.sql.Connection; import java.util.Collection; @@ -324,8 +326,8 @@ public final class JRubyFacade { return getContainer().getComponentByType(ProfilesManager.class); } - public void reloadConfiguration() { - getContainer().getComponentByType(ServerSettings.class).load(); + public void setGlobalProperty(String key, @Nullable String value) { + getContainer().getComponentByType(GlobalSettingsUpdater.class).setProperty(key, value); } public Settings getSettings() { @@ -463,6 +465,4 @@ public final class JRubyFacade { public ComponentContainer getContainer() { return Platform.getInstance().getContainer(); } - - } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb index 18c7e8f0797..d80ef45e1df 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb @@ -39,6 +39,7 @@ class SettingsController < ApplicationController else access_denied unless is_admin? end + is_global=(@project.nil?) load_properties(@project) @@ -46,22 +47,30 @@ class SettingsController < ApplicationController if @category && @definitions_per_category[@category] @definitions_per_category[@category].each do |property| value=params[property.getKey()] + persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => params[:resource_id], :user_id => nil}) - persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => (@project ? @project.id : nil), :user_id => nil}) + # update the property if persisted_property if value.empty? - Property.delete_all('prop_key' => property.key(), 'resource_id' => (@project ? @project.id : nil), 'user_id' => nil) + Property.delete_all('prop_key' => property.key(), 'resource_id' => params[:resource_id], 'user_id' => nil) + java_facade.setGlobalProperty(property.getKey(), nil) if is_global elsif persisted_property.text_value != value.to_s persisted_property.text_value = value.to_s - persisted_property.save + if persisted_property.save && is_global + java_facade.setGlobalProperty(property.getKey(), value.to_s) + end @persisted_properties_per_key[persisted_property.key]=persisted_property end - elsif !value.blank? - persisted_property=Property.create(:prop_key => property.key(), :text_value => value.to_s, :resource_id => (@project ? @project.id : nil)) + + # create the property + elsif value.present? + persisted_property=Property.new(:prop_key => property.key(), :text_value => value.to_s, :resource_id => params[:resource_id]) + if persisted_property.save && is_global + java_facade.setGlobalProperty(property.getKey(), value.to_s) + end @persisted_properties_per_key[persisted_property.key]=persisted_property end end - java_facade.reloadConfiguration() params[:layout]='false' render :partial => 'settings/properties' diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb index 8e6c6805d49..4e9af8d7cd4 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb @@ -56,14 +56,14 @@ class Property < ActiveRecord::Base Property.delete_all('prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil) prop.save end - reload_java_configuration + Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, value) unless resource_id end prop end def self.clear(key, resource_id=nil) Property.delete_all('prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil) - reload_java_configuration + Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, nil) unless resource_id end def self.by_key(key, resource_id=nil) @@ -78,7 +78,7 @@ class Property < ActiveRecord::Base property = Property.find(:first, :conditions => {:prop_key => key, :resource_id => nil, :user_id => nil}); property.text_value = value property.save - reload_java_configuration + Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, value) property end @@ -117,8 +117,4 @@ class Property < ActiveRecord::Base errors.add_to_base(validation_result.getErrorKey()) unless validation_result.isValid() end end - - def self.reload_java_configuration - Java::OrgSonarServerUi::JRubyFacade.getInstance().reloadConfiguration() - end end 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 c356cfaab36..65727c2a8c7 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 @@ -62,8 +62,7 @@ public class ServerSettingsTest extends AbstractDbUnitTestCase { setupData("db/shared"); ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home); - settings.activateDatabaseSettings(new PropertiesDao(getMyBatis())); - settings.load(home); + settings.activateDatabaseSettings(new PropertiesDao(getMyBatis()), home); assertThat(settings.getString("global_only"), is("is_global")); assertThat(settings.getString("global_and_project"), is("is_global")); |