Apache Camel set privateKey SFTP - spring-boot

I am trying to connect to a sftp server in order to store some files. I want to connect via the SSH.
My question is almost identical with:
Camel SFTP component - SSH private key URI works with privateKeyFile, doesn't work with privateKey
and the following answer: Apache Camel - How to set a private key in a dinamic sftp endpoint
But I still I didn't manage to connect.
My working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKeyFile=path/to/privateRsa)
My NON working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKey=byteArrayRsa)
I debugged the com.jcraft.jsch.KeyPair class -> following method:
public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException
And seems that instead of having the correct byte[] prvkey, I got its address.
How can I send the correct byte array? I tried to create a bean as it is suggested in one of the links, but it didn't worked. (I am using camel 3.1)

This doesn't work, you cannot simply use a ByteArray reference in a route definition. It works when you use a Registry like org.apache.camel.impl.SimpleRegistry to store the key reference.
simpleRegistry.put("MySshKey", byteArrayRsa)
camelCtx.setRegistry(simpleRegistry)
Then you can use a reference in the route:
privateKey=#MySshKey
Maybe you could also use Spring Injection.

I have successfully connected SFTP server using apache camel's SFTP component using the ssh private key. My requirement was to download files from SFTP server. Similarly, It can be applied to use SFTP to upload file, Sample code is as follows :
public class MySFTPFileTransferRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("sftp://my_host/?username=user_name&password=&preferredAuthentications=publickey&useUserKnownHostsFile=false&privateKeyFile=/Users/XXXX/.ssh/id_rsa")
.to("file:/Users/XXXX/Downloads/source/").
log("Upload file ${file:name} is complete.");
}
}

Related

Camel: Calling a HTTPS REST endpoint with a proxy

I'm consuming data from a REST endpoint with in the middle of the route a proxy. I'm having CNTLM running locally (localhost:3128 ): it will authenticate for me on the corporate proxy, so I don't need to pass my credentials.
I have been unable to get my rest call to work, despite numerous attempts. For e.g., getting:
SSLException: Unrecognized SSL message
Connection handshake abruptly terminated
Connection reset
you name it, have got it
Below the simplest version of the many attempts made.
Apparently (from internet reading), that should work, but it doesn't.
How should Camel be configured, in particular camel-http ?
Notes:
The REST API I'm calling is using HTTPS but doesn't require a certificate.
The code works on my local machine when no proxy is involved. It fails on the intranet where there is a proxy
#Component
public class MyRoute extends RouteBuilder
public void configure() throws Exception {
//Tried different way to set the proxy, including inline with toD(...)
System.setProperty("https.proxyHost", "localhost");
System.setProperty("https.proxyPort", "3128");
getCamelContext().getGlobalOptions().put("http.proxyHost", "localhost");
getCamelContext().getGlobalOptions().put("https.proxyPort", "3128");
getContext().getGlobalOptions().put("https.proxyHost", "localhost");
getContext().getGlobalOptions().put("https.proxyPort", "3128");
from("timer:credentials?repeatCount=1")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setBody(simple(jsonAuth))
.to(baseUrlApi +"/v1/auth/tokens/?bridgeEndpoint=true")
.unmarshal().json(JsonLibrary.Jackson, AuthResponseDto.class)
.setHeader("Authorization", simple("Bearer ${body.data.accessToken.token}"))
// etc..
}
}

Having trouble sending data to my websocket created in Spring-Boot from Flutter

I am attempting to send data through IOWebSocketChannel in Flutter.io to a WebSocket created in Spring-Boot.
In spring-boot I have created the typical WebSocket config and controllers that are dealing with client's manipulation of my servers WebSocket. I will post them below just for reference.
WebSocketConfiguration.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/websocket")
.setAllowedOrigins("*") // allow us to connect to ws://localhost:8080/websocket with the default Spring port configuration.
.withSockJS(); // allows a client which does not support WebSocket natively mimic a WebSocket over an HTTP connection
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry){ //The configureMessageBroker method sets up a simple (in-memory) message broker for our application
registry.enableSimpleBroker("/topic"); //topic to be routed back to client
registry.setApplicationDestinationPrefixes("/app"); //This configuration allows Spring to understand that any message sent to a WebSocket channel name prefixed with /app should be routed to a #MessageMapping in our application.
}
}
WebSocketController.java
#Controller
public class WebSocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketController.class);
#MessageMapping("/send")
#SendTo("/topic/messages")
public Message send(Message message) {
LOGGER.info(String.format("Received message [%s]", message.toString()));
LocalDateTime timestamp = LocalDateTime.now();
return new Message(message.getFrom(), message.getMessage(), timestamp);
}
}
Now When I try using IOWebSocketChannel I perform the typical protocol of connecting to my configured websocket. Below is the code
final channel = IOWebSocketChannel.connect(
"ws://10.0.2.2:8080/websocket"
);
I have then created a method that is supposed to send data to my websocket so I attempt to connect to that endpoint which you see is created in WebSocketController.java called app/send/. Below is the code:
void _sendMessage() {
IOWebSocketChannel channel = IOWebSocketChannel.connect('ws://10.0.2.2:8080/app/send');
channel.sink.add(
json.encode({
"message": "bars",
})
);
}
Now when I check my Spring-Boot server nothing is logged, however, whenever I hot reload in Flutter Spring Boot and my connection to the websocket times out, tomcat server returns this:
So my question is if anybody has been able to make a breakthrough with sending data through websockets from Flutter into Spring-Boot using IOWebSocketChannel? I am also wondering if anyone has found a way to successfully use a STOMP protocol in Flutter.io? I was using stomp_client as it seemed like it was going to do the trick, however correct if I'm wrong, but flutter was giving me errors saying that there doesn't exist any html files, so I'm assuming that library is only for dart in the web.
Your Spring configuration looks good. But client-side you need some tweaks.
I spent some time to figure this out with the https://pub.dev/packages/stomp package. Use a modified version of the connect function provided here. Make sure to use this custom implementation of the connect function.
Future<StompClient> client = customStomp.connect('ws://10.0.2.2:8080/websocket', ...)
Once connected, according to your configuration, you can then send message on the following destination: /app/send.

Refresh springboot configuration dynamically

Is there any way to refresh springboot configuration as soon as we change .properties file?
I came across spring-cloud-config and many articles/blogs suggested to use this for a distributed environment. I have many deployments of my springboot application but they are not related or dependent on one another. I also looked at few solutions where they suggested providing rest endpoints to refresh configs manually without restarting application. But I want to refresh configuration dynamically whenever I change .properties file without manual intervention.
Any guide/suggestion is much appreciated.
Can you just use the Spring Cloud Config "Server" and have it signal to your Spring Cloud client that the properties file changed. See this example:
https://spring.io/guides/gs/centralized-configuration/
Under the covers, it is doing a poll of the underlying resource and then broadcasts it to your client:
#Scheduled(fixedRateString = "${spring.cloud.config.server.monitor.fixedDelay:5000}")
public void poll() {
for (File file : filesFromEvents()) {
this.endpoint.notifyByPath(new HttpHeaders(), Collections
.<String, Object>singletonMap("path", file.getAbsolutePath()));
}
}
If you don't want to use the config server, in your own code, you could use a similar scheduled annotation and monitor your properties file:
#Component
public class MyRefresher {
#Autowired
private ContextRefresher contextRefresher;
#Scheduled(fixedDelay=5000)
public void myRefresher() {
// Code here could potentially look at the properties file
// to see if it changed, and conditionally call the next line...
contextRefresher.refresh();
}
}

JAX-RS/Jersey 2 file download - Is there a common API for server and generated client proxy

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.

How to pass binary data between microservices in Spring Cloud?

I'd like to ask. How to pass binary data between microservices in Spring Cloud?
Should (Can) I use #FeignClient or #RibbonClient ? How it should be? I've already read that #FeignClient is not deal with this issue What else? OkHttp?
Thx in advance
Spring Cloud integrates with some http clients, like you mentioned. Ribbon has some non-http clients/transports built in, but I haven't used that and AFIAK, netflix doesn't use them either.
You can use the Spring Cloud LoadBalancerClient interface directly. It gives you access to a host and port, then you can use any client you want.
public class MyClass {
#Autowired
private LoadBalancerClient loadBalancer;
public void doStuff() {
ServiceInstance instance = loadBalancer.choose("myService");
String host = instance.getHost();
int port = instance.getPort();
// ... do something with the host and port
}
}
I also did a sample integration with OkHttp.

Resources