java.lang.VerifyError in ktor server POST in full stack tutorial - kotlin

In the official kotlin tutorial https://kotlinlang.org/docs/multiplatform-full-stack-app.html
I have a ktor server running, and when I do a GET I get the correct json response. When I POST:
POST http://localhost:9090/shoppingList
Content-Type: application/json
{
"desc": "Peppers 🌶",
"priority": 5
}
The server returns a 500 with this error:
2023-01-10 08:53:06.605 [DefaultDispatcher-worker-1] INFO ktor.application - Responding at http://0.0.0.0:9090
2023-01-10 08:53:12.317 [eventLoopGroupProxy-4-2] ERROR ktor.application - Unhandled: POST - /shoppingList
java.lang.VerifyError: class kotlinx.serialization.json.internal.StreamingJsonDecoder overrides final method kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
at ...
This is just the first part of the tutorial for building a full stack web app in Kotlin so I'd like to work out of the tutorial or I am missing something.
Server code below but this is also copy and paste right out of the turorial and using the tutorial git repo
val shoppingList = mutableListOf(
ShoppingListItem("Cucumbers 🥒", 1),
ShoppingListItem("Tomatoes 🍅", 2),
ShoppingListItem("Orange Juice 🍊", 3)
)
fun main() {
embeddedServer(Netty, 9090) {
install(ContentNegotiation) {
json()
}
install(CORS) {
allowMethod(HttpMethod.Get)
allowMethod(HttpMethod.Post)
allowMethod(HttpMethod.Delete)
anyHost()
}
install(Compression) {
gzip()
}
routing {
route(ShoppingListItem.path) {
get {
call.respond(shoppingList)
}
post {
shoppingList += call.receive<ShoppingListItem>()
call.respond(HttpStatusCode.OK)
}
delete("/{id}") {
val id = call.parameters["id"]?.toInt() ?: error("Invalid delete request")
shoppingList.removeIf { it.id == id }
call.respond(HttpStatusCode.OK)
}
}
}
routing {
get("/hello") {
call.respondText("Hello, API!")
}
}
}.start(wait = true)

This was because the main branch of the tutorial repo is out of date and needs its dependencies updated per this issue: https://github.com/kotlin-hands-on/jvm-js-fullstack/issues/21

Related

gradle publishing not working - No such Algorithm

I have a nexus repository that I want to publish my gradles versions catalog to. Here's the gradle build script:
buildscript {
dependencies {
classpath("com.android.tools.build:gradle:7.2.0")
}
}
plugins {
id ("version-catalog")
id ("maven-publish")
}
catalog {
versionCatalog {
from(files("gradle/libs.versions.toml"))
}
}
publishing {
publications {
create<MavenPublication>("VersionCatalog") {
groupId = "com.phinneyridge"
artifactId = "com.phinneyridge.version.catalog"
version = "1.0.0"
from(components["versionCatalog"])
}
repositories {
maven {
name = "PhinneyRidgeRepository"
url = uri(System.getenv()["PhinneyRidgeReleaseRepoUrl"].toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
println(url)
println(credentials.username + ":" + credentials.password)
}
}
}
}
Here's the output I get when I try to publish:
gradlew publish
Type-safe dependency accessors is an incubating feature.
Configure project :
https://ridgetop/nexus/repository/maven-releases/
publisher:mavenpublisher
Task :publishVersionCatalogPublicationToPhinneyRidgeRepositoryRepository FAILED
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':publishVersionCatalogPublicationToPhinneyRidgeRepositoryRepository'.
Failed to publish publication 'VersionCatalog' to repository 'PhinneyRidgeRepository'
java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.
SSLContextImpl$DefaultSSLContext)
If I changed the maven repository to mavenLocal(), the script actually publishes the version catalog to the local maven repository just fine.
What's also interesting, is that I have a custom gradle plugin in a different build script and it fairly well the same looking code pattern, and it successfully publishes the custom gradle plugin to same repository. Here's the code for that working script:
plugins {
id ("java-gradle-plugin")
"java-library"
id ("maven-publish")
id("com.gradle.plugin-publish") version "0.20.0"
id("org.jetbrains.kotlin.jvm") version "1.6.20"
id("org.gradle.kotlin.kotlin-dsl") version "2.1.7"
}
gradlePlugin {
group = "com.phinneyridge"
plugins {
create("PhinneyRidgePlugin") {
id = "com.phinneyridge.project"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeProjectPlugin"
}
create("PhinneyRidgeAndroidAppPlugin") {
id = "com.phinneyridge.android.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidAppPlugin"
}
create("PhinneyRidgeAndroidLibPlugin") {
id = "com.phinneyridge.android.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidLibPlugin"
}
create("PhinneyRidgeAndroidKotlinAppPlugin") {
id = "com.phinneyridge.android.kotlin.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidKotlinAppPlugin"
}
create("PhinneyRidgeAndroidKotlinLibPlugin") {
id = "com.phinneyridge.android.kotlin.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidKotlinLibPlugin"
}
create("PhinneyRidgeJavaAppPlugin") {
id = "com.phinneyridge.java.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaAppPlugin"
}
create("PhinneyRidgeJavaLibPlugin") {
id = "com.phinneyridge.java.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaLibPlugin"
}
create("PhinneyRidgeJavaKotlinAppPlugin") {
id = "com.phinneyridge.java.kotlin.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaKotlinAppPlugin"
}
create("PhinneyRidgeJavaKotlinLibPlugin") {
id = "com.phinneyridge.java.kotlin.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaKotlinLibPlugin"
}
create("PhinneyRidgeDSAnnotations") {
id = "com.phinneyridge.dsannotations"
implementationClass = "com.phinneyridge.osgi.DSAnnotations"
}
create("Aar2Jar") {
id = "com.phinneyridge.aar2jar"
implementationClass = "com.phinneyridge.android.Aar2Jar"
}
}
}
group = "com.phinneyridge"
version = "1.0.0"
publishing {
repositories {
maven {
url = uri(System.getenv().get("PhinneyRidgeReleaseRepoUrl").toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
gradleApi();
implementation ("com.android.tools.build:gradle:7.1.3")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20")
}
There's some differences, but the maven repository is constructed exactly the same. So the question is why am I getting the NoSuchAlgorithm error when I try to publish the version catalog?
If I try publishing with stacktrace I see the following:
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.securit
y.ssl.SSLContextImpl$DefaultSSLContext)
at org.gradle.internal.resource.transport.http.HttpClientConfigurer.jdkSupportsTLSProtocol(HttpClientConfigurer.java:115)
And here's what jdkSupportTLSProtocol looks like:
private static boolean jdkSupportsTLSProtocol(#SuppressWarnings("SameParameterValue") final String protocol) {
try {
for (String supportedProtocol : SSLContext.getDefault().getSupportedSSLParameters().getProtocols()) {
if (protocol.equals(supportedProtocol)) {
return true;
}
}
return false;
} catch (NoSuchAlgorithmException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
It's the SSLContext.getDefault() that throws the NoSuchAlgorithException. I'm just currently at a lost understand what I need to change to get around this problem.
I figured out a solution to the problem.
I added a line of code to the maven repository declaration:
maven {
javax.net.ssl.SSLContext.setDefault(javax.net.ssl.SSLContext.getInstance("default"))
name = "PhinneyRidgeRepository"
url = uri(System.getenv()["PhinneyRidgeReleaseRepoUrl"].toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
println(url)
println(credentials.username + ":" + credentials.password)
}
}
That solved the problem, but I don't have an explanation why I had to set the SSLContext in one situation and not the other. According to the gradle documentation SSLContext.getDefault() is supposeto automagically call SSLContext.getInstance("Default") when the SSLContext is not initialized. (By the way the instance name seems to be case insensitive, so "default" or "Default" both worked.) My speculation is that this is a gradle bug, probably because the publication in one case is from component. That's the only obvious difference that I see that sets the two contrasting situations apart. By the way SSLContext.getInstance("TLSv1.2") would not work; I had to use "default"

Camerax Analyzer ImageProxy - Image Already Closed

I am using CameraX (Beta) and using Analyzer with an executor to analyze the image and draw bounds/overlay on it. Based on the documentation, I need to close the image proxy to continue to get the images into the analyze function. When I add the imageProxy.close() call, it fails with Image Already closed error. What am I missing here?
Analyzer Code:
private val successListener = OnSuccessListener<List<DetectedObject>> { papers ->
isAnalyzing.set(false)
val rectPoints= mutableListOf<Rect>()
Log.d(TAG," overlayRef Info: ${overlayRef.get()}")
for (paper in papers) {
val bounds = paper.boundingBox
val paperId = paper.trackingId
rectPoints+=bounds
Log.d(TAG, "Successful Paper Analysis - Bounds of the paper: $bounds")
Log.d(TAG," Labels found on the paper: ${paper.labels}")
}
Log.d(TAG, "Invoking pointsRectListener for : $rectPoints")
pointsRectListener?.invoke(rectPoints)
}
private val failureListener = OnFailureListener { e ->
isAnalyzing.set(false)
Log.e(TAG, "Paper analysis failure.", e)
}
#SuppressLint("UnsafeExperimentalUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy?.image ?: return
Log.d(TAG,"entered analysis..analysis in progress?$isAnalyzing.get()")
if (!isAnalyzing.get()){
isAnalyzing.set(true)
Log.d(TAG,"No other analysis in progress..so starting analysis now")
val currentTimestamp = System.currentTimeMillis()
if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {
currentTimestamp - lastAnalyzedTimestamp
analysisSizeListener?.invoke(Size(imageProxy.width, imageProxy.height))
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
objectDetector.process(image)
.addOnSuccessListener(successListener)
.addOnFailureListener(failureListener)
}
}
imageProxy.close()
}
Code where I am instantiating and binding to lifecycle
paperAnalyzer=ImageAnalysis.Builder()
.setTargetAspectRatio(screenAspectRatio)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetRotation(rotation)
.build()
.also {
it.setAnalyzer(cameraExecutor, PaperAnalyzer( WeakReference(overlayView)).apply {
pointsRectListener = { rectPoints ->
overlayView.rectPoints = rectPoints
}
analysisSizeListener = {
updateOverlayTransform(overlayView, it)
}
}
)
}
cameraProvider.unbindAll()
try {
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, paperAnalyzer)
// Attach the viewfinder's surface provider to preview use case
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
It seems like the logic you're trying to accomplish looks like this.
fun analyze(imageProxy) {
if (isAnalyzing) {
imageProxy.close() // 1
} else {
val image = imageInput.fromMediaImage(...)
objectDetector.process(image)
.addOnSuccessListener(OnSuccessListener { result ->
// Do something with the result
imageProxy.close() // 2
isAnalyzing.set(false)
})
.addOnFailureListener(OnFailureListener { exception ->
// Do something with the exception
imageProxy.close() // 3
isAnalyzing.set(false)
})
}
}
I might be wrong, but it looks like you're doing 1, but not 2 and 3. Could you update your code to follow this pattern and see if you're still encountering the issue.

how to start api app on intellij without tomcat or any type of server?

I have an API app created using vert.x framework , i am able to build the application but not able to run . when i try to run the app , i automatically get redirected to the "cucumber.api.cli.main not found error" . I delete the automatic configuration but next time i try to run the app it gets generated . What is the configuration i should run on.
I have tried to research about this , but most of the questions and answers asks me to set up tom cat server or glass fish server which i don't want to do .
Here is my hello world application using IntelliJ Idea, vert.x -
Verticle :
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Router;
import static com.sun.xml.internal.ws.spi.db.BindingContextFactory.LOGGER;
public class MyVerticle extends AbstractVerticle {
#Override
public void start(Future<Void> startFuture) throws Exception {
Router router = Router.router(vertx);
router.route("/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.putHeader("content-type", "text/html")
.end("<h1> Hello Vert.x </h>");
});
vertx.createHttpServer().requestHandler(router::accept)
.listen(8070, http -> {
if (http.succeeded()) {
LOGGER.info("Started Server at port 8070");
} else {
startFuture.fail(http.cause());
}
});
vertx.createHttpServer().requestHandler(req -> {
req.response()
.putHeader("content-type", "text/plain")
.end("Hello from Vert.x!");
}).listen(8888, http -> {
if (http.succeeded()) {
startFuture.complete();
System.out.println("HTTP server started on port 8888");
} else {
startFuture.fail(http.cause());
}
});
router.route("/test").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.putHeader("content-type","text/html")
.end("<h2> This is another end point with same port </h2>");
});
vertx.createHttpServer().requestHandler(router::accept).listen(8070,http ->{
if(http.succeeded()){
LOGGER.info("Another server started 8070");
}else{
startFuture.fail(http.cause());
}
});
}
#Override
public void stop() {
LOGGER.info("Shutting down application");
}
}
Main Method to deploy Verticle
import com.testproject.starter.verticles.MyVerticle;
import io.vertx.core.Vertx;
public class MyVerticleTest {
public static void main(String[] args) {
Vertx vertex = Vertx.vertx();
MyVerticle myVerticle = new MyVerticle();
vertex.deployVerticle(myVerticle);
}
}
Now you can follow the below URLs -
1. http://localhost:8888
2. http://localhost:8070/test
The application doesn't required to have tomcat to run.
Reference : https://vertx.io/docs/
Useful links - https://github.com/vert-x3/vertx-awesome

resilience4J + Spring Boot 2.x

I am using resilience4j for fault tolerance in reative API spring boot application. I can see that all events are treated as success even though Mono is returning errors.
service layer
#Override
#CircuitBreaker(name = "member-service")
public Mono<String> getMemberInfo(String memberId) {
return wsClient.getMemberInfo(memberId);
// This call will return WSException whenever there is 4XX or 5XX error
}
application.yml configuration
resilience4j.circuitbreaker:
backends:
member-service:
ringBufferSizeInClosedState: 1
ringBufferSizeInHalfOpenState: 2
waitInterval: 10000
failureRateThreshold: 75
registerHealthIndicator: true
recordExceptions:
- com.xx.WSException
ignoreExceptions:
- com.xxx.WSClientException
I intentionally changed URI path, so that WebClient always return 404 error, which throws WSException. When I see below endpoint, type is always SUCCESS. Did I miss something?
http://localhost:8088/circuitbreaker-events/member-service
{
"circuitBreakerEvents": [
{
"circuitBreakerName": "member-service",
"type": "SUCCESS",
"creationTime": "2019-02-18T21:56:21.588+05:30[Asia/Calcutta]",
"durationInMs": 6
},
{
"circuitBreakerName": "member-service",
"type": "SUCCESS",
"creationTime": "2019-02-18T21:56:21.588+05:30[Asia/Calcutta]",
"durationInMs": 5
},
{
"circuitBreakerName": "member-service",
"type": "SUCCESS",
"creationTime": "2019-02-18T21:56:21.588+05:30[Asia/Calcutta]",
"durationInMs": 6
}
}
Your method always returns Mono. Even if there is an error. And #CircuitBreaker annotation reacts on exception (not error Mono).
So you should apply CircuitBreakerOperator to your reactive code.
public class MemberService {
private final WsClient wsClient;
private final CircuitBreaker circuitBreaker;
public MemberService(WsClient wsClient,
CircuitBreakerRegistry circuitBreakerRegistry,
CircuitBreakerProperties circuitBreakerProperties) {
this.wsClient = wsClient;
this.circuitBreaker = circuitBreakerRegistry.circuitBreaker(
"member-service",
() -> circuitBreakerProperties
.createCircuitBreakerConfig("member-service"));
}
public Mono<String> getMemberInfo(String memberId) {
return wsClient.getMemberInfo(memberId)
.transform(CircuitBreakerOperator.of(circuitBreaker));
}
}
You need resilience4j-reactor and resilience4j-spring-boot2 dependencies.
The upcoming 0.14.0 version of Resilience4j will support Mono when you use the annotation as follows: #CircuitBreaker(name = "member-service", type = ApiType.WEBFLUX).

RxAndroid response of one call to make another request

I'm new to RxAndroid and trying to chain responses.
I'm using this github API to retrieve data. Along with each issue there are comments link and events link associated with it which I want to fetch and update existing object with list of comments and events to form something like this.
[
issue: {
comments: [
{
.
.
},
{
.
.
}
]
events : [
{
.
.
},
{
.
.
}
]
]
]
I could retrieve initial response with following code
GitHubService gitHubService = ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);
gitHubService.getIssuesList()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(issues -> Arrays.asList(issues))
.subscribe(adapter::add);
Now How do I retrieve comments and events before updating adapter ? I want to show 3 comments and 3 events as well.
Thanks to #Riccardo Ciovati for your example !
Here is my solution. and it works perfectly !
public static void getIssuesForRepo(final IssuesListAdapter adapter) {
GitHubService gitHubService = ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);
gitHubService.getIssuesList()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(issues -> Arrays.asList(issues))
.flatMap(issues -> Observable.from(issues))
.filter(issue -> issue.getCommentsUrl() != null)
.flatMap(new Func1<Issue, Observable<Issue>>() {
#Override
public Observable<Issue> call(Issue issue) {
return gitHubService.getComments(issue.getNumber())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(comments -> {
issue.setCommentList(Arrays.asList(comments));
return issue;
});
}
})
.toList()
.subscribe(adapter::add);
}
where
public interface GitHubService {
String ENDPOINT = "https://api.github.com/";
#GET("repos/crashlytics/secureudid/issues")
Observable<Issue[]> getIssuesList();
#GET("repos/crashlytics/secureudid/issues/{number}/comments")
Observable<Comment[]> getComments(#Path("number") long number);
}