I'm attempting to use a http proxy with Camel's http4 component. The proxy works when testing using Intellij's HTTP Proxy "Check Connection" option.
However I don't know how to configure it correctly via Camel. When running the following integration test a "ConnectException: Connection timed out" is thrown. Can anyone clarify how to set the proxy details correctly please?
public class SimpleHttpProxyIT extends CamelTestSupport {
public static final String DIRECT_START = "direct:start";
public static final String MOCK_RESULT = "mock:result";
#Produce(uri = DIRECT_START)
protected ProducerTemplate basic;
#EndpointInject(uri = MOCK_RESULT)
protected MockEndpoint resultEndpoint;
#Test
public void testBasic() throws Exception {
basic.sendBody(null);
resultEndpoint.setExpectedMessageCount(1);
resultEndpoint.assertIsSatisfied();
}
#Override
public RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from(DIRECT_START)
.id("SunriseTest")
.log(LoggingLevel.INFO, "About to hit sunrise")
.setHeader(Exchange.HTTP_URI, simple("http://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getProperties().put("http.proxyAuthHost", "myproxy.company.org");
exchange.getProperties().put("http.proxyAuthPort", "10000");
exchange.getProperties().put("http.proxyAuthMethod", "Basic");
exchange.getProperties().put("http.proxyAuthUsername", "myusername");
exchange.getProperties().put("http.proxyAuthPassword", "mypassword");
}
})
.recipientList(simple("http4:dummyhost"))
.log(LoggingLevel.INFO, "Done")
.to(MOCK_RESULT);
}
};
}
}
I would think it should should be exchange.setProperty(...)
Setting the properties in the URI worked. I misread the documentation on "Using Proxy Settings Outside of the URI" (http://camel.apache.org/http4.html) as this was referring to setting them on the context, not the exchange.
public class SimpleHttpProxyIT extends CamelTestSupport {
public static final String DIRECT_START = "direct:start";
public static final String MOCK_RESULT = "mock:result";
#Produce(uri = DIRECT_START)
protected ProducerTemplate basic;
#EndpointInject(uri = MOCK_RESULT)
protected MockEndpoint resultEndpoint;
#Test
public void testBasic() throws Exception {
basic.sendBody(null);
resultEndpoint.setExpectedMessageCount(1);
resultEndpoint.assertIsSatisfied();
}
#Override
public RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from(DIRECT_START)
.id("SunriseTest")
.log(LoggingLevel.INFO, "About to hit sunrise")
.setHeader(Exchange.HTTP_URI, simple("http://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400"))
.recipientList(simple("http4:dummyhost?proxyAuthHost=myproxy.company.org&proxyAuthPort=10000&proxyAuthUsername=myusername&proxyAuthPassword=mypassword"))
.log(LoggingLevel.INFO, "Done")
.to(MOCK_RESULT);
}
};
}
}
Related
I am consuming data from REST endpoints (order of 1000+) which all have the same structure:
<server uri>/v1/source/<some ID>
I am using RouteBuilder components like this connecting to the individual endpoint <ID>:
#Component
public class Route_to_<ID> extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:mytimer?repeatCount=1") //
.setBody(simple("${null}")) //
.setHeader(Exchange.CONTENT_TYPE, simple("text/event-stream"))
.setHeader("CamelHttpMethod", simple("GET"))
.to(
<server uri>/v1/source/<ID>
+ _deviceName + "::" + _deviceProperty //
+ "?disableStreamCache=true" //
) //
.process(data -> {
... do same stuff for all endpoints ...
});
}
}
The corresponding SpringBootApplicationlooks like this:
#SpringBootApplication
#ComponentScan(basePackages = "my.package.where.components.reside")
public class MyRouteHandler {
}
Is there an elegant way to start all the individual routes to endpoints <ID>in one go using one single SpringBootApplication? Or does every route need an individual SpringBootApplication which is to be started individually?
You could use toD with dynamic uri that gets the version, source and id from message body, headers or exchange properties. You can also use property-placeholders to define host, port and other configs.
Since the REST endpoints all use the same structure you can change the version, source and id and use the same URI for most if not all the REST API calls.
Example:
public class ExampleTest extends CamelTestSupport {
static final String API_DYNAMIC_URI = "https://{{api.uri}}:{{api.port}}/{{api.version}}"
+ "/${exchangeProperty.source}/${exchangeProperty.id}"
+ "?disableStreamCache=true";
#Test
public void exampleTest() throws Exception {
context.adviceWith(context.getRouteDefinition("exampleRoute"),
new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
weaveById("apiEndpoint")
.replace()
.toD("mock:${exchangeProperty.source}/${exchangeProperty.id}")
.setBody().simple("Source: ${exchangeProperty.source} id: ${exchangeProperty.id}");
}
});
Map<String, Object> body1 = new HashMap<>();
body1.put("source", "source1");
body1.put("id", "A");
Map<String, Object> body2 = new HashMap<>();
body2.put("source", "source2");
body2.put("id", "B");
MockEndpoint source1MockEndpoint = getMockEndpoint("mock:source1/A");
source1MockEndpoint.expectedMessageCount(1);
MockEndpoint source2MockEndpoint = getMockEndpoint("mock:source2/B");
source2MockEndpoint.expectedMessageCount(2);
startCamelContext();
template.sendBody("direct:start", body1);
template.sendBody("direct:start", body2);
template.sendBody("direct:start", body2);
source1MockEndpoint.assertIsSatisfied();
source2MockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.routeId("exampleRoute")
.setProperty("source").simple("${body['source']}")
.setProperty("id").simple("${body['id']}")
.toD(API_DYNAMIC_URI).id("apiEndpoint")
.log("Received: ${body}");
}
};
}
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
Properties properties = new Properties();
properties.put("api.uri", "localhost");
properties.put("api.port", "3000");
properties.put("api.version", "v1");
return properties;
}
#Override
public boolean isUseAdviceWith() {
return true;
}
}
I have a route that a processor consumes the message and set the body to null.
public class KafkaRedirect implements Processor {
#Override
public void process(final Exchange exchange) throws Exception {
... some logic to send to another party
/**
* This is added to consume the message
*/
exchange.getIn().setBody(null);
}
}
In the test I send message to the route and I want to test if the message is sent and the body is null.
#Test
public void testMyRoute() throws Exception {
final MockEndpoint thirdPartyEndpoin = getMandatoryEndpoint("mock://myRoute", MockEndpoint.class);
context().getRouteDefinition("myRouteId")
.adviceWith(context(), new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myProcessId").after().to(thirdPartyEndpoin);
}
});
startCamelContext();
thirdPartyEndpoin.expectedMessageCount(1);
/* I NEED TO TEST IF THE BODY IS NULL*/
thirdPartyEndpoin.expectedBodiesReceived(null);
template.sendBody(ROUTE_DIRECT_START, "{\"foo\":\"foo\"}");
thirdPartyEndpoin.assertIsSatisfied(TimeUnit.SECONDS.toMillis(EXPECTED_TIMEOUT_SECONDS));
}
Try with something like (typed from top of my head)
thirdPartyEndpoin.message(0).body().isNull();
There doesn't seem to a clean and simple example of creating a secure websocket connection anywhere on the interwebs, nor instructions to set one up... any ideas?
I would provide some guidelines for websocket authentication. Since websocket is upgraded from http, the authentication is based on http too. You can equip the http connection with ssl or basic or digest auth.
I had worked with spring websocket auth before, ssl is just to upgrade http to https. I would post digest auth for spring websocket here.
1.Configure the server to user digest auth, spring security can get it:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public final static String REALM="MY_REALM";
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN")
.and().withUser("test").password("test").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().exceptionHandling().authenticationEntryPoint(getDigestEntryPoint())
.and().addFilter(getDigestAuthenticationFilter(getDigestEntryPoint()));
}
#Bean
public MyDigestAuthenticationEntryPoint getDigestEntryPoint() {
MyDigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new MyDigestAuthenticationEntryPoint();
digestAuthenticationEntryPoint.setKey("mykey");
digestAuthenticationEntryPoint.setNonceValiditySeconds(120);
digestAuthenticationEntryPoint.setRealmName(REALM);
return digestAuthenticationEntryPoint;
}
public DigestAuthenticationFilter getDigestAuthenticationFilter(
MyDigestAuthenticationEntryPoint digestAuthenticationEntryPoint) throws Exception {
DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();
digestAuthenticationFilter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint);
digestAuthenticationFilter.setUserDetailsService(userDetailsServiceBean());
return digestAuthenticationFilter;
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
public class MyDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint {
#Override
public void afterPropertiesSet() throws Exception{
super.afterPropertiesSet();
setRealmName(WebSecurityConfig.REALM);
}
}
2.Extend from AbstractSecurityWebSocketMessageBrokerConfigurer:
#Configuration
#EnableWebSocketMessageBroker
public class WssBrokerConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpSubscribeDestMatchers("/topic/notification").permitAll()
.simpDestMatchers("/**").authenticated()
.anyMessage().denyAll();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/ws");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hpdm-ws").setAllowedOrigins("*").withSockJS();
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter(mapper);
return converter;
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
3.Digest auth for client refer to this post:
spring websocket with digest authentication
In javax websockets we can use something like the follows
Session.getAsyncRemote().sendText(String text)
Session.getBasicRemote().sendText();
How can we send an asynchronous messages using spring websocket.
From WebSocketSession of spring webscockets can we extract RemoteEndPoint and send an async messages
PS Note: I am using Basic Spring websockets...
The configuration and code is as follows:
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy
#EnableWebSocket
public class WebMVCConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
private static final String ENDPOINT_URL = "/echo";
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(socketHandler(), ENDPOINT_URL).setAllowedOrigins("*");
}
#Bean
public WebSocketHandler socketHandler() {
return new WebSocketTestHandler();
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
public class SpringMVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ApplicationConfig.class, RabbitMQConfig.class, RabbitConnectionFactory.class,
WebPropertyPlaceHolderConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
#Configuration
public class WebSocketTestHandler extends TextWebSocketHandler {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("Connection is established to Server....:: Session Open : {}", session.isOpen());
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
}
#Override
public void afterConnectionClosed(WebSocketSession curSession, CloseStatus status) throws Exception {
}
}
So inside handleTextMessage(WebSocketSession session,TextMessage message) {
Inside this method am creating multiple threads And sending same session Object and some other parameters..Inside each thread am not modifying any session object related parameters but am trying to execute
TextMessage socketMessage = new TextMessage(message);
session.sendMessage(socketMessage);
}
So each thread is trying to send messages using same session Object..But am facing the following error
java.lang.IllegalStateException: Blocking message pending 10000 for BLOCKING
at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.lockMsg(WebSocketRemoteEndpoint.java:130) ~[websocket-common-9.3.8.v20160314.jar:9.3.8.v20160314]
at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.sendString(WebSocketRemoteEndpoint.java:379) ~[websocket-common-9.3.8.v20160314.jar:9.3.8.v20160314]
at org.springframework.web.socket.adapter.jetty.JettyWebSocketSession.sendTextMessage(JettyWebSocketSession.java:188) ~[spring-websocket-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:105) ~[spring-websocket-4.2.4.RELEASE.jar:4.2.4.RELEASE]
So is it possible to send asynchronous messages using spring websockets?
If yes please let me know what configuration changes are required in the above code..Or Can we extract the core AsyncRemoteEndPoint and BasicRemoteEndpoint from spring Websocket Session and can we send asynchronous messages..or if not both the above cases ..move the code to common place and put synchonized(sessionObject)
{
sendmessage
}..Sorry if the framing of question is not clear or already a duplicate question
Please note I am not using any Stomp client or anyother features over spring websocket..Am using plain spring websockets..And is it possible to do without using Future(java feature)(If yes..it would be better)?
I used ConcurrentWebSocketSessionDecorator on the session.
according to:
https://jira.spring.io/browse/SPR-13602
The decorator "enforces sending messages one at a time with a send buffer and send time limit per session. That helps quite a bit to limit the impact of slow clients"
I am trying to make use of Camel Cache for the first time. So I have created a small app based on camel-java maven archetype.
My code is based on the examples from here. Here is the snippet
public class AddingToCache extends RouteBuilder {
public void configure() {
from("direct:start")
.log("START")
.setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_ADD))
.setHeader(CacheConstants.CACHE_KEY, constant("Custom_key"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getOut().setBody("My custom out");
}
})
.log("starting ...")
.to("cache://cache1")
.to("direct:next");
}
}
public class ReadingFromCache extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:next")
.setHeader(CacheConstants.CACHE_OPERATION, constant(CacheConstants.CACHE_OPERATION_GET))
.setHeader(CacheConstants.CACHE_KEY, constant("Custom_key"))
.to("cache://cache1")
.choice()
.when(header(CacheConstants.CACHE_ELEMENT_WAS_FOUND).isNotNull())
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Object body = exchange.getIn().getBody();
System.out.println("Cache body - " + body);
}
})
.otherwise()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Object body = exchange.getIn().getBody();
System.out.println("Cache body when not found - " + body);
}
})
.end()
.to("direct:finish");
}
}
your routes are likely running, you just haven't invoked them yet (from the code you posted above anyways). you need to send a message to the direct:start or direct:next routes using a ProducerTemplate to exercise the routes...
ProducerTemplate template = camelContext.createProducerTemplate();
template.sendBody("direct:start", "message");