Skip to content

Commit

Permalink
Merge pull request #432 from mhawryluk/conditional-bean-includes
Browse files Browse the repository at this point in the history
Conditional beans include support in commons-spring
  • Loading branch information
Roman Janusz authored Mar 6, 2023
2 parents 7635485 + 5b9b974 commit 6b61445
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.avsystem.commons
package spring

import java.{util => ju}

import com.avsystem.commons.spring.AttrNames._
import scala.annotation.nowarn
import com.typesafe.config._
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder
Expand All @@ -13,9 +10,13 @@ import org.springframework.beans.factory.support._
import org.springframework.beans.{MutablePropertyValues, PropertyValue}
import org.springframework.core.io.Resource

import java.{util => ju}
import scala.annotation.nowarn

class HoconBeanDefinitionReader(registry: BeanDefinitionRegistry)
extends AbstractBeanDefinitionReader(registry) {

import com.avsystem.commons.spring.HoconBeanDefinitionReader.Keys._
import com.typesafe.config.ConfigValueType._

private implicit class ConfigValueExtensions(value: ConfigValue) {
Expand Down Expand Up @@ -344,9 +345,22 @@ class HoconBeanDefinitionReader(registry: BeanDefinitionRegistry)
}
}

def loadBeanDefinitions(config: Config): Int = {
val beans = if (config.hasPath("beans")) config.getObject("beans") else ConfigFactory.empty.root
val aliases = if (config.hasPath("aliases")) config.getObject("aliases") else ConfigFactory.empty.root
private def readConditionals(config: Config): Config = {
if (!config.hasPath(Conditionals)) config
else config.getList(Conditionals).asScala.foldLeft(config.withoutPath(Conditionals)) { (currentConfig, conditionalObject) =>
val props = getProps(conditionalObject.as[ConfigObject])

if (props(Condition).as[Boolean])
readConditionals(props(Config).as[Config]).withFallback(currentConfig)
else
currentConfig
}
}

def loadBeanDefinitions(resourceConfig: Config): Int = {
val config = readConditionals(resourceConfig)
val beans = if (config.hasPath(Beans)) config.getObject(Beans) else ConfigFactory.empty.root
val aliases = if (config.hasPath(Aliases)) config.getObject(Aliases) else ConfigFactory.empty.root
val result = readBeans(beans)
readAliases(aliases)
result
Expand All @@ -355,3 +369,12 @@ class HoconBeanDefinitionReader(registry: BeanDefinitionRegistry)
def loadBeanDefinitions(resource: Resource): Int =
loadBeanDefinitions(ConfigFactory.parseURL(resource.getURL).resolve)
}
object HoconBeanDefinitionReader {
object Keys {
final val Conditionals = "conditionals"
final val Condition = "condition"
final val Config = "config"
final val Beans = "beans"
final val Aliases = "aliases"
}
}
6 changes: 6 additions & 0 deletions commons-spring/src/test/resources/conditionalInclude.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
beans {
beanFromConditional = {
%class = com.avsystem.commons.spring.ConditionalTestBean, %construct = true
int = 100
}
}
7 changes: 7 additions & 0 deletions commons-spring/src/test/resources/conditionalsDisabled.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
featureFlag.enabled = false

beans.beanFromConditional = null

conditionals = [
{condition: ${featureFlag.enabled}, config: {include "conditionalInclude.conf"}},
]
7 changes: 7 additions & 0 deletions commons-spring/src/test/resources/conditionalsEnabled.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
featureFlag.enabled = true

beans.beanFromConditional = null

conditionals = [
{condition: ${featureFlag.enabled}, config: {include "conditionalInclude.conf"}},
]
20 changes: 20 additions & 0 deletions commons-spring/src/test/resources/conditionalsNested.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
featureFlag.enabled = true

beans {
testBean {
%class = com.avsystem.commons.spring.TestBean
}
}

conditionals = [
{condition: ${featureFlag.enabled}, config: {beans.testBean.int = 0}},
{condition: ${featureFlag.enabled}, config: {beans.testBean.int = 1}},
{
condition: ${featureFlag.enabled}, config: {
conditionals = [
{condition: true, config: {beans.testBean.int = 2}}
]
}
},
{condition: false, config: {beans.testBean.int = 3}},
]
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.avsystem.commons
package spring

import java.{util => ju}

import com.typesafe.config.{Config, ConfigFactory}
import org.scalatest.BeforeAndAfterEach
import org.scalatest.funsuite.AnyFunSuite
import org.springframework.beans.factory.support.DefaultListableBeanFactory
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.StandardReflectionParameterNameDiscoverer

import java.{util => ju}
import scala.beans.BeanProperty

class TestBean(val constrInt: Int = 1, val constrString: String = "constrDefault") {
Expand All @@ -25,7 +25,17 @@ object TestBean {
new TestBean(theInt, theString)
}

class HoconBeanDefinitionReaderTest extends AnyFunSuite {
class ConditionalTestBean(int: Int) {

import ConditionalTestBean.initializedCount

initializedCount += 1
}
object ConditionalTestBean {
var initializedCount = 0
}

class HoconBeanDefinitionReaderTest extends AnyFunSuite with BeforeAndAfterEach {
def createContext(resource: String): GenericApplicationContext = {
val beanFactory = new DefaultListableBeanFactory
beanFactory.setParameterNameDiscoverer(new StandardReflectionParameterNameDiscoverer)
Expand All @@ -40,6 +50,10 @@ class HoconBeanDefinitionReaderTest extends AnyFunSuite {
ctx
}

override def beforeEach(): Unit = {
ConditionalTestBean.initializedCount = 0
}

test("hocon bean definition reader should work") {
val ctx = createContext("testBean.conf")

Expand Down Expand Up @@ -82,4 +96,23 @@ class HoconBeanDefinitionReaderTest extends AnyFunSuite {
assert(testBeanFMDefAll.constrInt == -1)
assert(testBeanFMDefAll.constrString == "factoryDefault")
}

test("file should be included with true condition") {
val ctx = createContext("conditionalsEnabled.conf")
val testBean = ctx.getBean("beanFromConditional", classOf[ConditionalTestBean])
assert(testBean != null)
assertResult(1)(ConditionalTestBean.initializedCount)
}

test("file should not be included with false condition") {
val ctx = createContext("conditionalsDisabled.conf")
assert(!ctx.containsBean("beanFromConditional"))
assertResult(0)(ConditionalTestBean.initializedCount)
}

test("hocon bean definition with nested conditionals should work") {
val ctx = createContext("conditionalsNested.conf")
val testBean = ctx.getBean("testBean", classOf[TestBean])
assert(testBean.int == 2)
}
}

0 comments on commit 6b61445

Please sign in to comment.