How can you avoid circular dependencies when you're designing two classes with a producer/consumer relationship? Here ListenerImpl needs a reference to Broadcaster in order to register/unregister itself, and Broadcaster needs a reference back to the Listeners in order to send messages. This example is in Java but it can apply to any OO language.
public interface Listener {
void callBack(Object arg);
}
public class ListenerImpl implements Listener {
public ListenerImpl(Broadcaster b) { b.register(this); }
public void callBack(Object arg) { ... }
public void shutDown() { b.unregister(this); }
}
public class Broadcaster {
private final List listeners = new ArrayList();
public void register(Listener lis) { listeners.add(lis); }
public void unregister(Listener lis) {listeners.remove(lis); }
public void broadcast(Object arg) { for (Listener lis : listeners) { lis.callBack(arg); } }
}
I don't see that being a circular dependency.
Listener depends on nothing.
ListenerImpl depends on Listener and Broadcaster
Broadcaster depends on Listener.
Listener
^ ^
/ \
/ \
Broadcaster <-- ListenerImpl
All arrows end at Listener. There's no cycle. So, I think you're OK.
Any OOP language? OK. Here's a ten-minute version in CLOS.
Broadcasting framework
(defclass broadcaster ()
((listeners :accessor listeners
:initform '())))
(defgeneric add-listener (broadcaster listener)
(:documentation "Add a listener (a function taking one argument)
to a broadcast's list of interested parties"))
(defgeneric remove-listener (broadcaster listener)
(:documentation "Reverse of add-listener"))
(defgeneric broadcast (broadcaster object)
(:documentation "Broadcast an object to all registered listeners"))
(defmethod add-listener (broadcaster listener)
(pushnew listener (listeners broadcaster)))
(defmethod remove-listener (broadcaster listener)
(let ((listeners (listeners broadcaster)))
(setf listeners (remove listener listeners))))
(defmethod broadcast (broadcaster object)
(dolist (listener (listeners broadcaster))
(funcall listener object)))
Example subclass
(defclass direct-broadcaster (broadcaster)
((latest-broadcast :accessor latest-broadcast)
(latest-broadcast-p :initform nil))
(:documentation "I broadcast the latest broadcasted object when a new listener is added"))
(defmethod add-listener :after ((broadcaster direct-broadcaster) listener)
(when (slot-value broadcaster 'latest-broadcast-p)
(funcall listener (latest-broadcast broadcaster))))
(defmethod broadcast :after ((broadcaster direct-broadcaster) object)
(setf (slot-value broadcaster 'latest-broadcast-p) t)
(setf (latest-broadcast broadcaster) object))
Example code
Lisp> (let ((broadcaster (make-instance 'broadcaster)))
(add-listener broadcaster
#'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
(add-listener broadcaster
#'(lambda (obj) (format t "I has object: ~A~%" obj)))
(broadcast broadcaster 'cheezburger))
I has object: CHEEZBURGER
I got myself a CHEEZBURGER object!
Lisp> (defparameter *direct-broadcaster* (make-instance 'direct-broadcaster))
(add-listener *direct-broadcaster*
#'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
(broadcast *direct-broadcaster* 'kitty)
I got myself a KITTY object!
Lisp> (add-listener *direct-broadcaster*
#'(lambda (obj) (format t "I has object: ~A~%" obj)))
I has object: KITTY
Unfortunately, Lisp solves most of the design pattern problems (such as yours) by eliminating the need for them.
In contrast to Herms' answer, I do see a loop. It's not a dependency loop, it's a a reference loop: LI holds the B object, the B object holds (an Array of) LI object(s). They don't free easily, and care needs to be taken to ensure that they free when possible.
One workaround is simply to have the LI object hold a WeakReference to the broadcaster. Theoretically, if the broadcaster has gone away, there's nothing to deregister with anyway, so then your deregistration will simply check if there is a broadcaster to deregister from, and do so if there is.
I'm not a java dev, but something like this:
public class ListenerImpl implements Listener {
public Foo() {}
public void registerWithBroadcaster(Broadcaster b){ b.register(this); isRegistered = true;}
public void callBack(Object arg) { if (!isRegistered) throw ... else ... }
public void shutDown() { isRegistered = false; }
}
public class Broadcaster {
private final List listeners = new ArrayList();
public void register(Listener lis) { listeners.add(lis); }
public void unregister(Listener lis) {listeners.remove(lis); }
public void broadcast(Object arg) { for (Listener lis : listeners) { if (lis.isRegistered) lis.callBack(arg) else unregister(lis); } }
}
Use weak references to break the cycle.
See this answer.
Here's an example in Lua (I use my own Oop lib here, see references to 'Object' in the code).
Like in Mikael Jansson's CLOS example, your can use functions directly, removing the need of defining listeners (note the use of '...', it's Lua's varargs):
Broadcaster = Object:subclass()
function Broadcaster:initialize()
self._listeners = {}
end
function Broadcaster:register(listener)
self._listeners[listener] = true
end
function Broadcaster:unregister(listener)
self._listeners[listener] = nil
end
function Broadcaster:broadcast(...)
for listener in pairs(self._listeners) do
listener(...)
end
end
Sticking to your implementation, here's an example that could be written in any dynamic language I guess:
--# Listener
Listener = Object:subclass()
function Listener:callback(arg)
self:subclassResponsibility()
end
--# ListenerImpl
function ListenerImpl:initialize(broadcaster)
self._broadcaster = broadcaster
broadcaster:register(this)
end
function ListenerImpl:callback(arg)
--# ...
end
function ListenerImpl:shutdown()
self._broadcaster:unregister(self)
end
--# Broadcaster
function Broadcaster:initialize()
self._listeners = {}
end
function Broadcaster:register(listener)
self._listeners[listener] = true
end
function Broadcaster:unregister(listener)
self._listeners[listener] = nil
end
function Broadcaster:broadcast(arg)
for listener in pairs(self._listeners) do
listener:callback(arg)
end
end
Related
Controller calls the service method which in turn has blocking call to DB. But we are using subscribeOn to schedule the subscription on another thread. The question i have is, does this approach work without blocking other event loop threads?
//Controller
#GetMapping("configuration")
#ResponseStatus(HttpStatus.OK)
public Mono<Command> getConfiguration(Principal agent) {
Mono<String> mono = service.getConfiguration(agent.getName());
return mono.flatMap(cmd -> {
return Mono.just(Command.toCmd(cmd));
}).onErrorMap(throwable -> new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
"Error getting config", throwable));
}
//Service method
public Mono<String> getConfiguration(String name) {
return Mono.fromCallable(() -> {
return dbService.getConfigCommandFromServer(name);
}).subscribeOn(Schedulers.boundedElastic());
}
~~~
Hello recently started studying Webflux.
And sometimes I encounter the tasks that you need to form a simple DTO and return it
Take for example the usual class dto
#Data
#Builder
public static class Dto {
private long id;
private String code1;
private String code2;
}
And a primitive service with two methods...
#Nullable Mono<String> getCode1(long id);
#Nullable String getCode2(long id);
And wrote a method that forms at the output of Mono
private Mono<Dto> fill(long id) {
var dto = Dto.builder()
.id(id)
.build();
//doOnNext
var dtoMono1 = service.getCode1(id)
.map(code -> {
dto.setCode1(code);
return dto;
})
.doOnNext(innerDto -> innerDto.setCode2(service.getCode2(id)));
//map
var dtoMono2 = service.getCode1(id)
.map(code -> {
dto.setCode1(code);
return dto;
})
.map(unused -> service.getCode2(id))
.map(code -> {
dto.setCode1(code);
return dto;
});
//just
var dtoMono3 = Mono.just(dto)
.flatMap(innerDto -> service.getCode1(innerDto.getId()));
//just
var dtoMono4 = Mono.fromCallable(() -> dto)
.subscribeOn(Schedulers.boundedElastic())
.flatMap(innerDto -> service.getCode1(innerDto.getId()));
}
QUESTION:
Is it possible to simply create DTO and use it in the Webflux call
chain ... Or I need to wrap it in mono.just or mono.fromcallable
(what are the pros and cons)
How best to fill in values via doOnNext
or through MAP. An extra line (Return DTO) appears in the case of
MAP and some people also told me if for example NULL comes, doOnNext will
miss it and go further to fill up current dto. But on the other, the MAP is used
to transform the object, and the doOnNext is more for debugging and
logging
Thanks you...
How about using zip operator in such a case?
I hope this example can help you:
private Mono<Dto> fill(long id) {
return Mono.zip(someService.getCode1(id), Mono.just(someService.getCode2(id)))
.map(tuple ->
Dto.builder()
.id(id)
.code1(tuple.getT1())
.code2(tuple.getT2())
.build()
);
}
I am trying to build a kind of hub service that can emit through a hot Flux (output) but you can also register/unregister Flux producers/publishers (input)
I know I can do something like:
class Hub<T> {
/**
* #return unregister function
*/
Function<Void, Void> registerProducer(final Flux<T> flux) { ... }
Disposable subscribe(Consumer<? super T> consumer) {
if (out == null) {
// obviously this will not work!
out = Flux.merge(producer1, producer2, ...).share();
}
return out;
}
}
... but as these "producers" are registered and unregistered, how do I add a new flux source to the existing subscribed to flux? or remove a unregistered source from it?
TIA!
Flux is immutable by design, so as you've implied in the question, there's no way to just "update" an existing Flux in situ.
Usually I'd recommend avoiding using a Processor directly. However, this is one of the (rare-ish) cases where a Processor is probably the only sane option, since you essentially want to be publishing elements dynamically based on the producers that you're registering. Something similar to:
class Hub<T> {
private final FluxProcessor<T, T> processor;
private final FluxSink<T> sink;
public Hub() {
this.processor = DirectProcessor.<T>create().serialize();
this.sink = processor.sink();
}
public Disposable registerProducer(Flux<T> flux) {
return flux.subscribe(sink::next);
}
public Flux<T> read() {
return processor;
}
}
If you want to remove a producer, then you can keep track of the Disposable returned from registerProducer() and call dispose() on it when done.
1. Is it possible to put non-POJO class instances as the value of a cache?
For example, I have a QueryThread class which is a subclass of java.lang.Thread and I am trying to put this instance in a cache. It looks like the put operation is failing because this cache is always empty.
Consider the following class:
public class QueryThread extends Thread {
private IgniteCache<?, ?> cache;
private String queryId;
private String query;
private long timeIntervalinMillis;
private volatile boolean running = false;
public QueryThread(IgniteCache<?, ?> dataCache, String queryId, String query, long timeIntervalinMillis) {
this.queryId = queryId;
this.cache = dataCache;
this.query = query;
this.timeIntervalinMillis = timeIntervalinMillis;
}
public void exec() throws Throwable {
SqlFieldsQuery qry = new SqlFieldsQuery(query, false);
while (running) {
List<List<?>> queryResult = cache.query(qry).getAll();
for (List<?> list : queryResult) {
System.out.println("result : "+list);
}
System.out.println("..... ");
Thread.sleep(timeIntervalinMillis);
}
}
}
This class is not a POJO. How do I store an instance of this class in the cache?
I tried implementing Serializable (didn't help).
I need to be able to do this:
queryCache.put(queryId, queryThread);
Next I tried broadcasting the class using the IgniteCallable interface. But my class takes multiple arguments in the constructor. I feel PeerClassLoading is easy if the class takes a no-arg constructor:
IgniteCompute compute = ignite.compute(ignite.cluster().forServers());
compute.broadcast(new IgniteCallable<MyServiceImpl>() {
#Override
public MyServiceImpl call() throws Exception {
MyServiceImpl myService = new MyServiceImpl();
return myService;
}
});
2. How do I do PeerClassLoading in the case of a class with multi-arg constructor?
It's restricted to put Thread instances to the cache, Thread instance cannot be serialized due to call to Native Methods. Thats why you always get empty value.
PeerClassLoading is a special distributed ClassLoader in Ignite for inter-node byte-code exchange. So, it's only about sharing classes between nodes. It doesn't make sense how many arguments in constructor class have.
But, on the other hand, object, that you created, will be serialised and sent to other nodes and for deserialisation it will need a default(non-arg) constructor.
I haven't any idea about how to do the same in c++/cli.
Is not clear for me how a I can create delegate and how I can invoke it.
Can someone help me?
Thanks.
public class Writer {
internal Dictionary<Type, Action<object>> Reflective = new Dictionary<Type, Action<object>>();
public Writer()
{
Reflective.Add(typeof(float), (value) => Write((float)value));
Reflective.Add(typeof(double), (value) => Write((double)value));
}
public void Write(float value)
{
Console.WriteLine("Float");
}
public void Write(double value)
{
Console.WriteLine("Double");
}
public void Write<T>(T[] values)
{
var method = this.Reflective[typeof(T)];
foreach (var value in values)
{
method(value);
}
}
}
I won't write the whole thing for you, but here's a couple of the non-obvious things to get you started:
typeof(float) ==> System::Single::typeid
// I like to specify the full namespace for explicitness.
Lambdas: C++/CLI does not support lambdas. You'll need to declare a full-fledged method, and construct a delegate to that. Fortunately, you already have that, your two Write methods should work. Don't forget when declaring the delegate, if it's an instance method, you'll need to specify the object to invoke the function on, which should be this in your code.