How to pass request-id in quarkus through activeMQ? - activemq

My application is generating a request and then sending that request to a listener(activemq here) for further processing, I am trying to add a request-id to the logs for better tracing of errors but the request-id is not passing to the listener it works fine before that, i have tried the approach mentioned in this answer https://stackoverflow.com/a/70892802/19343662
These are the files i have created...
RequestIdFilter:
package com.cashfree.common.dexterreport.filters;
import io.vertx.core.http.HttpServerRequest;
import java.util.UUID;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import org.slf4j.MDC;
#Provider
public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Context
HttpServerRequest request;
private static final String REQUEST_ID_HEADER_NAME = "X-Request-Id";
private static final String REQUEST_ID = "requestId";
#Override
public void filter(ContainerRequestContext context) {
MDC.put(REQUEST_ID, getRequestIdFromHeader(request));
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
MDC.remove(REQUEST_ID);
}
private String getRequestIdFromHeader(final HttpServerRequest request) {
String requestId = request.getHeader(REQUEST_ID_HEADER_NAME);
if (requestId == null || requestId.length() == 0) {
requestId = generateUniqueRequestId();
}
return requestId;
}
private String generateUniqueRequestId() {
return UUID.randomUUID().toString();
}
}
MdcContextProvider
import java.util.Map;
import org.eclipse.microprofile.context.spi.ThreadContextProvider;
import org.eclipse.microprofile.context.spi.ThreadContextSnapshot;
import org.slf4j.MDC;
public class MdcContextProvider implements ThreadContextProvider {
#Override
public ThreadContextSnapshot currentContext(Map<String, String> props) {
Map<String, String> propagate = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.setContextMap(propagate);
return () -> {
MDC.setContextMap(old);
};
};
}
#Override
public ThreadContextSnapshot clearedContext(Map<String, String> props) {
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.clear();
return () -> {
MDC.setContextMap(old);
};
};
}
#Override
public String getThreadContextType() {
return "SLF4J MDC";
}
}
A ThreadContextProvider file created at src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider having line specifying the ref path of mdcContextProvider : com.org.common.report.Provider.MdcContextProvider
But this is not working although i was expecting this, since the the thread started by listener and the thread which passes the message to it has no connection between them, is there any way where i can pass the request-id to the listener...

I was able to do it with a workaround.
Because each Listener runs/spawn up its own thread. you cannot expect the request ID to be available in MDC property as MDC property is thread local.
I was able to achieve it by passing the value as part of ActiveMQ's message
Example:
Wrap every message you exchange in a Generic class
class ActiveMqMsg {
private Message message;
private Map<String, Object> properties;
}
and expect Listener to deserialize it to ActiveMqMsg and read the properties and set it to MDC property again.
Hope this helps.

Related

How to assert/validate the JSON body and properties returned by a Micronaut controller

I am a Micronaut/Java beginner and I am trying to design some tests for my controllers. I could not find many examples online so here is my question.
Below is the controller with 2 #GET requests:
#Controller("/api/v1")
public class MyController {
private final ClientNetworkList clientNetworkList;
private final ClientStatus clientStatus;
public MyController(
ClientNetworkList clientNetworkList,
ClientStatus clientStatus
){
this.ClientNetworkList = clientNetworkList;
this.ClientStatus = clientStatus;
}
#Get(uri = "/networkList", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<NetworkListPackage> packagesNetworkList() {
return ClientNetworkList.fetchPackages();
}
#Get(uri = "/channels/{stringParm}/status/", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<ChannelStatusPackage> packagesStatus(stringParm) {
return ClientStatus.fetchPackages(genesis);
}
}
The java object POJOs:
#Introspected
public class NetworkListPackage {
private List<NetworkList> networkList = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public List<NetworkList> getNetworkList() {
return networkList;
}
public void setNetworkList(List<NetworkList> networkList) {
this.networkList = networkList;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
public class NetworkList {
private String name;
private Boolean authEnabled;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getAuthEnabled() {
return authEnabled;
}
public void setAuthEnabled(Boolean authEnabled) {
this.authEnabled = authEnabled;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
#Introspected
public class ChannelStatusPackage {
private String chaincodeCount;
private String txCount;
private String latestBlock;
private String peerCount;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getChaincodeCount() {
return chaincodeCount;
}
public void setChaincodeCount(String chaincodeCount) {
this.chaincodeCount = chaincodeCount;
}
public String getTxCount() {
return txCount;
}
public void setTxCount(String txCount) {
this.txCount = txCount;
}
public String getLatestBlock() {
return latestBlock;
}
public void setLatestBlock(String latestBlock) {
this.latestBlock = latestBlock;
}
public String getPeerCount() {
return peerCount;
}
public void setPeerCount(String peerCount) {
this.peerCount = peerCount;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
And the potential tests:
#MicronautTest
class MyControllerTest {
#Inject
#Client("/")
RxStreamingHttpClient client;
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/channels/{stringParam}/status/");
Flowable<ChannelStatusPackage> channelStatusPackageStream = client.jsonStream(request, ChannelStatusPackage.class);
Iterable<ChannelStatusPackage> channelStatusPackages = channelStatusPackageStream.blockingIterable();
//then:
//How to assert the returned body compared to the POJO?
//How to handle the parameter in the request url?
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/networkList");
Flowable<NetworkListPackage> networkListPackageStream = client.jsonStream(request, NetworkListPackage.class);
Iterable<NetworkListPackage> networkListPackages = networkListPackageStream.blockingIterable();
//then:
//How to assert the returned body and compared to the POJO?
//How to assert the returned properties ?
}
}
Based on the previous code, how can I test that the returned bodies and properties of the requests matches the POJOs?
What are the usual test to be carried out?
Thank you very much for helping.
Normaly, the basic assertion start by testing the object type, so this should validate your schema.
An other way to test it is to use RestAssured, witch is a bit more readable.
You need to import the fallowing dependencies in you build.gradle
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
You need test annotation processor to enable micronaut injection and junit 5 for the BeforeEach.
The full test dependencies:
testAnnotationProcessor("io.micronaut:micronaut-inject-java")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
Then you can wright your tests like that:
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"chaincodeCount", equalTo("chaincodeCountValue"),
"txCount", equalTo("txCountValue"),
"latestBlock", equalTo("latestBlockValue"),
"peerCount", equalTo("peerCountValue"),
"additionalProperties.key1", equalTo("additionalPropertyValue1"),
"additionalProperties.key2", equalTo("additionalPropertyValue2")
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"networkList.name[0]", equalTo("nameValue0"),
"networkList.authEnabled[0]", equalTo("authEnabledValue0"),
"networkList.additionalProperties[0].key1", equalTo("additionalPropertiesValue1"),
"networkList.additionalProperties[0].key2", equalTo("additionalPropertyValue2")
);
}
}
This is not really the way you wanted to do your tests, but I hope it will help.
So I ended up using the "hasItems" matcher or/and the jackson schema matcher.
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.hasItems;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("channelsStatus.json"))
.body("keySet()",hasItems(
"chaincodeCount",
"txCount",
"latestBlock",
"peerCount",
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("networkList.json"))
.body("networkList.keySet()",hasItems(
"name",
"authEnabled",
);
}
}
``
Another option is to use jsonPath similar to Spring Boot MockMvc ResultMatcher:
testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
testImplementation 'org.hamcrest:hamcrest:2.2'
Get the response as HttpResponse<String> and then use JsonPath.parse(response.body()) to assert the json path:
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
URI uri = UriBuilder.of("/api/v1/channels/{stringParam}/status/").expand(singletonMap("stringParam", "value"));
HttpResponse<String> response = client.toBlocking().exchange(HttpRequest.GET(uri), String.class);
assertEquals(HttpStatus.OK, response.getStatus());
ReadContext ctx = JsonPath.parse(response.body());
assertThat(ctx.read("$"), isA(Object.class));
assertThat(ctx.read("$.chaincodeCount"), is("chaincodeCountValue"));
}
Example for an endpoint test using Micronaut vs Spring Boot

spring-boot-starter-aop around annotated interface's method won't advice my aspect

I have the following implementation:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
and
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
and
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
#Around(value="#annotation(annotation)")
public Object intercept(ProceedingJoinPoint jp,
RequiresAuthorization annotation) throws Throwable {
LOGGER.info("BEGIN");
jp.proceed();
LOGGER.info("END");
}
}
The maven dependencies are properly configured with the spring-boot-starter-aop dependency. So what happens is that AuthorizerAspect won't intercept around the getResource method if the #RequiresAuthorization is used on the declared method of the BusinessResource interface, but if I change the implementation to annotate the same method now in the BusinessResourceImpl class, the aspect will take place.
NOTE: With the annotation in the interface level, the proxy isn't even created, whereas the annotation being placed in the implementation level will create a proxy for the resource.
Question is: Is there a way to advice objects which the annotation is present just on the interface?
May this alternative be useful for those who like me found no direct approach to sort that limitation on Spring AOP through proxies:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
And
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
And
import import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
public Object invoke(MethodInvocation invocation) throws Throwable {
LOGGER.info("BEGIN");
invocation.proceed();
LOGGER.info("END");
}
#Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean("requiresAuthorizationPointcut")
public AbstractPointcutAdvisor createPointcut() {
return new AbstractPointcutAdvisor() {
private static final long serialVersionUID = 4733447191475535406L;
#Override
public Advice getAdvice() {
return AuthorizerAspect.this;
}
#Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
if (method.isAnnotationPresent(RequiresAuthorization.class)) {
return true;
}
if (method.getDeclaringClass().isInterface()) {
String methodName = method.getName();
try {
Method targetMethod = targetClass.getMethod(methodName, method.getParameterTypes());
return targetMethod != null && targetMethod.isAnnotationPresent(RequiresAuthorization.class);
} catch (NoSuchMethodException |
SecurityException e) {
LOGGER.debug("FAILURE LOG HERE",
e.getMessage());
return false;
}
}
return method.isAnnotationPresent(RequiresAuthorization.class);
}
};
}
};
}
}
So as you'll notice, we're sorting it by using method interceptors.

Spring AMQP - Sender and Receiving Messages

I am facing an issue in receiving a message from RabbitMQ.
I am sending a message like below
HashMap<Object, Object> senderMap=new HashMap<>();
senderMap.put("STATUS", "SUCCESS");
senderMap.put("EXECUTION_START_TIME", new Date());
rabbitTemplate.convertAndSend(Constants.ADAPTOR_OP_QUEUE,senderMap);
If we see in RabbitMQ, we will get a fully qualified type.
In the current scenario, we have n number of producer for the same consumer. If i use any mapper, it leads to an exception.
How will i send a message so that it doesn't contain any type_id and i can receive the message as Message object and later i can bind it to my custom object in the receiver.
I am receiving message like below.
Could you please let me know how to use Jackson2MessageConverter so that message will get directly binds to my Object/HashMap from Receiver end. Also i have removed the Type_ID now from the sender.
How Message looks in RabbitMQ
priority: 0 delivery_mode: 2 headers:
ContentTypeId: java.lang.Object
KeyTypeId: java.lang.Object content_encoding: UTF-8 content_type: application/json
{"Execution_start_time":1473747183636,"status":"SUCCESS"}
#Component
public class AdapterOutputHandler {
private static Logger logger = Logger.getLogger(AdapterOutputHandler.class);
#RabbitListener(containerFactory="adapterOPListenerContainerFactory",queues=Constants.ADAPTOR_OP_QUEUE)
public void handleAdapterQueueMessage(HashMap<String,Object> message){
System.out.println("Receiver:::::::::::"+message.toString());
}
}
Connection
#Bean(name="adapterOPListenerContainerFactory")
public SimpleRabbitListenerContainerFactory adapterOPListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
DefaultClassMapper classMapper = new DefaultClassMapper();
messageConverter.setClassMapper(classMapper);
factory.setMessageConverter(messageConverter);
}
Exception
Caused by: org.springframework.amqp.support.converter.MessageConversionException: failed to convert Message content. Could not resolve __TypeId__ in header and no defaultType provided
at org.springframework.amqp.support.converter.DefaultClassMapper.toClass(DefaultClassMapper.java:139)
I don't want to use __TYPE__ID from sender because they are multiple senders for the same queue and only one consumer.
it leads to an exception
What exception?
TypeId: com.diff.approach.JobListenerDTO
That means you are sending a DTO, not a hash map as you describe in the question.
If you want to remove the typeId header, you can use a message post processor...
rabbitTemplate.convertAndSend(Constants.INPUT_QUEUE, dto, m -> {
m.getMessageProperties.getHeaders().remove("__TypeId__");
return m;
});
(or , new MessagePostProcessor() {...} if you're not using Java 8).
EDIT
What version of Spring AMQP are you using? With 1.6 you don't even have to remove the __TypeId__ header - the framework looks at the listener parameter type and tells the Jackson converter the type so it automatically converts to that (if it can). As you can see here; it works fine without removing the type id...
package com.example;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class So39443850Application {
private static final String QUEUE = "so39443850";
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(So39443850Application.class, args);
context.getBean(RabbitTemplate.class).convertAndSend(QUEUE, new DTO("baz", "qux"));
context.getBean(So39443850Application.class).latch.await(10, TimeUnit.SECONDS);
context.getBean(RabbitAdmin.class).deleteQueue(QUEUE);
context.close();
}
private final CountDownLatch latch = new CountDownLatch(1);
#RabbitListener(queues = QUEUE, containerFactory = "adapterOPListenerContainerFactory")
public void listen(HashMap<String, Object> message) {
System.out.println(message.getClass() + ":" + message);
latch.countDown();
}
#Bean
public Queue queue() {
return new Queue(QUEUE);
}
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(new Jackson2JsonMessageConverter());
return template;
}
#Bean
public SimpleRabbitListenerContainerFactory adapterOPListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
return factory;
}
public static class DTO {
private String foo;
private String baz;
public DTO(String foo, String baz) {
this.foo = foo;
this.baz = baz;
}
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBaz() {
return this.baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
}
}
Result:
class java.util.HashMap:{foo=baz, baz=qux}
This is described in the documentation...
In versions prior to 1.6, the type information to convert the JSON had to be provided in message headers, or a custom ClassMapper was required. Starting with version 1.6, if there are no type information headers, the type can be inferred from the target method arguments.
You can also configure a custom ClassMapper to always return HashMap.
Want to use "a" different Java calss when receive message?
Config #Bean Jackson2JsonMessageConverter with a custom ClassMapper
Want to use "many" different Java calss when receive message? such as :
#MyAmqpMsgListener
void handlerMsg(
// Main message class, by MessageConverter
#Payload MyMsg myMsg,
// Secondary message class - by MessageConverter->ConversionService
#Payload Map<String, String> map,
org.springframework.messaging.Message<MyMsg> msg,
org.springframework.amqp.core.Message amqpMsg
) {
// ...
}
Provide a custom #Bean Converter, ConversionService, RabbitListenerAnnotationBeanPostProcessor :
#Bean
FormattingConversionServiceFactoryBean rabbitMqCs(
Set<Converter> converters
) {
FormattingConversionServiceFactoryBean fac = new FormattingConversionServiceFactoryBean();
fac.setConverters(converters);
return fac;
}
#Bean
DefaultMessageHandlerMethodFactory messageHandlerMethodFactory(
#Qualifier("rabbitMqCs")
FormattingConversionService rabbitMqCs
) {
DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory();
defaultFactory.setConversionService(rabbitMqCs);
return defaultFactory;
}
// copied from RabbitBootstrapConfiguration
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor(
MessageHandlerMethodFactory handlerFac
) {
RabbitListenerAnnotationBeanPostProcessor bpp = new RabbitListenerAnnotationBeanPostProcessor();
bpp.setMessageHandlerMethodFactory(handlerFac);
return bpp;
}
#Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
public RabbitListenerEndpointRegistry defaultRabbitListenerEndpointRegistry() {
return new RabbitListenerEndpointRegistry();
}
References:
Jackson2JsonMessageConverter
AMQP-461
Debugging source code PayloadArgumentResolver

Intellij plugin development, how to prevent an action from occuring, like closing a tab

Is this possible?
I need to subscribe to the event somehow and possibly return false or similar, i am guessing. I have no idea how though.
What event is that?
Where do I register it?
Anyone?
EDIT:
I have tried this:
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.editor.impl.EditorComponentImpl;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
public class MyPlugin implements ApplicationComponent {
static {
/*MessageBus bus = ApplicationManager.getApplication().get
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC,
new FileDocumentManagerAdapter() {
#Override
public void beforeDocumentSaving(Document document) {
// create your custom logic here
}
});*/
}
private final AWTEventListener listener;
public MyPlugin() {
System.out.println("111111111111111111");
listener = new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if ( event instanceof KeyEvent ) {
KeyEvent kv = (KeyEvent) event;
Component component = kv.getComponent();
if ( component instanceof EditorComponentImpl) {
EditorComponentImpl cp = (EditorComponentImpl) component;
}
System.out.println("3333333" + component.getClass());
}
System.out.println("aaaaaaa" + event.getClass());
}
};
}
#Override
public void initComponent() {
System.out.println("bbbbbbbbbbbbbbbbbbbbbbbbbbb");
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.KEY_EVENT_MASK);
}
#Override
public void disposeComponent() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
}
#NotNull
#Override
public String getComponentName() {
return "temp";
}
}
But it does not work. I get events but the wrong kind.
Two plugins were developed in the end to accomplish this:
https://plugins.jetbrains.com/space/index?pr=idea&lg=opensource%40momomo.com

Netty 4.0 channel gets null

The setup
Windows 7 Professionnal
Eclipse Juno
Java jre7
Netty 4.0.0 Beta2
I have a netty server running on another machine. Then I have a program running on my machine, which is made to simulate many clients communicating with the server concurrently. In order to do that, I have a thread pool implemented with java.util.concurrent.ExecutorService . Each client creates a thread and submit it to the ExecutorService. Just before it ends, that thread creates another one with the same code. The submited code does those steps :
connect to server by sending a handshake (netty bootstrap A and channel A)
get the token from the handshake response
connect to server (netty bootstrap B and channel B)
send one request to server
receive the response
close the connection
create another thread with the same code
The problem
I sometimes get a NullPointerException in NettySocketCommunication.sendMessage(), on channel.write(byteBuf) when sending a request to the server.
01728 16:25:23.870 [nioEventLoopGroup-3804-2] ERROR
c.f.s.virtualuser.VirtualUser - java.lang.RuntimeException:
java.lang.NullPointerException at
c.f.s.virtualuser.VirtualUser.processMessageStep(VirtualUser.java:324)
at
c.f.s.virtualuser.VirtualUser.processNextStep(VirtualUser.java:252)
at
c.f.s.virtualuser.VirtualUser.onChannelConnected(VirtualUser.java:395)
at
c.f.s.c.m.handler.ClientSocketBasedHandler.channelActive(ClientSocketBasedHandler.java:95)
at
io.netty.channel.DefaultChannelHandlerContext.invokeChannelActive(DefaultChannelHandlerContext.java:774)
at
io.netty.channel.DefaultChannelHandlerContext.fireChannelActive(DefaultChannelHandlerContext.java:760)
at
io.netty.channel.ChannelStateHandlerAdapter.channelActive(ChannelStateHandlerAdapter.java:58)
at
io.netty.channel.DefaultChannelHandlerContext.invokeChannelActive(DefaultChannelHandlerContext.java:774)
at
io.netty.channel.DefaultChannelHandlerContext.fireChannelActive(DefaultChannelHandlerContext.java:760)
at
io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:884)
at
io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:223)
at
io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:417)
at
io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:365)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:302) at
io.netty.channel.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:110)
at java.lang.Thread.run(Unknown Source)
Caused by:
java.lang.NullPointerException: null at
c.f.s.c.m.NettySocketCommunication.sendMessage(NettySocketCommunication.java:109)
at
c.f.s.virtualuser.VirtualUser.processMessageStep(VirtualUser.java:317)
... 15 common frames omitted
The code
I removed some logging and comments to make the code blocks shorter. I also have the VirtualUser.java class (not shown here) that implements both IVirtualUserCommunication and IVirtualUserMessages interfaces.
[AbstractVirtualUserCommunication.java]
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.util.CharsetUtil;
import [...].Amf3;
import [...].Properties;
import [...].LogFactory;
import [...].Logger;
import [...].AbstractRequest;
import [...].IRouterMessage;
import [...].RouterMessage;
import [...].FrimaHandshake;
import [...].IVirtualUserCommunication;
public abstract class AbstractVirtualUserCommunication implements IVirtualUserCommunication
{
protected static Logger Log = LogFactory.getInstance().getLogger(AbstractVirtualUserCommunication.class);
protected String CONFIG_APPLICATION = "target.server.application";
protected String application;
protected String CONFIG_VERSION = "target.server.version";
protected String version;
protected String CONFIG_HANDSHAKE_PORT = "netty.handshake.port";
protected final int defaultHandshakePort = 80;
// The following variables are used by both HTTP and SOCKET communication
protected Bootstrap bootstrapHandshake; // Netty bootstrap used only for handshake
protected Channel channelHandshake; // Netty channel used only for handshake
protected String token; // The token received through handshake process
// Host & port are set in the connect() method
protected String host;
protected int port;
protected Bootstrap bootstrap; // Netty bootstrap used for communication
protected Channel channel; // Netty channel used for communication
/** Connect to the server to get the token */
public void sendHandshake(String host)
{
// Get properties, with default values if they are not specified
this.application = Properties.getString(CONFIG_APPLICATION, "snowstorm");
this.version = Properties.getString(CONFIG_VERSION, "0.0.1");
int handshakePort = Properties.getInt(CONFIG_HANDSHAKE_PORT, defaultHandshakePort);
bootstrapHandshake = new Bootstrap();
try
{
bootstrapHandshake.group(new NioEventLoopGroup());
bootstrapHandshake.channel(NioSocketChannel.class);
bootstrapHandshake.handler(new HandShakeInitializer(/* this */));
// Connect and listen on handshake host/port
channelHandshake = bootstrapHandshake.connect(host, handshakePort).sync().channel();
channelHandshake.closeFuture().sync();
}
catch (InterruptedException e)
{
Log.error(e);
}
finally
{
bootstrapHandshake.shutdown();
}
}
/** Method called after completion of the handshake (the token has been set). */
protected abstract void afterHandshake();
/** Connect to the target server for stress test script execution. */
protected void connect(ChannelHandler handler)
{
bootstrap = new Bootstrap();
try
{
// Initialize the pipeline
bootstrap.group(new NioEventLoopGroup());
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(handler);
// Connect and listen on host/port
channel = bootstrap.connect(host, port).sync().channel();
if (channel == null)
{
Log.error("PROBLEM : The channel is null in the afterHandshake() method");
}
channel.closeFuture().sync();
}
catch (InterruptedException e)
{
Log.error(e);
}
finally
{
bootstrap.shutdown();
}
}
/** Create a RouterMessage with the specified request. */
protected IRouterMessage buildMessage(AbstractRequest request)
{
RouterMessage routerMessage = new RouterMessage();
routerMessage.bytes = Amf3.serialize(request);
routerMessage.token = this.token;
routerMessage.application = this.application;
routerMessage.version = this.version;
return routerMessage;
}
#Override
public void disconnect()
{
// TODO Is it dangerous to not call channel.close() ??
if (channel != null)
{
channel.close().awaitUninterruptibly();
}
else
{
Log.error("PROBLEM : The channel is null when calling the disconnect() method");
}
bootstrap.shutdown();
}
#Override
public boolean isConnected()
{
if (channel == null)
{
return false;
}
return channel.isActive();
}
private class HandShakeInitializer extends ChannelInitializer<SocketChannel>
{
public HandShakeInitializer()
{
super();
}
#Override
protected void initChannel(SocketChannel socketChannel) throws Exception
{
socketChannel.pipeline().addLast("encoder", new HttpRequestEncoder());
socketChannel.pipeline().addLast("decoder", new HttpResponseDecoder());
socketChannel.pipeline().addLast("handler", new HandShakeHandler(/* communication */));
}
}
private class HandShakeHandler extends ChannelInboundMessageHandlerAdapter<Object>
{
public HandShakeHandler()
{
super();
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
{
super.channelActive(ctx);
ctx.write(FrimaHandshake.create(null, version, application));
ctx.flush();
}
#Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception
{
if (msg instanceof DefaultLastHttpContent)
{
DefaultLastHttpContent defaultLastHttpContent = (DefaultLastHttpContent) msg;
String content = defaultLastHttpContent.data().toString(CharsetUtil.UTF_8);
// Format = token~publicDNS and we only need the token here
token = content;// .split("~")[0];
Log.debug("Starting a bot with token " + token);
afterHandshake();
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
Log.error(cause);
ctx.close();
}
}
}
[NettySocketCommunication.java]
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import [..­.].AbstractRequest;
import [..­.].IRouterMessage;
import [..­.].Serializer;
import [..­.].ClientSocketBasedHandler;
import [..­.].ClientSocketBasedInitializer;
import [..­.].IVirtualUserMessages;
public class NettySocketCommunication extends AbstractVirtualUserCommunication
{
private ClientSocketBasedHandler handler;
private ChannelFuture testChannelFuture;
public NettySocketCommunication()
{
super();
Log.setLevelToInfo();
this.handler = new ClientSocketBasedHandler();
}
#Override
public void setVirtualUser(IVirtualUserMessages virtualUser)
{
this.handler.setVirtualUser(virtualUser);
}
#Override
public void connect(String host, int port)
{
this.host = host;
this.port = port;
// Get the token from the server through the handshake process
sendHandshake(host);
}
#Override
public boolean connectTest(String host, int port)
{
boolean connectSuccess = false;
bootstrap = new Bootstrap();
// Initialize the pipeline
bootstrap.group(new NioEventLoopGroup());
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ClientSocketBasedInitializer(new ClientSocketBasedHandler()));
// Listen on host/port (connect a channel)
testChannelFuture = bootstrap.connect(host, port);
testChannelFuture.awaitUninterruptibly();
if (testChannelFuture.isSuccess())
{
connectSuccess = true;
}
testChannelFuture.channel().close().awaitUninterruptibly();
bootstrap.shutdown();
return connectSuccess;
}
#Override
protected void afterHandshake()
{
super.connect(new ClientSocketBasedInitializer(handler));
}
#Override
public void sendMessage(AbstractRequest request)
{
IRouterMessage routerMessage = buildMessage(request);
ByteBuf byteBuf = Serializer.encode(routerMessage, true);
// Send message
channel.write(byteBuf);
channel.flush();
}
}
[ClientSocketBasedHandler.java]
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import java.util.ArrayList;
import java.util.List;
import [...].Amf3;
import [...].LogFactory;
import [...].Logger;
import [...].IMessage;
import [...].IRouterMessage;
import [...].IVirtualUserMessages;
public class ClientSocketBasedHandler extends ChannelInboundMessageHandlerAdapter<IRouterMessage>
{
protected static Logger Log = LogFactory.getInstance().getLogger(ClientSocketBasedHandler.class);
private IVirtualUserMessages virtualUser;
public ClientSocketBasedHandler()
{
super();
Log.setLevelToInfo();
}
public void setVirtualUser(IVirtualUserMessages virtualUser)
{
this.virtualUser = virtualUser;
}
#Override
public void messageReceived(ChannelHandlerContext ctx, IRouterMessage routerMessage) throws Exception
{
List<IMessage> messages = deserializeMessages(routerMessage.getBytes());
for (IMessage message : messages)
{
Log.debug("Received socket : " + message);
if (virtualUser == null)
{
throw new RuntimeException("Must call the setVirtualUser() method before receiving messages");
}
virtualUser.onManticoreMessageReceived(message);
}
}
protected List<IMessage> deserializeMessages(byte[] bytes)
{
Object probablyMessages = Amf3.deserialize(bytes);
List<IMessage> messages = null;
// List of Messages
if (probablyMessages instanceof ArrayList)
{
messages = (List<IMessage>) probablyMessages;
}
// Single Message
else if (probablyMessages instanceof IMessage)
{
messages = new ArrayList<IMessage>(1);
messages.add((IMessage) probablyMessages);
}
// Probably Pollution
else
{
Log.error("Cannot deserialize message '{}'", probablyMessages.toString());
}
return messages;
}
#Override
public void channelActive(ChannelHandlerContext ctx)
{
if (virtualUser != null)
{
virtualUser.onChannelConnected();
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
Log.error(cause);
ctx.close();
}
}
The search
I looked at netty channels related questions on stack overflow, but couldn't find anything relevant to my case.
Links
http://netty.io/4.0/api/io/netty/channel/Channel.html
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html