Spring DSL in Grails - resources.groovy - bean configuration in a different file? - spring

This question already exists in a way, but the existing question is missing some important links.
I'm trying to move the configuration of beans for my tests into separate files that end in *TestsSpringBeans.groovy
I've attempted to do this after reading "Loading Bean Definitions from the File System" (search for it) in the Groovy documentation.
Here are the relevant code segments:
import grails.util.*
beans = {
...
switch(Environment.current) {
case Environment.TEST:
loadBeans("classpath:*TestsSpringBeans.groovy")
break
}
}
resources.groovy - Loading the *TestSpringBeans files from the File System.
somePlace(jobdb.Company) {
name = "SomeCompany"
addr1 = "addr1"
addr2 = "addr2"
city = "city"
email = "somedude#h0tmail.com"
fax = "555-555-5555"
phone = "444-444-4444"
state = "PA"
zip = "19608"
version: 0
created = new Date()
updated = new Date()
website = "http://www.yahoo.com"
deleted = false
}
CompanyServiceTestsSpringBeans.groovy - Defining a bean for the Integration Test
// Retrieve configured bean from
Company someplace = ApplicationHolder.getApplication().getMainContext().getBean('somePlace')
CompanyServiceTests.groovy - Obtain the bean somePlace within the Integration Test...
Upon calling getBean('somePlace') within the test an error is displayed which reads that No bean named 'somePlace' is defined
The CompanyServiceTests.groovy file is stored with my integration tests, should I be storing this file somewhere else in the project directory structure?

Since your tests run in a way that using the classpath as a reference point is less important, you might try to load the beans { ... } file by referencing via project directory specific path. (e.g. $baseDir/test/resources/MyCustomBeans.groovy) or load the beans explicitly in your tests via #BeforeClass if you are using JUnit4 annotations:
def bb = new BeanBuilder()
def resource = new FileSystemResource('src/test/resources/testContext.groovy')
bb.loadBeans(resource)
appCtx = bb.createApplicationContext()
...

Related

Specifying rootPath for Liquibase via jOOQ generation

I'm trying to utilize jOOQ's ability to generate from Liquibase files. My file structure is as follows:
C
- dev
-- testproject
--- src/main/resources
---- db
----- changelog.xml
In order to reference this file from the jOOQ configuration, I have the following in my build.gradle.kts:
jooq {
configurations {
create("main") {
jooqConfiguration.apply {
generator.apply {
database.apply {
name = "org.jooq.meta.extensions.liquibase.LiquibaseDatabase"
properties.add(Property().apply {
key = "rootPath"
value = "C:/dev/testproject/src/main/resources/db/"
})
properties.add(Property().apply {
key = "scripts"
value = "changelog.xml"
})
}
}
}
}
}
}
I'm using plugin version 7.1.1 and have the following dependencies:
dependencies {
implementation("org.liquibase:liquibase-core:4.8.0") // I tried removing this, no change
jooqGenerator("org.postgresql:postgresql:42.3.2")
jooqGenerator("org.jooq:jooq-meta-extensions-liquibase:3.17.2")
jooqGenerator(files("src/main/resources")) // I don't think this is necessary
}
When I try to run jooqGenerate, the error I get is:
Caused by: liquibase.exception.ChangeLogParseException: The file changelog.xml was not found in
Specifying files by absolute path was removed in Liquibase 4.0. Please use a relative path or add '/' to the classpath parameter.
at liquibase.parser.core.xml.XMLChangeLogSAXParser.parseToNode(XMLChangeLogSAXParser.java:82)
at liquibase.parser.core.xml.AbstractChangeLogParser.parse(AbstractChangeLogParser.java:15)
at liquibase.Liquibase.getDatabaseChangeLog(Liquibase.java:369)
Notice how it doesn't say which directories it looked in. As far as I can tell, the resource accessor is not receiving the rootPath from the configuration. The relevant output from Liquibase is here. Again, it should say it looked in the rootPath, but it doesn't print anything else, so there must be no directories searched.
Not sure if this is helpful, but the jOOQ configuration file in build/tmp/generateJooq definitely has the rootPath:
<property>
<key>rootPath</key>
<value>C:/dev/testproject/src/main/resources/db/</value>
</property>
I'm not sure where I'm going wrong. I've also tried the following values of scripts without setting rootPath and seen the same behavior:
C:/dev/testproject/src/main/resources/db/changelog.xml
src/main/resources/db/changelog.xml
/src/main/resources/db/changelog.xml
classpath:src/main/resources/db/changelog.xml
classpath:/src/main/resources/db/changelog.xml
This was causing the problem (or rather, the confusion):
jooqGenerator(files("src/main/resources"))
Apparently, this sets the classpath of the jooqGenerator task to be src/main/resources! So, knowing that, I fixed my configuration to look like this:
database.apply {
name = "org.jooq.meta.extensions.liquibase.LiquibaseDatabase"
properties.add(Property().apply {
key = "scripts"
value = "classpath:db/changelog.xml"
})
}
Everything is working nicely now.

Need wire to emit interfaces for client and server in same gradle project

I want to use interfaces for both client and server in the same android app. Usecase is to run a okhttpmockwebserver serving gRPC requests within the same app the client is running in. For this i created two library projects with their own wire configuration for client and server similar to those
wire {
kotlin {
includes = ['com..caompany.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
}
wire {
kotlin {
includes = ['com..company.android.proto.*']
out "${buildDir}/protos"
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
}
Executing the wire-gradle-plugin fails with this exception:
com.company.android.proto.HelloReply$Companion$ADAPTER$1 is defined multiple times.
Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete, origin: .../com/company/android/proto/HelloReply$Companion$ADAPTER$1.dex
It would help me if wire could either
Generate all classes and interfaces at once including server and client role or
Exclude the generation of class files, only generating service interfaces for client or server
Is there a workaround i can achieve a similar result without gradle plugin support?
You can have multiple kotlin blocks at the same time. Wire will throw if you generate the same class twice so you need to define the rule as unique between both.
You need one block which will generate client role interfaces. You need one block to generate server roles interfaces. Lastly, you need to generate regular types in yet another block, or in one of them (but not both).
Something like this
wire {
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'client'
}
kotlin {
includes = ['all.services.or.package']
rpcCallStyle = 'suspending'
rpcRole = 'server'
}
kotlin {
excludes = ['all.services.or.package']
rpcRole = 'none'
}
}

Use different file path for testing in Spring Boot

I have a class in my project housing a method to retrieve files (as a list). In order to write unit tests, and to be able to put everything into a versioning tool, I want put a small example directory into my project. However, when I do that, my method needs to be able to distinguish whether or not it should read from the real (project-external) path or the testing environment.
This is my method:
fun getDirectoryContentObject(baseUserDir: String): UserLicenses {
val dirExists = Files.exists(Paths.get(licenseLocation + baseUserDir))
if(!dirExists) {
return UserLicenses(baseUserDir, listOf())
}
val userLicenses = UserLicenses(baseUserDir, listOf())
Files.walk(Paths.get(licenseLocation + baseUserDir)).forEach { outerIt ->
val dirOrFileName = outerIt.fileName.toString()
if (dirOrFileName != baseUserDir && !dirOrFileName.endsWith(licenseFileExtension)) {
val fileList: MutableList<String> = mutableListOf()
Files.walk(Paths.get(outerIt.toString())).forEach { innerIt ->
val subDirOrFileName = innerIt.fileName.toString()
if (subDirOrFileName.endsWith(licenseFileExtension)) {
fileList += subDirOrFileName
}
}
userLicenses.licenseVersions += LicenseVersions(dirOrFileName, fileList)
}
}
return userLicenses
}
The licenseLocation value is set by #Value from application.yml and point to the files outside the project.
How can I tell the method to get the files from the inside the project if it is being executed by a unit test?
You can use spring resource api to get file instance.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/ResourceUtils.html
External Example:
filepath: file:/some-os-path/some-file-somewhere.abc
ResourceUtils.getFile(filepath);
Test Example:
filepath: classpath:file-from-resources-folder.abc
ResourceUtils.getFile(filepath);

ConfigurationProperties loading list from YML

I'm trying to load Configuration from YML. I can load value and I can also load list if these are comma seperated values. But i can't load a typical YML List.
Configuration Class
#Component
#PropertySource("classpath:routing.yml")
#ConfigurationProperties
class RoutingProperties(){
var angular = listOf("nothing")
var value: String = ""
}
Working routing.yml
angular: /init, /home
value: Hello World
Not Working routing.yml
angular:
- init
- home
value: Hello World
Why can't i load the second version of yml / do I have a syntaxt error?
ENV: Kotlin, Spring 2.0.0.M3
As #flyx say, #PropetySource not worked with yaml files. But in spring you may override almost everything :)
PropertySource has additional parameter: factory. It's possible to create your own PropertySourceFactory base on DefaultPropertySourceFactory
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
And when use this factory in propertysource annotation:
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
Last that you need is to initialized variable angular with mutableList
Full code sample:
#Component
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
#ConfigurationProperties
open class RoutingProperties {
var angular = mutableListOf("nothing")
var value: String = ""
override fun toString(): String {
return "RoutingProperties(angular=$angular, value='$value')"
}
}
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
#SpringBootApplication
#EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class))
open class Application {
companion object {
#JvmStatic
fun main(args: Array<String>) {
val context = SpringApplication.run(Application::class.java, *args)
val bean = context.getBean(RoutingProperties::class.java)
println(bean)
}
}
}
Kinda old post, i know. But i am at the very same topic right now.
As of now, it seems that PropertySource does indeed work with yaml Files. Given the restriction that it only allows for primitive types (it seems) and it cant handle nested elements. I'm probably gonna dig a bit deeper and update my answer accordingly, but as of now, the accepted answer seems like a functioning workaround.
Well, according to the docs, your YAML file will be rewritten into a property file. The first YAML file becomes:
angular=/init, /home
value=Hello World
While the second one becomes:
angular[0]=init
angular[1]=home
value=Hello World
These are obviously two very different things and therefore behave differently.
Moreover, later in the docs, it is stated that YAML does not even work with #PropertySource:
24.6.4 YAML shortcomings
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
That makes me kind of wonder why the first case works for you at all.
The docs say this about the generated …[index] properties:
To bind to properties like that using the Spring DataBinder utilities (which is what #ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above
So, let's have a look at Kotlin docs: listOf returns a new read-only list of given elements. So the list is not mutable as required by the docs, which I assume is why it doesn't work. Try using a mutable list (since I have never used Kotlin, I cannot give you working code). Also try to declare it as java.util.List if that's possible in Kotlin.

Injecting Spring Beans to Groovy Script

I've seen many examples about Groovy objects as Spring beans but not vice versa. I'm using Groovy in a Java EE application like this:
GroovyCodeSource groovyCodeSource = new GroovyCodeSource(urlResource);
Class groovyClass = loader.parseClass(groovyCodeSource, false);
return (GroovyObject) groovyClass.newInstance();
In this way, classes written in Groovy with #Configurable annotation are being injected with Spring beans. It's OK for now.
How can I get the same by using GroovyScriptEngine? I don't want to define a class and I want it to work like a plain script. Is Spring/Groovy capable of that?
I've seen a post about this but I'm not sure whether it answers my question or not:
HERE
Do you mean that you'd like to add properties to the script, and inject those? Would you provide getter and setter? This does not make much sense to me. What makes sense, is adding the mainContext to the bindings of the script, or adding selected beans to the bindings.
These beans - or the context - would then be accessible directly in the script, as if it was injected.
def ctx = grailsApplication.mainContext
def binding = new Binding([:])
Map variables = [
'aService',
'anotherService'
].inject([config:grailsApplication.config, mainContext:ctx]) { m, beanName ->
def bean = ctx.getBean(beanName)
m[beanName] = bean
m
}
binding.variables << variables
def compiler = new CompilerConfiguration()
compiler.setScriptBaseClass(baseScriptClassName)
def shell = new GroovyShell(new GroovyClassLoader(), binding, compiler)
script=shell.parse(scriptStr)
script.binding=binding
script.init()
script.run()

Resources