Baggage field not visible in Spring Integration - spring-cloud-sleuth

I'm using Spring Sleuth with Spring Integration. I create a baggage field, which I want to be propagated.
#Configuration
public class SleuthConfig {
#Bean
BaggageField myBaggageField() {
return BaggageField.create("myBaggageField");
}
#Bean
public BaggagePropagationCustomizer baggagePropagationCustomizer(List<BaggageField> baggageFields) {
return builder -> baggageFields.forEach(baggageField -> builder.add(BaggagePropagationConfig.SingleBaggageField.local(baggageField)));
}
}
Then in the first step of my integration flow I set the value:
myBaggageField.updateValue(value)
At this point, both traceId and spanId are equal. Later in my flow I try to get the value of the baggage:
myBaggageField.getValue()
The result is null. At this point spanId is different from traceId. Why can't I access my baggage field? Why it isn't propagated and how to fix this?

Related

Spring messaging with models in different packages

Here's what I'm trying to do:
Application 1 (consumer)
com.producer.model.Event - simple serialisable model (id, name)
Application 2 (producer)
com.consumer.integration.model.Event - simple serialisable model (id, name)
Serialisation configuration
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
Now when I produce a message
#Override
public void publishEvent(Event event) {
log.debug("Publish event Event : {}", event);
jmsTemplate.convertAndSend(eventTopic, event);
}
The consumer
#Override
#JmsListener(destination = "${jmsConfig.eventTopic}", containerFactory = "topicListenerFactory")
public void handleEvent(Event event) {
log.debug("Received an event {}", event);
}
The consumer side complains that the packages of the models are different.
MessageConversionException: Failed to resolve type id [com.producer.model.Event]
...
Caused by: java.lang.ClassNotFoundException: com.producer.model.Event
So deserialisation fails in the consumer because it can't find the package passed on with _type value.
Why do we even need to pass any package related info? It leaks information that is not needed...
What is the correct way to handle these situations. It should be quite a usual case?
EDIT:
With the help of Gary Russell I got it solved. Here's what you'd want to do.
Define a mapper in the producer AND the consumer with desired typings:
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
HashMap<String, Class<?>> typeIdMappings = new HashMap<>();
typeIdMappings.put(Event.class.getSimpleName(), Event.class);
converter.setTypeIdMappings(typeIdMappings);
converter.setTypeIdPropertyName("_type");
return converter;
}
It's important that
setTypeIdPropertyName matches in the consumer and the producer
setTypeIdMappings keys match in the consumer and the producer
This way you could match multiple objects between two services with one mapper.
See
/**
* Specify mappings from type ids to Java classes, if desired.
* This allows for synthetic ids in the type id message property,
* instead of transferring Java class names.
* <p>Default is no custom mappings, i.e. transferring raw Java class names.
* #param typeIdMappings a Map with type id values as keys and Java classes as values
*/
public void setTypeIdMappings(Map<String, Class<?>> typeIdMappings) {
On the producer side map the source class to a type id and on the consumer side map the type id to the destination class.
(That's why it's called MappingJackson...).

By default where Akka.net stores its messages?

I have downloaded a sample code from github and run AtLeastOnceDelivery.sln
Every new run it is sending messages with it. And if I change the message namespace it shows an error started with
Error loading snapshot [SnapshotMetadata<pid: delivery, seqNr: 0, timestamp: 2018/09/24>], remaining attempts: [0]
If I could clear the persistence hopefully it will accept then changed namespace and restart messaging id.
By default, all snapshots are stored as files directly in ./snapshots directory of the application, while events are stored in the memory. Because of that you should consider using a one of the akka.persistence plugins for the production purposes.
Your problem happens because you're using akka.net default serializers (dedicated for networking) which are not very version tolerant - so changing any fields, their types, class names or namespaces makes previous version of the class non-deserializable - and in future will be subject to change. This is also why it's strongly discouraged to use default serializers for persistence.
How to make a custom Akka.NET Serializer
While there are plans to improve serializers API, at the current moment (Akka.NET v1.3.9), to make your own serializer you need to simply inherit from Akka.Serialization.Serializer class:
public sealed class MySerializer : Serializer
{
public MySerializer(ExtendedActorSystem system) : base(system) { }
public override int Identifier => /* globaly unique serializer id */;
public override bool IncludeManifest => true;
public override byte[] ToBinary(object obj)
{
// serialize object
}
public override object FromBinary(byte[] bytes, Type type)
{
// deserialize object
}
}
Keep in mind that Identifier property must be unique in cluster scope - usually values below 100 are used by akka.net internal serializers, therefore it's better to use higher values.
How to bind serializer to be used for a given type
By convention Akka.NET uses empty interfaces to mark message types that are supposed to be serialized. Then you can setup your HOCON configuration to use a specific serializer for a given interface:
akka.actor {
serializers {
my-serializer = ""MyNamespace.MySerializer, MyAssembly""
}
serialization-bindings {
""MyNamespace.MyInterface, MyAssembly"" = my-serializer
}
}
Where MyInterface is interface assigned to a message type you want to serialize/deserialize with MySerializer.

Spring AOP in different projects does not work

I am new to Spring AOP and AspectJ but the simplicity they can provide makes me want to use them.
The questions is: I have two projects, one is a spring boot application server and the other one contains all the utilities functions core. I want to implement logging aspect in both projects and here is what I did:
server
#Aspect
#Component
public class MethodLoggingAspect {
#Around("#annotation(logExecutionTime)")
public Object methodLog(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
final long start = System.nanoTime();
Object proceed = joinPoint.proceed();
final long end = System.nanoTime();
logger.info("method={}, millis={}", joinPoint.getSignature().toShortString(), TimeUnit.NANOSECONDS.toMillis(end - start));
return proceed;
}
}
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class BeanConfiguration {
}
core
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecutionTime {
}
I use the annotation LogExecutionTime both in core and application server these are two different jars and server has dependency core. I expect the aspect will work in both two parts when I run server, but the fact is that only methods in server have the aspect weaved in.
I also tried to define the aspect in core and use aspectJ to do compile-time weaving. But there is Immutable library in core, which will throw a compile time error when I use aspectj-maven-plugin.
Can anyone help me with it? Thanks!

Invoked Stateless EJB never change

I'm writing a course on EJBs on JBOSS AS 7 and I have some troubles.
I have a simple local stateless EJB :
#Stateless
public class CitationEJB {
String citation ="Hello Citation";
public String getCitation(){
System.out.println("getting citation from :"+this.toString());
return this.citation;
}
public void setCitation(String citation) {
System.out.println("changing citation to : "+citation);
this.citation = citation;
}
#PostConstruct
public void sayHello(){
System.out.println("Hello, I'm a new EJB");
}
}
Then I invoke a EJB via JNDI in a JSF ManagedBean :
#ManagedBean
#SessionScoped
public class CitationBean {
//#EJB trying this time with JNDI
CitationEJB ejb;
public String getCitation() throws NamingException{
ejb = lookupCitationEJB();
return ejb.getCitation();
}
public String getCitation2() throws NamingException{
ejb.setCitation("hello Toto");
CitationEJB ejb = lookupCitationEJB();
return ejb.getCitation();
}
private static CitationEJB lookupCitationEJB() throws NamingException {
Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
Context context = new InitialContext(jndiProperties);
String jndiName = "java:global/CitationsDyn/CitationEJB!com.citations.ejb.CitationEJB";
//jndiName = "java:app/CitationsDyn/CitationEJB"; // Works also
return (CitationEJB) context.lookup(jndiName);
}
}
Then I show up the CitationEJB.getCitation() with JSF. Everything works fine except that when I make F5, and so a new request, I always have the same object : when I use CitationEJB.setCitation("Hello toto"), then F5, I do have "Hello Toto" and not a brand new Object.
When I use the #EJB annotation to get the EJB, I have the expected behaviour with a new object for every request.
So what I learned is that the EJB is picked in a pool, but when is it destroyed ? I guess that the JNDI lookup is not bound to a Scope as is a JSF page. But how is it exactly specified ?
The lifecycle of a Stateless Session Bean is managed by the container. A number of instances will be created and placed in an instance pool when the EJB is deployed (for example JBoss 6 creates 10 instances by default). The number can scale up or down based on the demand. The EJBs are generally not destoryed after use, but rather put back in to the pool to be used again and again.
For your application where you want to keep state, a Stateful Session Bean would be the properly choice (or Single Session Bean if you wanted to share state between the instances). With a Stateful Session Bean, the application can cause the EJB to be destoryed by annotating a method with #Remove.

JEE6 Producer for NewCookie

I would like to create a CDI producer for
javax.ws.rs.core.NewCookie(java.lang.String name,
java.lang.String value,
java.lang.String path,
java.lang.String domain,
java.lang.String comment,
int maxAge,
boolean secure)
in such a way that the value will be different each time. I did some JEE6 a while ago but my memory is poor!
for ex. my producer for a simple logger is
#Produces
public Logger produceLogger(final InjectionPoint injectionPoint) {
final String injectingClass = injectionPoint.getMember().getDeclaringClass().getName();
logger.info("creating logger for : " + injectingClass);
return Logger.getLogger(injectingClass);
}
Any help appreciated
If you are able to calculate a unique value within a producer method without any additional parameters, then all you have to do is annotate a method with the return value NewCookie:
#Produces NewCookie createCookie() {
// create cookie and its value
}
If you need to create it subject to some external parameter, then this producer method can have parameters like any other method - but, all of these are injection points and must be obtainable by the container.
#Produces NewCookie createCookie(String value) {
// create cookie with parameter value
}
Now, a primitive type (as well as a String) has the problem, that you for sure have other instances of the same type with a different meaning, so you either use a special class like MyValue wrapping your String and use this as an injection point or annotate it with a custom annotation.
#Produces NewCookie createCookie(#CookieValue String value) {
// create cookie with parameter value
}
Then of course, you need again some place where this injected value is produced.
#Produces #CookieValue String createCookieValue() {
// create value
}
Check out the JavaEE 6 Tutorial or the CDI Spec for more information.