Quarkus Mutiny Web Client Decode JSON safely - java

I've been getting into Quarkus and trying to utilize the Mutiny Vertx WebClient. My code works but I prefer not to have to rely on unsafe/unchecked assignments which is how I've currently written the code with bodyAsJson method on HttpResponse. Is there a better way, or more standard way to decode JSON from the Mutiny Vertx client? I realize I could just call bodyAsJsonObject and return that, but I need to do processing on the data the comes back from API calls so I need to decode it to a class representing the data shape/structure.
package com.something.app.language;
import com.something.app.model.Language;
import io.micrometer.core.annotation.Timed;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.List;
#ApplicationScoped
public class LanguageService {
#Inject
Vertx vertx;
private WebClient client;
#PostConstruct
void init() {
this.client = WebClient.create(vertx);
}
#Timed
public Uni<List<Language>> getLanguages() {
return this.client
.get(80, "somehost.com", "/languages")
.timeout(1000)
.send()
.onItem()
.transform(resp -> {
if (resp.statusCode() == 200) {
return resp.bodyAsJson(List.class);
} else {
throw new RuntimeException("");
}
});
}
}

There are several ways. First, Vert.x uses Jackson under the hood, so everything that can be done with Jackson is possible.
You can use resp.bodyAsJson(MyStructure.class), which would create an instance of MyStructure.
If you have a JSON Array, you can map every element to the object class.
Finally, you can implement your own body codec (See https://vertx.io/docs/apidocs/io/vertx/ext/web/codec/BodyCodec.html).

Related

How to send JsonArray data in apache-pulsar-client?

I am a beginner who just started developing pulsar-client with spring boot.
First of all, I learned the basics through pulsar doc and git, but I was stuck testing batch transmission of messages from the pulsar-client producer.
In particular, I want to send JsonArray data in batches, but I keep getting a JsonArray.getAsInt error.
Please take a look at my code and tell me what's wrong
package com.refactorizando.example.pulsar.producer;
import static java.util.stream.Collectors.toList;
import com.refactorizando.example.pulsar.config.PulsarConfiguration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import org.apache.pulsar.client.api.CompressionType;
import org.apache.pulsar.client.api.Message;
import org.apache.pulsar.client.api.MessageId;
import org.apache.pulsar.client.api.Producer;
import org.apache.pulsar.client.api.PulsarClient;
import org.apache.pulsar.client.api.PulsarClientException;
import org.apache.pulsar.client.impl.schema.JSONSchema;
import org.apache.pulsar.shade.com.google.gson.JsonArray;
import org.apache.pulsar.shade.com.google.gson.JsonElement;
import org.apache.pulsar.shade.com.google.gson.JsonObject;
import org.apache.pulsar.shade.com.google.gson.JsonParser;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
#RequiredArgsConstructor
#Slf4j
public class PulsarProducer {
private static final String TOPIC_NAME = "Json_Test";
private final PulsarClient client;
#Bean(name = "producer")
public void producer() throws PulsarClientException {
// batching
Producer<JsonArray> producer = client.newProducer(JSONSchema.of(JsonArray.class))
.topic(TOPIC_NAME)
.batchingMaxPublishDelay(60, TimeUnit.SECONDS)
.batchingMaxMessages(2)
.enableBatching(true)
.compressionType(CompressionType.LZ4)
.create();
String data = "{'users': [{'userId': 1,'firstName': 'AAAAA'},{'userId': 2,'firstName': 'BBBB'},{'userId': 3,'firstName': 'CCCCC'},{'userId': 4,'firstName': 'DDDDD'},{'userId': 5,'firstName': 'EEEEE'}]}";
JsonElement element = JsonParser.parseString(data);
JsonObject obj = element.getAsJsonObject();
JsonArray arr = obj.getAsJsonArray("users");
try {
producer.send(arr);
} catch (Exception e) {
log.error("Error sending mesasage");
e.printStackTrace();
}
producer.close();
}
}
I'm still a beginner developer, so I couldn't find it on stackOverflow because I couldn't search well. If you have anything related to it, please leave a link and I'll delete the question.
Thanks for reading my question and have a nice day!
I tried several things, such as converting to JsonObject and sending, converting to String and sending, etc., but the same error came out.
cho ,
Welcome to Pulsar and Spring Pulsar! I believe there are a few things to cover to fully answer your question.
Spring Pulsar Usage
In your example you are crafting a Producer directly from the PulsarClient. There is absolutely nothing wrong/bad about using that API directly. However, if you want to use Spring Pulsar, the recommended approach to send messages in a Spring Boot app using Spring Pulsar is via the auto-configured PulsarTemplate (or ReactivePulsarTemplate if using Reactive). It simplifies usage and allows configuring the template/producer using configuration properties. For example, instead of building up and then using Producer.send() you would instead inject the pulsar template and use it as follows:
pulsarTemplate.newMessage(foo)
.withTopic("Json_Test")
.withSchema(Schema.JSON(Foo.class))
.withProducerCustomizer((producerBuilder) -> {
producerBuilder
.batchingMaxPublishDelay(60, TimeUnit.SECONDS)
.batchingMaxMessages(2)
.enableBatching(true)
.compressionType(CompressionType.LZ4);
})
.send();
Furthermore you can replace the builder calls w/ configuration properties like:
spring:
pulsar:
producer:
batching-enabled: true
batching-max-publish-delay: 60s
batching-max-messages: 2
compression-type: lz4
and then your code becomes:
pulsarTemplate.newMessage(foo)
.withTopic("Json_Test")
.withSchema(Schema.JSON(Foo.class))
.send();
NOTE: I replace json array w/ Foo for simplicity.
Schemas
In Pulsar, the Schema knows how to de/serialize the data. The built-in Pulsar Schema.JSON by default uses the Jackson json lib to de/serialize the data. This requires that the data must be able to be handled by Jackson ObjectMapper.readValue/writeValue methods. It handles POJOs really well, but does not handle the JSON impl you are using.
I noticed the latest json-lib is 2.4 and (AFAICT) has 9 CVEs against it and was last released in 2010. If I had to use a Json level API for my data I would pick a more contemporary and well supported / used lib such as Jackson or Gson.
I switched your sample to use Jackson ArrayNode and it worked well. I did have to replace the single quotes in your data string to backslash double-quote as Jackson by default does not like single-quoted data. Here is the re-worked sample app using Jackson ArrayNode:
#SpringBootApplication
public class HyunginChoSpringPulsarUserApp {
public static void main(String[] args) {
SpringApplication.run(HyunginChoSpringPulsarUserApp.class, args);
}
#Bean
ApplicationRunner sendDataOnStartup(PulsarTemplate<ArrayNode> pulsarTemplate) {
return (args) -> {
String data2 = "{\"users\": [{\"userId\": 1,\"firstName\": \"AAAAA\"},{\"userId\": 2,\"firstName\": \"BBBB\"},{\"userId\": 3,\"firstName\": \"CCCCC\"},{\"userId\": 4,\"firstName\": \"DDDDD\"},{\"userId\": 5,\"firstName\": \"EEEEE\"}]}";
ArrayNode jsonArray = (ArrayNode) ObjectMapperFactory.create().readTree(data2).get("users");
System.out.printf("*** SENDING: %s%n", jsonArray);
pulsarTemplate.newMessage(jsonArray)
.withTopic("Json_Test")
.withSchema(Schema.JSON(ArrayNode.class))
.send();
};
}
#PulsarListener(topics = "Json_Test", schemaType = SchemaType.JSON, batch = true)
public void listenForData(List<ArrayNode> user) {
System.out.printf("***** LISTEN: %s%n".formatted(user));
}
}
The output looks like:
*** SENDING: [{"userId":1,"firstName":"AAAAA"},{"userId":2,"firstName":"BBBB"},{"userId":3,"firstName":"CCCCC"},{"userId":4,"firstName":"DDDDD"},{"userId":5,"firstName":"EEEEE"}]
***** LISTEN: [{"userId":1,"firstName":"AAAAA"},{"userId":2,"firstName":"BBBB"},{"userId":3,"firstName":"CCCCC"},{"userId":4,"firstName":"DDDDD"},{"userId":5,"firstName":"EEEEE"}]
Data Model
Your data is an array of users. Do you have a requirement to use a Json level API or you instead deal with a List<User> POJOs? This would simplify things and make it much better experience to use. The Java record is a great choice such as:
public record(String userId, String firstName) {}
then you can pass in a List<User> to your PulsarTemplate and everything will work well. For example:
#SpringBootApplication
public class HyunginChoSpringPulsarUserApp {
public static void main(String[] args) {
SpringApplication.run(HyunginChoSpringPulsarUserApp.class, args);
}
#Bean
ApplicationRunner sendDataOnStartup(PulsarTemplate<User> pulsarTemplate) {
return (args) -> {
String data2 = "{\"users\": [{\"userId\": 1,\"firstName\": \"AAAAA\"},{\"userId\": 2,\"firstName\": \"BBBB\"},{\"userId\": 3,\"firstName\": \"CCCCC\"},{\"userId\": 4,\"firstName\": \"DDDDD\"},{\"userId\": 5,\"firstName\": \"EEEEE\"}]}";
ObjectMapper objectMapper = ObjectMapperFactory.create();
JsonNode usersNode = objectMapper.readTree(data2).get("users");
List<User> users = objectMapper.convertValue(usersNode, new TypeReference<>() {});
System.out.printf("*** SENDING: %s%n", users);
for (User user : users) {
pulsarTemplate.newMessage(user)
.withTopic("Json_Test2")
.withSchema(Schema.JSON(User.class))
.send();
}
};
}
#PulsarListener(topics = "Json_Test2", schemaType = SchemaType.JSON, batch = true)
public void listenForData(List<User> users) {
users.forEach((user) -> System.out.printf("***** LISTEN: %s%n".formatted(user)));
}
public record User(String userId, String firstName) {}
}
*** SENDING: [User[userId=1, firstName=AAAAA], User[userId=2, firstName=BBBB], User[userId=3, firstName=CCCCC], User[userId=4, firstName=DDDDD], User[userId=5, firstName=EEEEE]]
...
***** LISTEN: User[userId=1, firstName=AAAAA]
***** LISTEN: User[userId=2, firstName=BBBB]
***** LISTEN: User[userId=3, firstName=CCCCC]
***** LISTEN: User[userId=4, firstName=DDDDD]
***** LISTEN: User[userId=5, firstName=EEEEE]
I hope this helps. Take care.

Migrate from HystrixCommand to Resilience4j

Resilience4j version: 1.7.0
Java version: 1.8
I have challenge in implementing TimeLimiter feature of Resilience4j. I am able to get the Circuit Breaker (CB) work.
We have 2 services Lets say serviceA and serviceB. We have used Command design pattern which encapsulates logic to communicate with ServiceB. RabbitMQ is used to establish inter microservice communication. We had implemented Hystrix CB by making all our Command classes extend HystrixCommand. When we decided to move to Resilience4j main challenge was to retain the existing design pattern than configuring Resilence4J CB.
We have Synchronous communication at present between ServiceA and ServiceB. Though we use RabbitMQ to communicate which is Async communication, with the help of Spring wrapper method RabbitTemplate.convertSendAndReceive() we are able to achieve Sync mode of communication with RabbitMQ.
When I removed HystrixCommand reference which was the Base class for all my Command classes, naturally there was a need to implement a custom Base Command class which will be implemented using Resilience4J Decorators.
I managed introduce a Resilience4JCommand abstract class which will implement a execute() and execute run() from all my command classes. Also defined a abstract run() which all my existing Command classes will override and implement business logic.
I understood from many of the discussion that our method which needs to implement CB pattern needs to return of type CompletableFuture and also understood from many places that fallback method also must have same return type. My Base Command Class Resilience4JCommand looks something like below
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import com.ge.hc.XYZ.exception.ResourceNotFoundException;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.bulkhead.annotation.Bulkhead.Type;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
#Component
public abstract class Resilience4JCommand<R> {
/** The class logger. */
protected static final Logger LOGGER = LoggerFactory.getLogger(Resilience4JCommand.class);
public R execute() {
R result = null;
try {
result = executeWithCircuitBreaker().get();
} catch (Exception e) {
System.out.println("Inside Catch block of executeAsync ...........**************\n\n ");
e.printStackTrace();
throw new RuntimeException(e);
}
return result;
}
#Bulkhead(name = "XYZStreamingServer3", fallbackMethod = "getFallback", type = Bulkhead.Type.THREADPOOL)
#TimeLimiter(name = "XYZStreamingServer2", fallbackMethod = "getFallback")
#CircuitBreaker(name = "XYZStreamingServer1", fallbackMethod = "getFallback")
public CompletableFuture<R> executeWithCircuitBreaker() {
return CompletableFuture.supplyAsync(new Supplier<R>() {
#Override
public R get() {
return run();
}
});
}
protected abstract R run();
public CompletableFuture<R> getFallback(Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
if (e != null) {
e.printStackTrace(pw);
}
String reason = sw.toString();
LOGGER.error("Calling XYZ-hystrix fallback method for command: {}; fallback reason: {}",
this.getClass().getSimpleName(), (reason.isEmpty() ? "unknown" : reason));
throw new ResourceNotFoundException("Circuit Breaker ");
}
}
But nothing works with above setup. I am able to achieve CB alone work without the need of writing new method executeWithCircuitBreaker() which returns CompletableFuture. I can make CB work just with below execute()
Bulkhead AND TimeLimiter do not work with return type other than CompletableFuture
#CircuitBreaker(name = SCHEME_NAME, fallbackMethod = "getFallback")
public R execute() {
return run();
}
I have spent more than a week in setting up this .. Helpful if someone can point me what I am missing 😢
My application.properties looks something like belwo
management.health.circuitbreakers.enabled=true
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=always
resilience4j.circuitbreaker.instances.XYZStreamingServer1.registerHealthIndicator=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.eventConsumerBufferSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.failureRateThreshold=50
resilience4j.circuitbreaker.instances.XYZStreamingServer1.minimumNumberOfCalls=5
resilience4j.circuitbreaker.instances.XYZStreamingServer1.automaticTransitionFromOpenToHalfOpenEnabled=true
resilience4j.circuitbreaker.instances.XYZStreamingServer1.waitDurationInOpenState=5s
resilience4j.circuitbreaker.instances.XYZStreamingServer1.permittedNumberOfCallsInHalfOpenState=3
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowSize=10
resilience4j.circuitbreaker.instances.XYZStreamingServer1.slidingWindowType=COUNT_BASED
resilience4j.timelimiter.instances.XYZStreamingServer2.timeoutDuration=5s
resilience4j.timelimiter.instances.XYZStreamingServer2.cancelRunningFuture=true
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.maxThreadPoolSize=10
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.coreThreadPoolSize=5
resilience4j.thread-pool-bulkhead.instances.XYZStreamingServer3.queueCapacity=5

unable to use Stripe API from a Java Jersey Jackson REST API due to snake case to camel case conversion

On the server side, I develop a REST API with Java and Jersey / Jackson, and this API makes calls to the Stripe API.
The Stripe API returns all objects with properties names in snake case, such as client_secret for class PaymentIntent.
The JSON returned by my REST API using Jersey automatically converts these properties to camel case, with names such as clientSecret.
On the client side, I use the Stripe JS library, which also expects properties names in snake case, and therefore I get errors when I try to read properties of objects returned by my REST API.
I have seen many posts about configuring Jersey to use snake case instead of camel case, but I have not been able to apply what I found to my use case, which is using camel case for my own classes, and snake case for Stripe classes I have no control on.
Here is my current code on the server site:
package com.knowledgeplaces.metalmsapi.resources;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.knowledgeplaces.metalmsapi.records.PaymentIntentArgsRec;
import com.knowledgeplaces.metalmsapi.records.PaymentIntentResponseRec;
import com.knowledgeplaces.metalmsapi.utils.MetaLmsConstants;
import com.stripe.Stripe;
import com.stripe.model.PaymentIntent;
import com.stripe.exception.StripeException;
import com.stripe.net.ApiResource;
#Path("/eShops/{eShopId}/stripePaymentIntent")
public class StripePaymentIntentRest {
// request one Payment Intent
#POST
#Produces({ MediaType.APPLICATION_JSON })
public PaymentIntentResponseRec createService(
#Context HttpServletRequest req,
#PathParam("eShopId") Integer eShopId,
PaymentIntentArgsRec paymentIntentArgs) {
PaymentIntent paymentIntent;
PaymentIntentResponseRec paymentIntentResponse;
// get Stripe API secret key
if (paymentIntentArgs.stripeTestMode()) {
Stripe.apiKey = "***********************";
} else {
Stripe.apiKey = "***********************";
}
// create Payment Intent
List<Object> paymentMethodTypes = new ArrayList<>();
paymentMethodTypes.add("card");
Map<String, Object> params = new HashMap<>();
params.put("amount", paymentIntentArgs.amount());
params.put("currency", paymentIntentArgs.currency());
params.put(
"payment_method_types",
paymentMethodTypes);
try {
// paymentIntent = PaymentIntent.create(params);
paymentIntent = ApiResource.GSON.fromJson(PaymentIntent.create(params).toJson(), PaymentIntent.class);
paymentIntentResponse = new PaymentIntentResponseRec(null, paymentIntent);
} catch (StripeException ex) {
paymentIntentResponse = new PaymentIntentResponseRec(MetaLmsConstants.StripeApiError, null);
}
return paymentIntentResponse;
}
}
With this code, I get a Payment Intent object with properties in camel case.
If I uncomment the line
paymentIntent = PaymentIntent.create(params);
And comment the line
paymentIntent = ApiResource.GSON.fromJson(PaymentIntent.create(params).toJson(), PaymentIntent.class);
Then I get the following error:
No serializer found for class com.stripe.net.StripeResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.knowledgeplaces.metalmsapi.records.PaymentIntentResponseRec["paymentIntent"]->com.stripe.model.PaymentIntent["lastResponse"])
Please advise on how to get rid off this error.
I have reviewed my code and here is a working solution:
try {
paymentIntent = PaymentIntent.create(params);
String paymentIntentJson = paymentIntent.toJson();
stringResponse = new StringResponseRec(null, paymentIntentJson);
} catch (StripeException ex) {
stringResponse = new StringResponseRec(ex.getMessage(), null);
}
return stringResponse;
The problem was related to snake case support in Jersey Jackson or records in Java 16, I don't know.
So, instead of loading a record of type PaymentIntentResponseRec which has a Stripe PaymentIntent object as a property, I load a record of type StringResponseRec which has a property of type string, and I load that string from the paymentIntent.toJson() provided by the Stripe API.
On the client side, my Angular app which uses the Stripe JS library gets my PaymentIntent object fine.
It works, but if you think there is a more elegant solution, feel free to comment.

Do a HTTP POST instead of a GET

The WebService I'm trying to use is SOAP and receives only the HTTP method POST on its requests, but for some reason, I'm not being able to make that happen and it's sending only GET.
I've been loosely following this guide: https://spring.io/guides/gs/consuming-web-service/
I've thought that it wouldn’t be the case of the wrong method, but I've tried via Postman with the logged envelope and it worked fine, also this:
debugger image with GET
This is how I've done it:
package com.example.ws;
import com.demo.partner.wsdl.ObjectFactory;
import com.demo.partner.wsdl.StoreInformation;
import com.example.util.PartnerWSHttpHeaderCallBack;
import com.example.util.SoapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class StoreInformationClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(StoreInformationClient.class);
#Autowired
private ObjectFactory objectFactory;
public StoreInformation getStoreInformation() {
StoreInformation storeInformation = objectFactory.createStoreInformation();
storeInformation.setStoreID("99633");
return (StoreInformation) getWebServiceTemplate()
.marshalSendAndReceive("https://aurl.com/api/ws.wsdl", SoapUtils.buildEnvelope(storeInformation, StoreInformation.class),
new PartnerWSHttpHeaderCallBack());
}
}
package com.example.configuration;
import com.demo.partner.wsdl.ObjectFactory;
import com.example.ws.StoreInformationClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class AppConfiguration {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.demo.partner.wsdl");
return marshaller;
}
#Bean
public StoreInformationClient countryClient(Jaxb2Marshaller marshaller) {
StoreInformationClient client = new StoreInformationClient();
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
#Bean
public ObjectFactory objectFactory() {
return new ObjectFactory();
}
}
package com.example.util;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.ws.transport.http.HttpUrlConnection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class PartnerWSHttpHeaderCallBack implements WebServiceMessageCallback {
#Override
public void doWithMessage(WebServiceMessage webServiceMessage) {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Basic definitelyABase64value");
headers.put("SOAPAction", "http://www.partner.com/action/GetStoreInformation");
headers.put("Content-Type", "text/xml");
addRequestHeader(headers);
}
private void addRequestHeader(Map<String, String> headers) {
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpUrlConnection) {
HttpUrlConnection conn = (HttpUrlConnection) connection;
for (Map.Entry<String, String> entry : headers.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
try {
conn.addRequestHeader(k, v);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Had a look at both 6 api and 8 api docs for spring and find that way is almost impossible (if not it is impossible) because as you are finding there does not appear to be a method in that hierarchy set of classes and interfaces (IN YOUR FORM OF CODE) a sensible coherent way of converting the http connection to "POST" , it will only send "GET".
I do understand that the HttpUrlConnection is not java.net and is an org.spring. but the spring connections must be obtained through extensions and the instance context of the connected objects.
the spring framework documentation seems erroneous also because it shows no-where org.springframework.ws.transport.TransportConstants and org.springframework.ws.transport.http.HttpTransportConstants are implemented.
Niether taking a look searching the web.
All i can suggest "trying" is this however "Spring.org project" needs to clearly document how to set the constants for those interfaces AND if anything actually implements them !
/*
org.springframework.ws.transport.TransportConstants
import org.springframework.ws.transport.http.HttpTransportConstants
*/
HttpTransportConstants httCxt = ((HttpTransportConstants)TransportContextHolder.getTransportContext());
// perhaps httCxt."METHOD_POST";
httCxt.METHOD_POST;
// no really, there should be an implementing class in the .transport packages that uses and sets this however i could not locate one throughout the ws classes and interfaces.
// all interface variables are static and final (because interfaces are abstract they can be cast together)
TransportContext context = (TransportContext)httCxt;
Here is an article that seems to have some way alike
https://pretagteam.com/question/soap-request-to-webservice-with-java
however i think they use a java.net HttpURLConnection
There is a similar HttpURLConnection object in spring
*** The spring docs also do not show full hierarchy on identical classes and interfaces**
org.springframework.ws.transport.http
Class HttpUrlConnection IMPLEMENTS org.springframework.ws.transport
Interface WebServiceConnection
The following worked many years back for applets and since finding ambiguous docs naming it appears this would work (or be immensely close)
To make a org.springframework.ws.transport.http.HttpUrlConnection
make a protected method
protected org.springframework.ws.transport.http.HttpUrlConnection makeConnHttp(String uuu){
java.net.URL urli = new java.net.URL(uuu);
Java.net.UrlConnection urlcon = new Java.net.UrlConnection(urli);
Java.net.HttpUrlConnection netUrl = (Java.net.HttpUrlConnection)urlcon;
netUrl.setRequestMethod("POST");
org.springframework.ws.transport.http.HttpUrlConnection wsUrlConn = new org.springframework.ws.transport.http.HttpUrlConnection(netUrl);
return (org.springframework.ws.transport.http.HttpUrlConnection)wsUrlConn;
}
public org.springframework.ws.transport.http.HttpUrlConnection getWSconnectionHttp(String uuu){
return (org.springframework.ws.transport.http.HttpUrlConnection)makeConnHttp(uuu);
}
// spring 2.1.4 HttpUrlConnection is also a WebServiceConnection
org.springframework.ws.transport.WebServiceConnection wdConnsvc = (org.springframework.ws.transport.WebServiceConnection)getWSconnectionHttp(uuu);
It is not known to me whether the api process calls open()/close() on UrLConnection or that has to be done manually.
Where your code goes wrong
Notice how your code gets a TransportContext and it gets a WebServiceConnection.
That produced object instance is a SEPERATE object instance not related to the later constructed spring framework HttpUrlConnection.
You could open the connection but it is not the WebServiceConnection object constructed with the transport context.
The problem is the org spring HttpUrlConnection IS( a WebServiceConnection) also by polymorphism , so you do not need to obtain a WebServiceConnection because you would have that from creating a org.spring HttpUrlConnection.
However an org spring HttpUrlConnection must be fed an implementing coded java.net.HttpUrlConnection for the actual networking, the org spring HttpUrlConnection is more of a joiner wrapper for obtaining and managing data bound to the java.net.HttpUrlConnection and the containers registrations of the service endpoints.
Your code would require joining the two parts by replacing any instance of org.springframework....HttpUrlConnection with an/or-the externally created HttpUrlConnection using lava.lang.reflect.
However, the best method for binding objectes in that problem is extend from the class org.springframework.ws.transport.http.HttpUrlConnection to prevent binding and access problems.

NoSuchMethod when calling AWS Lambda using the AWS Sdk

I've created & deployed one simple GET API in API Gateway and here is the ARN and there is no authentication whatsoever on this function, I can simply call it on my browser
arn:aws:lambda:ap-southeast-1:XXXXXXXXXXXXXX:function:La
and the public url that can be browsed using the browser is:
https://xxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/v1/lambda/geta
and I'm using Spring boot project and the below code to invoke the API (Following this Doc)
The interface as the lambda service
package com.xxxxxxx.services.interfaces;
import com.amazonaws.services.lambda.invoke.LambdaFunction;
public interface ILambdaGetBalance {
#LambdaFunction(functionName="La")
String getA();
}
The service using that interface to call the lambda function
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.xxxxxxxx.services.interfaces.ILambdaGetBalance;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
#Service
public class LambdaService {
#Value("${aws.access-key}")
private String accessKey;
#Value("${aws.secret-key}")
private String secretKey;
#Value("${aws.lambda.region-name}") // this is ap-southeast-1
private String regionName;
public void test() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey,
secretKey);
AWSLambda client = AWSLambdaClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(regionName)
.build();
final ILambdaGetBalance getBalance = LambdaInvokerFactory.builder()
.lambdaClient(client)
.build(ILambdaGetBalance.class);
getBalance.getA();
}
}
after calling the getA function the system will through the following exception:
java.lang.NoSuchMethodError: com.amazonaws.services.lambda.AWSLambdaClient.beforeClientExecution(Lcom/amazonaws/AmazonWebServiceRequest;)Lcom/amazonaws/AmazonWebServiceRequest;
Any idea why is this happening? What am I missing?
Looks like your aws-java-sdk-lambda and aws-java-sdk-core modules may have incompatible versions. How are you resolving the dependencies for your project? The beforeClientExecution method was added to the AmazonWebServiceClient base class in version 1.11.106 of aws-java-sdk-core - see here: https://github.com/aws/aws-sdk-java/blame/master/aws-java-sdk-core/src/main/java/com/amazonaws/AmazonWebServiceClient.java#L590

Categories

Resources