dynamically change consumers - rabbitMQ - spring-rabbit

I am trying to dynamically change consumers.but at one point no.of consumers are going beyond the max - consumers.
the code is:
public class RabbitMQConfig extends AMQPConfig implements RabbitListenerConfigurer{
#Autowired
RabbitListenerEndpointRegistry endpoint;
static int temp=0;
SimpleMessageListenerContainer container;
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
SimpleRabbitListenerContainerFactory factory;
#Override
public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar){
registrar.setContainerFactory(rabbitListenerContainerFactory());
registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
}
public AmqpTemplate getAmqpTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter ((MessageConverter)consumerJackson2MessageConverter());
return rabbitTemplate;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory()
{
factory=new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(this.connectionFactory);
factory.setConcurrentConsumers(15);
factory.setMaxConcurrentConsumers(20);
factory.setPrefetchCount(10);
return factory;
}
public void throttleConsumers(){
Collection<MessageListenerContainer> containers= endpoint.getListenerContainers();
SimpleMessageListenerContainer eachSimpleMessageListenerContainer=null;
for(MessageListenerContainer eachContainer : containers)
{
if(eachContainer.getClass().getName().equalsIgnoreCase("org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer")){
eachSimpleMessageListenerContainer =(SimpleMessageListenerContainer)eachContainer;
eachSimpleMessageListenerContainer.setConcurrentConsumers(12);
}
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(this.connectionFactory);
}
}
The consumer is :
#Component
public class Consumer {
#Autowired
private AmqpTemplate rabbitTemplate;
#Value("${rabbitmq.queue}")
String queueName;
static long messageCount=0L;
#Autowired
RabbitMQConfig config;
#RabbitListener(queues="rabbitmq.queue")
public void receivedMessage(String message) throws IOException {
messageCount++;
System.out.println(message);
if(messageCount>10000 && messageCount<15000 )
{
System.out.println("consumed: "+messageCount);
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
System.out.println("error sleeping:"+e.getMessage());
}
config.throttleConsumers();
rabbitTemplate.convertAndSend(queueName,message);
RestTemplate restTemplate = new RestTemplate();
String quote = restTemplate.getForObject("http://localhost:8080/rabbitmq-consumer/throttle", String.class);
System.out.println(quote);
}
}
}
presently my min consumers are 15 and max are 20 and I am throttling to 12 once the response time of another program is greater than 800. while we are testing number of consumers are going upto 29 which we are unable understand.
Kindly help us to understand where we are going wrong.

I just tested it with similar code and saw no problems if I change the min before I reached the max...
#SpringBootApplication
public class So50014436Application {
public static void main(String[] args) {
SpringApplication.run(So50014436Application.class, args).close();
}
#Bean
public ApplicationRunner runner(RabbitTemplate template, AmqpAdmin admin,
RabbitListenerEndpointRegistry registry) {
return args -> {
for (int i = 0; i < 1000; i++) {
template.convertAndSend("so50014436", "foo");
}
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(() -> {
while (true) {
System.out.println(admin.getQueueProperties("so50014436"));
try {
Thread.sleep(5_000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
Thread.sleep(30_000);
System.out.println("Changing min to 12");
registry.getListenerContainers().forEach(c -> {
((SimpleMessageListenerContainer) c).setConcurrentConsumers(12);
});
Thread.sleep(60_000);
exec.shutdownNow();
};
}
#RabbitListener(queues = "so50014436")
public void listen(String in) throws Exception {
Thread.sleep(1_000);
}
#Bean
public Queue queue() {
return new Queue("so50014436");
}
}
and
spring.rabbitmq.listener.simple.concurrency=10
spring.rabbitmq.listener.simple.max-concurrency=15
and
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=832, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=978, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=933, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=11, QUEUE_MESSAGE_COUNT=883, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=11, QUEUE_MESSAGE_COUNT=830, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=12, QUEUE_MESSAGE_COUNT=774, QUEUE_NAME=so50014436}
Changing min to 12
{QUEUE_CONSUMER_COUNT=14, QUEUE_MESSAGE_COUNT=712, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=14, QUEUE_MESSAGE_COUNT=642, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=570, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=495, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=420, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=345, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=270, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=195, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=120, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=45, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
However, if I wait until we've reached the max, I do see a problem...
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=695, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=940, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=10, QUEUE_MESSAGE_COUNT=890, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=11, QUEUE_MESSAGE_COUNT=847, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=11, QUEUE_MESSAGE_COUNT=792, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=12, QUEUE_MESSAGE_COUNT=736, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=12, QUEUE_MESSAGE_COUNT=676, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=12, QUEUE_MESSAGE_COUNT=616, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=13, QUEUE_MESSAGE_COUNT=552, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=13, QUEUE_MESSAGE_COUNT=487, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=14, QUEUE_MESSAGE_COUNT=420, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=14, QUEUE_MESSAGE_COUNT=350, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=14, QUEUE_MESSAGE_COUNT=280, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=15, QUEUE_MESSAGE_COUNT=205, QUEUE_NAME=so50014436}
Changing min to 12
{QUEUE_CONSUMER_COUNT=17, QUEUE_MESSAGE_COUNT=128, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=17, QUEUE_MESSAGE_COUNT=43, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=17, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=17, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
{QUEUE_CONSUMER_COUNT=16, QUEUE_MESSAGE_COUNT=0, QUEUE_NAME=so50014436}
...where we go above the max. It's not clear why you got 29, though.
I have openened a JIRA Issue for this.
That said, it's not clear why you are changing the min to 12 when you are already at the max (15).

Related

Repository returning null when autowiring in Kinesis KCL Consumer

I'm currently working with AWS Kinesis using the KCL Library. I can consume the records and print them with println. But when I try to call a repository class, it returns null (I'm autowiring it).
RecordProcessor
public class ScoreRecordProcessor implements ShardRecordProcessor {
private String shardId;
#Autowired
private ConstatacoesRepository repo;
#Override
public void initialize(InitializationInput initializationInput) {
shardId = initializationInput.shardId();
System.out.println(String.format("Inicializando leitura na shard %s # sequence: %s", shardId,
initializationInput.extendedSequenceNumber().toString()));
}
#Override
public void processRecords(ProcessRecordsInput processRecordsInput) {
ObjectMapper mapper = new ObjectMapper();
for (KinesisClientRecord record : processRecordsInput.records()) {
byte[] byteArr = new byte[record.data().remaining()];
record.data().get(byteArr);
System.out.println("Constatacao recebida -> " + new String(byteArr));
try {
ResponseScoreDTO score = mapper.readValue(new String(byteArr), ResponseScoreDTO.class);
for(Constatacao constatacao : score.getConstatacao()) {
Constatacoes entidadeBanco = new Constatacoes();
entidadeBanco.setArea(constatacao.getArea());
entidadeBanco.setConstatacaoNotaFiscal(constatacao.getConstatacao());
entidadeBanco.setCriticidade(constatacao.getCriticidade());
entidadeBanco.setEfetivaEscrituracao(constatacao.getEfetivaEscReg());
entidadeBanco.setEscopo(constatacao.getEscopo());
entidadeBanco.setIdSolicitacaoNotaFiscal(BigInteger.valueOf(Long.valueOf(score.getIdTransacao())));
entidadeBanco.setTxtConstatacao(null);
repo.save(entidadeBanco);
System.out.println("Entidade salva com sucesso.");
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
#Override
public void leaseLost(LeaseLostInput leaseLostInput) {
}
#Override
public void shardEnded(ShardEndedInput shardEndedInput) {
System.out.println(String.format("Shard %s chegou ao fim.", shardId));
}
#Override
public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
}
}
RecordProcessorFactory
public class ScoreRecordProcessorFactory implements ShardRecordProcessorFactory {
#Override
public ShardRecordProcessor shardRecordProcessor() {
return new ScoreRecordProcessor();
}
}
Repository
#Repository
public interface ConstatacoesRepository extends JpaRepository<Constatacoes,BigInteger>{
}
Print of the console

How can i use #autowire in runnable spring boot

I have few MongoTemplate and Repos and i need to call them using #Autowire in my runnable class that is being executed by exceutor class using multi threading, now the problem is that when i run the application my AutoWire for mongoTempelate and Repos returns null pointer exception.
Executor class:
#Component
public class MessageConsumer implements ConsumerSeekAware {
#Autowired
AlarmDataRepository alarmDataRepository;
int assignableCores = ((Runtime.getRuntime().availableProcessors()));
ExecutorService executor = Executors.newFixedThreadPool(
assignableCores > 1 ? assignableCores : 1
);
int counter = 0;
List<String> uniqueRecords = new ArrayList<String>();
#KafkaListener(topics = "teltonikaTest", groupId = "xyz")
public void processMessages(#Payload List<String> payload, #Header(KafkaHeaders.RECEIVED_PARTITION_ID) List<Integer> partitions, #Header(KafkaHeaders.OFFSET) List<Long> offsets) throws UnsupportedEncodingException, DecodeException {
System.out.println("assignable resources are: " + assignableCores);
log.info("Batch Size is: {}", payload.size());
if(counter==0){
log.info("Teletonica Packets Received!");
}
for (int i = 0; i < payload.size(); i++) {
log.info("processing message='{}' with partition off-set='{}'", payload.get(i), partitions.get(i) + " _" + offsets.get(i));
}
uniqueRecords = payload.stream().distinct().collect(Collectors.toList());
Runnable worker = new TeltonikaWorkerThread(uniqueRecords);
executor.execute(worker);
counter++;
}
}
public class TeltonikaWorkerThread implements Runnable{
List<String> records;
List<CurrentDevice> currentDevices = new ArrayList<>();
#Autowired
CurrentDeviceRepository currentDeviceRepository;
#Autowired
MongoTemplate mongoTemplate;
public TeltonikaWorkerThread(List<String> records) {
this.records = records;
}
public void run() {
try {
processMessage();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (DecodeException e) {
e.printStackTrace();
}
}
public void processMessage() throws UnsupportedEncodingException,DecodeException {
for(Object record : records){
if(record!="0"){
try{
int IMEILength = record.toString().indexOf("FF");
String IMEI = record.toString().substring(0,IMEILength);
}
catch (Exception e){
e.printStackTrace();
}
}
}
}
}
If I understand correctly, your problem is about multiple beans and Spring doesn't know which one should be injected. There are several options here.
For example, you can use #Qualifier annotation based on the bean name or #Primary annotation.
If your problem is something else, please add an example to your question.

How to make Hibernate use setFixedCHAR instead of setString

Can I somehow modify the way Hibernate binds parameters to the query?
For example, I want hibernate to use OracleResultSet.setFixedChar() when executing on an string column, instead of rs.setString() when executing a JPA query via Spring data.
This is how I would do it without Hibernate:
try(PreparedStatement ps = con.executeQuery("...")) {
if(ps.isWrapped(OraclePreparedStatement.class) {
ps.unwrap(OraclePreparedStatement.class).setFixedCHAR(0, myStringField);
} else {
ps.setString(0, myStringField);
}
try(ResultSet rs = ps.getResultSet()) {
while(rs.next()) {
... do stuff ...
}
}
}
Repository method (Spring data JPA):
List<Object> findByMyStringField(String myStringField);
How can I influence how Hibernate binds my variable. With the above example setString is used always.
As background: the problem is that all our Legacy DB's use CHAR columns and not VARCHAR2, so we have to deal with whitespace and setFixedCHAR should do exactly what we would want.
Found a solution by implementing a SqlTypeDescriptor & Custom Dialect:
#Autowired
private DataSource source;
#Bean
public HibernateJpaVendorAdapter getHibernateJPAVendorAdapter() {
return new CustomHibernateJpaVendorAdaptor();
}
private static class CustomHibernateJpaVendorAdaptor extends HibernateJpaVendorAdapter {
#Override
protected Class<?> determineDatabaseDialectClass(Database database) {
// if HSQL is copied from Spring Sourcecode to keep everything the same
if (Database.HSQL.equals(database)) {
return CustomHsqlDialect.class;
}
try {
if (source.isWrapperFor(OracleDataSource.class)) {
return CustomOracleDialect.class;
}
} catch (SQLException e) {
}
return super.determineDatabaseDialectClass(database);
}
private class CustomHsqlDialect extends HSQLDialect {
public CustomHsqlDialect() {
registerColumnType(Types.BOOLEAN, "boolean");
registerHibernateType(Types.BOOLEAN, "boolean");
}
}
}
#NoArgsConstructor
public static class CustomOracleDialect extends Oracle12cDialect {
private static final OracleCharFix INSTANCE = new OracleCharFix();
#Override
protected SqlTypeDescriptor getSqlTypeDescriptorOverride(final int sqlCode) {
switch (sqlCode) {
case Types.VARCHAR:
return INSTANCE;
default:
return super.getSqlTypeDescriptorOverride(sqlCode);
}
}
}
#Slf4j
private static class OracleCharFix extends CharTypeDescriptor {
#Override
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<>(javaTypeDescriptor, this) {
#Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
if (st.isWrapperFor(OraclePreparedStatement.class)) {
OraclePreparedStatement ops = st.unwrap(OraclePreparedStatement.class);
if (ops.getParameterMetaData().getParameterType(index) == Types.CHAR) {
ops.setFixedCHAR(index, javaTypeDescriptor.unwrap(value, String.class, options));
} else {
st.setString(index, javaTypeDescriptor.unwrap(value, String.class, options));
}
} else {
st.setString(index, javaTypeDescriptor.unwrap(value, String.class, options));
}
}
#Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
throws SQLException {
//Is nolonger used by Hibernate in the current Version
st.setString(name, javaTypeDescriptor.unwrap(value, String.class, options));
}
private boolean checkIfCHARByName(ResultSetMetaData metadata, String name)
throws SQLException {
for (int i = 1; i <= metadata.getColumnCount(); i++) {
if (metadata.getColumnType(i) == Types.CHAR && Objects.equals(metadata.getColumnName(i), name)) {
return true;
}
}
return false;
}
};
}

validate raw message against schema for method annotated with jmslistener

I have a need to apply some pre-checks and common steps on the all the jms listeners like validating the raw message against a schema (JSON schema). Example -
#Component
public class MyService {
#JmsListener(destination = "myDestination")
public void processOrder(Order order) { ... }
}
Now, before the spring converts the Message from the queue to Order, I need to do the following -
log the original message with headers into a custom logger.
validate the json message (text message) against a json schema (lets assume I have only one schema here for the simplicity)
If schema validation fail, log the error and throw exception
If schema validation passes, continue to control to spring to do the conversion and continue with the process order method.
Does the spring JMS architecture provides any way to inject the above need?
I know AOP crosses the mind, but I am not sure will it work with #JmsListener.
A rather simple technique would be to set autoStartup to false on the listener container factory.
Then, use the JmsListenerEndpointRegistry bean to get the listener container.
Then getMessageListener(), wrap it in an AOP proxy and setMessageListener().
Then start the container.
There might be a more elegant way, but I think you'd have to get into the guts of the listener creation code, which is quite involved.
EDIT
Example with Spring Boot:
#SpringBootApplication
public class So49682934Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So49682934Application.class, args);
}
#JmsListener(id = "listener1", destination = "so49682934")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsListenerEndpointRegistry registry, JmsTemplate template) {
return args -> {
DefaultMessageListenerContainer container =
(DefaultMessageListenerContainer) registry.getListenerContainer("listener1");
Object listener = container.getMessageListener();
ProxyFactory pf = new ProxyFactory(listener);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
container.setMessageListener(pf.getProxy());
registry.start();
Thread.sleep(5_000);
Foo foo = new Foo("baz");
template.convertAndSend("so49682934", foo);
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
and
spring.jms.listener.auto-startup=false
and
m2018-04-06 11:42:04.859 INFO 59745 --- [enerContainer-1] e.So49682934Application$MyJmsInterceptor : ActiveMQTextMessage {commandId = 5, responseRequired = true, messageId = ID:gollum.local-60138-1523029319662-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:gollum.local-60138-1523029319662-4:2:1:1, destination = queue://so49682934, transactionId = null, expiration = 0, timestamp = 1523029324849, arrival = 0, brokerInTime = 1523029324849, brokerOutTime = 1523029324853, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1050, properties = {typeId=com.example.So49682934Application$Foo}, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = {"bar":"baz"}}
2018-04-06 11:42:04.882 INFO 59745 --- [enerContainer-1] ication$$EnhancerBySpringCGLIB$$e29327b8 : Foo [bar=baz]
EDIT2
Here's how to do it via infrastructure...
#SpringBootApplication
#EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
#JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
MessagingMessageListenerAdapter listener = super.createMessageListener(container);
ProxyFactory pf = new ProxyFactory(listener);
pf.setProxyTargetClass(true);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
return (MessagingMessageListenerAdapter) pf.getProxy();
}
};
}
};
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
Note: the BPP must be static and #EnableJms is required since the presence of this BPP disables boot's.
2018-04-06 13:44:41.607 INFO 82669 --- [enerContainer-1] .So496829341Application$MyJmsInterceptor : ActiveMQTextMessage {commandId = 5, responseRequired = true, messageId = ID:gollum.local-63685-1523036676402-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:gollum.local-63685-1523036676402-4:2:1:1, destination = queue://so496829341, transactionId = null, expiration = 0, timestamp = 1523036681598, arrival = 0, brokerInTime = 1523036681598, brokerOutTime = 1523036681602, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1050, properties = {typeId=com.example.So496829341Application$Foo}, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = {"bar":"baz"}}
2018-04-06 13:44:41.634 INFO 82669 --- [enerContainer-1] ication$$EnhancerBySpringCGLIB$$9ff4b13f : Foo [bar=baz]
EDIT3
Avoiding AOP...
#SpringBootApplication
#EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
#JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
return new MessagingMessageListenerAdapter() {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
EDIT4
To access other annotations on the listener method, it can be done, but reflection is needed to get a reference to the Method...
#JmsListener(id = "listen1", destination="so496829341")
#Schema("foo.bar")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface Schema {
String value();
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
InvocableHandlerMethod handlerMethod =
(InvocableHandlerMethod) new DirectFieldAccessor(listener)
.getPropertyValue("handlerMethod");
final Schema schema = AnnotationUtils.getAnnotation(handlerMethod.getMethod(), Schema.class);
return new MessagingMessageListenerAdapter() {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
logger.info(schema.value());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}

Spring 4.0.5 websockt integration apollo with the exception "Message broker is not active"

For a project,i want to switch the project from simple broker to full-feature broker(like rabiitmq, apollo).
the exception stack:
Caused by: org.springframework.messaging.MessageDeliveryException: Message broker is not active.
at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler.handleMessageInternal(StompBrokerRelayMessageHandler.java:392)
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.handleMessage(AbstractBrokerMessageHandler.java:177)
at org.springframework.messaging.support.ExecutorSubscribableChannel.sendInternal(ExecutorSubscribableChannel.java:64)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:116)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:98)
at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:125)
at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:48)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:94)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:144)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:112)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:107)
at cn.clickmed.cmcp.websocket.plain.util.WebSocketUtil.sendMessage(WebSocketUtil.java:61)
at cn.clickmed.cmcp.websocket.plain.util.WebSocketUtil.sendMessage(WebSocketUtil.java:65)
at cn.clickmed.cmcp.websocket.plain.entity.WebSocketEntity.postPersist(WebSocketEntity.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
here is the config code:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
for(String mapping : WebSocketConstant.WEBSOCKETTYPE.keySet()) {
registry.addEndpoint(mapping).withSockJS();
}
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/websocket");
// registry.enableSimpleBroker("/topic");
registry.enableStompBrokerRelay("/topic","/queue").setRelayHost("localhost").setRelayPort(61613).setSystemHeartbeatReceiveInterval(2000).setSystemHeartbeatSendInterval(2000);
// registry.setUserDestinationPrefix("/topic/");
}
#Bean
public Broker broker() throws Exception {
final Broker broker = new Broker();
// Configure STOMP over WebSockects connector
final AcceptingConnectorDTO ws = new AcceptingConnectorDTO();
ws.id = "ws";
ws.bind = "ws://localhost:61613";
ws.protocols.add( new StompDTO() );
// Create a topic with name 'test'
final TopicDTO topic = new TopicDTO();
topic.id = "todoListener";
// Create virtual host (based on localhost)
final VirtualHostDTO host = new VirtualHostDTO();
host.id = "localhost";
host.topics.add( topic );
host.host_names.add( "localhost" );
host.host_names.add( "127.0.0.1" );
host.auto_create_destinations = false;
// Create a web admin UI (REST) accessible at: http://localhost:61680/api/index.html#!/
final WebAdminDTO webadmin = new WebAdminDTO();
webadmin.bind = "http://localhost:61680";
// Create JMX instrumentation
final JmxDTO jmxService = new JmxDTO();
jmxService.enabled = true;
// Finally, glue all together inside broker configuration
final BrokerDTO config = new BrokerDTO();
config.connectors.add( ws );
config.virtual_hosts.add( host );
config.web_admins.add( webadmin );
config.services.add( jmxService );
broker.setConfig( config );
broker.setTmp( new File( System.getProperty( "java.io.tmpdir" ) ) );
broker.start( new Runnable() {
#Override
public void run() {
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:启动了");
}
} );
return broker;
}
}
send message code:
public class WebSocketUtil implements ILog{
public static Map<String, List<WebSocketSession>> webSocketSessionMap = new HashMap<String, List<WebSocketSession>>();
public static SimpMessagingTemplate simpMessagingTemplate = null;
public static void doSendMessage(String webSocketType) throws IOException {
if(WebSocketUtil.webSocketSessionMap.get(webSocketType) != null) {
String msg = "";
if(WebSocketConstant.WEBSOCKETTYPE_TODOLIST.equals(webSocketType)) {
msg = "change";
}
for(WebSocketSession webSocketSession :WebSocketUtil.webSocketSessionMap.get(webSocketType)) {
webSocketSession.sendMessage(new TextMessage(msg));
}
}
}
public static String getWebSocketType(String url) {
int length = url.indexOf("/cmcp/");
String theUrl = url.substring(length+5);
if(theUrl.startsWith(WebSocketConstant.COMPATIBLEMAPPING)) {
String [] arr = theUrl.split("/");
theUrl = "/"+arr[2];
}
if(!WebSocketConstant.WEBSOCKETTYPE.containsKey(theUrl)) {
logger.error("please config the websocketConstant !!!!");
throw new RuntimeException();
}
String theType = WebSocketConstant.WEBSOCKETTYPE.get(theUrl);
return theType;
}
public synchronized static void initSimpMessagingTemplate() {
if(simpMessagingTemplate == null) {
simpMessagingTemplate = SpringUtil.getBeanByName("brokerMessagingTemplate");
}
}
public static void sendMessage(String topic, String message) {
if(simpMessagingTemplate == null) {
initSimpMessagingTemplate();
}
simpMessagingTemplate.convertAndSend("/topic"+topic, message);
}
public static void sendMessage(String topic) {
sendMessage(topic,"change");
}
}
this is the trigger entity:
#MappedSuperclass
public class WebSocketEntity extends CommonEntity {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 1L;
#PostPersist
public void postPersist() throws IOException{
if(this instanceof TodoEntity) {
WebSocketUtil.sendMessage(WebSocketConstant.WEBSOCKETTYPE_TODOLIST_MAPPING);
}
}
#PostRemove
public void postRemove() throws IOException{
if(this instanceof TodoEntity) {
WebSocketUtil.sendMessage(WebSocketConstant.WEBSOCKETTYPE_TODOLIST_MAPPING);
}
}
}
please give me help! thanks!

Resources