]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4602 Invalidate dryRun cache when changing settings
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 2 Sep 2013 16:02:35 +0000 (18:02 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Mon, 2 Sep 2013 16:06:41 +0000 (18:06 +0200)
15 files changed:
sonar-core/src/main/java/org/sonar/core/persistence/DryRunDatabaseFactory.java
sonar-core/src/main/java/org/sonar/core/user/UserDao.java
sonar-core/src/main/java/org/sonar/core/user/UserMapper.java
sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml
sonar-core/src/test/java/org/sonar/core/user/UserDaoTest.java
sonar-plugin-api/src/main/java/org/sonar/api/config/GlobalPropertyChangeHandler.java
sonar-plugin-api/src/main/java/org/sonar/api/config/SettingsChangeHandler.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java
sonar-server/src/main/java/org/sonar/server/platform/DryRunCacheListener.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/platform/SettingsChangeNotifier.java
sonar-server/src/main/java/org/sonar/server/startup/CleanDryRunCache.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/webapp/WEB-INF/app/models/property.rb
sonar-server/src/test/java/org/sonar/server/platform/SettingsChangeNotifierTest.java

index b7ca73a233af4cd4f8313013d11d6cc9804e1eaa..8134c3d278e0a57520941b8e0dd689d8deb8b5c0 100644 (file)
@@ -44,7 +44,8 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 
 public class DryRunDatabaseFactory implements ServerComponent {
-  private static final String SONAR_DRY_RUN_CACHE_LAST_UPDATE = "sonar.dryRun.cache.lastUpdate";
+  public static final String SONAR_DRY_RUN_CACHE_KEY_PREFIX = "sonar.dryRun.cache.";
+  public static final String SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY = SONAR_DRY_RUN_CACHE_KEY_PREFIX + "lastUpdate";
   private static final Logger LOG = LoggerFactory.getLogger(DryRunDatabaseFactory.class);
   private static final String DIALECT = "h2";
   private static final String DRIVER = "org.h2.Driver";
@@ -59,6 +60,10 @@ public class DryRunDatabaseFactory implements ServerComponent {
   private final Settings settings;
   private final ResourceDao resourceDao;
 
+  public static String getCacheLastUpdateKey(Long rootProjectId) {
+    return SONAR_DRY_RUN_CACHE_KEY_PREFIX + rootProjectId + ".lastUpdate";
+  }
+
   public DryRunDatabaseFactory(Database database, ServerFileSystem serverFileSystem, Settings settings, ResourceDao resourceDao) {
     this.database = database;
     this.serverFileSystem = serverFileSystem;
@@ -100,14 +105,14 @@ public class DryRunDatabaseFactory implements ServerComponent {
   }
 
   private boolean isValid(@Nullable Long projectId, long lastTimestampInCache) {
-    long globalTimestamp = settings.getLong(SONAR_DRY_RUN_CACHE_LAST_UPDATE);
+    long globalTimestamp = settings.getLong(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY);
     if (globalTimestamp > lastTimestampInCache) {
       return false;
     }
     if (projectId != null) {
       // For modules look for root project last modification timestamp
       Long rootId = resourceDao.getRootProjectByComponentId(projectId).getId();
-      long projectTimestamp = settings.getLong("sonar.dryRun.cache." + rootId + ".lastUpdate");
+      long projectTimestamp = settings.getLong(getCacheLastUpdateKey(rootId));
       if (projectTimestamp > lastTimestampInCache) {
         return false;
       }
index a23f34426ba6b4da8ad381453f30bc843d308191..a3c21ea51d98eaa63f7424436f3bdf0b918a8103 100644 (file)
@@ -41,6 +41,19 @@ public class UserDao implements BatchComponent, ServerComponent {
     this.mybatis = mybatis;
   }
 
+  public UserDto getUser(long userId) {
+    SqlSession session = mybatis.openSession();
+    try {
+      return getUser(userId, session);
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
+  public UserDto getUser(long userId, SqlSession session) {
+    return session.getMapper(UserMapper.class).selectUser(userId);
+  }
+
   /**
    * Search for user by login. Disabled users are ignored.
    *
index 8678b52d363c8a83c0ee33e7764b16f6806cf029..b8d0a9b84593bf5cf99db5f8b70cc5e09c8608d2 100644 (file)
@@ -23,10 +23,14 @@ import org.apache.ibatis.annotations.Param;
 import org.sonar.api.user.UserQuery;
 
 import javax.annotation.CheckForNull;
+
 import java.util.List;
 
 public interface UserMapper {
 
+  @CheckForNull
+  UserDto selectUser(long userId);
+
   /**
    * Select user by login. Note that disabled users are ignored.
    */
@@ -41,13 +45,22 @@ public interface UserMapper {
   GroupDto selectGroupByName(String name);
 
   void removeUserFromGroups(long userId);
+
   void deleteUserActiveDashboards(long userId);
+
   void deleteUserDashboards(long userId);
+
   void deleteUserIssueFilters(String login);
+
   void deleteUserIssueFilterFavourites(String login);
+
   void deleteUserMeasureFilters(long userId);
+
   void deleteUserMeasureFilterFavourites(long userId);
+
   void deleteUserProperties(long userId);
+
   void deleteUserRoles(long userId);
+
   void deactivateUser(long userId);
 }
index 4b3a0ad059938a36968e60c1be49bc16f9624097..996b3f3710fd883bfbceee2c11f484b7adf6d6d0 100644 (file)
     u.active as "active"
   </sql>
 
+    <select id="selectUser" parameterType="long" resultType="User">
+    SELECT
+    <include refid="userColumns"/>
+    FROM users u WHERE u.id=#{id}
+  </select>
+
   <select id="selectUserByLogin" parameterType="string" resultType="User">
     SELECT
     <include refid="userColumns"/>
index 2badeb166a76d34c54bc47b1e85c9fae4ebf6534..525a29989d31636d1b893c2215479d5c426570c1 100644 (file)
@@ -31,7 +31,6 @@ import java.util.List;
 
 import static org.fest.assertions.Assertions.assertThat;
 
-
 public class UserDaoTest extends AbstractDaoTestCase {
 
   private UserDao dao;
@@ -45,7 +44,10 @@ public class UserDaoTest extends AbstractDaoTestCase {
   public void selectUserByLogin_ignore_inactive() {
     setupData("selectActiveUserByLogin");
 
-    UserDto user = dao.selectActiveUserByLogin("inactive_user");
+    UserDto user = dao.getUser(50);
+    assertThat(user.getLogin()).isEqualTo("inactive_user");
+
+    user = dao.selectActiveUserByLogin("inactive_user");
     assertThat(user).isNull();
   }
 
@@ -69,7 +71,7 @@ public class UserDaoTest extends AbstractDaoTestCase {
   @Test
   public void selectUsersByLogins_empty_logins() throws Exception {
     // no need to access db
-    Collection<UserDto> users = dao.selectUsersByLogins(Collections.<String>emptyList());
+    Collection<UserDto> users = dao.selectUsersByLogins(Collections.<String> emptyList());
     assertThat(users).isEmpty();
   }
 
index 3f65b21872d0318d0ac35b229ed8a8ab74c2ccba..ec7c8683a1cfe2fd40065c01c7bc3af5e60040ae 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.api.config;
 
-import org.sonar.api.ServerExtension;
-
 import javax.annotation.Nullable;
 
 /**
@@ -32,7 +30,7 @@ import javax.annotation.Nullable;
  *
  * @since 3.0
  */
-public abstract class GlobalPropertyChangeHandler implements ServerExtension {
+public abstract class GlobalPropertyChangeHandler implements SettingsChangeHandler {
 
   public static final class PropertyChange {
     private String key;
@@ -65,4 +63,11 @@ public abstract class GlobalPropertyChangeHandler implements ServerExtension {
    * This method gets called when a property is changed.
    */
   public abstract void onChange(PropertyChange change);
+
+  @Override
+  public void onChange(SettingsChange change) {
+    if (change.isGlobal()) {
+      onChange(PropertyChange.create(change.key(), change.newValue()));
+    }
+  }
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/SettingsChangeHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/SettingsChangeHandler.java
new file mode 100644 (file)
index 0000000..bd618eb
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.api.config;
+
+import org.sonar.api.ServerExtension;
+
+import javax.annotation.Nullable;
+
+/**
+ * Observe changes of properties done from web application. It does not support :
+ * <ul>
+ * <li>changes done programmatically on the component org.sonar.api.config.Settings</li>
+ * </ul>
+ *
+ * @since 4.0
+ */
+public interface SettingsChangeHandler extends ServerExtension {
+
+  public static final class SettingsChange {
+    private String key;
+    private String newValue;
+    private String componentKey;
+    private String userLogin;
+
+    private SettingsChange(String key, @Nullable String newValue, @Nullable String componentKey, @Nullable String userLogin) {
+      this.key = key;
+      this.newValue = newValue;
+      this.componentKey = componentKey;
+      this.userLogin = userLogin;
+    }
+
+    public static SettingsChange create(String key, @Nullable String newValue, @Nullable String componentKey, @Nullable String userLogin) {
+      return new SettingsChange(key, newValue, componentKey, userLogin);
+    }
+
+    public String key() {
+      return key;
+    }
+
+    public String newValue() {
+      return newValue;
+    }
+
+    public String componentKey() {
+      return componentKey;
+    }
+
+    public String userLogin() {
+      return userLogin;
+    }
+
+    public boolean isGlobal() {
+      return componentKey == null && userLogin == null;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("[key=%s, newValue=%s, componentKey=%s]", key, newValue, componentKey);
+    }
+  }
+
+  /**
+   * This method gets called when a property is changed.
+   */
+  public void onChange(SettingsChange change);
+
+}
index 85eedeeb50edd9e1e25ed5d09a5593f8424d435d..8dac49cfe9bc6307130fcc65ee4a5691b81b147c 100644 (file)
@@ -25,6 +25,7 @@ import com.thoughtworks.xstream.XStream;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.database.configuration.Property;
+import org.sonar.core.persistence.DryRunDatabaseFactory;
 import org.sonar.core.properties.PropertyDto;
 import org.sonar.server.platform.PersistentSettings;
 
@@ -80,7 +81,9 @@ public class PropertiesBackup implements Backupable {
   private boolean shouldBeExported(String propertyKey) {
     // "sonar.core.id" must never be restored, it is unique for a server and it created once at the 1rst server startup
     // default permissions properties should not be exported as they reference permission_templates entries in the DB
-    return !CoreProperties.SERVER_ID.equals(propertyKey) && !propertyKey.startsWith(PERMISSION_PROPERTIES_PREFIX);
+    return !CoreProperties.SERVER_ID.equals(propertyKey)
+      && !propertyKey.startsWith(PERMISSION_PROPERTIES_PREFIX)
+      && !propertyKey.startsWith(DryRunDatabaseFactory.SONAR_DRY_RUN_CACHE_KEY_PREFIX);
   }
 
   private boolean shouldNotBeErased(String propertyKey) {
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/DryRunCacheListener.java b/sonar-server/src/main/java/org/sonar/server/platform/DryRunCacheListener.java
new file mode 100644 (file)
index 0000000..75494ae
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform;
+
+import org.sonar.api.config.SettingsChangeHandler;
+import org.sonar.core.persistence.DryRunDatabaseFactory;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+
+public class DryRunCacheListener implements SettingsChangeHandler {
+
+  private final PersistentSettings settings;
+  private final ResourceDao resourceDao;
+
+  public DryRunCacheListener(PersistentSettings settings, ResourceDao resourceDao) {
+    this.settings = settings;
+    this.resourceDao = resourceDao;
+  }
+
+  @Override
+  public void onChange(SettingsChange change) {
+    if (change.isGlobal()) {
+      settings.saveProperty(DryRunDatabaseFactory.SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY, String.valueOf(System.nanoTime()));
+    } else if (change.componentKey() != null) {
+      ResourceDto rootProject = resourceDao.getRootProjectByComponentKey(change.componentKey());
+      settings.saveProperty(DryRunDatabaseFactory.getCacheLastUpdateKey(rootProject.getId()), String.valueOf(System.nanoTime()));
+    }
+  }
+}
index 6b3b74d0f4dd82d5a07d55861dd05fe59bd2ea63..b04a38f3d941aa257bd6addc1b6d594b8d61537c 100644 (file)
@@ -51,7 +51,13 @@ import org.sonar.core.measure.MeasureFilterFactory;
 import org.sonar.core.metric.DefaultMetricFinder;
 import org.sonar.core.notification.DefaultNotificationManager;
 import org.sonar.core.permission.ComponentPermissionFacade;
-import org.sonar.core.persistence.*;
+import org.sonar.core.persistence.DaoUtils;
+import org.sonar.core.persistence.DatabaseVersion;
+import org.sonar.core.persistence.DefaultDatabase;
+import org.sonar.core.persistence.DryRunDatabaseFactory;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.persistence.SemaphoreUpdater;
+import org.sonar.core.persistence.SemaphoresImpl;
 import org.sonar.core.purge.PurgeProfiler;
 import org.sonar.core.qualitymodel.DefaultModelFinder;
 import org.sonar.core.resource.DefaultResourcePermissions;
@@ -76,20 +82,63 @@ import org.sonar.server.configuration.Backup;
 import org.sonar.server.configuration.ProfilesManager;
 import org.sonar.server.db.DatabaseMigrator;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
-import org.sonar.server.issue.*;
+import org.sonar.server.issue.ActionPlanService;
+import org.sonar.server.issue.ActionService;
+import org.sonar.server.issue.AssignAction;
+import org.sonar.server.issue.CommentAction;
+import org.sonar.server.issue.DefaultIssueFinder;
+import org.sonar.server.issue.InternalRubyIssueService;
+import org.sonar.server.issue.IssueBulkChangeService;
+import org.sonar.server.issue.IssueChangelogService;
+import org.sonar.server.issue.IssueCommentService;
+import org.sonar.server.issue.IssueFilterService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.issue.IssueStatsFinder;
+import org.sonar.server.issue.PlanAction;
+import org.sonar.server.issue.PublicRubyIssueService;
+import org.sonar.server.issue.ServerIssueStorage;
+import org.sonar.server.issue.SetSeverityAction;
+import org.sonar.server.issue.TransitionAction;
 import org.sonar.server.notifications.NotificationCenter;
 import org.sonar.server.notifications.NotificationService;
 import org.sonar.server.permission.InternalPermissionService;
 import org.sonar.server.permission.InternalPermissionTemplateService;
-import org.sonar.server.plugins.*;
+import org.sonar.server.plugins.ApplicationDeployer;
+import org.sonar.server.plugins.DefaultServerPluginRepository;
+import org.sonar.server.plugins.InstalledPluginReferentialFactory;
+import org.sonar.server.plugins.PluginDeployer;
+import org.sonar.server.plugins.PluginDownloader;
+import org.sonar.server.plugins.ServerExtensionInstaller;
+import org.sonar.server.plugins.UpdateCenterClient;
+import org.sonar.server.plugins.UpdateCenterMatrixFactory;
 import org.sonar.server.qualitymodel.DefaultModelManager;
 import org.sonar.server.rule.RubyRuleService;
 import org.sonar.server.rules.ProfilesConsole;
 import org.sonar.server.rules.RulesConsole;
-import org.sonar.server.startup.*;
+import org.sonar.server.startup.CleanDryRunCache;
+import org.sonar.server.startup.DeleteDeprecatedMeasures;
+import org.sonar.server.startup.GenerateBootstrapIndex;
+import org.sonar.server.startup.GeneratePluginIndex;
+import org.sonar.server.startup.GwtPublisher;
+import org.sonar.server.startup.JdbcDriverDeployer;
+import org.sonar.server.startup.LogServerId;
+import org.sonar.server.startup.RegisterMetrics;
+import org.sonar.server.startup.RegisterNewDashboards;
+import org.sonar.server.startup.RegisterNewMeasureFilters;
+import org.sonar.server.startup.RegisterNewProfiles;
+import org.sonar.server.startup.RegisterPermissionTemplates;
+import org.sonar.server.startup.RegisterQualityModels;
+import org.sonar.server.startup.RegisterRules;
+import org.sonar.server.startup.RegisterServletFilters;
+import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
+import org.sonar.server.startup.ServerMetadataPersister;
 import org.sonar.server.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
-import org.sonar.server.ui.*;
+import org.sonar.server.ui.CodeColorizers;
+import org.sonar.server.ui.JRubyI18n;
+import org.sonar.server.ui.PageDecorations;
+import org.sonar.server.ui.SecurityRealmFactory;
+import org.sonar.server.ui.Views;
 import org.sonar.server.user.DefaultUserService;
 import org.sonar.server.user.NewUserNotifier;
 
@@ -209,6 +258,7 @@ public final class Platform {
     coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class);
     coreContainer.addPicoAdapter(new DatabaseSessionProvider());
     coreContainer.addSingleton(ServerMetadataPersister.class);
+    coreContainer.addSingleton(DryRunCacheListener.class);
     coreContainer.startComponents();
   }
 
@@ -339,6 +389,7 @@ public final class Platform {
     startupContainer.addSingleton(RenameDeprecatedPropertyKeys.class);
     startupContainer.addSingleton(LogServerId.class);
     startupContainer.addSingleton(RegisterServletFilters.class);
+    startupContainer.addSingleton(CleanDryRunCache.class);
     startupContainer.startComponents();
 
     startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
index 0c6ef3f2a1d63e5e9d47a29b08ff6e2b3bc56e82..931a446ac76c6e73a4a7b329f0d834816731b7f3 100644 (file)
@@ -21,26 +21,44 @@ package org.sonar.server.platform;
 
 import com.google.common.annotations.VisibleForTesting;
 import org.sonar.api.ServerComponent;
-import org.sonar.api.config.GlobalPropertyChangeHandler;
+import org.sonar.api.config.SettingsChangeHandler;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.user.UserDao;
+import org.sonar.core.user.UserDto;
 
 import javax.annotation.Nullable;
 
 public class SettingsChangeNotifier implements ServerComponent {
 
   @VisibleForTesting
-  GlobalPropertyChangeHandler[] changeHandlers;
+  SettingsChangeHandler[] changeHandlers;
+  private final ResourceDao resourceDao;
+  private final UserDao userDao;
 
-  public SettingsChangeNotifier(GlobalPropertyChangeHandler[] changeHandlers) {
+  public SettingsChangeNotifier(ResourceDao resourceDao, UserDao userDao, SettingsChangeHandler[] changeHandlers) {
+    this.resourceDao = resourceDao;
+    this.userDao = userDao;
     this.changeHandlers = changeHandlers;
   }
 
-  public SettingsChangeNotifier() {
-    this(new GlobalPropertyChangeHandler[0]);
+  public SettingsChangeNotifier(ResourceDao resourceDao, UserDao userDao) {
+    this(resourceDao, userDao, new SettingsChangeHandler[0]);
   }
 
-  public void onGlobalPropertyChange(String key, @Nullable String value) {
-    GlobalPropertyChangeHandler.PropertyChange change = GlobalPropertyChangeHandler.PropertyChange.create(key, value);
-    for (GlobalPropertyChangeHandler changeHandler : changeHandlers) {
+  public void onPropertyChange(String key, @Nullable String value, @Nullable Long componentId, @Nullable Long userId) {
+    String resourceKey = null;
+    if (componentId != null) {
+      ResourceDto resource = resourceDao.getResource(componentId);
+      resourceKey = resource != null ? resource.getKey() : null;
+    }
+    String userLogin = null;
+    if (userId != null) {
+      UserDto user = userDao.getUser(userId);
+      userLogin = user != null ? user.getLogin() : null;
+    }
+    SettingsChangeHandler.SettingsChange change = SettingsChangeHandler.SettingsChange.create(key, value, resourceKey, userLogin);
+    for (SettingsChangeHandler changeHandler : changeHandlers) {
       changeHandler.onChange(change);
     }
   }
diff --git a/sonar-server/src/main/java/org/sonar/server/startup/CleanDryRunCache.java b/sonar-server/src/main/java/org/sonar/server/startup/CleanDryRunCache.java
new file mode 100644 (file)
index 0000000..d926111
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.startup;
+
+import org.apache.commons.io.FileUtils;
+import org.sonar.core.persistence.DryRunDatabaseFactory;
+import org.sonar.server.platform.DefaultServerFileSystem;
+import org.sonar.server.platform.PersistentSettings;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * @since 4.0
+ */
+public final class CleanDryRunCache {
+
+  private DefaultServerFileSystem serverFileSystem;
+  private PersistentSettings settings;
+
+  public CleanDryRunCache(DefaultServerFileSystem serverFileSystem, PersistentSettings settings) {
+    this.serverFileSystem = serverFileSystem;
+    this.settings = settings;
+  }
+
+  private File getRootCacheLocation() {
+    return new File(serverFileSystem.getTempDir(), "dryRun");
+  }
+
+  public void start() throws IOException {
+    // Delete folder where dryRun DB are stored
+    FileUtils.deleteQuietly(getRootCacheLocation());
+    // Delete all lastUpdate properties to force generation of new DB
+    Map<String, String> properties = settings.getProperties();
+    for (String propKey : properties.keySet()) {
+      if (propKey.startsWith(DryRunDatabaseFactory.SONAR_DRY_RUN_CACHE_KEY_PREFIX)) {
+        settings.deleteProperty(propKey);
+      }
+    }
+  }
+}
index cd8a3f00a6d9d87942bffaf45b767c855aa9723c..169a25e6975637c36aa3949d32ea8124aa7b50ff 100644 (file)
@@ -40,13 +40,16 @@ import org.sonar.api.test.MutableTestable;
 import org.sonar.api.test.TestPlan;
 import org.sonar.api.test.Testable;
 import org.sonar.api.utils.ValidationMessages;
-import org.sonar.api.web.*;
+import org.sonar.api.web.Footer;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.Page;
+import org.sonar.api.web.RubyRailsWebservice;
+import org.sonar.api.web.Widget;
 import org.sonar.core.component.SnapshotPerspectives;
 import org.sonar.core.i18n.RuleI18nManager;
 import org.sonar.core.measure.MeasureFilterEngine;
 import org.sonar.core.measure.MeasureFilterResult;
 import org.sonar.core.persistence.Database;
-import org.sonar.server.db.DatabaseMigrator;
 import org.sonar.core.persistence.DryRunDatabaseFactory;
 import org.sonar.core.purge.PurgeDao;
 import org.sonar.core.resource.ResourceIndexerDao;
@@ -54,8 +57,16 @@ import org.sonar.core.resource.ResourceKeyUpdaterDao;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.server.configuration.Backup;
 import org.sonar.server.configuration.ProfilesManager;
-import org.sonar.server.platform.*;
-import org.sonar.server.plugins.*;
+import org.sonar.server.db.DatabaseMigrator;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.platform.ServerIdGenerator;
+import org.sonar.server.platform.ServerSettings;
+import org.sonar.server.platform.SettingsChangeNotifier;
+import org.sonar.server.plugins.DefaultServerPluginRepository;
+import org.sonar.server.plugins.InstalledPluginReferentialFactory;
+import org.sonar.server.plugins.PluginDeployer;
+import org.sonar.server.plugins.PluginDownloader;
+import org.sonar.server.plugins.UpdateCenterMatrixFactory;
 import org.sonar.server.rules.ProfilesConsole;
 import org.sonar.server.rules.RulesConsole;
 import org.sonar.server.user.NewUserNotifier;
@@ -64,9 +75,14 @@ import org.sonar.updatecenter.common.UpdateCenter;
 import org.sonar.updatecenter.common.Version;
 
 import javax.annotation.Nullable;
+
 import java.net.InetAddress;
 import java.sql.Connection;
-import java.util.*;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.collect.Lists.newArrayList;
 
@@ -326,9 +342,11 @@ public final class JRubyFacade {
     return get(ProfilesManager.class);
   }
 
-  public void setGlobalProperty(String key, @Nullable String value) {
-    get(ServerSettings.class).setProperty(key, value);
-    get(SettingsChangeNotifier.class).onGlobalPropertyChange(key, value);
+  public void updateProperty(String key, @Nullable String value, @Nullable Long componentId, @Nullable Long userId) {
+    if (componentId == null && userId == null) {
+      get(ServerSettings.class).setProperty(key, value);
+    }
+    get(SettingsChangeNotifier.class).onPropertyChange(key, value, componentId, userId);
   }
 
   public Settings getSettings() {
index b76da8946cd47e70ef5b0212891d8d6b9137383c..6ff6f22958405505632919bd9d0eb837c4701086 100644 (file)
@@ -176,7 +176,7 @@ class Property < ActiveRecord::Base
   private
 
   def self.setGlobalProperty(key, value, resource_id, user_id)
-    Api::Utils.java_facade.setGlobalProperty(key, value) unless (resource_id || user_id)
+    Api::Utils.java_facade.updateProperty(key, value, resource_id, user_id)
   end
 
   def self.all(key, resource_id=nil, user_id=nil)
index a0875cb5b0a0f7c20e11276c256dd47b9caac0c1..2988c58462f31d537c009a194d9f65a2ccadd082 100644 (file)
@@ -21,7 +21,9 @@ package org.sonar.server.platform;
 
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
-import org.sonar.api.config.GlobalPropertyChangeHandler;
+import org.sonar.api.config.SettingsChangeHandler;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.user.UserDao;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.argThat;
@@ -31,27 +33,27 @@ import static org.mockito.Mockito.verify;
 public class SettingsChangeNotifierTest {
   @Test
   public void onGlobalPropertyChange() {
-    GlobalPropertyChangeHandler handler = mock(GlobalPropertyChangeHandler.class);
-    SettingsChangeNotifier notifier = new SettingsChangeNotifier(new GlobalPropertyChangeHandler[]{handler});
+    SettingsChangeHandler handler = mock(SettingsChangeHandler.class);
+    SettingsChangeNotifier notifier = new SettingsChangeNotifier(mock(ResourceDao.class), mock(UserDao.class), new SettingsChangeHandler[] {handler});
 
-    notifier.onGlobalPropertyChange("foo", "bar");
+    notifier.onPropertyChange("foo", "bar", null, null);
 
-    verify(handler).onChange(argThat(new ArgumentMatcher<GlobalPropertyChangeHandler.PropertyChange>() {
+    verify(handler).onChange(argThat(new ArgumentMatcher<SettingsChangeHandler.SettingsChange>() {
       @Override
       public boolean matches(Object o) {
-        GlobalPropertyChangeHandler.PropertyChange change = (GlobalPropertyChangeHandler.PropertyChange) o;
-        return change.getKey().equals("foo") && change.getNewValue().equals("bar");
+        SettingsChangeHandler.SettingsChange change = (SettingsChangeHandler.SettingsChange) o;
+        return change.key().equals("foo") && change.newValue().equals("bar") && change.componentKey() == null && change.userLogin() == null;
       }
     }));
   }
 
   @Test
   public void no_handlers() {
-    SettingsChangeNotifier notifier = new SettingsChangeNotifier();
+    SettingsChangeNotifier notifier = new SettingsChangeNotifier(mock(ResourceDao.class), mock(UserDao.class));
 
     assertThat(notifier.changeHandlers).isEmpty();
 
     // does not fail
-    notifier.onGlobalPropertyChange("foo", "bar");
+    notifier.onPropertyChange("foo", "bar", null, null);
   }
 }