I am using Camel version 3.4.0 , Hazelcast version 4.2.4 and Spring Boot-version 2.5.12.
My code to override the setup method in camelTestSupport is:
#Override
public void setUp() throws Exception {
super.setUp();
System.out.println("INNNNN");
AdviceWithRouteBuilder.adviceWith(context, "HAZELCAST_REQ_QUEUE_ROUTE_ID", route ->
route.interceptSendToEndpoint("kafka:someId*")
.skipSendToOriginalEndpoint()
.to(MOCK_KAFKA_ENDPOINT));
context.start();
/*AdviceWithRouteBuilder mockKafka = new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock the endpoints for testing
interceptFrom("hazelcast:queue:*");
interceptSendToEndpoint("kafka:someId*")
.skipSendToOriginalEndpoint()
.to(MOCK_KAFKA_ENDPOINT);
}
};
//context.
mockKafka.from("HAZELCAST_REQ_QUEUE_ROUTE_ID")
context.getRgetRouteDefinition("HAZELCAST_REQ_QUEUE_ROUTE_ID")
.adviceWith(context, mockKafka);*/
}
Related
When creating sample project to learn Spring state machine I stumbled upon an error:
Consider defining a bean of type 'org.springframework.statemachine.data.redis.RedisStateMachineRepository' in your configuration.
State machine configuration:
#EnableStateMachineFactory
#Configuration
#RequiredArgsConstructor
public class PaymentStateMachineConfig extends StateMachineConfigurerAdapter<PaymentStatus, PaymentEvent> {
private final PaymentActions actions;
private final PaymentStateMachineListener paymentStateMachineListener;
private final StateMachineRuntimePersister<PaymentStatus, PaymentEvent, String> stateMachineRuntimePersister;
#Override
public void configure(StateMachineConfigurationConfigurer<PaymentStatus, PaymentEvent> config) throws Exception {
config.withConfiguration().listener(paymentStateMachineListener);
config.withPersistence().runtimePersister(stateMachineRuntimePersister);
}
#Override
public void configure(StateMachineStateConfigurer<PaymentStatus, PaymentEvent> states) throws Exception {
states.withStates()
.initial(PaymentStatus.PENDING)
.end(PaymentStatus.ACCEPTED)
.end(PaymentStatus.DENIED)
.states(EnumSet.allOf(PaymentStatus.class));
}
#Override
public void configure(StateMachineTransitionConfigurer<PaymentStatus, PaymentEvent> transitions) throws Exception {
transitions
.withExternal()
.source(PaymentStatus.PENDING)
.target(PaymentStatus.AUTHORIZED)
.event(PaymentEvent.AUTHORIZE)
.name("PAYMENT STATE MACHINE")
.action(actions.authorizePayment())
.and()
.withExternal()
.source(PaymentStatus.AUTHORIZED)
.target(PaymentStatus.ACCEPTED)
.event(PaymentEvent.ACCEPT)
.action(actions.acceptPayment());
}
#Bean
public StateMachineService<PaymentStatus, PaymentEvent> stateMachineService(
final StateMachineFactory<PaymentStatus, PaymentEvent> stateMachineFactory,
final StateMachineRuntimePersister<PaymentStatus, PaymentEvent, String> stateMachinePersist) {
return new DefaultStateMachineService<>(stateMachineFactory, stateMachinePersist);
}
}
StateMachinePersister Configuration.
#Configuration
public class MachinePersister {
#Bean
public StateMachineRuntimePersister<PaymentStatus, PaymentEvent, String> stateMachineRuntimePersister(
RedisStateMachineRepository redisStateMachineRepository) {
return new RedisPersistingStateMachineInterceptor<>(redisStateMachineRepository);
}
}
After downgrading project to JDK8 everything works fine as it is. Any ideas for a workaround with this one?
I have multiple route builders within my project,
I want to define a single interceptSendToEndpoint that will affect all of the defined routes
For Example:
Public Route1 extends RouteBuilder {
public void configure() throws Exception {
from("direct:endpoint1").toD("http:\\someAddress1");
}
}
Public Route2 extends RouteBuilder {
public void configure() throws Exception {
from("direct:endpoint2").toD("http:\\someAddress2");
}
}
what I want to do here is to define a central interceptSendToEndpoint that will automatically capture all traffic sent to the camel HTTP component for all routes.
public class InterceptRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("http:*")
.process(exchange -> System.out.println("Hi from intercept"));
}
}
However due to how camel injects the intercept scope, I'm unable to do this easily,
Is there a way to tell the camel context that this intercept is for all defined routes within the context?
Note: I'm using camel 3.0.0
Camel team works for similar task in there (https://issues.apache.org/jira/projects/CAMEL/issues/CAMEL-16757?filter=allissues&orderby=cf%5B12310200%5D+ASC%2C+priority+DESC%2C+updated+DESC).
Create an abstract base class and define your in there and then extends it in main route
public abstract class BaseRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("http:*")
.process(exchange -> System.out.println("Hi from intercept"));
}
}
public class TaskRoute2 extends BaseRoute {
#Override
public void configure() throws Exception {
super.configure();
from("direct:endpoint2").toD("http:\\someAddress2");
}
}
public class TaskRoute2 extends BaseRoute {
#Override
public void configure() throws Exception {
super.configure();
from("direct:endpoint2").toD("http:\\someAddress2");
}
}
I've a Stompclient which connects to a Spring boot server and performs some subscriptions. The code coverage for this websocket client is at 0%. I can only find the code samples for how to unit test Spring boot Websocket server. But this is client side verifying the Stompclient is working fine. Please let me know if my question is missing any details.
Here's my sample connect method which I need to write unit test case for.
StompSession connect(String connectionUrl) throws Exception {
WebSocketClient transport = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(transport);
stompClient.setMessageConverter(new StringMessageConverter());
ListenableFuture<StompSession> stompSession = stompClient.connect(connectionUrl, new WebSocketHttpHeaders(), new MyHandler());
return stompSession.get();
}
Note: The client is part of a lightweight SDK so it cannot have heavy dependency for this unit test.
Thanks to Artem for suggestion I look into the Spring websocket test examples. Here's how I solved it for me, hope this helps someone.
public class WebSocketStompClientTests {
private static final Logger LOG = LoggerFactory.getLogger(WebSocketStompClientTests.class);
#Rule
public final TestName testName = new TestName();
#Rule
public ErrorCollector collector = new ErrorCollector();
private WebSocketTestServer server;
private AnnotationConfigWebApplicationContext wac;
#Before
public void setUp() throws Exception {
LOG.debug("Setting up before '" + this.testName.getMethodName() + "'");
this.wac = new AnnotationConfigWebApplicationContext();
this.wac.register(TestConfig.class);
this.wac.refresh();
this.server = new TomcatWebSocketTestServer();
this.server.setup();
this.server.deployConfig(this.wac);
this.server.start();
}
#After
public void tearDown() throws Exception {
try {
this.server.undeployConfig();
}
catch (Throwable t) {
LOG.error("Failed to undeploy application config", t);
}
try {
this.server.stop();
}
catch (Throwable t) {
LOG.error("Failed to stop server", t);
}
try {
this.wac.close();
}
catch (Throwable t) {
LOG.error("Failed to close WebApplicationContext", t);
}
}
#Configuration
static class TestConfig extends WebSocketMessageBrokerConfigurationSupport {
#Override
protected void registerStompEndpoints(StompEndpointRegistry registry) {
// Can't rely on classpath detection
RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy();
registry.addEndpoint("/app")
.setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy))
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry configurer) {
configurer.setApplicationDestinationPrefixes("/publish");
configurer.enableSimpleBroker("/topic", "/queue");
}
}
#Test
public void testConnect() {
TestStompClient stompClient = TestStompClient.connect();
assert(true);
}
}
I am developing a data analytics app with Apache Flink and Neo4J (Community Edition).
In this application, the Flink sink must save/update relations in Neo4J.
Which is the best way for Neo4J session management, and why?
First implementation:
public class MySink extends RichSinkFunction<Link> {
private DbConfiguration dbconfig;
private Driver driver;
#Override
public void open(Configuration parameters) throws Exception {
this.driver = Neo4JManager.open(this.dbconfig);
}
#Override
public void close() throws Exception {
this.driver.close();
}
#Override
public void invoke(Link link) throws Exception {
Session session = this.driver.session();
Neo4JManager.saveLink(session, link);
session.close();
}
}
Second implementation:
public class MySink extends RichSinkFunction<Link> {
private DbConfiguration dbconfig;
private Driver driver;
private Session session;
#Override
public void open(Configuration parameters) throws Exception {
this.driver = Neo4JManager.open(this.dbconfig);
this.session = driver.session();
}
#Override
public void close() throws Exception {
this.session.close();
this.driver.close();
}
#Override
public void invoke(Link link) throws Exception {
Neo4JManager.saveLink(this.session, link);
}
}
In both implementations, the following functions have been used:
public class Neo4JManager {
public static Driver open(DbConfiguration dbconf) {
AuthToken auth = AuthTokens.basic(dbconf.getUsername(), dbconf.getPassword());
Config config = Config.build().withEncryptionLevel(Config.EncryptionLevel.NONE ).toConfig();
return GraphDatabase.driver(dbconf.getHostname(), auth, config);
}
public static void saveLink(Session session, Link link) {
Value params = parameters("x", link.x, "y", link.y);
session.run('CREATE (Person {id:{x}}-[FOLLOWS]->(Person {id:{y}}))'
}
}
Thank you.
I have a Springboot application, where I have some Camel routes configured.
public class CamelConfig {
private static final Logger LOG = LoggerFactory.getLogger(CamelConfig.class);
#Value("${activemq.broker.url:tcp://localhost:61616}")
String brokerUrl;
#Value("${activemq.broker.maxconnections:1}")
int maxConnections;
#Bean
ConnectionFactory jmsConnectionFactory() {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(new ActiveMQConnectionFactory(brokerUrl));
pooledConnectionFactory.setMaxConnections(maxConnections);
return pooledConnectionFactory;
}
#Bean
public RoutesBuilder route() {
LOG.info("Initializing camel routes......................");
return new SpringRouteBuilder() {
#Override
public void configure() throws Exception {
from("activemq:testQueue")
.to("bean:queueEventHandler?method=handleQueueEvent");
}
};
}
}
I want to test this route from activemq:testQueue to queueEventHandler::handleQueueEvent.
I tried different things mentioned here http://camel.apache.org/camel-test.html, but doesn't seem to get it working.
I am trying to do something like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {CamelConfig.class, CamelTestContextBootstrapper.class})
public class CamelRouteConfigTest {
#Produce(uri = "activemq:testQueue")
protected ProducerTemplate template;
#Test
public void testSendMatchingMessage() throws Exception {
template.sendBodyAndHeader("testJson", "foo", "bar");
// Verify handleQueueEvent(...) method is called on bean queueEventHandler by mocking
}
But my ProducerTemplate is always null. I tried auto-wiring CamelContext, for which I get an exception saying it cannot resolve camelContext. But that can be resolved by adding SpringCamelContext.class to #SpringBootTest classes. But my ProducerTemplate is still null.
Please suggest. I am using Camel 2.18 and Spring Boot 1.4.
In Camel 2.22.0 and ongoing, which supports Spring Boot 2 you can use the following template to test your routes with Spring Boot 2 support:
#RunWith(CamelSpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
...
})
#EnableAutoConfiguration
#DisableJmx
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class RouteTest {
#TestConfiguration
static class Config {
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext camelContext) {
// configure Camel here
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
// Start your manual routes here
}
};
}
#Bean
RouteBuilder routeBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:someEndpoint").to("mock:done");
}
};
}
// further beans ...
}
#Produce(uri = "direct:start")
private ProducerTemplate template;
#EndpointInject(uri = "mock:done")
private MockEndpoint mockDone;
#Test
public void testCamelRoute() throws Exception {
mockDone.expectedMessageCount(1);
Map<String, Object> headers = new HashMap<>();
...
template.sendBodyAndHeaders("test", headers);
mockDone.assertIsSatisfied();
}
}
Spring Boot distinguishes between #Configuration and #TestConfiguration. The primer one will replace any existing configuration, if annotated on a top-level class, while #TestConfiguration will be run in addition to the other configurations.
Further, in larger projects you might run into auto-configuration issues as you can't rely on Spring Boot 2 to configure your custom database pooling or what not correctly or in cases where you have a specific directory structure and the configurations are not located within a direct ancestor directory. In that case it is proabably preferable to omit the #EnableAutoConfiguration annotation. In order to tell Spring to still auto-configure Camel you can simply pass CamelAutoConfiguration.class to the classes mentioned in #SpringBootTest
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
RouteTest.Config.class,
CamelAutoConfiguration.class
}
As no automatic configuration is performed, Spring won't load the test configuration inside your test class nor initialize Camel as well. By adding those configs to the boot classes manually Spring will do it for you.
For one route with MQ and Spring Boot like this:
#Component
public class InboundRoute extends RouteBuilder {
#Override
public void configure() {
JaxbDataFormat personDataFormat = new JaxbDataFormat();
personDataFormat.setContextPath(Person.class.getPackage().getName());
personDataFormat.setPrettyPrint(true);
from("direct:start").id("InboundRoute")
.log("inbound route")
.marshal(personDataFormat)
.to("log:com.company.app?showAll=true&multiline=true")
.convertBodyTo(String.class)
.inOnly("mq:q.empi.deim.in")
.transform(constant("DONE"));
}
}
I use adviceWith in order to replace the endpoint and use only mocks:
#RunWith(CamelSpringBootRunner.class)
#UseAdviceWith
#SpringBootTest(classes = InboundApp.class)
#MockEndpoints("mock:a")
public class InboundRouteCamelTest {
#EndpointInject(uri = "mock:a")
private MockEndpoint mock;
#Produce(uri = "direct:start")
private ProducerTemplate template;
#Autowired
private CamelContext context;
#Test
public void whenInboundRouteIsCalled_thenSuccess() throws Exception {
mock.expectedMinimumMessageCount(1);
RouteDefinition route = context.getRouteDefinition("InboundRoute");
route.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() {
weaveByToUri("mq:q.empi.deim.in").replace().to("mock:a");
}
});
context.start();
String response = (String) template.requestBodyAndHeader("direct:start",
getSampleMessage("/SimplePatient.xml"), Exchange.CONTENT_TYPE, MediaType.APPLICATION_XML);
assertThat(response).isEqualTo("DONE");
mock.assertIsSatisfied();
}
private String getSampleMessage(String filename) throws Exception {
return IOUtils
.toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name());
}
}
I use the following dependencies: Spring Boot 2.1.4-RELEASE and Camel 2.23.2. The complete source code is available on Github.
This is how I did this finally:
#RunWith(SpringRunner.class)
public class CamelRouteConfigTest extends CamelTestSupport {
private static final Logger LOG = LoggerFactory.getLogger(CamelRouteConfigTest.class);
private static BrokerService brokerSvc = new BrokerService();
#Mock
private QueueEventHandler queueEventHandler;
#BeforeClass
// Sets up an embedded broker
public static void setUpBroker() throws Exception {
brokerSvc.setBrokerName("TestBroker");
brokerSvc.addConnector("tcp://localhost:61616");
brokerSvc.setPersistent(false);
brokerSvc.setUseJmx(false);
brokerSvc.start();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new CamelConfig().route();
}
// properties in .yml has to be loaded manually. Not sure of .properties file
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
PropertySource<?> applicationYamlPropertySource = loader.load(
"properties", new ClassPathResource("application.yml"),null);// null indicated common properties for all profiles.
Map source = ((MapPropertySource) applicationYamlPropertySource).getSource();
Properties properties = new Properties();
properties.putAll(source);
return properties;
} catch (IOException e) {
LOG.error("application.yml file cannot be found.");
}
return null;
}
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
MockitoAnnotations.initMocks(this);
jndi.bind("queueEventHandler", queueEventHandler);
return jndi;
}
#Test
// Sleeping for a few seconds is necessary, because this line template.sendBody runs in a different thread and
// CamelTest takes a few seconds to do the routing.
public void testRoute() throws InterruptedException {
template.sendBody("activemq:productpushevent", "HelloWorld!");
Thread.sleep(2000);
verify(queueEventHandler, times(1)).handleQueueEvent(any());
}
#AfterClass
public static void shutDownBroker() throws Exception {
brokerSvc.stop();
}
}
Did you try using Camel test runner?
#RunWith(CamelSpringJUnit4ClassRunner.class)
If you are using camel-spring-boot dependency, you may know that it uses auto configuration to setup Camel:
CamelAutoConfiguration.java
It means that you may also need to add #EnableAutoConfiguration to your test.