I've written a basic spring boot service that consumes some data via rest API and publishes it to rabbitmq and kafka.
To test the service class handling kafka producing, I followed this guide: https://www.baeldung.com/spring-boot-kafka-testing
In isolation, the test (KafkaMessagingServiceImplTest) works perfectly both in intellij idea and via mvn on the command line. Running all project tests in idea works fine. However, when I run all project tests via maven on the command line, this test fails with an NPE when trying to make the assertion on the payload String.
I've narrowed down the location of the root problem to another test class (AppPropertiesTest) which is solely testing my AppProperties component (which is a component I use to pull config from application.properties in a tidy way). When, and only when, the tests within that test class are run alongside the failing test using 'mvn clean install' in project root, does the NPE show up. Commenting out the tests in this class or annotating it with #DirtiesContext fixes the problem. Apparently something loaded into the spring context by this test class causes an issue with the timing/order of events/countdownlatch in the other test. Of course, I don't want to use #DirtiesContext as it can lead to a much slower build as the project increases in complexity. It also does not explain the problem.. and I can't handle that :)
AppPropertiesTest uses constructor injection to inject the AppProperties component. It also extends a abstract class 'GenericServiceTest' which is annotated by:
#SpringBootTest
#TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
and contains nothing else. As you probably know, the SpringBootTest annotation builds a test spring context and wires in boilerplate to allow effective testing of a spring app's dependency injection etc. and the TestConstructor annotation allows constructor injection in some of my tests. FWIW, I have tried removing the TestConstructor annotation and using plain old Autowiring in the AppProperties class to see if it makes a difference but it does not.
The failing test class also extends GenericServiceTest, as it requires the spring context to inject some of the dependencies such as the consumer and the messaging service being tested and AppProperties instance within etc.
So I know where the problem lies but I don't know what the problem is. Even when the test fails with the NPE, I can see in the logs that the consumer has successfully consumed the message before the failure, as per the Baeldung guide :
TestKafkaConsumer : received payload='ConsumerRecord(topic = test-kafka-topic, partition = 0, leaderEpoch = 0, offset = 0, CreateTime = 1618997289238, serialized key size = -1, serialized value size = 43, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = This is a test message to be sent to Kafka.)'
However, the payLoad is null when we get back to the assertion. I've tried all kinds of things like Thread.sleep() in the failing test to give it more time and I've increased the await() timeout but no joy.
I find it bizarre that the tests are fine in IDEA and in isolation. Now it's starting to drive me a little crazy and I can't debug it because the problem doesn't occur in my IDE.
If anyone has any ideas, it would be greatly appreciated!
Thanks.
EDIT: Someone very reasonably suggested that I add some code so here goes :)
The Failing Test (fails at assertTrue(payload.contains(testMessage)) because payLoad is null). The autowired kafkaMessagingService simply has the dependencies of AppProperties and KakfaTemplate injected and calls kafkaTemplate.send():
#EmbeddedKafka(partitions = 1, brokerProperties = { "listeners=PLAINTEXT://localhost:9092", "port=9092" })
class KafkaMessagingServiceImplTest extends GenericServiceTest {
#Autowired
#Qualifier("kafkaMessagingServiceImpl")
private IMessagingService messagingService;
#Autowired
private TestKafkaConsumer kafkaConsumer;
#Value("${app.topicName}")
private String testTopic;
#Test
public void testSendAndConsumeKafkaMessage() throws InterruptedException {
String testMessage = "This is a test message to be sent to Kafka.";
messagingService.sendMessage(testMessage);
kafkaConsumer.getLatch().await(2000, TimeUnit.MILLISECONDS);
String payload = kafkaConsumer.getPayload();
assertTrue(payload.contains(testMessage));
}
The TestConsumer (used to consume in the test above)
#Component
public class TestKafkaConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(TestKafkaConsumer.class);
private CountDownLatch latch = new CountDownLatch(1);
private String payload = null;
#KafkaListener(topics = "${app.topicName}")
public void receive(ConsumerRecord<?, ?> consumerRecord) {
LOGGER.info("received payload='{}'", consumerRecord.toString());
setPayload(consumerRecord.toString());
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
Project dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.5.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka-test -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<version>2.5.6.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
AppPropertiesTest class (the context of which seems to cause the problem)
class AppPropertiesTest extends GenericServiceTest {
private final AppProperties appProperties;
public AppPropertiesTest(AppProperties appProperties) {
this.appProperties = appProperties;
}
#Test
public void testAppPropertiesGetQueueName() {
String expected = "test-queue";
String result = appProperties.getRabbitMQQueueName();
assertEquals(expected, result);
}
#Test
public void testAppPropertiesGetDurableQueue() {
boolean isDurableQueue = appProperties.isDurableQueue();
assertTrue(isDurableQueue);
}
}
The AppProperties class that the AppPropertiesTest class is testing:
#Component
#ConfigurationProperties("app")
public class AppProperties {
// a whole bunch of properties by name that are prefixed by app. in the application.properties file. Nothing else
}
The Generic service test class which both tests extend.
#SpringBootTest
#TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public abstract class GenericServiceTest {
}
The failure (you can see on the line above the payload has been received and printed out).
2021-04-21 14:15:07.113 INFO 493384 --- [ntainer#0-0-C-1] service.TestKafkaConsumer : received payload='ConsumerRecord(topic = test-kafka-topic, partition = 0, leaderEpoch = 0, offset = 0, CreateTime = 1619010907076, serialized key size = -1, serialized value size = 43, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = This is a test message to be sent to Kafka.)'
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.791 s <<< FAILURE! - in
service.KafkaMessagingServiceImplTest
[ERROR] testSendAndConsumeKafkaMessage Time elapsed: 2.044 s <<< ERROR!
java.lang.NullPointerException
at service.KafkaMessagingServiceImplTest.testSendAndConsumeKafkaMessage(KafkaMessagingServiceImplTest.java:42)
The problem is that TestListener is a #Component so it is being added twice - the record is going to the other instance.
I added more debugging to verify the getter is called on a different instance.
#Component
public class TestKafkaConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(TestKafkaConsumer.class);
private final CountDownLatch latch = new CountDownLatch(1);
private String payload = null;
#KafkaListener(id = "myListener", topics = "${app.kafkaTopicName}")
public void receive(ConsumerRecord<?, ?> consumerRecord) {
LOGGER.info("received payload='{}'", consumerRecord.toString());
setPayload(consumerRecord.toString());
if (payload != null) {
LOGGER.info(this + ": payload is not null still");
}
latch.countDown();
if (payload != null) {
LOGGER.info(this + ": payload is not null after latch countdown");
}
}
public CountDownLatch getLatch() {
return latch;
}
public String getPayload() {
LOGGER.info(this + ": getting Payload");
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
}
If you don't want to use #DirtiesContext, you can at least stop the listener containers after the tests complete:
#SpringBootTest
#TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public abstract class GenericDataServiceTest {
#AfterAll
static void stopContainers(#Autowired KafkaListenerEndpointRegistry registry) {
registry.stop();
}
}
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Related
I cannot see the messages in the SQS queue being consumed by the #SqsListener
import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener; //others
#Component
public class Consumer{
private static final Logger logger = LoggerFactory.getLogger(Consumer.class);
#SqsListener(value = "TEST-MY-QUEUE")
public void receiveMessage(String stringJson) {
System.out.println("***Consuming message: " + stringJson);
logger.info("Consuming message: " + stringJson);
}
}
My configuration (Here I print the client queues, and I can deffo spot the queue I want to consume - TEST-MY-QUEUE . It prints the URL correctly in the region. I am also able to see the region loaded correctly (same as queue) in regionProvider
#Configuration
public class AwsConfiguration {
#Bean
#Primary
AmazonSQSAsync sqsClient() {
AmazonSQSAsync amazonSQSAsync = AmazonSQSAsyncClientBuilder.defaultClient();
System.out.println("Client queues = " + amazonSQSAsync.listQueues()); //The queue I want to consume is here
return amazonSQSAsync;
}
#Bean
AwsRegionProvider regionProvider() {
DefaultAwsRegionProviderChain defaultAwsRegionProviderChain = new DefaultAwsRegionProviderChain();
System.out.println("Region = " + defaultAwsRegionProviderChain.getRegion());
return defaultAwsRegionProviderChain;
}
#Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(AmazonSQSAsync amazonSQSAsync, QueueMessageHandler queueMessageHandler) {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
simpleMessageListenerContainer.setAmazonSqs(amazonSQSAsync);
simpleMessageListenerContainer.setMessageHandler(queueMessageHandler);
simpleMessageListenerContainer.setMaxNumberOfMessages(10);
simpleMessageListenerContainer.setTaskExecutor(threadPoolTaskExecutor());
return simpleMessageListenerContainer;
}
#Bean
public QueueMessageHandler queueMessageHandler(AmazonSQSAsync amazonSQSAsync) {
QueueMessageHandlerFactory queueMessageHandlerFactory = new QueueMessageHandlerFactory();
queueMessageHandlerFactory.setAmazonSqs(amazonSQSAsync);
QueueMessageHandler queueMessageHandler = queueMessageHandlerFactory.createQueueMessageHandler();
return queueMessageHandler;
}
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.initialize();
return executor;
}
And pom.xml (Java 11, spring boot, spring cloud aws)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-core</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws-messaging</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
I noticed very similar issues in the questions here and I changed my dependencies in pom.xml to be spring-cloud-starter-aws-messaging but didnt fix for me. I double checked the names (queue, annotation) all seems fine
When I run my app, starts fine but I dont see any logs or exception. Not one message consumed.
What am I missing?
Thank you
You are using a third party API. To use invoke Amazon Simple Queue Service (SQS) from a Java project, use the Official AWS SDK for Java V2. If you are not aware how to use this SDK, see this DEV Guide:
Developer guide - AWS SDK for Java 2.x
For AWS SQS specific information, see:
Working with Amazon Simple Queue Service
This has links to AWS Github where you will find POM dependencies, code, etc.
At the end it was an issue with the config (using the credentials)
In application.yml
credentials:
useDefaultAwsCredentialsChain: true #Will use credentials in /.aws
And then in the AWSConfig class where you create the AmazonSQSAsync, just make it use that config
public AmazonSQSAsync amazonSQSAsync() {
DefaultAWSCredentialsProviderChain defaultAWSCredentialsProviderChain = new DefaultAWSCredentialsProviderChain();
return AmazonSQSAsyncClientBuilder.standard().withRegion(region)
.withCredentials(defaultAWSCredentialsProviderChain)
.build();
#Override
public void contextDestroyed(ServletContextEvent sce) {
try{
DateUtil.clean();
}catch(Exception e){
LOGGER.error("MyServletContextListener contextDestroyed error: ", e);
}
I am doing unit testing on the above piece of code, and am trying to get 100% line coverage by hitting the Exception clause, however, i cant seem to make it work with my implementation. Would appreciate any help yall. Please look below for my implementation.
#Test (expected= Exception.class)
public void test_contextDestroyed_Exception() {
DateUtil wrapper = Mockito.spy(new DateUtil());
Exception e = mock(Exception.class);
when(wrapper).thenThrow(e);
Mockito.doThrow(e)
.when(myServletContextListener)
.contextDestroyed(sce);
myServletContextListener.contextDestroyed(sce);
}
Since DateUtil.clean() is a static method, you can't mock it with Mockito.
You need to use PowerMockito instead:
<properties>
<powermock.version>1.6.6</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Check official PowerMock documentation for version compatibility with JUnit and Mockito.
Once you did that, you should add the following to your test class:
#RunWith(PowerMockRunner.class) //<-- this says to JUnit to use power mock runner
#PrepareForTest(DateUtil.class) //<-- this prepares the static class for mock
public class YourTestClass {
#Test(expected = Exception.class)
public void test_contextDestroyed_Exception() {
PowerMockito.mockStatic(DateUtil.class);
when(DateUtil.clean()).thenThrow(new Exception("whatever you want"));
//prepare your test
//run your test:
myServletContextListener.contextDestroyed(sce);
}
}
you can use mockito-inline artifact from Mockito to test static methods and follow the below approach,
try (MockedStatic<DateUtil> utilities = Mockito.mockStatic(DateUtil.class)) {
utilities.when(DateUtil::clean).thenThrow(Exception.class);
// assert exception here
}
for more info,
https://frontbackend.com/java/how-to-mock-static-methods-with-mockito
I want to run some integration test using Arquillian, Arquillian cube and Mongo. The desired scenario is:
Start the application in a managed container. Here I want to use Shrinkwrap to add just the service I want to test (for example dao service)
Start the database inside a docker container. Populate the db with some initial data
Run the test against the database
My test looks like this:
#Inject
MongoProducer producer;
#Test
#UsingDataSet(locations = "initialData.json")
public void shouldGetAllFromMongo() {
FindIterable<Document> documents = producer.getMongoClient().getDatabase("bearsdb").getCollection("bears").find();
documents.forEach((Block<? super Document>) e-> System.out.println(e));
}
The initialData.json is under src/test/resources and format of the seeding data is as bellow:
{
"bears": [
{
"firstName": "grizz",
"lastName": "the bear",
"age": 3
},
{
"firstName": "panpan",
"lastName": "the bear",
"age": 3
},
{
"firstName": "icebear",
"lastName": "the bear",
"age": 4
}
]}
My docker-compose file looks like this:
version: '3'
services:
mongo-test-db:
image: mongo:latest
environment:
- MONGO-INITDB-DATABASE=bearsdb
- MONGO-INITDB_ROOT_USERNAME=panda
- MONGO-INITDB_ROOT_PASSWORD=pass
ports:
- 27117:27017
I don't really know if the environments help but I saw this in an example.
My pom.xml contains:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-bom</artifactId>
<version>${version.shrinkwrap.resolvers}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.15.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.arquillian</groupId>
<artifactId>arquillian-universe</artifactId>
<version>${version.arquillian_universe}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
and as dependencies
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.arquillian.cube</groupId>
<artifactId>arquillian-cube-docker</artifactId>
<version>${org.arquillian.cube.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.arquillian.universe</groupId>
<artifactId>arquillian-ape-sql-container-dbunit</artifactId>
<scope>test</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.arquillian.universe</groupId>
<artifactId>arquillian-ape-nosql-mongodb</artifactId>
<scope>test</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.3</version>
</dependency>
Please note that I don't use the arquillian-junit-standalone dependency.
Additional note is that I'm using ShwripWrack to package and I deploy the war into an managed Wildfly 8.2.0.Final server. Additionl in the same test class I've tested also against an Postgres running inside docker and for this the #UsingDataSet works ok. Bellow is the working sql test and the createDeploy method:
#Deployment
public static WebArchive createDeployment() {
JavaArchive[] javaArchives = Maven.resolver().resolve(
"org.assertj:assertj-core:3.15.0",
"org.arquillian.cube:arquillian-cube-docker:1.18.2",
"org.mongodb:mongo-java-driver:3.4.3")
.withTransitivity().as(JavaArchive.class);
WebArchive war = ShrinkWrap.create(WebArchive.class, "app.war")
.addClasses(PersonDao.class, Person.class)
.addClasses(MongoProducer.class, PropertyProducer.class, Property.class)
.addAsLibraries(javaArchives)
.addAsResource("test-persistence.xml", ArchivePaths.create("META-INF/persistence.xml"))
.addAsResource("META-INF/application.properties", ArchivePaths.create("META-INF/application.properties"))
.addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
System.out.println(war.toString(true));
return war;
}
#Test
#org.arquillian.ape.rdbms.UsingDataSet("datasets/persons.xml")
public void shouldFindAll() {
List<Person> messages = personDao.findAll();
assertThat(messages.size()).isEqualTo(1);
}
The issue with the above test is that the database doesn't get initialize and nothing is printed out.
I managed to resolve my problem. The issue is that I forgot to add an Junit rule where the configuration to the Mongo database was set.
#Rule
public MongoDbRule mongoDbRule = new MongoDbRule(MongoDbConfigurationBuilder.mongoDb()
.host("localhost")
.port(27117)
.databaseName("pandadb")
.build());
The full test class looks like:
#RunWith(Arquillian.class)
public class PersonDaoDockerIT {
#Rule
public MongoDbRule mongoDbRule = new MongoDbRule(MongoDbConfigurationBuilder.mongoDb()
.host("localhost")
.port(27117)
.databaseName("pandadb")
.build());
#Deployment
public static WebArchive createDeployment() {
JavaArchive[] javaArchives = Maven.resolver().resolve(
"org.assertj:assertj-core:3.15.0",
"org.arquillian.cube:arquillian-cube-docker:1.18.2",
"org.mongodb:mongo-java-driver:3.4.3")
.withTransitivity().as(JavaArchive.class);
WebArchive war = ShrinkWrap.create(WebArchive.class, "app.war")
.addClasses(PersonDao.class, Person.class)
.addClasses(MongoProducer.class, PropertyProducer.class, Property.class)
.addPackages(true, "com.lordofthejars.nosqlunit")
.addAsLibraries(javaArchives)
.addAsResource("test-persistence.xml", ArchivePaths.create("META-INF/persistence.xml"))
.addAsResource("META-INF/application.properties", ArchivePaths.create("META-INF/application.properties"))
.addAsResource("datasets/", ArchivePaths.create("datasets/"))
.addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
System.out.println(war.toString(true));
return war;
}
#Inject
PersonDao personDao;
#Inject
MongoProducer producer;
#Test
public void injectionPointShouldBeNotNull() {
assertThat(personDao).isNotNull();
}
#Test
public void mongoProducerShouldBeNotNull() {
assertThat(producer).isNotNull();
}
#Test
#org.arquillian.ape.rdbms.UsingDataSet("datasets/persons.xml")
public void shouldFindAll() {
List<Person> messages = personDao.findAll();
assertThat(messages.size()).isEqualTo(1);
}
#Test
#UsingDataSet(locations = "/datasets/initialData.json")
public void shouldGetAllFromMongo() {
FindIterable<Document> documents = producer.getMongoClient().getDatabase("pandadb").getCollection("bears").find();
documents.forEach((Block<? super Document>) System.out::println);
}
}
When using the springcloud bus, a custom message is created and sent through rabbitmq, but after the message is sent, it does not go to rabbitmq. When you try to call /actuator/bus-refresh, you can see the bus messages emitted from the console page of rabbitmq.
I tried to start a micro service to register a custom event listener but failed to receive it. However, if the sender registers a listener himself, he can receive it but does not send it from rabbitmq.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
About #RemoteApplicationEventScan annotations, I code all under the same package, so I should be able to scan to TestEvent. I also tried to specify basepackage.
#SpringBootApplication
#RemoteApplicationEventScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
#RequestMapping("test")
public class TestController {
#Autowired
private ApplicationContext context;
#RequestMapping("test")
public String test() {
final TestEvent testEvent = new TestEvent(this, context.getId(), null,"test");
context.publishEvent(testEvent);
return "success";
}
}
#Data
public class TestEvent extends RemoteApplicationEvent {
private String action;
public TestEvent(Object source, String originService, String destinationService, String action) {
super(source, originService, destinationService);
this.action = action;
}
}
When I called http://localhost:8080/actuator/bus-refresh I can see the information in the rabbitmq.
{" type ":" AckRemoteApplicationEvent ", "timestamp" : 1554350325406, "originService" : "application: 0: b3461fbec3536203a7020ff9d24bb11b", "destinationService" : "* *", "id" : "e6b875bd - 2402-494 - f - a870 - 4917324 d2c5c ackId ", "" :" af93075e - 55 d2-41 f8 - ba27 - e3c80cf19eea ", "ackDestinationService" : "* *", "the event" is: "org. Springframework. Cloud. Bus. Event. RefreshRemoteApplicationEvent"}.
But when I call to http://localhost:8080/test/test, I don't.
I came into the same issue a few days ago and it turned out that it was because the originService was incorrect. passing in context.getId() as originService doesn't work.
Short answer: use org.springframework.cloud.bus.BusProperties#id. You can inject BusProperties to your component. Or you can configure your own spring cloud bus id as stated in the document.
I am not 100% sure this is the proper way. Maybe I missed something in the document. It is just based on what I read from the source code of org.springframework.cloud.bus.BusAutoConfiguration, method acceptLocal.
Hope it works for you.
I'm working on a project with GWT and J2SE clients. The GWT part is working great, but now there are problems with the J2SE client;
"The server understands the content type of the request entity and the
syntax of the request entity is correct but was unable to process the
contained instructions"
"The serialized representation must have this media type:
application/x-java-serialized-object or this one:
application/x-java-serialized-object+xml"
This code was working some months/versions ago... Both PUT and POST produce this error while GET is working. Whats wrong here?
Here's a really simple test case
// Shared Interface
public interface J2SeClientServerResourceInt
{
#Post("json")
public J2seStatusDto postJ2seStatus(J2seStatusDto pJ2seStatusDto);
}
// Java Bean
public class J2seStatusDto implements Serializable
{
private static final long serialVersionUID = 6901448809350740172L;
private String mTest;
public J2seStatusDto()
{
}
public J2seStatusDto(String pTest)
{
setTest(pTest);
}
public String getTest()
{
return mTest;
}
public void setTest(String pTest)
{
mTest = pTest;
}
}
// Server
public class J2seServerResource extends ClaireServerResource implements J2SeServerResourceInt
{
#Override
public J2seStatusDto postJ2seStatusDto(J2seStatusDto pJ2seStatusDto)
{
return pJ2seStatusDto;
}
}
// J2SE Client
public class ClaireJsSeTestClient
{
public static void main(String[] args)
{
Reference lReference = new Reference("http://localhost:8888//rest/j2se");
ClientResource lClientResource = new ClientResource(lReference);
lClientResource.accept(MediaType.APPLICATION_JSON);
J2SeServerResourceInt lJ2SeServerResource = lClientResource.wrap(J2SeServerResourceInt.class);
J2seStatusDto lJ2seStatusDto = new J2seStatusDto("TEST");
J2seStatusDto lJ2seResultDto = lJ2SeServerResource.postJ2seStatusDto(lJ2seStatusDto);
}
}
// Maven J2Se Client
<dependencies>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet</artifactId>
<version>3.0-M1</version>
</dependency>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet.ext.jackson</artifactId>
<version>3.0-M1</version>
</dependency>
</dependencies>
// Maven GAE Server
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet</artifactId>
<version>3.0-M1</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.servlet</artifactId>
<version>3.0-M1</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.jackson</artifactId>
<version>3.0-M1</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.gwt</artifactId>
<version>3.0-M1</version>
</dependency>
<dependency>
<groupId>org.restlet.gwt</groupId>
<artifactId>org.restlet</artifactId>
<version>3.0-M1</version>
<scope>compile</scope>
</dependency>
Thierry Boileau fixed our problem (mistake);
https://github.com/restlet/restlet-framework-java/issues/1029#issuecomment-76212062
Due to the constraints of the GAE platform (there is no support of chunked encoding) you have to specify that the request entity is buffered first;
cr.setRequestEntityBuffering(true);
Thanks for the great support at restlet.com