How do I setup routing metadata (in payload using just RSocket-Java when server is using Spring Boot Rsocket.
Flux<Payload> s = connection.flatMapMany(requester -> requester.requestStream(DefaultPayload.create("Some Message")))
Server is using #MessageMapping("/route")
Interaction type
RSocket interaction type on SpringBoot using #MessageMapping is decided based on signature of annotated method (more info in spring docs)
Let's assume it is having signature:
#MessageMapping("/route")
Flux<String> getStreamOfStrings(String message) {...}
Based on cardinality table from spring docs interaction type is Request-Stream.
RSocket client
RSocket java client needs to have specified mime-type for metadata:
RSocket rsocketClient = RSocketConnector.create()
//metadata header needs to be specified
.metadataMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString())
// value of spring.rsocket.server.port eg 7000
.connect(TcpClientTransport.create(7000))
.block();
Data
Data will be simple string:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes("request msg".getBytes());
Metadata
Routing in RSocket is defined as metadata extension and needs to be sent together with data to specify routing. Here is example how it can be created (see other classes in package io.rsocket.metadata)
CompositeByteBuf metadata = ByteBufAllocator.DEFAULT.compositeBuffer();
RoutingMetadata routingMetadata = TaggingMetadataCodec.createRoutingMetadata(ByteBufAllocator.DEFAULT, List.of("/route"));
CompositeMetadataCodec.encodeAndAddMetadata(metadata,
ByteBufAllocator.DEFAULT,
WellKnownMimeType.MESSAGE_RSOCKET_ROUTING,
routingMetadata.getContent());
Request-stream request
Data and metadata are created so you can execute requestSteam using:
rsocketClient.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
Related
I am trying to leverage Spring Cloud Feign client for declaring rest endpoints(third-party-endpoints) which will be called based on the request that my controller receives.
I have declared a feign client interface like:
#FeignClient(name = "my-client", url = "https://abc.xyz.com", configuration = MyClientConfiguration.class)
public interface MyFeignClient{
}
MyClientConfiguration is an unannotated feign configuration class just exposing BasicAuthRequestInterceptor bean preconfigured with some credential.
public class MyClientConfiguration {
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("USERID","PWD");
}
}
So, what I have right now is a preconfigured feign client.
I am not sure how do I deal with the requirement that asks for being able to define/override the feign configuration with the url and credentials received in the body of different POST request.
So its not about overriding the feign configuration via properties file(like other somewhat similar SO questions), but instead being able to configure the feign client on the fly based on the url and credentials received in the body of the incoming requests.
I am aware of ways to do it with WebClient and looking for something similar to what WebClient provides via its uri and headers methods. Webclient allows us to override the webClient bean configuration with different uri and different auth credentials,
webClient
.put()
.uri("base-uri", uriBuilder -> uriBuilder.path("/rest").path("/api").path("/2").path("/issue").path("/" + id).build())
.headers(httpHeaders -> {
httpHeaders.setBasicAuth("decrypted-username", "decrypted-pwd");
})
Does Spring Cloud Feign provide any such facility where it is possible to have a generic configuration yet allowing to override those configurations at runtime?
EDIT 1 : START
The closest I got to is implementing via the feign's builder pattern :
MyFeignClient myFeignClient = Feign.builder()
.contract(new SpringMvcContract())
.requestInterceptor(new BasicAuthRequestInterceptor("decrypted-username", "decrypted-pwd")
.target(MyFeignClient.class, myRequestDTO.getBaseUrl());
I was challenged with an exception because I didn't specify which Contract to use. It was using the Default Contract.I then changed to SpringMvcContract as the annotation that I am using are the Spring MVC ones and not feign's default.
Getting DecodeException as of now. Trying to fix it. Will post update when done.
EDIT 1 : END
EDIT 2 : START
I was finally able to get this working. I figured out that the feign-gson does a great job at deserialization with minimal configuration.
My final feign configuration with the Decoder in place looks like :
MyFeignClient myFeignClient = Feign.builder()
.contract(new SpringMvcContract())
.decoder(new GsonDecoder())
.requestInterceptor(new BasicAuthRequestInterceptor("decrypted-username", "decrypted-pwd")
.target(MyFeignClient.class, myRequestDTO.getBaseUrl());
EDIT 2 : END
I'm building a springboot app which use spring integration and spring sleuth as well. The app reads from a csv file and for each record in the csv file a call is made to an API using spring resttemplate. Each time a file is read, all corresponding calls to the API are having the same X-B3-TraceId. They do have a different spanId.
I would like to have different X-B3-TraceId for each call to the API. I believe the spring integration is setting a traceId for each file read operation and using the same throughout during each call to the API.
#Bean
public IntegrationFlow bridgeFlow() {
return IntegrationFlows.from(ABC_SERVICE_QUEUE_CHANNEL)
.bridge(e -> e.poller(Pollers.fixedDelay(period).maxMessagesPerPoll(MAX_MSG_PER_POLL)))
.handle(someService, "someMethod")
.route(router())
.get();
}
"someMethod" has the call to the API using resttemplate as,
ResponseEntity<String> response = restTemplate.exchange(someUrl, HttpMethod.POST, requestEntity, String.class);
I tried manually setting the X-B3-TraceId headers but that seem to be getting overridden
If you'd like to override some header with your own value, you should use something like this in your IntegrationFlow:
.enrichHeaders(s -> s.header("X-B3-TraceId", "some_value", true))
The last argument is important here to be set to true.
See its JavaDocs:
#param overwrite true to overwrite an existing header.
On the other hand it is not clear from your code snippet how that X-B3-TraceId header appears in the message at all. Who sets it for us? And where is your file reader and splitter for records in it?
I have two Java processes - which get spawned from the same Jar using different run configurations
Process A - Client UI component , Developed Using Spring bean xml based approach. No Spring Boot is there.
Process B - A new Springboot Based component , hosts REST End points.
Now from Process A , on various button click how can I call the REST end points on Process B using Feign Client.
Note - Since Process A is Spring XML based , right at the moment we can not convert that to Spring boot. Hence #EnableFeignClients can not be used to initialise the Feign Clients
So Two questions
1) If the above is possible how to do it ?
2) Till Process A is moved to Spring boot - is Feign still an easier option than spring REST template ?
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSockets and you can easily use feign without spring boot. And Yes, feign still better option to use because Feign Simplify the HTTP API Clients using declarative way as Spring REST does.
1) Define http methods and endpoints in interface.
#Headers({"Content-Type: application/json"})
public interface NotificationClient {
#RequestLine("POST")
String notify(URI uri, #HeaderMap Map<String, Object> headers, NotificationBody body);
}
2) Create Feign client using Feign.builder() method.
Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
There are various decoders available in feign to simplify your tasks.
You are able to just initialise Feign in any code (without spring) just like in the readme example:
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
...
}
Please take a look at the getting started guide: feign on github
we want to integrate third party library(Eclipse XText LSP) into our SpringBoot webapp.
This library works "interactively" with the user (like chat). XText API requires input and output stream to work. We want to use WebSocket to let users interact with this library smoothly (send/retrieve json messages).
We have a problem with SpringBoot because SpringBoot support for WebSocket doesn't expose input/output streams. We wrote custom TextWebSocketHandler (subclass) but none of it's methods provide access to in/out streams.
We also tried with HandshakeInterceptor (to obtain in/out streams after handshake ) but with no success.
Can we use SpringBoot WebSocket API in this scenario or should we use some lower level (Servlet?) API ?
Regards Daniel
I am not sure if this will fit your architecture or not, but I have achieved this by using Spring Boot's STOMP support and wiring it into a custom org.eclipse.lsp4j.jsonrpc.RemoteEndpoint, rather than using a lower level API.
The approach was inspired by reading through the code provided in org.eclipse.lsp4j.launch.LSPLauncher.
JSON handler
Marhalling and unmarshalling the JSON needs to be done with the API provided with the xtext language server, rather than Jackson (which would be used by the Spring STOMP integration)
Map<String, JsonRpcMethod> supportedMethods = new LinkedHashMap<String, JsonRpcMethod>();
supportedMethods.putAll(ServiceEndpoints.getSupportedMethods(LanguageClient.class));
supportedMethods.putAll(languageServer.supportedMethods());
jsonHandler = new MessageJsonHandler(supportedMethods);
jsonHandler.setMethodProvider(remoteEndpoint);
Response / notifications
Responses and notifications are sent by a message consumer which is passed to the remoteEndpoint when constructed. The message must be marshalled by the jsonHandler so as to prevent Jackson doing it.
remoteEndpoint = new RemoteEndpoint(new MessageConsumer() {
#Override
public void consume(Message message) {
simpMessagingTemplate.convertAndSendToUser('user', '/lang/message',
jsonHandler.serialize(message));
}
}, ServiceEndpoints.toEndpoint(languageServer));
Requests
Requests can be received by using a #MessageMapping method that takes the whole #Payload as a String to avoid Jackson unmarshalling it. You can then unmarshall yourself and pass the message to the remoteEndpoint.
#MessageMapping("/lang/message")
public void incoming(#Payload String message) {
remoteEndpoint.consume(jsonHandler.parseMessage(message));
}
There may be a better way to do this, and I'll watch this question with interest, but this is an approach that I have found to work.
I autogenerate the JAX-RS interfaces from Swagger.
I use Jersey 2.25.1.
All works fine for most of the use cases. We have the same interface for the server and client parts.
Clients are generated from the interface with org.glassfish.jersey.client.proxy.WebResourceFactory.
Now I need to implement file download via streaming (files gonna be huge, typically in the gigabyte range, so streaming is required).
I can use the following signature for the server:
#GET
#Path("/DownloadFile")
#Produces({"application/octet-stream"})
StreamingOutput downloadFileUniqueId();
But StreamingOutput cannot obviously be used in the client.
Is there any feature in JAX-RS / Jersey to have a common interface between server and client ?
I've seen for the upload, this is possible using FormDataMultiPart, I'd like a similar solution for download...
Ok, found a working solution using a javax.ws.rs.core.Response object as return type:
Server code:
public Response downloadFile(String uniqueId){
InputStream inputStream = filePersistenceService.read(uniqueId);
Response.ok(outputStream -> IOUtils.copy(inputStream, outputStream)).build()
}
Client code:
Response response = client.downloadFile(uniqueId);
InputStream resultInputStream = response.readEntity(InputStream.class);
This works fine with clients generated by org.glassfish.jersey.client.proxy.WebResourceFactory.