From: Teryk Bellahsene Date: Wed, 10 Jun 2015 16:19:53 +0000 (+0200) Subject: SONAR-6610 invalidate ruby metric cache when creating/updating/deleting a metric X-Git-Tag: 5.2-RC1~1531 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c6af5401a5fe606ac36ca880290b137bf221427c;p=sonarqube.git SONAR-6610 invalidate ruby metric cache when creating/updating/deleting a metric --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java index 9653baf3339..776f3dd6142 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/CreateAction.java @@ -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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java index 95430acaf10..d388560ba89 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java @@ -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 loadIds(DbSession dbSession, Request request) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/UpdateAction.java index 07bfeaf66bf..51e1562b715 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/UpdateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/UpdateAction.java @@ -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 index 00000000000..9aaafefd9a4 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/ruby/CallInvalidateMetricCache.java @@ -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(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/ruby/PlatformRubyBridge.java b/server/sonar-server/src/main/java/org/sonar/server/ruby/PlatformRubyBridge.java index 2fbe60b39ef..8222a263a08 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ruby/PlatformRubyBridge.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ruby/PlatformRubyBridge.java @@ -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. diff --git a/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyBridge.java b/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyBridge.java index 36e893d770e..5ad43ead960 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyBridge.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyBridge.java @@ -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 index 00000000000..b43b0e4d522 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/ruby/RubyMetricCache.java @@ -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 index 00000000000..c062d0e29af --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/ruby/call_invalidate_metric_cache.rb @@ -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 diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/CreateActionTest.java index 5b33efe6e9f..915f6cc8dde 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/CreateActionTest.java @@ -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); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java index 3853745ce14..ed4ee6fba1f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/DeleteActionTest.java @@ -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(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/MetricsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/MetricsWsTest.java index a78a2621368..619ad00c7aa 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/MetricsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/MetricsWsTest.java @@ -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) )); diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/UpdateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/UpdateActionTest.java index 92365534125..f83484a72eb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/metric/ws/UpdateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/ws/UpdateActionTest.java @@ -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); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/ruby/PlatformRubyBridgeTest.java b/server/sonar-server/src/test/java/org/sonar/server/ruby/PlatformRubyBridgeTest.java index 38bca7ca846..45502db309a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ruby/PlatformRubyBridgeTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ruby/PlatformRubyBridgeTest.java @@ -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); + } + } + } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ruby/database_version.rb b/server/sonar-server/src/test/resources/org/sonar/server/ruby/database_version.rb index 46797b911dc..430c218cd33 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/ruby/database_version.rb +++ b/server/sonar-server/src/test/resources/org/sonar/server/ruby/database_version.rb @@ -12,3 +12,11 @@ class DatabaseVersion end end + +class Metric + + def self.clear_cache + + end + +end