I have 3 SpringBoot projects:
Producer (producer of messages)
Consumer (consumer of messages)
Commons (Domain objects shared b/w Producer and Consumer)
Message class is as below:
public class Message {
private String eventType;
private String Payload; //serialized
}
Payload class is as below:
public class Payload {
private DomainObjectA a;
private DomainObjectB b;
private DomainObjectC c;
private DomainObjectD d;
}
There are 2 type of events CREATE and UPDATE, and the Producer application can produce 2 types of Messages(to ActiveMQ) depending on type of event.
I need to maintain a single queue in Consumer project which will accept Messages for both kind of events.
For CREATE event I need to build all domain objects, while for UPDATE event need to build DomainObjectA and DomainObjectB only.
if("CREATE".equals(eventType)
{
build DomainObjectA;
build DomainObjectB;
build DomainObjectC;
build DomainObjectD;
set above objects in Payload object
set Payload from above in Message object
set CREATE as eventType in Message object
}
else if("UPDATE".equals(eventType)
{
build DomainObjectA;
build DomainObjectB;
set above objects in Payload object
set Payload from above in Message object
set UPDATE as eventType in Message object
}
Currently, the producer need to know which domain objects to build in Payload depending on type of event, so this is not TypeSafe (as producer might try to set unwanted domain objects).
How can I make this typesafe and have a solution as shown below:
public class Message {
private String eventType;
//want to make Payload dynamic depending on the eventType
// for CREATE eventType i want CreatePayload while for UPDATE i want UpdatePayload
private String Payload;
}
public class CreatePayload implements Serializable {
private DomainObjectA a;
private DomainObjectB b;
private DomainObjectC c;
private DomainObjectD d;
}
public class UpdatePayload implements Serializable {
private DomainObjectA a;
private DomainObjectB b;
}
Any pointers are appreciated.Thank you.
Related
I am using the otel java agent above multimple spring boot microservices.
I want to achieve a simple scenario. Let's suppose that my system is called system A. An arbitrary System B, which is probably not based on otel, makes a request to my system with a generated traceId of it's own attached to request headers. I want somehow to keep logging this traceId inside my distributed logging.
What i have tried so far :
Option 1 -> Write an extension for the java agent and add a customIdGenerator which reads the id produced from System B from a flat source. [traceId should be 32d long, the traceId from System b cannot guarantee that].
public class DemoIdGenerator implements IdGenerator {
private static final AtomicLong traceId = new AtomicLong(0);
private static final AtomicLong spanId = new AtomicLong(0);
#Override
public String generateSpanId() {
return String.format("%016d", spanId.incrementAndGet());
}
// has to be 32d
#Override
public String generateTraceId() {
return String.format("%032d", traceId.incrementAndGet());
}
}
Option 2 -> Write a custom SpanProcessor, add the id as span attribute and propagate the attribute while overriding the onStart(..) SpanProcessor method. - [cannot retrieve attributes since spans are immutable].
public class DemoSpanProcessor implements SpanProcessor {
#Override
public void onStart(Context parentContext, ReadWriteSpan span) {
// cannot retrieve parent context span attributes
span.setAttribute("parent_external_id", "id_from_parent");
}
...
}
Another problem is that when i will try to attach SpanContext to logger pattern, the context does not contain span attributes either. so I can't use them for logging with OpenTelemtryAppender.
final class AutoValue_ImmutableSpanContext extends ImmutableSpanContext {
private final String traceId;
private final String spanId;
private final TraceFlags traceFlags;
private final TraceState traceState;
private final boolean remote;
private final boolean valid;
...
Any idea? Has anyone tried to achieve something similar using Baggage interface maybe?
I found a suitable way using Baggage api propagation. I wrote a custom span processor so i achieved propagating custom attributes through Baggage.current() and populate them on spans while overriding the onStart() method of span processor.
https://github.com/open-telemetry/opentelemetry-java-instrumentation/discussions/7396#discussioncomment-4539912
In akka-typed, the convention is to create Behavior classes with static inner classes that represent the messages that they receive. Heres a simple example
public class HTTPCaller extends AbstractBehavior<HTTPCaller.MakeRequest> {
public interface Command {}
// this is the message the HTTPCaller receives
public static final class MakeRequest implements Command {
public final String query;
public final ActorRef<Response> replyTo;
public MakeRequest(String query, ActorRef<Response> replyTo) {
this.query = query;
this.replyTo = replyTo;
}
}
// this is the response message
public static final class Response implement Command {
public final String result;
public Response(String result) {
this.result = result;
}
}
public static Behavior<Command> create() {
return Behaviors.setup(HTTPCaller::new);
}
private HTTPCaller(ActorContext<Command> context) {
super(context);
}
#Override
public Receive<Command> createReceive() {
return newReceiveBuilder()
.onMessage(MakeRequest.class, this::onMakeRequest).build();
}
private Behavior<MakeRequest> onMakeRequest(MakeRequest message) {
String result = // make HTTP request here using message.query
message.replyTo.tell(new Response(result));
return Behaviors.same();
}
}
Let's say that 20 other actors send MakeRequest messages to the single HTTPCaller actor. Now, each of these other actors have inner classes that implement their own Command. Since MakeRequest is being used by all 20 classes it must be a subtype of all 20 of those actors' Command inner interface.
This is not ideal. I'm wondering what the Akka way of getting around this is.
There's no requirement that a message (e.g. a command) which an actor sends (except for messages to itself...) have to conform to that actor's incoming message type. The commands sent to the HTTPCaller actor only have to (and in this case only do) extend HTTPCaller.Command.
So imagine that we have something like
public class SomeOtherActor extends AbstractBehavior<SomeOtherActor.Command> {
public interface Command;
// yada yada yada
ActorRef<HTTPCaller.Command> httpCallerActor = ...
httpCallerActor.tell(new HTTPCaller.MakeRequest("someQuery", getContext().getSystem().ignoreRef());
}
In general, when defining messages which are sent in reply, those are not going to extend the message type of the sending actor. In HTTPCaller, for instance, Response probably shouldn't implements Command: it can be a standalone class (alternatively, if it is something that might be received by the HTTPCaller actor, it should be handled in the receive builder).
My code above does bring up one question: if Response is to be received by SomeOtherActor, how can it extend SomeOtherActor.Command?
The solution there is message adaptation: a function to convert a Response to a SomeOtherActorCommand. For example
// in SomeOtherActor
// the simplest possible adaptation:
public static final class ResponseFromHTTPCaller implements Command {
public final String result;
public ResponseFromHTTPCaller(HTTPCaller.Response response) {
result = response.result;
}
// at some point before telling the httpCallerActor...
// apologies if the Java lambda syntax is messed up...
ActorRef<HTTPCaller.Response> httpCallerResponseRef =
getContext().messageAdapter(
HTTPCaller.Response.class,
(response) -> { new ResponseFromHTTPCaller(response) }
);
httpCallerActor.tell(new HTTPCaller.MakeRequest("someQuery", httpCallerResponseRef);
There is also the ask pattern, which is more useful for one-shot interactions between actors where there's a timeout.
I am on a project using Java and Spring Boot that processes several different message types from the same queue. Each message gets processed conditionally based on the message type, using an implementation of MessageProcessingService abstract class for each message type.
As of now, we have 5 different message types coming into the same consumer. We are using the same queue because we leverage group policies in JMS, and each message type has the same business key as the group policy.
So what we end up with is that every time a requirement requires receiving a new message type, we add a new implementation of a MessageProcessingService and another dependency to the consumer object. I want to find a better strategy to selectively choose the message processing
Here is an example similar to what we are doing. I do not guarantee the syntax is compilable or syntactically perfect, just demonstrating the problem. Notice all the messages resolve around a person
Consumer:
#Component
public class PersonMessageConsumer {
private MessageProcessingService<HeightUpdate> heightUpdateMessageProcessingService;
private MessageProcessingService<WeightUpdate> weightUpdateMessageProcessingService;
private MessageProcessingService<NameUpdate> nameUpdateMessageProcessingService;
private MessageProcessingService<ShowSizeUpdate> shoeSizeUpdateMessageProcessingService;
public PersonMessageConsumer(
MessageProcessingService<HeightUpdate> heightUpdateMessageProcessingService,
MessageProcessingService<WeightUpdate> weightUpdateMessageProcessingService,
MessageProcessingService<NameUpdate> nameUpdateMessageProcessingService,
MessageProcessingService<ShowSizeUpdate> shoeSizeUpdateMessageProcessingService) {
this.heightUpdateMessageProcessingService = heightUpdateMessageProcessingService;
this.weightUpdateMessageProcessingService = weightUpdateMessageProcessingService;
this.nameUpdateMessageProcessingService = nameUpdateMessageProcessingService;
this.shoeSizeUpdateMessageProcessingService = shoeSizeUpdateMessageProcessingService;
}
#JmsListener(destination = "${queueName}")
public void receiveMessage(TextMessage message) {
String messageType = message.getHeader("MessageType");
switch (messageType) {
case "HeightUpdate":
heightUpdateMessageProcessingService.processMessage(message.getText());
return;
case "WeightUpdate":
weightUpdateMessageProcessingServivce.processMessage(message.getText());
return;
// And other message types
default:
throw new UnknownMessageTypeException(messageType);
}
}
Message POJO example
public class HeightUpdate implements PersonMessage {
#Getter
#Setter
private int height;
}
PersonMessage interface
public interface PersonMessage {
int getPersonId();
}
MessageProcessingService
public abstract class MessageProcessingService<T extends PersonMessage> {
public void processMessage(String messageText) {
//Common message processing, we do some more involved work here but just as a simple example
T message = new ObjectMapper.readValue(messageText, getClassType());
Person person = personRepository.load(message.getPersonId());
Person originalPerson = person.deepCopy();
processMessageLogic(person, message);
if (originalPerson.isDifferentFrom(person)) {
personRespository.update(person);
}
}
protected abstract void processMessageLogic(Person person, T message);
protected abstract Class getClassType();
}
Abstract class implementation example
#Service("heightUpdateMessageProcessingService")
public class HeightUpdateMessageProcessingService extends MessageProcessingService<HeightUpdate> {
#Override
protected void processMessageLogic(Person person, HeightUpdate update) {
person.setHeight(update.getHeight());
}
#Override
protected Class getMessageType() {
return HeightUpdate.getClass();
}
}
So my question is whether or not there is a better design pattern or way of coding this in java and spring that is a little easier to clean and maintain and keeps SOLID principles in mind
Add an abstract method in the MessageProcessingService to return the messageType that each concrete implementation can handle.
Rather than wiring each individual service into PersonMessageConsumer, wire in a List<MessageProcessingService> so that you get all of them at once.
Transform that List into a Map<String, MessageProcessingService>, using the messageType as the key.
Replace the switch statement by looking up the appropriate service in the Map and then invoking its processMessage method.
In the future you can add new instances of MessageProcessingService without having to edit PersonMessageConsumer because Spring will automatically add those new instances to the List<MessageProcessingService> that you wire in.
Whiel generating proxy class by using SVCUTIL.exe or By Adding service reference from VS, it inherits the IExtensibleDataObject to the data contract classes by default.
WCF data contract
[DataContract]
public class Employee
{
[DataMember(Order = 1)]
public string Id { get; set; }
}
WCF Servcie
public class Service1 : IService1
{
public Employee GetEmployeeById(Employee employee)
{
return employee;
}
}
Proxy Class generated by adding service reference from VS and Employee composite class in client side inherits IExtensibleDataObject interface by default even though i haven;'t implement this in service end.
Client side Employee Class
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Employee", Namespace="http://schemas.datacontract.org/2004/07/WcfService1")]
[System.SerializableAttribute()]
public partial class Employee : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string IdField;
[global::System.ComponentModel.BrowsableAttribute(false)]
public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
get {
return this.extensionDataField;
}
set {
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public string Id {
get {
return this.IdField;
}
set {
if ((object.ReferenceEquals(this.IdField, value) != true)) {
this.IdField = value;
this.RaisePropertyChanged("Id");
}
}
}
Now the question is, While generating proxy from some other client (For ex: Java), will they implement IExtensibleDataObject interface by default?
No, because IExtensibleDataObject is an interface from .Net Framework.
The service will run even without it. It provides a way to store extra data, that is not present in the contract:
The IExtensibleDataObject interface provides a single property that
sets or returns a structure used to store data that is external to a
data contract. The extra data is stored in an instance of the
ExtensionDataObject class and accessed through the ExtensionData
property. In a roundtrip operation where data is received, processed,
and sent back, the extra data is sent back to the original sender
intact. This is useful to store data received from future versions of
the contract. If you do not implement the interface, any extra data is
ignored and discarded during a roundtrip operation.
You can read more here: https://msdn.microsoft.com/en-us/library/system.runtime.serialization.iextensibledataobject%28v=vs.100%29.aspx
Hope it helps
I'm building an IM program using java IO, and I have an Object called Message.
what field do you recommend me to add to Message Class?
I did the folowing:
public class Message implements Serializable {
static private final long serialVersionUID=12525452;
enum commands{
LEAVE,
ONLINELISTREQUEST,
SENT,
DELIVERED,
READ;
}
enum types{
TEXT,
VEDIO,
PICTURE,
AUDIO,
COMMAND,
//...... what to add??
}
// fields..
private String From;
private String To;
private String Body;
private int type;
private String url;
private int command;
//what to add??
ALso have variable as STATE which will be having values as:-
SEEN, SENT, etc..
This will help in tracking of message and also launch a threads which will keep on checking whether messages which don't have status as SENT/ RECEIVED just resend them
Just use the teachings of Object Oriented concepts. A class should have attributes that are really properties of the entity represented by that class.