replace tokens in resource and webapp files - java

I would like to improve the way our gradle build replaces #VERSION# with project.version in some of our resource and webapp files.
I currently (groovy) have:
def replaceVersionInResources(pattern) {
processResources {
filesMatching(pattern) {
it.filter(ReplaceTokens, tokens: ['VERSION': project.version])
}
}
}
which is imported in our build.gradle files and then called with:
replaceVersionInResources('**/*.conf')
Note that the project.version must be set before calling replaceVersionInResources.
The improved version should retrieve the file-pattern from an extension property and allow the
project.version to be set at a later point.
I was able to achieve this for Java source code and am looking for a similar solution for resources and webapp files.
The current webapp code is very similar (obviously with the same downsides):
def replaceVersionInWebapp(matchingPattern) {
if (project.tasks.findByName('bootWar')) {
bootWar {
filesMatching(matchingPattern) {
it.filter(ReplaceTokens, tokens: ['VERSION': project.version])
}
}
}
}
Here is our solution for Java Source Code.
Feedback is welcome!
I created a Sync task, which used the main SourceSet as input:
from(project.the<SourceSetContainer>()[SourceSet.MAIN_SOURCE_SET_NAME].java)
and then afterwards wired the output of this task as input to the CompileJava tasks:
tasks.withType<JavaCompile>().configureEach {
setSource(replaceVersionInSource.get().outputs)
}
Everything below is from one file (buildSrc/src/main/kotlin/versioning.gradle.kts):
// Based on https://discuss.gradle.org/t/indirection-lazy-evaluation-for-copy-spec-filter-properties/7173/7
class DeferredReplaceTokens(`in`: Reader) : FilterReader(`in`) {
private var actualReader: FilterReader? = null
var tokens: MapProperty<String, String>? = null
private fun reader(): FilterReader {
actualReader = actualReader ?: ReplaceTokens(this.`in`).also { replaceTokens ->
val tokens = this.tokens
if (tokens == null) {
println("No tokens set. tokens is null")
} else {
tokens.keySet().get()
.map { key -> key to tokens.getting(key).get() }
.onEach { println("Token: ${it.first} : ${it.second}") }
.map { (key, value) -> ReplaceTokens.Token().also { it.key = key; it.value = value } }
.forEach(replaceTokens::addConfiguredToken)
}
}
return actualReader!!
}
override fun read(cbuf: CharArray, off: Int, len: Int): Int = reader().read(cbuf, off, len)
override fun read(): Int = reader().read()
override fun close() = reader().close()
}
Here is the extension I've created, with the tokens-map and the resourcePattern.
open class ReplaceVersionExtension #Inject constructor(
project: Project
) {
val tokens: MapProperty<String, String> = project.objects.mapProperty(String::class.java, String::class.java)
.convention(project.provider { mutableMapOf("VERSION" to "${project.version}") })
val resourcePattern: Property<String> = project.objects.property(String::class.java)
val webappPattern: Property<String> = project.objects.property(String::class.java)
}
val replaceVersion = extensions.create<ReplaceVersionExtension>("replaceVersion")
val replaceVersionInSource by tasks.registering(Sync::class) {
onlyIf { replaceVersion.tokens.get().isNotEmpty() }
from(project.the<SourceSetContainer>()[SourceSet.MAIN_SOURCE_SET_NAME].java)
inputs.property("tokens", replaceVersion.tokens)
filter<DeferredReplaceTokens>("tokens" to replaceVersion.tokens)
into(layout.buildDirectory.dir("src"))
}
Here is the part where I tell the JavaCompile tasks to use the output from the Sync task above.
tasks.withType<JavaCompile>().configureEach {
setSource(replaceVersionInSource.get().outputs)
}
This code is then imported and if necessary configured using the extension:
replaceVersion {
tokens.put("VERSION", "CUSTOM_VERSION_TEXT")
tokens.put("RELEASE", "2021")
}

Related

How consume multiple endpoints at same time using Kotlin Coroutine in a BackEnd Controller

Context: I found few tutorials explaining how consume mutilple endpoints from Kotlin at same time but they are based on Android and in my case it is a backend application. I have some experience using CompleteableFuture but I assume I should use Coroutine since it is a Kotlin and there is no Spring dependency.
Following some suggestions, I reached
#Singleton
class PersonEndpoint()
{
#Inject
lateinit var employeClient: EmployeClient
override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {
var combinedResult: String
GlobalScope.launch {
val resultA: String
val resultB: String
val employeesA = async{ employeClient.getEmployeesA()}
val employeesB = async{ employeClient.getEmployeesB()}
try{
combinedResult = employeesA.await() + employeesB.await()
print(combinedResult)
} catch (ex: Exception) {
ex.printStackTrace()
}
// ISSUE 1
if I try add return over here it is not allowed.
I understand it is working how it is designed to work: GlobalScope is running in different thread
}
// ISSUE 2
if I try return combinedResult over here combinedResult isn't initialized.
I understand it is working how it is designed to work: GlobalScope is running in different thread and I can
debug and see that return over here executes earlier than employeesA.await = employeesB.await
}
So, how can I execute combinedResult = employeesA.await() + employeesB.await() before returning to the client?
*** Edited after Denis/ answer
#Singleton
class CustomerEndpoint(){
fun serve(): Collection<Int> {
return runBlocking {
async {
getItemDouble(1)
}
async {
getItemTriple(1)
}
}.map { it.await() }
}
suspend fun getItemDouble(i: Int): Int {
delay(1000)
return i * 2
}
suspend fun getItemTriple(i: Int): Int {
delay(1000)
return i * 3
}
override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {
val result = serve()
println("Got result $result")
...
}
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis
fun main() {
val durationMs = measureTimeMillis {
val result = serve()
println("Got result $result")
}
println("The processing is done in $durationMs ms")
}
fun serve(): Collection<Int> {
return runBlocking {
(1..2).map {
async {
getItem(it)
}
}.map { it.await() }
}
}
suspend fun getItem(i: Int): Int {
delay(1000) // Emulate item retrieval work
return i * 2
}
Note that here we have two nested calls - getItem(1) and getItem(2). We can see that they are executed in parallel as overall running time is ~1 second.
Edited in August 05th 2021
private suspend fun myMethod(): List<Any> {
return runBlocking {
listOf(
async { method1() },
async { method2() }
).map { it.await() }
}
}
method1 and method2 are methods calling different endpoints.

runtime error while accessing google api in android emulator

I am trying to access the googleFit API.
It seems pretty straightforward. Get the google sign-in permissions and required authorizations then query for Step count.
My code doesn't seem to work.
When I debug this the fitnessOption declaration part throws "source code doesn't match byte code" error. I cleaned my project, rebuild it it didn't work
Android gurus, Where am I going wrong??
fun getAuthorizationAndReadData() {
try {
MainActivity().fitSignIn(FitActionRequestCode.READ_DATA)
} catch () {
Log.i("e", "error!!!!")
}
}
MainActivity
enum class FitActionRequestCode {
READ_DATA
}
private val fitnessOptions: GoogleSignInOptionsExtension = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ).build()
fun fitSignIn(requestCode: FitActionRequestCode) {
if (oAuthPermissionsApproved()) {
readHistoryData()
} else {
requestCode.let {
GoogleSignIn.requestPermissions(
this,
requestCode.ordinal,
getGoogleAccount(), fitnessOptions)
}
}
}
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
private fun performActionForRequestCode(requestCode: FitActionRequestCode) = when (requestCode) {
FitActionRequestCode.READ_DATA -> readHistoryData()
}
private fun readHistoryData(): Task<DataReadResponse> {
// Begin by creating the query.
val readRequest = queryFitnessData()
// Invoke the History API to fetch the data with the query
return Fitness.getHistoryClient(this, getGoogleAccount())
.readData(readRequest)
.addOnSuccessListener { dataReadResponse ->
printData(dataReadResponse)
Log.i(ContentValues.TAG, "Data read was successful!") }
.addOnFailureListener { e ->
Log.e(ContentValues.TAG, "There was a problem reading the data.", e)
}
}
private fun queryFitnessData(): DataReadRequest {
// [START build_read_data_request]
// Setting a start and end date using a range of 1 week before this moment.
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
val now = Date()
calendar.time = now
val endTime = calendar.timeInMillis
calendar.add(Calendar.WEEK_OF_YEAR, -1)
val startTime = calendar.timeInMillis
return DataReadRequest.Builder()
.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build()
}
Try following steps once.
Click Build -> Clean then Disable Instant Run, in Settings -> Build, Execution, Deployment

Start obserable chain from Single.onErrorResumeNext in Rxjava2

loadSingle return Single object, if it fails I want to call getObservable(rsList) which return Observable.
I am trying with onErrorResumeNext but it needs Single object.
How can I call getObservable(rsList) on failure of loadSingle() ?
Thanks in advance!!
repo.loadSingle()
.subscribeOn(Schedulers.io())
.onErrorResumeNext {
repo.getObservable(rsList)
}
.flatMapObservable {
if (it != null && it.status == Status.SUCCESS) {
upRsList(it.data)
}
repo.getObservable(rsList)
}
({ //observable success
}, {
//observable error
})
Api interface
interface HomeApi{
fun getSingel():Single<List<String>>
fun getObservable():Observable<HomeResponse>
}
Dependencies
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
implementation "io.reactivex.rxjava3:rxjava:3.0.4"
implementation "io.reactivex.rxjava3:rxkotlin:3.0.0"
Required classes
internal interface Repo {
fun loadSingle(): Single<Result<List<String>>>
fun getObservable(list: List<String>): Observable<String>
}
internal class RepoImpl : Repo {
override fun loadSingle(): Single<Result<List<String>>> {
return Single.error(RuntimeException("fail"))
}
override fun getObservable(list: List<String>): Observable<String> {
if (list === emptyList<String>()) {
return Observable.just("42")
}
return Observable.just("success")
}
}
internal sealed class Result<T> {
data class Success<T>(val value: T) : Result<T>()
data class Failure<T>(private val failure: Throwable) : Result<T>()
}
Test
Wrap the error via #onErrorReturn into a default value, and handle the result accordingly.
class So64751341 {
#Test
fun `64751341`() {
val repo: Repo = RepoImpl()
val testScheduler = TestScheduler()
val flatMapObservable = repo.loadSingle()
.subscribeOn(testScheduler)
.onErrorReturn { failure -> Result.Failure(failure) }
.flatMapObservable { result ->
when (result) {
is Result.Success -> repo.getObservable(result.value)
is Result.Failure -> repo.getObservable(emptyList())
}
}
val test = flatMapObservable.test()
testScheduler.triggerActions()
test // return default value 42 onError
.assertValue("42")
}
}
Repo#loadSingle() throws exception synchronously
internal class RepoExceptionImpl : Repo {
override fun loadSingle(): Single<Result<List<String>>> {
throw java.lang.RuntimeException("whatever")
}
override fun getObservable(list: List<String>): Observable<String> {
if (list === emptyList<String>()) {
return Observable.just("42")
}
return Observable.just("success")
}
}
Test
Repo#loadSingle must be wrapped with Single#defer. Single#defer will catch the exception and emit it as #onError to the subscriber, which in turn will be handled by #onErrorReturn
#Test
fun `64751341_exception`() {
val repo: Repo = RepoExceptionImpl()
val testScheduler = TestScheduler()
val flatMapObservable = Single.defer {
repo.loadSingle()
}
.subscribeOn(testScheduler)
.onErrorReturn { failure -> Result.Failure(failure) }
.flatMapObservable { result ->
when (result) {
is Result.Success -> repo.getObservable(result.value)
is Result.Failure -> repo.getObservable(emptyList())
}
}
val test = flatMapObservable.test()
testScheduler.triggerActions()
test // return default value 42 onError
.assertValue("42")
}

Generated java source code not found by kotlin compiler

I'm writing a gradle plugin to generate Java code. My plugin generates code that's based on the Android R class, so it's dependent on the
processVariantResources
Task. In my plugin, I'm using this code to do that:
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<MyPluginExtension>(PLUGIN_NAME, MyPluginExtension::class.java)
project.tasks.whenTaskAdded { task ->
if (task.name.startsWith("process") && task.name.contains("Resources") && !task.name.contains("Test")) {
val index = task.name.indexOf("Resources")
val variantName = task.name.substring(7, index)
//Create my task and add it to the project, make it dependent on processResources
project.task("my${variantName}ResourceTask") {
it.doLast {
generateSomeCodeForVariant(project, extension, variantName)
}
taskList.add(it)
}.dependsOn(task)
}
}
project.afterEvaluate {
val appExtension = project.extensions.findByType(AppExtension::class.java)
appExtension?.applicationVariants?.all { variant ->
val myTask = project.tasks.getByName("my${variant.name.capitalize()}ResourcesTask")
val outputDir = "${project.buildDir}/generated/source/myplugin/${variant.name}"
//register my task as java generating
variant.registerJavaGeneratingTask(myTask, File(outputDir))
}
}
}
then, in the build.gradle of the project where I'm using this plugin, I've added
android {
sourceSets {
main {
java.srcDirs += ['build/generated/source/myplugin']
kotlin.srcDirs += ['build/generated/source/myplugin']
}
}
}
My plugin actually generates source code to the directory:
build/generated/source/myplugin/com/mygroup/myartifact
Anyway, the code gets generated correctly, and put in the correct place, but I can't get the compiler to recognize my generated code. Any help would be appreciated.
I refactored the code and implemented the strategy describe here:
my new code looks like this:
project.afterEvaluate {
val appExtension = project.extensions.findByType(AppExtension::class.java)
appExtension?.applicationVariants?.all { variant ->
val processResourcesTask = project.tasks.getByName("process${variant.name.capitalize()}Resources")
val myTask = it.task("my${variant.name.capitalize()}ResourceTask") {
it.doLast {
doSomeStuff(project, extension, variant.name.capitalize())
}
}.dependsOn(processResourcesTask)
val outputDir = "${project.buildDir}/generated/source/myplugin/${variant.name}"
variant.registerJavaGeneratingTask(myTask, File(outputDir))
val kotlinCompileTask = it.tasks.findByName("compile${variant.name.capitalize()}Kotlin") as? SourceTask
if (kotlinCompileTask != null) {
kotlinCompileTask.dependsOn(myTask)
val srcSet = it.objects.sourceDirectorySet("myplugin", "myplugin").srcDir(outputDir)
kotlinCompileTask.source(srcSet)
}
}
}
I also removed the sourceSets definition from the client build.gradle file, and
now everything works correctly.

Gradle - throw exception if project still has SNAPSHOT dependencies

I want to fail the gradle build if the current project still has snapshot dependencies.
My code so far only looks for java dependencies, missing the .NET ones so it only works for java projects. I want to make it work for all projects.
def addSnapshotCheckingTask(Project project) {
project.tasks.withType(JavaCompile) { compileJava ->
project.tasks.create(compileJava.name + 'SnapshotChecking', {
onlyIf {
project.ext.isRelease || project.ext.commitVersion != null
}
compileJava.dependsOn it
doLast {
def snapshots = compileJava.classpath
.filter { project.ext.isRelease || !(it.path ==~ /(?i)${project.rootProject.projectDir.toString().replace('\\', '\\\\')}.*build.libs.*/) }
.filter { it.path =~ /(?i)-SNAPSHOT/ }
.collect { it.name }
.unique()
if (!snapshots.isEmpty()) {
throw new GradleException("Please get rid of snapshots for following dependencies before releasing $snapshots")
}
}
})
}
}
I need some help in generifying this snippet to be applicable to all types of dependencies(not just java)
Thanks!
L.E. Could something like this work?
https://discuss.gradle.org/t/how-can-i-check-for-snapshot-dependencies-and-throw-an-exception-if-some-where-found/4064
So I got it working by tweaking a bit the response of #lance-java, it looks something like:
Task snapshotCheckingTask = project.tasks.create('snapshotCheckingTask', {
doLast {
def snapshots = new ArrayList()
def projectConfigurations = project.configurations.findAll { true }
projectConfigurations.each {
if (it.isCanBeResolved()) {
it.resolvedConfiguration.resolvedArtifacts.each {
if (it.moduleVersion.id.version.endsWith('-SNAPSHOT')) {
snapshots.add(it)
}
}
}
}
if (!snapshots.isEmpty()) {
throw new GradleException("Please get rid of snapshots for following dependencies before releasing $snapshots")
} else {
throw new GradleException("Hah, no snapshots!")
}
}
})
project.tasks.release.dependsOn snapshotCheckingTask
cc #Eugene
P.S. However, this does not take into account .net dependencies
Something like
Collection<ResolvedArtifact> snapshotArtifacts = project.configurations*.resolvedConfiguration.resolvedArtifacts.filter { it.moduleVersion.id.version.endsWith('-SNAPSHOT') }
if (!snapshotArtifacts.empty) {
// throw exception
}
See
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html#getResolvedConfiguration--
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/ResolvedConfiguration.html#getResolvedArtifacts--
Here's what I came up with using Gradle 7.2 and the modern Kotlin DSL:
tasks.register("releaseEnforcement") {
group = "verification"
description = "Check whether there are any SNAPSHOT dependencies."
doLast {
val violations = project.configurations
.filter { it.name == "compileClasspath" || it.name == "runtimeClasspath" }
.flatMap { configuration ->
configuration.resolvedConfiguration.resolvedArtifacts
.map { it.moduleVersion.id }
.filter { it.version.endsWith("-SNAPSHOT") }
}
.toSet()
if (violations.isNotEmpty()) {
error("Snapshot dependencies found:\n\n${violations.joinToString(separator = "\n")}")
}
}
}

Categories