]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-192 core extension can now declare MyBatis mapper and alias
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 3 Dec 2018 10:24:13 +0000 (11:24 +0100)
committerSonarTech <sonartech@sonarsource.com>
Fri, 21 Dec 2018 19:21:02 +0000 (20:21 +0100)
server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java
server/sonar-db-core/src/test/java/org/sonar/db/CoreTestDb.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfExtension.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/TestDb.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java

index 703bf316975eacd15b12888fd11a5612642b6161..33229b0a43fec7f232789122cf7c72b4bcf55b27 100644 (file)
@@ -84,6 +84,10 @@ public class AbstractDbTester<T extends CoreTestDb> extends ExternalResource {
     this.db = db;
   }
 
+  public T getDb() {
+    return db;
+  }
+
   public void executeUpdateSql(String sql, Object... params) {
     try (Connection connection = getConnection()) {
       new QueryRunner().update(connection, sql, params);
index 6b963d27e319da0c01ed01e37644552e5ebcc690..fe7852f5a654bfcf2c987138d5d39c22dc6a3ead 100644 (file)
@@ -26,6 +26,7 @@ import java.net.URI;
 import java.sql.SQLException;
 import java.util.Map;
 import java.util.Properties;
+import java.util.function.BiConsumer;
 import javax.annotation.Nullable;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.io.FileUtils;
@@ -61,17 +62,18 @@ class CoreTestDb {
   private IDatabaseTester tester;
   private boolean isDefault;
 
-  static CoreTestDb create(@Nullable String schemaPath) {
-    if (schemaPath == null) {
-      if (DEFAULT == null) {
-        DEFAULT = new CoreTestDb(null);
-      }
-      return DEFAULT;
-    }
-    return new CoreTestDb(schemaPath);
+  protected CoreTestDb() {
+    // use static factory method
+  }
+
+  protected CoreTestDb(CoreTestDb base) {
+    this.db = base.db;
+    this.isDefault = base.isDefault;
+    this.commands = base.commands;
+    this.tester = base.tester;
   }
 
-  CoreTestDb(@Nullable String schemaPath) {
+  CoreTestDb init(@Nullable String schemaPath, BiConsumer<Database, Boolean> extendedStart) {
     if (db == null) {
       Settings settings = new MapSettings().addProperties(System.getProperties());
       if (isNotEmpty(settings.getString("orchestrator.configUrl"))) {
@@ -102,16 +104,23 @@ class CoreTestDb {
       commands = DatabaseCommands.forDialect(db.getDialect());
       tester = new DataSourceDatabaseTester(db.getDataSource(), commands.useLoginAsSchema() ? login : null);
 
-      extendStart(db);
+      extendedStart.accept(db, true);
+    } else {
+      extendedStart.accept(db, false);
     }
+    return this;
   }
 
-  /**
-   * to be overridden by subclasses to extend what's done when db is created
-   * @param db
-   */
-  protected void extendStart(Database db) {
-    // nothing done here
+  static CoreTestDb create(@Nullable String schemaPath) {
+    if (schemaPath == null) {
+      if (DEFAULT == null) {
+        DEFAULT = new CoreTestDb().init(null, (db, created) -> {
+        });
+      }
+      return DEFAULT;
+    }
+    return new CoreTestDb().init(schemaPath, (db, created) -> {
+    });
   }
 
   public void start() {
index 1172e62b86da1666c5cb89a09e9ed914a76a6ea1..8316c4f74b9a48b561beabed3a59d9e3bde46cd0 100644 (file)
@@ -23,6 +23,10 @@ import com.google.common.annotations.VisibleForTesting;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nullable;
 import org.apache.ibatis.logging.LogFactory;
 import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSession;
@@ -139,11 +143,16 @@ import org.sonar.db.webhook.WebhookDeliveryMapper;
 import org.sonar.db.webhook.WebhookMapper;
 
 public class MyBatis implements Startable {
-
+  private final List<MyBatisConfExtension> confExtensions;
   private final Database database;
   private SqlSessionFactory sessionFactory;
 
   public MyBatis(Database database) {
+    this(database, null);
+  }
+
+  public MyBatis(Database database, @Nullable MyBatisConfExtension[] confExtensions) {
+    this.confExtensions = confExtensions == null ? Collections.emptyList() : Arrays.asList(confExtensions);
     this.database = database;
   }
 
@@ -202,6 +211,7 @@ public class MyBatis implements Startable {
     confBuilder.loadAlias("User", UserDto.class);
     confBuilder.loadAlias("UuidWithProjectUuid", UuidWithProjectUuidDto.class);
     confBuilder.loadAlias("ViewsSnapshot", ViewsSnapshotDto.class);
+    confExtensions.forEach(ext -> ext.loadAliases(confBuilder::loadAlias));
 
     // keep them sorted alphabetically
     Class<?>[] mappers = {
@@ -268,6 +278,9 @@ public class MyBatis implements Startable {
       WebhookDeliveryMapper.class
     };
     confBuilder.loadMappers(mappers);
+    confExtensions.stream()
+      .flatMap(MyBatisConfExtension::getMapperClasses)
+      .forEach(confBuilder::loadMapper);
 
     sessionFactory = new SqlSessionFactoryBuilder().build(confBuilder.build());
   }
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfExtension.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfExtension.java
new file mode 100644 (file)
index 0000000..3d75572
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.db;
+
+import java.util.stream.Stream;
+import org.sonar.core.extension.PlatformLevel;
+
+@PlatformLevel(1)
+public interface MyBatisConfExtension {
+  default void loadAliases(LoadAliasContext context) {
+    // no alias to load
+  }
+
+  interface LoadAliasContext {
+    void loadAlias(String alias, Class<?> dtoClass);
+  }
+
+  Stream<Class<?>> getMapperClasses();
+}
index 82194a971a442489d69f0ad9b9fbd58608b6852d..41a7d21036b4c0716bc07aee1e76d9adb4fff2cb 100644 (file)
@@ -21,8 +21,11 @@ package org.sonar.db;
 
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Stream;
 import javax.annotation.Nullable;
 import org.apache.commons.dbcp2.BasicDataSource;
 import org.apache.commons.lang.StringUtils;
@@ -90,8 +93,8 @@ public class DbTester extends AbstractDbTester<TestDb> {
   private final WebhookDeliveryDbTester webhookDeliveryDbTester;
   private final AlmDbTester almDbTester;
 
-  public DbTester(System2 system2, @Nullable String schemaPath) {
-    super(TestDb.create(schemaPath));
+  private DbTester(System2 system2, @Nullable String schemaPath, MyBatisConfExtension... confExtensions) {
+    super(TestDb.create(schemaPath, confExtensions));
     this.system2 = system2;
 
     initDbClient();
@@ -125,6 +128,14 @@ public class DbTester extends AbstractDbTester<TestDb> {
     return new DbTester(system2, null);
   }
 
+  public static DbTester createWithExtensionMappers(Class<?> firstMapperClass, Class<?>... otherMapperClasses) {
+    return new DbTester(System2.INSTANCE, null, new DbTesterMyBatisConfExtension(firstMapperClass, otherMapperClasses));
+  }
+
+  public static DbTester createWithExtensionMappers(System2 system2, Class<?> firstMapperClass, Class<?>... otherMapperClasses) {
+    return new DbTester(system2, null, new DbTesterMyBatisConfExtension(firstMapperClass, otherMapperClasses));
+  }
+
   public static DbTester createForSchema(System2 system2, Class testClass, String filename) {
     String path = StringUtils.replaceChars(testClass.getCanonicalName(), '.', '/');
     String schemaPath = path + "/" + filename;
@@ -257,7 +268,7 @@ public class DbTester extends AbstractDbTester<TestDb> {
     return pluginDbTester;
   }
 
-  public WebhookDbTester webhooks(){
+  public WebhookDbTester webhooks() {
     return webhookDbTester;
   }
 
@@ -355,4 +366,38 @@ public class DbTester extends AbstractDbTester<TestDb> {
     }
   }
 
+  private static class DbTesterMyBatisConfExtension implements MyBatisConfExtension {
+    // do not replace with a lambda to allow cache of MyBatis instances in TestDb to work
+    private final Class<?>[] mapperClasses;
+
+    public DbTesterMyBatisConfExtension(Class<?> firstMapperClass, Class<?>... otherMapperClasses) {
+      this.mapperClasses = Stream.concat(
+        Stream.of(firstMapperClass),
+        Arrays.stream(otherMapperClasses))
+        .sorted(Comparator.comparing(Class::getName))
+        .toArray(Class<?>[]::new);
+    }
+
+    @Override
+    public Stream<Class<?>> getMapperClasses() {
+      return Arrays.stream(mapperClasses);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      DbTesterMyBatisConfExtension that = (DbTesterMyBatisConfExtension) o;
+      return Arrays.equals(mapperClasses, that.mapperClasses);
+    }
+
+    @Override
+    public int hashCode() {
+      return Arrays.hashCode(mapperClasses);
+    }
+  }
 }
index d8c4f250205f52558fe6364b13d65300427e9340..cd877df8beefea8bc4f71f52b3e39efa2a077a0b 100644 (file)
  */
 package org.sonar.db;
 
+import java.util.HashMap;
+import java.util.Map;
 import javax.annotation.Nullable;
 
 class TestDb extends CoreTestDb {
 
-  private static TestDb SINGLETON;
+  private static TestDb defaultSchemaBaseTestDb;
+  // instantiating MyBatis objects is costly => we cache them for default schema
+  private static final Map<MyBatisConfExtension[], TestDb> defaultSchemaTestDbsWithExtensions = new HashMap<>();
 
   private MyBatis myBatis;
 
-  private TestDb(@Nullable String schemaPath) {
-    super(schemaPath);
+  private TestDb(@Nullable String schemaPath, MyBatisConfExtension... confExtensions) {
+    super();
+    init(schemaPath, (db, created) -> myBatis = newMyBatis(db, confExtensions));
   }
 
-  static TestDb create(@Nullable String schemaPath) {
+  private TestDb(TestDb base, MyBatis myBatis) {
+    super(base);
+    this.myBatis = myBatis;
+  }
+
+  private static MyBatis newMyBatis(Database db, MyBatisConfExtension[] extensions) {
+    MyBatis newMyBatis = new MyBatis(db, extensions);
+    newMyBatis.start();
+    return newMyBatis;
+  }
+
+  static TestDb create(@Nullable String schemaPath, MyBatisConfExtension... confExtensions) {
+    MyBatisConfExtension[] extensionArray = confExtensions == null || confExtensions.length == 0 ? null : confExtensions;
     if (schemaPath == null) {
-      if (SINGLETON == null) {
-        SINGLETON = new TestDb(null);
+      if (defaultSchemaBaseTestDb == null) {
+        defaultSchemaBaseTestDb = new TestDb((String) null);
       }
-      return SINGLETON;
+      if (extensionArray != null) {
+        return defaultSchemaTestDbsWithExtensions.computeIfAbsent(
+          extensionArray,
+          extensions -> new TestDb(defaultSchemaBaseTestDb, newMyBatis(defaultSchemaBaseTestDb.getDatabase(), extensions)));
+      }
+      return defaultSchemaBaseTestDb;
     }
-    return new TestDb(schemaPath);
-  }
-
-  @Override
-  protected void extendStart(Database db) {
-    myBatis = new MyBatis(db);
-    myBatis.start();
+    return new TestDb(schemaPath, confExtensions);
   }
 
   MyBatis getMyBatis() {
index 707212e283a09dc265740a093159f40cbfe32bc2..d637737c8da591a0c29b2143d8ea3d5c4e43c9b7 100644 (file)
@@ -78,7 +78,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 public class ChangeParentActionTest {
 
   @Rule
-  public DbTester db = new DbTester(System2.INSTANCE, null);
+  public DbTester db = DbTester.create(System2.INSTANCE);
   @Rule
   public EsTester es = EsTester.create();
   @Rule