AWS IoT MQTT Client with Apache camel-mqtt - spring-boot

I am looking into building a AWS IoT Java Client with Apache Camel ( using camel-mqtt ) + Spring Boot. It sounds like a good match to me, but couldn't find any examples. Is there any drawback that I can't see ? Would be interested to see any pointers.

I got it working with the below configuration. sslContext bean holds the certificate/security :
#Bean
RouteBuilder awsIoTRoute() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?repeatCount=0&delay=5000&fixedRate=true&period=17s")
.setBody(simple("TEST MESSAGE"))
.to("mqtt:awsIoTPublisher?host=ssl://{{aws.iot.host}}:8883&publishTopicName={{aws.iot.pub.topic}}&clientId={{aws.iot.pub.clientId}}&sslContext=#sslContext")
.log("Sent :"+body().convertToString().toString());
from("mqtt:awsIoTReciever?host=ssl://{{aws.iot.host}}:8883&subscribeTopicName={{aws.iot.sub.topic}}&clientId={{aws.iot.sub.clientId}}&sslContext=#sslContext").log("Recieved : "+body().convertToString());
}
};
}

Related

Apache Camel CXF set Transport Properties

Im struggling with the Camel SXF Component. I need it to not use chunked encoding, but I do not find the correct way to set the Parameter.
According to the Apache CXF Docs(http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html) there should be a Parameter called "AllowChunking", but I had no luck when trying to using it. I tried this
.to("cxf:bean:pdsEndpointBean?loggingFeatureEnabled=true&properties.AllowChunking=false")
and this
#Bean
public CxfEndpoint pdsEndpointBean() {
CxfEndpoint cxfEndpoint = new CxfEndpoint();
cxfEndpoint.setAddress(endpoint);
cxfEndpoint.setEndpointName("foo");
cxfEndpoint.setWsdlURL("bar");
cxfEndpoint.setServiceClass(foo);
HashMap<String, Object> properties = new HashMap<>();
properties.put("AllowChunking",false);
cxfEndpoint.setProperties(properties);
return cxfEndpoint;
}
Can anyone help me out ? Thanks a lot :)
Using Camel 3.0.1
Try using CxfEndpointConfigurer like this:
cxfEndpoint.setCxfEndpointConfigurer(new CxfEndpointConfigurer() {
#Override
public void configure(final AbstractWSDLBasedEndpointFactory abstractWSDLBasedEndpointFactory) {
}
#Override
public void configureClient(final Client client) {
((HTTPConduit)client.getConduit()).getClient().setAllowChunking(false);
}
#Override
public void configureServer(final Server server) {
}
});
And always specify version of your camel

Spring Cloudstream 3 + RabbitMQ configuration to existing queue

I'm learning Cloudstream and cannot map the cloudstream Function<String, String> into existing queue.
I'm just creating the hello world app from spring cloud documentation, but don't really understand this part regarding binding names.
I have q.test (existing) on my rabbitmq app, but when I use this code and configuration, my app always create new queue q.test.anonymous.someRandomString.
Anybody has configuration example for this?
#SpringBootApplication
public class CloudstreamApplication {
public static void main(String[] args) {
SpringApplication.run(CloudstreamApplication.class, args);
}
#Bean
public Function<String, String> uppercase() {
return value -> {
System.out.println("Received: " + value);
return value.toUpperCase();
};
}
}
application.yml
spring.cloud.stream:
function.bindings:
uppercase-in-0: q.test
bindings:
uppercase-in-0.destination: q.test
Thanks
See the binder documentation - Using Existing Queues/Exchanges.
If you have an existing exchange/queue that you wish to use, you can completely disable automatic provisioning as follows, assuming the exchange is named myExchange and the queue is named myQueue:
spring.cloud.stream.bindings.<binding name>.destination=myExhange
spring.cloud.stream.bindings.<binding name>.group=myQueue
spring.cloud.stream.rabbit.bindings.<binding name>.consumer.bindQueue=false
spring.cloud.stream.rabbit.bindings.<binding name>.consumer.declareExchange=false
spring.cloud.stream.rabbit.bindings.<binding name>.consumer.queueNameGroupOnly=true

Feign with RibbonClient and Consul discovery without Spring Cloud

I was trying to setup Feign to work with RibbonClient, something like MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");, where myAppProd is an application which I can see in Consul. Now, if I use Spring annotations for the Feign client (#FeignClient("myAppProd"), #RequestMapping), everything works as Spring Cloud module will take care of everything.
If I want to use Feign.builder() and #RequestLine, I get the error:
com.netflix.client.ClientException: Load balancer does not have available server for client: myAppProd.
My first initial thought was that Feign was built to work with Eureka and only Spring Cloud makes the integration with Consul, but I am unsure about this.
So, is there a way to make Feign work with Consul without Spring Cloud?
Thanks in advance.
In my opinion, it's not feign work with consul, its feign -> ribbon -> consul.
RibbonClient needs to find myAppProd's serverList from its LoadBalancer.
Without ServerList, error: 'does not have available server for client'.
This job has been done by SpringCloudConsul and SpringCloudRibbon project, of course you can write another adaptor, it's just some glue code. IMHO, you can import this spring dependency into your project, but use it in non-spring way . Demo code:
just write a new feign.ribbon.LBClientFactory, that generate LBClient with ConsulServerList(Spring's class).
public class ConsulLBFactory implements LBClientFactory {
private ConsulClient client;
private ConsulDiscoveryProperties properties;
public ConsulLBFactory(ConsulClient client, ConsulDiscoveryProperties consulDiscoveryProperties) {
this.client = client;
this.properties = consulDiscoveryProperties;
}
#Override
public LBClient create(String clientName) {
IClientConfig config =
ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
ConsulServerList consulServerList = new ConsulServerList(this.client, properties);
consulServerList.initWithNiwsConfig(config);
ZoneAwareLoadBalancer<ConsulServer> lb = new ZoneAwareLoadBalancer<>(config);
lb.setServersList(consulServerList.getInitialListOfServers());
lb.setServerListImpl(consulServerList);
return LBClient.create(lb, config);
}
}
and then use it in feign:
public class Demo {
public static void main(String[] args) {
ConsulLBFactory consulLBFactory = new ConsulLBFactory(
new ConsulClient(),
new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()))
);
RibbonClient ribbonClient = RibbonClient.builder()
.lbClientFactory(consulLBFactory)
.build();
GitHub github = Feign.builder()
.client(ribbonClient)
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
interface GitHub {
#RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(#Param("owner") String owner, #Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
}
you can find this demo code here, add api.github.com to your local consul before running this demo.

Spring integration Java DSL : creating sftp inbound adapter

I want to create a flow using DSL. The flow is from the adapter, message will flow to channel.
#Bean
public IntegrationFlow sftpInboundFlow() {
prepareSftpServer();
return IntegrationFlows
.from(Sftp.inboundAdapter(this.sftpSessionFactory).getId("SftpInboundAdapter")
.preserveTimestamp(true)
.remoteDirectory("sftpSource")
.regexFilter(".*\\.txt$")
.localFilenameExpression("#this.toUpperCase() + '.a'").localDirectory(file).channel(MessageChannels.queue("sftpInboundResultChannel"))
.get());
}
Not sure of compilation error at getId() method . tried to convert from Java 8 lambda to Java 7
I think you want to add an id attribute for your component to register it with that bean name in the application context. You config must look like:
return IntegrationFlows
.from(Sftp.inboundAdapter(this.sftpSessionFactory)
.preserveTimestamp(true)
.remoteDirectory("sftpSource")
.regexFilter(".*\\.txt$")
.localFilenameExpression("#this.toUpperCase() + '.a'")
.localDirectory(file),
new Consumer<SourcePollingChannelAdapterSpec>() {
#Override
public void accept(SourcePollingChannelAdapterSpec e) {
e.id("SftpInboundAdapter");
}
})
.channel(MessageChannels.queue("sftpInboundResultChannel"))
.get();
There is no such a getId(String) method.
Yes I'll fix its JavaDocs eventuelly, but you are facing really compilation error, hence wrong language usage.

Compatibility of ContainerResponseFilter in jersey 1.17

Can i run my CustomFilter extended with ContainerResponseFilter in jersey1.17.
I am using GrizzlyWebServer. Please suggest . Given below is my sample server code to add the filter.
GrizzlyWebServer webServer = new GrizzlyWebServer(.............);
....
....
ServletAdapter adapter3 = new ServletAdapter();
adapter3.addInitParameter("com.sun.jersey.config.property.packages", "com.motilink.server.services");
adapter3.setContextPath("/");
adapter3.setServletInstance(new ServletContainer());
adapter3.addContextParameter(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, PoweredbyResponseFilter.class.getName());
webServer.addGrizzlyAdapter(adapter3, new String[]{"/"});
...
.....
MY Filter:
#FrontierResponse
#Provider
public class PoweredbyResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
System.out.println("hell");
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}
Resource Class:
#NameBinding
#Retention(value = RetentionPolicy.RUNTIME)
public #interface FrontierResponse {
}
#GET
#Produces("text/plain")
#Path("plain")
//#FrontierResponse
public String getMessage() {
System.out.println("hello world called");
return "Hello World";
}
and finally i call it from a browser
http:// localhost:4464/plain
Add the ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS property as a init-param and not as a context-param:
...
adapter3.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, PoweredbyResponseFilter.class.getName());
...
EDIT 1
From your answer it seems that you're actually trying to use Jersey 1.x (1.17) runtime with implemented JAX-RS 2.0 providers (ContainerRequestContext and ContainerResponseContext have been introduced in JAX-RS 2.0 and Jersey 1.x doesn't know how to use them).
So my advice would be - drop all your Jersey 1.17 dependencies and replace them with Jersey 2.x dependencies. Take a look at our helloworld-webapp example (particularly at App class) to see how to create a Grizzly server instance with JAX-RS application.
Note that it is sufficient to add just ServerProperties.PROVIDER_PACKAGES property to init-params and your Resources and Providers (incl. response filters) will be scanned and registered in the application.

Resources