From 5ba3a2722374e3fc6b1afd447e92ca4dcfdb9d6f Mon Sep 17 00:00:00 2001 From: fagossa Date: Wed, 24 Jul 2019 15:55:14 +0200 Subject: [PATCH] Fix #8474 This commit does the following : * List the available constructors while creating a module, when no available constructor is found * Add a new unit test * Add a new utility function in ConstructorUtils --- .../scala/play/api/inject/ModulesSpec.scala | 22 +++++++++++++++++++ .../play/libs/reflect/ConstructorUtils.java | 18 +++++++++++++++ .../main/scala/play/api/inject/Module.scala | 5 ++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/core/play-guice/src/test/scala/play/api/inject/ModulesSpec.scala b/core/play-guice/src/test/scala/play/api/inject/ModulesSpec.scala index f83b3e2b4e2..6eead1274ed 100644 --- a/core/play-guice/src/test/scala/play/api/inject/ModulesSpec.scala +++ b/core/play-guice/src/test/scala/play/api/inject/ModulesSpec.scala @@ -10,6 +10,7 @@ import org.specs2.matcher.BeEqualTypedValueCheck import org.specs2.mutable.Specification import play.api.Configuration import play.api.Environment +import play.api.PlayException import play.{ Environment => JavaEnvironment } class ModulesSpec extends Specification { @@ -63,6 +64,22 @@ class ModulesSpec extends Specification { } } + "list current constructor parameters when any match" in { + val env = Environment.simple() + val conf = Configuration( + "play.modules.enabled" -> Seq( + classOf[InvalidConfigModule].getName + ) + ) + + Modules.locate(env, conf) must throwA[PlayException].like { + case e => + val firstConstructorParameters = "(com.typesafe.config.Config, java.lang.String, long)" + val secondConstructorParameters = "(com.typesafe.config.Config, java.lang.String)" + e.getMessage must contain(s"$firstConstructorParameters, $secondConstructorParameters") + } + } + } } @@ -78,3 +95,8 @@ class ScalaGuiceModule(val environment: Environment, val configuration: Configur class JavaGuiceConfigModule(val environment: JavaEnvironment, val config: Config) extends AbstractModule { override def configure(): Unit = () } + +class InvalidConfigModule(val config: Config, val ignored: String) extends AbstractModule { + def this(config: Config, ignored: String, useless: Long) = this(config, ignored) + override def configure(): Unit = () +} diff --git a/core/play/src/main/java/play/libs/reflect/ConstructorUtils.java b/core/play/src/main/java/play/libs/reflect/ConstructorUtils.java index ffd21bb2284..928779b33a9 100644 --- a/core/play/src/main/java/play/libs/reflect/ConstructorUtils.java +++ b/core/play/src/main/java/play/libs/reflect/ConstructorUtils.java @@ -18,6 +18,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; +import java.util.Arrays; /** Imported from apache.commons.lang3 3.6 */ public class ConstructorUtils { @@ -115,6 +116,23 @@ public static Constructor getMatchingAccessibleConstructor( return result; } + /** + * Finds the class names of all accessible constructors. + * + * @param the constructor type + * @return the constructors parameters, empty array if no matching accessible constructor found + */ + public static String[][] getConstructorParameters(final Class type) { + Constructor[] allConstructors = type.getDeclaredConstructors(); + return Arrays.stream(allConstructors) + .map( + cons -> + Arrays.stream(cons.getParameterTypes()) + .map(Class::getCanonicalName) + .toArray(String[]::new)) + .toArray(String[][]::new); + } + /** * Learn whether the specified class is generally accessible, i.e. is declared in an entirely * {@code public} manner. diff --git a/core/play/src/main/scala/play/api/inject/Module.scala b/core/play/src/main/scala/play/api/inject/Module.scala index 1324d8c94a7..7b88306e266 100644 --- a/core/play/src/main/scala/play/api/inject/Module.scala +++ b/core/play/src/main/scala/play/api/inject/Module.scala @@ -171,7 +171,10 @@ object Modules { tryConstruct() } .getOrElse { - throw new PlayException("No valid constructors", "Module [" + className + "] cannot be instantiated.") + val parameters: Array[Array[String]] = ConstructorUtils.getConstructorParameters(moduleClass) + throw new PlayException(s"""No valid public constructors for ${moduleClass.getCanonicalName}. Expected one of: + ${parameters.map(_.mkString("(", ", ", ")")).mkString(", ")} + """.stripMargin, "Module [" + className + "] cannot be instantiated.") } } catch { case e: PlayException => throw e