Design pattern suggestion to perform pipeline operation - java

Problem statement:
I have to process request similar to a pipeline.
For example:
When a request comes, it has to undergo a sequence of operations, like (step1,step2,step3...).
So, in order to achieve that, I am using Template design pattern.
Please review and suggest if I am implementing this problem correctly, or there is a better solution.
I am suspecting my approach will introduce code smells, as I am changing values of objects very frequently.
Also, suggest if I & how can I use Java 8 to accomplish this?
Thanks.
Code:
package com.example.demo.design;
import java.util.List;
public abstract class Template {
#Autowired
private Step1 step1;
#Autowired
private Step2 step2;
#Autowired
private Save save;
List<String> stepOutput = null;
List<String> stepOutputTwo = null;
List<String> stepOutputThree = null;
public void step1(String action1) {
stepOutput = step1.method(action1);
}
public void step2(String action2) {
stepOutputTwo = step2.method(stepOutput, action2);
}
abstract public void step3();
public void save() {
save.persist(stepOutputThree);
}
final public void run(String action1, String action2) {
step1(action1);
step2(action2);
stepOutputTwo = step3();
}
}

In Java 8 streams model, that could look like the following:
final public void run(String action1, String action2) {
Stream.of(action1) // Stream<String>
.map(s -> step1.method(s)) // Stream<List<String>>
.map(l -> step2.method(l,action2) // Stream<List<String>>
.map(l -> step3.method(l)) // Stream<List<String>>
.forEach(l -> save.persist(l));
}

I had same issue! you can do something like this: and uncheckCall method is for handling exceptions.
final public void run(String action1, String action2) {
//other stuffs
Stream.of(step1.method(action1))
.map(stepOutput->uncheckCall(() ->step2.method(stepOutput,action2)))
.forEach(stepOutputThree -> uncheckCall(()->save.persist(stepOutputThree)));
//.....
}
for uncheckCall method:
public static <T> T uncheckCall(Callable<T> callable) {
try {
return callable.call();
} catch (RuntimeException e) {
// throw BusinessException.wrap(e);
} catch (Exception e) {
//throw BusinessException.wrap(e);
}
}

Well, when there are "pipelines", "sequence of operations", etc. the first design pattern that comes to mind is Chain of Responsibility, that looks like the following
and provides you with these benefits:
allows you to add new handlers when necessary (e.g. at runtime) without modifying other handlers and processing logic (Open/Closed Principle of SOLID)
allows a handler to stop processing a request if necessary
allows you to decouple processing logic of the handlers from each other (Single Responsibility Principle of SOLID)
allows you to define the order of the handlers to process a request outside of the handlers themselves
One example of real world usage is Servlet filters where you call doFilter(HttpRequest, HttpResponse, FilterChain) to invoke the next handler
protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) {
if (haveToInvokeNextHandler) {
chain.doFilter(req, resp);
}
}
In case of using classical Chain of Responsibility pattern your processing pipeline may look like the following:
API
public class StepContext {
private Map<String, Object> attributes = new HashMap<>();
public <T> T getAttribute(String name) {
(T) attributes.get(name);
}
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
}
public interface Step {
void handle(StepContext ctx);
}
public abstract class AbstractStep implements Step {
private Step next;
public AbstractStep() {
}
public AbstractStep(Step next) {
this.next = next;
}
protected void next(StepContext ctx) {
if (next != null) {
next.handle(ctx);
}
}
}
Implementation
public class Step1 extends AbstractStep {
public Step1(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action1 = ctx.getAttribute("action1");
List<String> output1 = doSomething(action1);
ctx.setAttribute("output1", output1);
next(ctx); // invoke next step
}
}
public class Step2 extends AbstractStep {
public Step2(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output1 = ctx.getAttribute("output1");
List<String> output2 = doSomething(output1, action2);
ctx.setAttribute("output2", output2);
next(ctx); // invoke next step
}
}
public class Step3 extends AbstractStep {
public Step3(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output2 = ctx.getAttribute("output2");
persist(output2);
next(ctx); // invoke next step
}
}
Client code
Step step3 = new Step3(null);
Step step2 = new Step2(step3);
Step step1 = new Step1(step2);
StepContext ctx = new StepContext();
ctx.setAttribute("action1", action1);
ctx.setAttribute("action2", action2);
step1.handle(ctx);
Also all this stuff can be simplified into a chain of handlers decoupled from each other by means of removing the corresponding next references in case your processing pipeline will have to always invoke all the available steps without controlling the necessity of invocation from the previous one:
API
public class StepContext {
private Map<String, Object> attributes = new HashMap<>();
public <T> T getAttribute(String name) {
(T) attributes.get(name);
}
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
}
public interface Step {
void handle(StepContext ctx);
}
Implementation
public class Step1 implements Step {
public void handle(StepContext ctx) {
String action1 = ctx.getAttribute("action1");
List<String> output1 = doSomething(action1);
ctx.setAttribute("output1", output1);
}
}
public class Step2 implements Step {
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output1 = ctx.getAttribute("output1");
List<String> output2 = doSomething(output1, action2);
ctx.setAttribute("output2", output2);
}
}
public class Step3 implements Step {
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output2 = ctx.getAttribute("output2");
persist(output2);
}
}
Client code
Note that in case of Spring framework (just noticed #Autowired annotation) the client code may be simplified even more as the #Autowired annotation can be used to inject all the beans of the corresponding type into a corresponding collection.
Here what the documentation states:
Autowiring Arrays, Collections, and Maps
In case of an array, Collection, or Map dependency type, the container autowires all beans matching the declared value type. For such purposes, the map keys must be declared as type String which will be resolved to the corresponding bean names. Such a container-provided collection will be ordered, taking into account Ordered and #Order values of the target components, otherwise following their registration order in the container. Alternatively, a single matching target bean may also be a generally typed Collection or Map itself, getting injected as such.
public class StepsInvoker {
// spring will put all the steps into this collection in order they were declared
// within the spring context (or by means of `#Order` annotation)
#Autowired
private List<Step> steps;
public void invoke(String action1, String action2) {
StepContext ctx = new StepContext();
ctx.setAttribute("action1", action1);
ctx.setAttribute("action2", action2);
steps.forEach(step -> step.handle(ctx))
}
}

Related

How to create a polymorphic structure that can work for both success and error responses in Java?

I'm working on a class that will get a list of strings and process them asynchronously using CompletableFutures. Each string is processed by invoking another class that will perform several operations and return a response or throw an exception if there is an error.
I would like to aggregate the responses that I get, whether they have a valid response or an exception and return them as a list to the caller. I would like the caller to be able to expect a list of SomeResponse and be able to interpret them using polymorphism.
However, I'm stuck on determining if this can be done using polymorphism at all, given that the fields for the success and error response are completely different. I have added some pseudo code below on one alternative I have thought of. Basically have SomeResponse be an interface with an isSuccess method. This will allow the caller to know if it's an error or not. However, the caller would still have to cast it to the correct implementation in order to get the value or the error. Is there a better way to approach this? My requirement is being able to return both a success and error response for each given request in the list. If there is an exception, we don't want to abort the entire operation.
public MyProcessorClass {
private final SomeOtherClass someOtherClass;
public List<SomeResponse> process(List<String> requestList) {
return requestList.stream().map(this::procesRequest)
.collectors(Collect.tolist()):
}
private processRequest(String request) {
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() => {
return new SomeSuccessResponse(someOtherClass.execute(request));
})
.exceptionally(e -> {
return new SomeErrorResponse(e.getCause);
});
return completableFuture.get();
}
}
public interface SomeResponse {
boolean isSuccess();
}
public class SomeSuccessResponse implements SomeResponse {
private final String value;
#Getter
private final boolean success;
public SomeSuccessResponse(String value) {
this.value = value;
this.success = true;
}
}
public class SomeErrorResponse implements SomeResponse {
private final Throwable error;
#Getter
private final boolean success;
public SomeErrorResponse(Throwable error) {
this.error = error;
this.success = false;
}
}
What you want is the visitor pattern https://en.wikipedia.org/wiki/Visitor_pattern
public class Main {
interface IResponse {
void acceptHandler(IResponseHandler handler);
}
static class ResponseA implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
static class ResponseB implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
public interface IResponseHandler {
void handle(ResponseA response);
void handle(ResponseB responseB);
}
public static void main(String[] args) {
final IResponseHandler handler = new IResponseHandler() {
#Override
public void handle(ResponseA response) {
System.out.println("Handle ResponseA");
}
#Override
public void handle(ResponseB responseB) {
System.out.println("Handle ResponseB");
}
};
final IResponse someResponse = new ResponseA();
someResponse.acceptHandler(handler);
}
}

Convert a ListenableFuture chain to the equivalent RxJava structure

Using Guava Listenable Futures
Assume I have the following class:
public class FooService {
ListenableFuture<Foo> getFoo() {
//code to create callable, then
return listeningExecutorService.submit(fooCallable);
}
}
and the following class:
public class BarService {
ListenableFuture<Bar> getBar(Foo foo) {
//code to create callable, then
return listeningExecutorService.submit(barCallable);
}
}
Note that getBar requires a Foo in the parameters.
If I want to chain these two operations together I would write a transformer function like this:
AsyncFunction<Foo, Bar> fooToBar = new AsyncFunction<Foo, Bar>() {
#Override
ListenableFuture<Bar> apply(Foo resultantFoo) {
return barService.get(resultantFoo);
}
};
and then apply the transformation like this:
public ListenableFuture<Bar> combinedFooToBar() {
ListenableFuture<Foo> futureFoo = fooService.get();
return Futures.transformAsync(futureFoo, fooToBar);
}
Question: what is the equivalent syntax for these classes and transformation function if we were to convert them into RxJava? Assume that we want to convert FooService and BarService into the appropriate RxJava structures. Assume we want to chain async tasks using the result of calling FooService as the parameter for BarService.
NB: I am just starting to learn about RxJava syntax. When I have finished studying the syntax I will attempt answer the question myself. However, in the meantime if anyone wants to answer they are welcome.
The Guava code translates into RxJava2 code as follows:
FooService.java
public class FooService {
Observable<Foo> getFoo() {
return Observable.fromCallable(new Callable<Foo>() {
#Override
public Foo call() throws Exception {
return new Foo();
}
});
}
}
BarService.java
public class BarService {
Observable<Bar> getBar(final Foo foo) {
return Observable.fromCallable(new Callable<Bar>() {
#Override
public Bar call() throws Exception {
return new Bar(foo);
}
});
}
}
FooBarService.java
public class FooBarService {
private final FooService fooService;
private final BarService barService;
public FooBarService(FooService fooService, BarService barService) {
this.fooService = fooService;
this.barService = barService;
}
Observable<Bar> getFooBar() {
return fooService.getFoo()
.concatMap(new Function<Foo, ObservableSource<? extends Bar>>() {
#Override
public ObservableSource<? extends Bar> apply(#NonNull Foo foo) throws Exception {
return barService.getBar(foo);
}
});
}
}
Hence, concatMap and flatMap are similar to Futures.transformAsync and map is similar to Futures.transform (non-async).
Note also this Github project called Future Converter for conversion between ListenableFuture and Observable.

Use of Generics during validation

I am using apache CXF.
The following API is used to post a Contact.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ResponseResult create(#Context HttpHeaders httpHeaders, #Context Request request, #Context UriInfo uriInfo,
UserContact contact) throws MDMException;
Here the UserContact class contains the contact information about a user which is passed as JSON in the body.
I need to do some business validations on this UserContact object. But I do not like to have too much validation code packed in a single class.
I would like to do something like the following. But I am facing issue with Generics.
interface Rule<S>
{
void applyRule(S s)throws Exception;
}
interface Validatable
{
void validate() throws Exception;
}
public class MyValidator
{
private HashMap<? extends Rule ,?> map = new HashMap<>();
public void validate() throws Exception
{
for(Rule rule : map.keySet())
{
rule.applyRule(map.get(rule));
}
}
public <S> void addRule(Rule<S> rule, S data)
{
this.map.put(rule, data);
}
}
class EMailValidationRule implements Rule<String>
{
private static final Pattern emailPattern = Pattern.compile("email-regex");
public void applyRule(String s) throws Exception
{
if(!emailPattern.matcher(s).matches())
throw new Exception("Not a valid EMail");
}
}
So the UserContact has to do the following for its validation purposes. This keeps the code compact (IMO).
class UserContact implements Validatable
{
// some
// code
// related to User Contact
public void validate() throws Exception
{
MyValidator validator = new MyValidator();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "+1234567890");
validator.validate();
}
}
I keep getting error like :
The method put(capture#5-of ? extends Rule, capture#6-of ?) in the type HashMap is not applicable
for the arguments (Rule, S)
Also is the above design good for doing validations?
The problem is that, although your encapsulation ensures it, the compiler can not be sure that the retrieved Rule<...> has a type argument of the same type as the retrieved data.
There is also the problem of not being able to insert a Rule<T> with data of a subtype of T. If you have Rule<S> rule, S data the types have to be an exact match. While a Rule<S> could handle a subtype of S just fine.
While MyValidator is a cool little class, I can't really see the point in having it. Especially because you create a new one every time you call validate. It would also be hard to cache because the rules are static (the same for every instance of the class) and the data comes from individual instances (I'd assume).
You could also just do this:
class UserContact implements Validatable
{
// some
// code
// related to User Contact
// 1 rule instance for the entire class, not a new one per call to 'validate'
private static EMailValidationRule emailRule = new EmailValidationRule();
private static PhoneValidationRule phoneRule = new PhoneValidationRule();
public void validate() throws Exception
{
emailRule.applyRule("developer#stackoverflow.com");
phoneRule.applyRule("+1234567890");
}
}
Never the less, here is a working version of MyValidator:
class MyValidator {
private Map<Rule<?>, RuleNode<?>> map = new HashMap<>();
public void validate() throws Exception {
for(RuleNode<?> node : map.values())
node.apply();
}
public <T, D extends T> void addRule(Rule<T> rule, D data) {
#SuppressWarnings("unchecked") // unchecked, but safe due to encapsulation
RuleNode<T> r = (RuleNode<T>) map.get(rule);
if(r == null) {
r = new RuleNode<T>(rule);
map.put(rule, r);
}
r.add(data);
}
private static class RuleNode<T> { // Maintains that the rule and data are compatible
private final Rule<T> rule;
private final List<T> list = new ArrayList<>();
public RuleNode(Rule<T> rule) {
this.rule = rule;
}
public void add(T data) {
list.add(data);
}
public void apply() throws Exception {
for(T data : list)
rule.applyRule(data);
}
}
}
You just need to make a generic Version of the MyValidator Class
A generic class is defined with thss format:
class name<T1, T2, ..., Tn> { /* ... */ }
Defining the class using generics you will specify the types you want to use in your class, in your case <R extends Rule<S> ,S>
class MyValidator<R extends Rule<S> ,S>{
private HashMap<R ,S> map = new HashMap<>();
public void validate() throws Exception{
for(Rule<S> rule : map.keySet()){
rule.applyRule(map.get(rule));
}
}
public void addRule(R rule, S data){
this.map.put(rule, data);
}
}
Once done you just have to build a MyValidator of the desired type :
MyValidator<Rule<String>, String> validator = new MyValidator<>();
And finally add the rules matching the types of the validator :
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
So for example addind a phone validator your UserContact will looks like :
class PhoneValidationRule implements Rule<String>{
private static final Pattern phonePattern = Pattern.compile("phone-regex");
public void applyRule(String s) throws Exception{
if(!phonePattern.matcher(s).matches())
throw new Exception("Not a valid phone");
}
}
class UserContact implements Validatable{
public void validate() throws Exception{
MyValidator<Rule<String>, String> validator = new MyValidator<>();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "developer#stackoverflow.com");
validator.validate();
}
}

Generics overloading of method - workaround

this question was asked many times but I couldn't find elegant workaround for it.
This example works as desired:
public class RequestWrapper<T> {
private final T request;
private final Class<T> type;
public RequestWrapper(T request, Class<T> type) {
this.request = request;
this.type = type;
}
public T getRequest() {
return request;
}
public Class<T> getType() {
return type;
}
}
public class Service {
private void invoke(String request) {
System.out.println("String:" + request);
}
private void invoke(Object request) {
System.out.println("Object:" + request + "," + request.getClass().getSimpleName());
}
public static void main(String[] args) {
RequestWrapper<String> sw = new RequestWrapper<String>("A", String.class);
RequestWrapper<Integer> iw = new RequestWrapper<Integer>(Integer.valueOf(0), Integer.class);
new Service().invoke(sw.getRequest());
new Service().invoke(iw.getRequest());
}
}
But I would need to add one more method to Service class which do something before/after call of invoke method:
public void invoke(RequestWrapper<?> wrapper) {
try {
// ...
invoke(wrapper.getType().cast(wrapper.getRequest()));
invoke(wrapper.getRequest());
} catch(Exception e ) {
// ...
}
}
then the main method would contain:
new Service().invoke(sw);
I understand the reason why the invoke(Object request) is used instead of invoke(String request).
What would be an elegant solution to call proper invoke method and be able to do some common actions before/after it?
To have an interface e.g. Invoker, implement it e.g. StringInvoker, Invoker> and call map.get(wrapper.getType()).invoke(wrapper.getRequest()) is possible solution but I expect something better.
You can check the type and explicitly cast it, for example (I also added Integer so you can see branching on more types):
Class<?> c = wrapper.getType();
if (c == String.class)
invoke((String) wrapper.getRequest()); // Invokes invoke(String)
else if (c == Integer.class)
invoke((Integer) wrapper.getRequest()); // Invokes invoke(Integer)
else
invoke(wrapper.getRequest()); // Invokes invoke(Object)
Note:
If you go on this path, you don't even need to store the request type in the RequestWrapper class because you can just as easily use the instanceof operator on the request itself to check its type. And if you "get rid" of the request type, your current RequestWrapper class will only contain the request so the RequestWrapper is not even needed in this case.
Visitor patter can serves to solve it. Only drawback is that there isn't possible to write:
new Service().invoke(new RequestWrapper<String>("A"));
My implementation:
public class Service {
public void invoke(RequestWrapper<?> wrapper) {
try {
// ...
wrapper.invoke(this);
} catch(Exception e ) {
// ...
}
}
public void invoke(String request) {
System.out.println("String:" + request);
}
public void invoke(Boolean request) {
System.out.println("Boolean:" + request);
}
public static void main(String[] args) {
RequestWrapper<Boolean> rw = new BooleanRequestWrapper(Boolean.TRUE);
new Service().invoke(rw);
}
}
abstract class RequestWrapper<T> {
protected final T request;
public RequestWrapper(T request) {
this.request = request;
}
public abstract void invoke(Service v);
}
class BooleanRequestWrapper extends RequestWrapper<Boolean> {
public BooleanRequestWrapper(Boolean request) {
super(request);
}
public void invoke(Service service) {
service.invoke(request);
}
}

Java - Execute a class method with a specify annotation

I have a android application, but it is not relevant.
I have a class called "Front controller" which will receive some message
through it's constructor. The message, for brievity, could be an integer.
I want somewhere else to create a new controller which will execute
a method based on the integer defined above
public class OtherController {
#MessageId("100")
public void doSomething(){
//execute this code
}
#MessageId("101")
public void doSomethingElse(){
//code
}
}
The front controller could be something like this:
public class FrontController {
private int id;
public FrontController(int id){
this.id=id;
executeProperControllerMethodBasedOnId();
}
public void executeProperControllerMethodBasedOnId(){
//code here
}
public int getId(){
return id;
}
}
So, if the Front Controller will receive the integer 100, it
will execute the method annotated with #MessageId(100). The
front controller don't know exactly the class where this method
is.
The problem which I found is that I need to register somehow
each controller class. I Spring I had #Component or #Controller
for autoloading. After each controllers are register, I need to
call the properly annotated method.
How to achieve this task? In Spring MVC, I had this system
implemented, used to match the HTTP routes. How could I implement
this in a plain java project?
Any suggestions?
Thanks to Google Reflections (hope you can integrate this in your android project.)
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections-maven</artifactId>
<version>0.9.8</version>
</dependency>
For optimisation I've added the requirement to also annotate the class with MessageType annotation and the classes should be in the same package (org.conffusion in my example):
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MessageType {
}
The OtherController looks like:
#MessageType
public class OtherController {
#MessageId(id=101)
public void method1()
{
System.out.println("executing method1");
}
#MessageId(id=102)
public void method2()
{
System.out.println("executing method2");
}
}
The implementation will look like:
public void executeProperControllerMethodBasedOnId() {
Set<Class<?>> classes = new org.reflections.Reflections("org.conffusion")
.getTypesAnnotatedWith(MessageType.class);
System.out.println("found classes " + classes.size());
for (Class<?> c : classes) {
for (Method m : c.getMethods()) {
try {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object o = c.newInstance();
if (mid.id() == id)
m.invoke(o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Maybe you can optimise and build a static hashmap containing already scanned message ids.
You need to implement some of the work by yourself using reflection, I would recommend to prepare message handlers on initial phase in regards to performance. Also you possibly want to think about Singleton/Per Request controllers. Some of the ways to implement the solution:
interface MessageProcessor {
void execute() throws Exception;
}
/* Holds single instance and method to invoke */
class SingletonProcessor implements MessageProcessor {
private final Object instance;
private final Method method;
SingletonProcessor(Object instance, Method method) {
this.instance = instance;
this.method = method;
}
public void execute() throws Exception {
method.invoke(instance);
}
}
/* Create instance and invoke the method on execute */
class PerRequestProcessor implements MessageProcessor {
private final Class clazz;
private final Method method;
PerRequestProcessor(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
public void execute() throws Exception {
Object instance = clazz.newInstance();
method.invoke(instance);
}
}
/* Dummy controllers */
class PerRequestController {
#MessageId(1)
public void handleMessage1(){System.out.println(this + " - Message1");}
}
class SingletonController {
#MessageId(2)
public void handleMessage2(){System.out.println(this + " - Message2");}
}
class FrontController {
private static final Map<Integer, MessageProcessor> processors = new HashMap<Integer, MessageProcessor>();
static {
try {
// register your controllers
// also you can scan for annotated controllers as suggested by Conffusion
registerPerRequestController(PerRequestController.class);
registerSingletonController(SingletonController.class);
} catch (Exception e) {
throw new ExceptionInInitializerError();
}
}
private static void registerPerRequestController(Class aClass) {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
processors.put(mid.value(), new PerRequestProcessor(aClass, m));
}
}
}
private static void registerSingletonController(Class aClass) throws Exception {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object instance = aClass.newInstance();
processors.put(mid.value(), new SingletonProcessor(instance, m));
}
}
}
/* To process the message you just need to look up processor and execute */
public void processMessage(int id) throws Exception {
if (processors.containsKey(id)) {
processors.get(id).execute();
} else {
System.err.print("Processor not found for message " + id);
}
}
}

Categories