]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6610 invalidate ruby metric cache when creating/updating/deleting a metric
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 10 Jun 2015 16:19:53 +0000 (18:19 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 10 Jun 2015 16:36:04 +0000 (18:36 +0200)
14 files changed:
server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java
server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java
server/sonar-server/src/main/java/org/sonar/server/metric/ws/UpdateAction.java
server/sonar-server/src/main/java/org/sonar/server/ruby/CallInvalidateMetricCache.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/ruby/PlatformRubyBridge.java
server/sonar-server/src/main/java/org/sonar/server/ruby/RubyBridge.java
server/sonar-server/src/main/java/org/sonar/server/ruby/RubyMetricCache.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/ruby/call_invalidate_metric_cache.rb [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/metric/ws/CreateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java
server/sonar-server/src/test/java/org/sonar/server/metric/ws/MetricsWsTest.java
server/sonar-server/src/test/java/org/sonar/server/metric/ws/UpdateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/ruby/PlatformRubyBridgeTest.java
server/sonar-server/src/test/resources/org/sonar/server/ruby/database_version.rb

index 9653baf33391f8aa38687cee48b376791c7e84db..776f3dd6142875135c8cf1863382b374828de620 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ServerException;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.user.UserSession;
 
 public class CreateAction implements MetricsWsAction {
@@ -54,10 +55,12 @@ public class CreateAction implements MetricsWsAction {
 
   private final DbClient dbClient;
   private final UserSession userSession;
+  private final RubyBridge rubyBridge;
 
-  public CreateAction(DbClient dbClient, UserSession userSession) {
+  public CreateAction(DbClient dbClient, UserSession userSession, RubyBridge rubyBridge) {
     this.dbClient = dbClient;
     this.userSession = userSession;
+    this.rubyBridge = rubyBridge;
   }
 
   @Override
@@ -113,6 +116,7 @@ public class CreateAction implements MetricsWsAction {
       JsonWriter json = response.newJsonWriter();
       writeMetric(json, metricInDb);
       json.close();
+      rubyBridge.metricCache().invalidate();
     } finally {
       MyBatis.closeQuietly(dbSession);
     }
index 95430acaf108586506d92d7f4da7b43e2786aa31..d388560ba896aa700ca33b1a98524c79da15efc0 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.user.UserSession;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -42,10 +43,12 @@ public class DeleteAction implements MetricsWsAction {
 
   private final DbClient dbClient;
   private final UserSession userSession;
+  private final RubyBridge rubyBridge;
 
-  public DeleteAction(DbClient dbClient, UserSession userSession) {
+  public DeleteAction(DbClient dbClient, UserSession userSession, RubyBridge rubyBridge) {
     this.dbClient = dbClient;
     this.userSession = userSession;
+    this.rubyBridge = rubyBridge;
   }
 
   @Override
@@ -79,6 +82,7 @@ public class DeleteAction implements MetricsWsAction {
     }
 
     response.noContent();
+    rubyBridge.metricCache().invalidate();
   }
 
   private List<Integer> loadIds(DbSession dbSession, Request request) {
index 07bfeaf66bf936ddd7dfe846f087cf0f11779476..51e1562b715d7f2c803f9eb0088bee202255911e 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ServerException;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.user.UserSession;
 
 public class UpdateAction implements MetricsWsAction {
@@ -56,10 +57,12 @@ public class UpdateAction implements MetricsWsAction {
 
   private final DbClient dbClient;
   private final UserSession userSession;
+  private final RubyBridge rubyBridge;
 
-  public UpdateAction(DbClient dbClient, UserSession userSession) {
+  public UpdateAction(DbClient dbClient, UserSession userSession, RubyBridge rubyBridge) {
     this.dbClient = dbClient;
     this.userSession = userSession;
+    this.rubyBridge = rubyBridge;
   }
 
   @Override
@@ -111,6 +114,7 @@ public class UpdateAction implements MetricsWsAction {
       JsonWriter json = response.newJsonWriter();
       writeMetric(json, metricInDb);
       json.close();
+      rubyBridge.metricCache().invalidate();
     } finally {
       MyBatis.closeQuietly(dbSession);
     }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ruby/CallInvalidateMetricCache.java b/server/sonar-server/src/main/java/org/sonar/server/ruby/CallInvalidateMetricCache.java
new file mode 100644 (file)
index 0000000..9aaafef
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ruby;
+
+public interface CallInvalidateMetricCache {
+  void callInvalidate();
+}
index 2fbe60b39ef68e630eeeb75af51222da66a29eb0..8222a263a0858c4c5883cb054eacd9994632a5fd 100644 (file)
@@ -32,6 +32,7 @@ import org.jruby.runtime.builtin.IRubyObject;
 public class PlatformRubyBridge implements RubyBridge {
   private static final String CALL_UPGRADE_AND_START_RB_FILENAME = "call_databaseversion_upgrade.rb";
   private static final String CALL_LOAD_JAVA_WEB_SERVICES_RB_FILENAME = "call_load_java_web_services.rb";
+  private static final String CALL_INVALIDATE_METRIC_CACHE_RB_FILENAME = "call_invalidate_metric_cache.rb";
 
   private final RackBridge rackBridge;
 
@@ -67,6 +68,20 @@ public class PlatformRubyBridge implements RubyBridge {
     };
   }
 
+  @Override
+  public RubyMetricCache metricCache() {
+    final CallInvalidateMetricCache callInvalidateMetricCache = parseMethodScriptToInterface(
+      CALL_INVALIDATE_METRIC_CACHE_RB_FILENAME, CallInvalidateMetricCache.class
+      );
+
+    return new RubyMetricCache() {
+      @Override
+      public void invalidate() {
+        callInvalidateMetricCache.callInvalidate();
+      }
+    };
+  }
+
   /**
    * Parses a Ruby script that defines a single method and returns an instance of the specified interface type as a
    * wrapper to this Ruby method.
index 36e893d770e46b6730b1a5819166d811267e5568..5ad43ead960ed14bdaf38f71e2214c5512be4a2d 100644 (file)
@@ -37,4 +37,6 @@ public interface RubyBridge {
    * @return a {@link RubyRailsRoutes}
    */
   RubyRailsRoutes railsRoutes();
+
+  RubyMetricCache metricCache();
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyMetricCache.java b/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyMetricCache.java
new file mode 100644 (file)
index 0000000..b43b0e4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.ruby;
+
+public interface RubyMetricCache {
+  void invalidate();
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/ruby/call_invalidate_metric_cache.rb b/server/sonar-server/src/main/resources/org/sonar/server/ruby/call_invalidate_metric_cache.rb
new file mode 100644 (file)
index 0000000..c062d0e
--- /dev/null
@@ -0,0 +1,10 @@
+# this script defines a method which calls the class method "clear_cache" of the Metric class defined
+# in /server/sonar-web/src/main/webapp/WEB-INF/app/models/metric.rb
+
+class RbCallInvalidateMetricCache
+  include Java::org.sonar.server.ruby.CallInvalidateMetricCache
+  def call_invalidate
+    Metric.clear_cache
+  end
+end
+RbCallInvalidateMetricCache.new
index 5b33efe6e9fe1bfbde1b77b273ffdad7cd2240bb..915f6cc8ddedb525ea4bb4dbc779f846f563919f 100644 (file)
@@ -32,17 +32,20 @@ import org.sonar.core.metric.db.MetricDto;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.DbTester;
-import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
-import org.sonar.server.measure.custom.persistence.CustomMeasureTesting;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.ServerException;
+import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
+import org.sonar.server.measure.custom.persistence.CustomMeasureTesting;
 import org.sonar.server.metric.persistence.MetricDao;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
 import static org.sonar.server.metric.ws.CreateAction.PARAM_DESCRIPTION;
 import static org.sonar.server.metric.ws.CreateAction.PARAM_DOMAIN;
 import static org.sonar.server.metric.ws.CreateAction.PARAM_KEY;
@@ -74,7 +77,7 @@ public class CreateActionTest {
     dbSession = dbClient.openSession(false);
     db.truncateTables();
 
-    ws = new WsTester(new MetricsWs(new CreateAction(dbClient, userSessionRule)));
+    ws = new WsTester(new MetricsWs(new CreateAction(dbClient, userSessionRule, mock(RubyBridge.class, RETURNS_DEEP_STUBS))));
     userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
   }
 
index 3853745ce14a199b5016916e4b44ea2f94c37c49..ed4ee6fba1f6a74cea52cdd287f525546a5738f0 100644 (file)
@@ -34,16 +34,19 @@ import org.sonar.core.metric.db.MetricDto;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.DbTester;
-import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
-import org.sonar.server.measure.custom.persistence.CustomMeasureTesting;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
+import org.sonar.server.measure.custom.persistence.CustomMeasureTesting;
 import org.sonar.server.metric.persistence.MetricDao;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
 import static org.sonar.server.metric.ws.MetricTesting.newMetricDto;
 
 @Category(DbTests.class)
@@ -66,7 +69,7 @@ public class DeleteActionTest {
     dbSession = dbClient.openSession(false);
     db.truncateTables();
     userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
-    ws = new WsTester(new MetricsWs(new DeleteAction(dbClient, userSessionRule)));
+    ws = new WsTester(new MetricsWs(new DeleteAction(dbClient, userSessionRule, mock(RubyBridge.class, RETURNS_DEEP_STUBS))));
     metricDao = dbClient.metricDao();
   }
 
index a78a2621368b4226c83ad5b0cf3969d1605d95aa..619ad00c7aa43f73343cd456fd0a4793093a4367 100644 (file)
@@ -24,6 +24,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.WsTester;
 
@@ -38,11 +39,12 @@ public class MetricsWsTest {
   public void setUp() {
     DbClient dbClient = mock(DbClient.class);
     UserSession userSession = mock(UserSession.class);
+    RubyBridge rubyBridge = mock(RubyBridge.class);
     ws = new WsTester(new MetricsWs(
       new SearchAction(dbClient),
-      new CreateAction(dbClient, userSession),
-      new UpdateAction(dbClient, userSession),
-      new DeleteAction(dbClient, userSession),
+      new CreateAction(dbClient, userSession, rubyBridge),
+      new UpdateAction(dbClient, userSession, rubyBridge),
+      new DeleteAction(dbClient, userSession, rubyBridge),
       new TypesAction(),
       new DomainsAction(dbClient)
       ));
index 92365534125c5ad7cd5fb739e79f6bcd09a12769..f83484a72eb4fbaeec439fa24d986c6ea2ca2d8a 100644 (file)
@@ -32,16 +32,19 @@ import org.sonar.core.metric.db.MetricDto;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.DbTester;
-import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.ServerException;
+import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
 import org.sonar.server.metric.persistence.MetricDao;
+import org.sonar.server.ruby.RubyBridge;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
 import static org.sonar.server.measure.custom.persistence.CustomMeasureTesting.newCustomMeasureDto;
 import static org.sonar.server.metric.ws.UpdateAction.PARAM_DESCRIPTION;
 import static org.sonar.server.metric.ws.UpdateAction.PARAM_DOMAIN;
@@ -75,7 +78,7 @@ public class UpdateActionTest {
     dbSession = dbClient.openSession(false);
     db.truncateTables();
 
-    ws = new WsTester(new MetricsWs(new UpdateAction(dbClient, userSessionRule)));
+    ws = new WsTester(new MetricsWs(new UpdateAction(dbClient, userSessionRule, mock(RubyBridge.class, RETURNS_DEEP_STUBS))));
     userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
   }
 
index 38bca7ca8469861ba7fb469061835ad5f397810b..45502db309a3b2472efa61aa08e5453622c202de 100644 (file)
@@ -88,4 +88,18 @@ public class PlatformRubyBridgeTest {
     }
   }
 
+  /**
+   * unit test only makes sure the wrapping and method forwarding provided by JRuby works so building the
+   * RubyRailsRoutes object and calling its trigger method is enough as it would otherwise raise an exception
+   */
+  @Test
+  public void testInvalidateCache() {
+    try {
+      underTest.metricCache().invalidate();
+    } catch (RaiseException e) {
+      e.printStackTrace();
+      throw new RuntimeException("Loading error with container loadPath " + container.getLoadPaths(), e);
+    }
+  }
+
 }
index 46797b911dcfb3a03d813d44d98af7fe09351860..430c218cd33c71fe98fef2732fbcde776b74a1c6 100644 (file)
@@ -12,3 +12,11 @@ class DatabaseVersion
   end
 
 end
+
+class Metric
+
+  def self.clear_cache
+
+  end
+
+end