Spring Boot 3 serverside forwarding
The best articles and links to interesting posts for technical team leaders building sophisticated websites, applications and mobile apps. Think about: software architecture, hardware architecture, design, programming, frameworks, scalability, performance, quality assurance, security, resolving issues, fixing bugs and Android.
Geplaatst door
Techie
op
10:00 AM
0
reacties
Labels: forwarding, jakarta servlet, java, kotlin, modelandview, oas, server-side, serverside, spring boot 3, spring mvc, swagger
Add to:
Del.icio.us
DZone
Reddit
Digg
To mock a Retrofit2 call which returns Call<Void?> in the interface client like this:
@POST("/events")
fun registerEvent(@Body requestDto: EventRequestDto): Call<Void?>
is not super-obvious, since it can show this error when invoked:
Cannot invoke "retrofit2.Call.request()" because "call$iv" is null
java.lang.NullPointerException: Cannot invoke "retrofit2.Call.request()" because "call$iv" is null
How do you mock a static companion method in Kotlin using MockK?
I wanted to mock this (static) method in a companion object in Kotlin in class MyClass:
companion object {
fun isNegativeAndFromSavingsAccount(amount: BigDecimal, accountType: accountType) = amount < BigDecimal.ZERO && accountType == AccountType.SAVINGS
}
Trying it with a regular 'every' like this doesn't work:
every { Declaration.isNegativeAndFromSavingsAccount(any(), any()) } returns false
Note it does compile fine!
But when running the test, you'll get this error:
io.mockk.MockKException: Failed matching mocking signature for left matchers: [any(), any()]
at io.mockk.impl.recording.SignatureMatcherDetector.detect(SignatureMatcherDetector.kt:97)
Setup:
This is the way it does work:
import io.mockk.mockkObject
mockkObject(Declaration.Companion) {
every { MyClass.isNegativeAndFromSavingsAccount(any(), any()) } returns false
}
Note this did not work, got the same error:
mockkObject(Declaration::class)
every { MyClass.isNegativeAndFromSavingsAccount(any(), any()) } returns false
I found several posts, but none of them gave a clear answer and/or were using some older version of MockK. E.g: https://github.com/mockk/mockk/issues/61
and this StackOverflow post.
Some more examples and variants of solutions can be found here, e.g when using @JvmStatic.
Geplaatst door
Techie
op
10:48 AM
0
reacties
Labels: kotlin, mock, mocking, mockk, mockkObject, staticMockk, test, testing, unittest, unittesting
Add to:
Del.icio.us
DZone
Reddit
Digg
Recently, the NVD (National Vulnerability Database) which the Owasp dependency check plugin uses to get its data from to check for vulnerabilities, has introduced the use of an API key. That's for them to better control access and throttling - imagine how many companies and organizations are using that API, each time a dependency check build is performed. Especially those that don't cache the NVD database and at each run retrieve it again. And be aware: "... previous versions of dependency-check utilize the NVD data feeds which will be deprecated on Dec 15th, 2023. Versions earlier then 9.0.0 are no longer supported and could fail to work after Dec 15th, 2023."
But this introduction doesn't go without some hiccups. For example it is possible to still get HTTP 403 Forbidden responses, even while you have a valid key. Here's my research while trying to fix it.
Setup:
First you should check if your API key is valid by execution this command:
curl -H "Accept: application/json" -H "apiKey: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -v https://services.nvd.nist.gov/rest/json/cves/2.0\?cpeName\=cpe:2.3:o:microsoft:windows_10:1607:\*:\*:\*:\*:\*:\*:\*
(where xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx is your NVD API key)
That should return JSON (and not a 404). Now you know your API key is valid.
Some have some success with setting the delay longer:
nvd {
apiKey = System.getProperty("ENV_NVD_API_KEY")
delay = 6000 // milliseconds, default is 2000 with API key, 8000 without
}
Commandline version:
--nvdDelay 6000
You can also increase the validForHours option, but that doesn't work if during you construct completely new Docker containers each build - you lose that history.
All NVD options you can pass to DependencyCheck are found here.
But currently (27 December 2023) all the above efforts don't always fix the problem of the 403. Sometimes it works for a while, but then not again. If you build many projects in your company at about the same time, you still have a chance of getting throttled of course.
The best solution is to create a local cache so you are less dependent on NVID API calls (and thus the throttling).
Geplaatst door
Techie
op
1:26 PM
0
reacties
Labels: 403, api, dependencycheck, forbidden, gradle, HTTP 403, local cache, NVD, NVD API, nvd.delay, nvd.key
Add to:
Del.icio.us
DZone
Reddit
Digg
Setup:
- Mapstruct 1.5.5
- Kotlin 1.8
- IntelliJ 2023.2.3
For a mapping between two classes, the source class did not have the mandatory (non-null, Kotlin!) target class field created. And I wanted to fill it with the current ZonedDateTime class, with the timezone of Amsterdam/Europe. And that specific ZoneId is a constant in my Application.kt class named MY_DEFAULT_TIME_ZONE.
So I looked at the expression field in @Mapping found here.
I got this solution working quite fast with:
@Mapping(target = "created", expression = "java(ZonedDateTime.now())").
But as you see, the ZoneId constant is still missing.
I had to try quite a few things to get that working, because the class ZoneId was not getting recognized in the generated implementation MapStruct mapper.
In the end this worked:
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, imports = [ZoneId::class, Application::class])
interface MyMapper {
@Mapping(target = "created", expression = "java(ZonedDateTime.now(my.package.Application.MY_DEFAULT_TIME_ZONE))")
fun myDtoToResponseDto(myDto: MyDto): ResponseDto
...
}
Note the imports field to have the class ZoneId and the constant available (imported) in the implementation class, generated by MapStruct.
In the Application.kt you then have to make the MY_DEFAULT_TIME_ZONE constant available to Java, since that's what MapStruct uses as language:
Application.kt
{
companion object {
@JvmField
val MY_DEFAULT_TIME_ZONE: ZoneId = ZoneId.of("Europe/Amsterdam")
...
}
I also tried this:
@Mapping(target = "created", source = ".", qualifiedByName = ["getValue"])
with a function:
@Named(value = "getValue")
fun getValue(myDto: MyDto): ZonedDateTime {
return ZonedDateTime.now(MY_DEFAULT_TIME_ZONE)
}
The advantage of this solution is that you can use Kotlin code and you don't have to wait and see if your expression has the correct syntax and will compile.
But then I got this error: ZonedDateTime does not have an accessible constructor. I also tried to wrap the field created in a small class, but that didn't work either (could be me :)
See this and this for more details on how that should work.
I also tried with the @JvmDefault annotation, but that is deprecated + it requires you to use the -Xjvm-default property, which I couldn't get to work in IntelliJ with Gradle.
And it is not always guaranteed to work, see here and here and here:
I'm definitely still a beginner in using MapStruct. So probably one of the other methods could work too... Any tips are welcome :)
Geplaatst door
Techie
op
2:18 PM
0
reacties
Labels: @mapping, expression, expressions, intellij, kotlin, mapstruct, ZonedDateTime
Add to:
Del.icio.us
DZone
Reddit
Digg
When you have multiple classes with the same name in your classpath, SpringDoc with Swagger API annotations potentially picks the wrong class with the same name when generating the Swagger UI documentation.
And you specified your endpoint like this, where you want to have it use org.example.BookDto:
@Operation(summary = "Get a list of books for a given shop")
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "A list of books",
content = [Content(mediaType = "application/json",
array = ArraySchema(schema = Schema(implementation = BookDto::class)))]
)
]
)
@GetMapping("/books/{shopId}")
fun getBooksByShopId(
@Parameter(description = "Shop to search for")
@PathVariable shopId: Long
): List<BookDto> {
return bookService.getBooksByShopId(shopId)
.map { BooksMapper.mapDto(it) }
}
Then whatever it finds first on the classpath will be visible in https://localhost:8080/swagger-ui.html. Not necessarily the class you meant, it might pick org.example.domain.BookDto.
Setup:
Several solutions exist:
Specify in your application.yml:
springdoc:
use-fqn: true
Disadvantage: the Swagger documentation in the swagger-ui.html endpoint has then the fully specified package classpath + classname in it. Looks ugly.
Setting it in the @Bean configuration:
import io.swagger.v3.core.jackson.TypeNameResolver
@Bean
fun openAPI(): OpenAPI? {
TypeNameResolver.std.setUseFqn(true)
return OpenAPI()
.addServersItem(Server().url("/"))
.info(
Info().title("Books Microservice")
.description("The Books Microservice")
.version("v1")
)
.externalDocs(
ExternalDocumentation()
.description("Books Microservice documentation")
.url("https://github.com/myproject/README.md")
)
}
Disadvantage: also in this solution the Swagger documentation in the swagger-ui.html endpoint has then the fully specified package classpath + classname in it. Looks ugly.
You can create your own ModelConverters, but that is much more work. Examples here: https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Extensions#extending-core-resolver and https://groups.google.com/g/swagger-swaggersocket/c/kKM546QXGY0
Make sure for each endpoint you specify the response class with full class package path:
@Operation(summary = "Get a list of books for a given shop")
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "A list of books",
content = [Content(mediaType = "application/json",
array = ArraySchema(schema = Schema(implementation = org.example.BookDto::class)))]
)
]
)
@GetMapping("/books/{shopId}")
fun getBooksByShopId(
@Parameter(description = "Shop to search for")
@PathVariable shopId: Long
): List<BookDto> {
return bookService.getBooksByShopId(shopId)
.map { BooksMapper.mapDto(it) }
}
See the bold Schema implementation value for what changed.
Geplaatst door
Techie
op
12:46 PM
0
reacties
Labels: class, fqn, fully qualified name, incorrect class, kotlin, openapi, openapi 3, spring boot, springdoc, swagger, swagger-ui.html, wrong class
Add to:
Del.icio.us
DZone
Reddit
Digg
Geplaatst door
Techie
op
10:00 AM
0
reacties
Labels: accessdeniedexception, authorization, aws, aws lambda, awskms, decrypt, kms, kms:decrypt, kotlin, lambda, parameter store, permissions, role, serverless, ssm, ssm:getparameter
Add to:
Del.icio.us
DZone
Reddit
Digg
AWS lambda
Kotlin 1.8.10
Java 17
Retrofit2
IntelliJ
Gradle
val os: OperatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean() if (os is UnixOperatingSystemMXBean) { logger.info("Number of open fd: " + (os as UnixOperatingSystemMXBean).openFileDescriptorCount) }
Note the call will fail at the moment the Too many files error starts to happen, because logging and many other calls require a file descriptor; and the MXBean itself probably too.... So all you can see is that number of open file descriptors increase and increase up to the exception.
java -javaagent:lib/file-leak-detector.jar
the error during startup was:
Failed to find Premain-Class manifest attribute in lib/file-leak-detector-1.13.jar
Error occurred during initialization of VM
agent library failed to init: instrument
That error shows up because the
MANIFEST.MF
file is missing the Premain-Class
entry, which tells the runtime what the
main method is to start the agent.
(note here I switched to v1.16-snapshot just to have the latest)
And in there, the Premium-Class is set!
Premain-Class: org.kohsuke.file_leak_detector.AgentMain
I then decided to add the new jar locally to the build of the lambda, for testing purposes, as described here: https://stackoverflow.com/questions/20700053/how-to-add-local-jar-file-dependency-to-build-gradle-file
The jar was put in the 'libs' directory which I created in the root directory of the (IntelliJ) Gradle project. Gradle depencency: implementation files('libs/file-leak-detector-1.16-SNAPSHOT-jar-with-dependencies.jar')
After that, the File Leak Detector started up fine, as can be seen from these messages:
File leak detector installed
Could not load field socket from SocketImpl: java.lang.NoSuchFieldException: socket
Could not load field serverSocket from SocketImpl: java.lang.NoSuchFieldException: serverSocket
Note the last two messages are due to Java17+ not allowing this anymore, you can find more details about this when searching for those exact error messages in the File Leak Detector source code.
I then did have SocketExceptions appear at the nightly runs too like “Caused by: java.net.SocketException: Too many open files” so I couldn't tell too much yet. It seems the lib-file-leak-detector is then not dumping the open files, probably because the above mentioned Java 17+ issue. Or something else went wrong, at least I couldn't see any dumps in AWS CloudWatch though.
So I set up my own listener from the library, so I could then dump the open files whenever I wanted. It is possible, but no full example is given; the *Demo.java examples give some ideas away. Here's what I used:
Note I skipped the jars during logging, which I noticed count for a lot of the open files listed.logger.info("Is leak detector agent installed = " + Listener.isAgentInstalled()") if (Listener.isAgentInstalled()) { try { val b = ByteArrayOutputStream() Listener.dump(b) logger.info("The current open files Listener dump = $b") val currentOpenFiles = Listener.getCurrentOpenFiles() logger.info("The current open files Listener list size = ${currentOpenFiles.size}")var jarFilesCounter = 0 currentOpenFiles.forEach { when (it) { is Listener.FileRecord -> { if (!it.file.name.endsWith("jar")) { logger.info("File named " + it.file + " is opened by thread:" + it.threadName) } else { jarFilesCounter++ } } else -> logger.info("Found record by Listener is not a file record, skipping") } } logger.info("Of the open files, $jarFilesCounter are .jar files, those were skipped in the logging of the list of files currently open") b.close()} catch (ex: Exception) { logger.error("Dump of current open files failed with exception: ", ex) } } else { logger.info("Leak detector agent is not installed, so not dumping open files") }
I tried both ways. My goal was to use the 'lsof' command, but that was not available in the lambda runtime. Then I tried to get the user of the process. And the process ID of the lambda itself. Then via /procs/fd one could find what files are kept open by a give PID.
These commands worked:
val pb = ProcessBuilder("sh", "-c", "echo $$")
val startedProcess = pb.start()
Geplaatst door
Techie
op
4:45 PM
1 reacties
Labels: aws, aws lambda, garbage collection, GC, java, kotlin, manifest.mf, MXBean, open fds, open file descriptors, too many open files, troubleshooting, ulimit
Add to:
Del.icio.us
DZone
Reddit
Digg
Geplaatst door
Techie
op
3:46 PM
0
reacties
Labels: dependency check, dependency-check, examples, owasp, owasp plugin, spring, suppressions, suppressions.xml, vulnerabilities
Add to:
Del.icio.us
DZone
Reddit
Digg
Geplaatst door
Techie
op
12:24 PM
0
reacties
Labels: _X_AMZN_TRACE_ID, aws, aws lambda, datadog, error, headers, lambda, malformed, trace, tracing, X-Amzn-Trace-Id
Add to:
Del.icio.us
DZone
Reddit
Digg
The AWS SAM CLI command
sam.cmd build MyFunction --template C:\techie\workspace\my-function\local\template.yaml --build-dir C:\techie\workspace\my-function\local\.aws-sam\build --debug
fails in an IntelliJ commandline terminal due to this
FileNotFoundError: [WinError 3] The system cannot find the path specified
error.
- Windows 10 Pro laptop
- IntelliJ 2022
- Kotlin 1.7
- Java 11 at least for compilation
- Serverless lambda written in Kotlin
- AWS SAM CLI, version 1.67.0
- AWS Toolkit plugin for IntelliJ
First I tried to install SAM AWS CLI using HomeBrew (formerly Brew) in WSL 1 (ubuntu) under Windows 10 using these steps:
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-homebrew.html
But that failed during Homebrew installation. Probably upgrading to WSL 2 would fix that. But then I also realized: IntelliJ then doesn't know about that at all, since SAM CLI is then installed in WSL, not in Windows.
It kept on saying after running brew postinstall --verbose --debug gcc:
==> Installing aws-sam-cli from aws/tap
Error: An exception occurred within a child process:
Errno::EFAULT: Bad address - /home/linuxbrew/.linuxbrew/bin/gcc-12
And also:
Warning: The post-install step did not complete successfully
You can try again using:
brew postinstall gcc
Also trying brew postinstall --verbose --debug gcc didn't succeed. This error mentioned here was also applicable: https://github.com/orgs/Homebrew/discussions/4052
I also didn't dare wsl --update because other configurations I already had set up might fail after that. Guess I will do that at a more quiet moment :)
So then I went for the manual installation in Windows, as found here: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html
In IntelliJ you then have to set the path to the executable to where you installed it:
So IntelliJ will use the Windows AWS SAM CLI, not the one in the terminal (WSL 1).
Than I ran my command, first outside IntelliJ to be able to control the parameters more easily:
C:\Users\techie>C:\Amazon\AWSSAMCLI\bin\sam.cmd build MyFunction --template C:\techie\workspace\my-function\local\template.yaml --build-dir C:\techie\workspace\my-function\local\.aws-sam\build --debug
But that gave this error:
FileNotFoundError: [WinError 3] The system cannot find the path specified: 'C:\\Users\\techie\\AppData\\Local\\Temp\\tmpmjdhug40\\7c98ad184709dded6b1c874ece2a0edea9c55b0a\\build\\classes\\kotlin\\test\\com\\mycompany\\myfunction\\domain\\MyServiceTest$should register valid make this a long text to exceed this successfully$2.class'
First I thought it was due to spaces in the test-methodname. But replacing them with an underscore didn't work either. Or maybe case-insensitive-ness; but my test-methodname is all lowercase.
I tried many things to exclude the whole test-class from the process, because why is it even included during the sam build command?
Options I tried were:
jar {
sourceSets {
main {
java {
exclude '**/TestExcludeClass.java'
}
kotlin {
exclude '**/TestExcludeKotlinClass.kt'
}
}
}
}
Note that excluding everything with 'exclude '**/*.kt' did make the sam build fail, so the changes were taken into account.It seems the initial step of the sam build (JavaGradleWorkflow:JavaGradleCopyArtifacts) just takes all .class files anyway, even test classes.
I tried turning off Telemetry That did have no affect on the error.
Then I found this comment: https://github.com/aws/aws-sam-cli/issues/4031#issuecomment-1173730737
Could it be that, that the path is just too long? Typical Windows limit so that could definitely be it.
And yes after applying below command in PowerShell as an Administrator
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
it worked!
Detailed explanation: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=powershell#enable-long-paths-in-windows-10-version-1607-and-later
The command ran fine! This is then the output:
Build Succeeded
Built Artifacts : ..\..\techie\workspace\my-function\local\.aws-sam\build
Built Template : ..\..\techie\workspace\my-function\local\.aws-sam\build\template.yaml
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke -t ..\..\techie\workspace\my-function\local\.aws-sam\build\template.yaml
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided --template-file ..\..\techie\workspace\my-function\local\.aws-sam\build\template.yaml
Geplaatst door
Techie
op
12:56 PM
0
reacties
Labels: aws, aws sam, aws sam cli, brew, homebrew, intellij, kotlin, lambda, Powershell, sam, sam cli, serverless, windows 10, wsl
Add to:
Del.icio.us
DZone
Reddit
Digg
Problem: when checking out a project in IntelliJ in Windows, all files are checked out with Window's newline CRLF (\r\n).
But if you then open a terminal in IntelliJ which runs WSL (Ubuntu) and you want to run a bash script like this shell script, you'll get this error:
#!/bin/sh
set -e
[unrelated stuff deleted]
It will fail with:
./deploy.sh: line 2: $'\r': command not found
: invalid optione 3: set: -
set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
Those errors are caused by the shell script having CRLF instead of just LF, which Ubuntu/Mac OS expects.
Sidenote: I made softlink from /bin/sh to bash in my system because dash does not support several features, see here.
So I tried setting IntelliJ's Code Style to use \n for newlines and line endings and line separator, also for new projects like mentioned here.
But still after creating a new project from Git, all files including the above script was set to CRLF. You can see this at the bottom of the screen in this screenshot:
Geplaatst door
Techie
op
11:00 AM
0
reacties
Labels: .gitattributes, carriage return, crlf, git, gitattributes, intellij, linefeed, newline, ubuntu, wsl
Add to:
Del.icio.us
DZone
Reddit
Digg
Context: Docker, CircleCI, Github.
The Docker build command
docker build -f .circleci/Dockerfile -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:`git describe --tags` -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_BUILD_NUM} -t $AWS_ACCOUNT_ID.ecr.$AWS_DEFAULT_REGION.amazonaws.com/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_SHA1} .
was failing with this message:
fatal: No names found, cannot describe anything.
invalid argument "********************************************/my-project:" for "-t, --tag" flag: invalid reference format
See 'docker build --help'.
Exited with code exit status 125
Geplaatst door
Techie
op
2:32 PM
0
reacties
Labels: --tag", "-t, cannot describe anything, circleci, docker, docker build, error, error message, exit status 125, fatal, flag, git, github, invalid argument, invalid reference format, no names found, release, tag
Add to:
Del.icio.us
DZone
Reddit
Digg
Geplaatst door
Techie
op
10:56 AM
0
reacties
Labels: database, database migration, database schema, flyway, flywayvalidateexception, half-completed, migrations, repair, schema
Add to:
Del.icio.us
DZone
Reddit
Digg