JDK 9 provides a concrete Publisher named SubmissionPublisher that acts as a compliant Reactive Streams Publisher relying on drop handling and/or blocking for flow control. In this tutorial, we’re gonna take a look at SubmissionPublisher and an example that generates items for Subscribers.
Related Articles:
– Java 9 Flow API – Reactive Streams
– Java 9 Flow API example – Publisher and Subscriber
– Java 9 Flow API example – Processor
I. Technologies
– Java 9
– Eclipse with Java 9 Support for Oxygen (4.7)
II. Overview
1. SubmissionPublisher
SubmissionPublisher is an implementation of Java 9 Flow.Publisher
that asynchronously issues items to its subscribers until closing.
Depending on usage, we can indicate the Executor
for SubmissionPublisher in its constructor methods:
– If we wanna submitting items in separate threads, and can estimate number of subscribers, consider using Executors.newFixedThreadPool(int)
and constructor method:
SubmissionPublisher(Executor executor, int maxBufferCapacity);
// maxBufferCapacity: the maximum capacity for each subscriber's buffer.
– Otherwise, just call the default constructor (no input parameter) that will use ForkJoinPool.commonPool()
.
If a Subscriber has only one action that requests and processes all items, we can consider using consume(Consumer)
method (which returns a CompletableFuture
object) like this:
publisher.consume((data) -> process(data));
There are 2 publication methods:
– submit()
: asynchronously publishes the given item to each subscriber, but blocks until resources are available.
– offer()
: publishes the given item asynchronously, to each current subscriber if possible, but the item may be dropped by one or more subscribers if resource limits are exceeded.
Some more useful methods:
close()
: issues onComplete signals to all subscribers, and disallows subsequent attempts to publish.closeExceptionally(Throwable error)
: issues onError signals to all subscribers with the given error, and disallows subsequent attempts to publish.estimateMaximumLag()
: returns an estimate of the maximum number of items produced but not yet consumed among all subscribers.estimateMinimumDemand()
: returns an estimate of the minimum number of items requested (via request) but not yet produced, among all subscribers.getNumberOfSubscribers()
.hasSubscribers()
.isSubscribed(Subscriber)
.getSubscribers()
: returns list of current subscribers.
2. Project
We will create a Publisher (extends SubmissionPublisher) that is subscribed by two Subscribers:
– We don’t need to define any implementation of Subscription interface. Why?
SubmissionPublisher contains a linked list of BufferedSubscriptions, everytime we invoke subscribe()
method to a Subscriber, there will be a new BufferedSubscription item in list which is related to that Subscriber automatically.
– Using submit(T item)
method, Publisher periodically publishes the items generated from a Supplier to Subscribers (Publisher submit()
method will invoke Subscription onNext()
method).
– After receiving all items successfully, Subscriber can request new data or cancel Subscription (random).
– When generated items reach to MAX_ITEM_TO_PUBLISH, we will stop Publisher by using close()
method (that will send onComplete signal to Subscribers).
III. Practice
1. Create subclass of SubmissionPublisher
package com.javasampleapproach.java9flow.submissionpublisher;
import static java.lang.Thread.currentThread;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
public class MyPublisher extends SubmissionPublisher {
private static final String LOG_MESSAGE_FORMAT = "Publisher >> [%s] %s%n";
private final int MAX_ITEM_TO_PUBLISH = 5;
private final ScheduledFuture> periodicTask;
private final ScheduledExecutorService scheduler;
private final AtomicInteger i;
MyPublisher(Executor executor, int maxBufferCapacity, long period, TimeUnit unit) {
super(executor, maxBufferCapacity);
// if using the default, normally the ForkJoinPool.commonPool(), call:
// super();
i = new AtomicInteger(0);
scheduler = new ScheduledThreadPoolExecutor(1);
periodicTask = scheduler.scheduleAtFixedRate(() -> {
Integer item = supplier.get();
log("publishing item: " + item + " ...");
submit(item);
log("estimateMaximumLag: " + super.estimateMaximumLag());
log("estimateMinimumDemand: " + super.estimateMinimumDemand());
if (item == MAX_ITEM_TO_PUBLISH) {
close();
}
}, 0, period, unit);
}
@Override
public void subscribe(Subscriber super Integer> subscriber) {
super.subscribe(subscriber);
}
public void close() {
log("shutting down...");
List> subscribers = getSubscribers();
for (Subscriber super Integer> subscriber : subscribers) {
log("Subscriber " + subscriber.toString() + " isSubscribed(): " + isSubscribed(subscriber));
}
periodicTask.cancel(false);
scheduler.shutdown();
super.close();
}
Supplier extends Integer> supplier = new Supplier<>() {
@Override
public Integer get() {
int value = i.incrementAndGet();
return Integer.valueOf(value);
}
};
private void log(String message, Object... args) {
String fullMessage = String.format(LOG_MESSAGE_FORMAT, currentThread().getName(), message);
System.out.printf(fullMessage, args);
}
}
2. Create implementation of Subscriber
package com.javasampleapproach.java9flow.submissionpublisher;
import java.util.Random;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Thread.currentThread;
public class MySubscriber implements Subscriber<Object> {
private static final String LOG_MESSAGE_FORMAT = "~~ Subscriber %s >> [%s] %s%n";
private static final Random RANDOM = new Random();
private Subscription subscription;
private AtomicInteger count;
private String name;
private int DEMAND = 0;
public MySubscriber(String name) {
this.name = name;
}
@Override
public void onSubscribe(Subscription subscription) {
log("Subscribed...");
this.subscription = subscription;
request(DEMAND);
}
public void setDEMAND(int n) {
this.DEMAND = n;
count = new AtomicInteger(DEMAND);
}
private void request(int n) {
log("request new " + n + " items...");
subscription.request(n);
}
@Override
public void onNext(Object item) {
log("itemValue: " + item);
if (count.decrementAndGet() == 0) {
if (RANDOM.nextBoolean()) {
request(DEMAND);
count.set(DEMAND);
} else {
log("Cancel subscribe...");
subscription.cancel();
}
}
}
@Override
public void onComplete() {
log("Complete!");
}
@Override
public void onError(Throwable t) {
log("Error: " + t.getMessage());
}
private void log(String message, Object... args) {
String fullMessage = String.format(LOG_MESSAGE_FORMAT, this.name, currentThread().getName(), message);
System.out.printf(fullMessage, args);
}
}
3. Create Test Class
package com.javasampleapproach.java9flow.submissionpublisher;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainApp {
public static void main(String[] args) {
final int MAX_BUFFER_CAPACITY = 128;
final ExecutorService executor = Executors.newFixedThreadPool(4);
MyPublisher publisher = new MyPublisher(executor, MAX_BUFFER_CAPACITY, 200, TimeUnit.MILLISECONDS);
MySubscriber subscriberA = new MySubscriber("A");
subscriberA.setDEMAND(3);
MySubscriber subscriberB = new MySubscriber("B");
subscriberB.setDEMAND(6);
publisher.subscribe(subscriberA);
publisher.subscribe(subscriberB);
}
}
4. Check Result
Case 1:
– Subscriber A requests 3 items, then cancel subscribe.
– Subscriber B requests 6 items, then cancel subscribe.
Publisher >> [pool-2-thread-1] publishing item: 1 ...
~~ Subscriber A >> [pool-1-thread-1] Subscribed...
~~ Subscriber B >> [pool-1-thread-2] Subscribed...
~~ Subscriber A >> [pool-1-thread-1] request new 3 items...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-2] request new 6 items...
Publisher >> [pool-2-thread-1] estimateMinimumDemand: -1
~~ Subscriber B >> [pool-1-thread-2] itemValue: 1
~~ Subscriber A >> [pool-1-thread-1] itemValue: 1
Publisher >> [pool-2-thread-1] publishing item: 2 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber A >> [pool-1-thread-3] itemValue: 2
~~ Subscriber B >> [pool-1-thread-4] itemValue: 2
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] publishing item: 3 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber A >> [pool-1-thread-2] itemValue: 3
~~ Subscriber B >> [pool-1-thread-1] itemValue: 3
~~ Subscriber A >> [pool-1-thread-2] Cancel subscribe...
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 4 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-3] itemValue: 4
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 2
Publisher >> [pool-2-thread-1] publishing item: 5 ...
~~ Subscriber B >> [pool-1-thread-4] itemValue: 5
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] publishing item: 6 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-1] itemValue: 6
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
~~ Subscriber B >> [pool-1-thread-1] Cancel subscribe...
Publisher >> [pool-2-thread-1] publishing item: 7 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 8 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 9 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 10 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 11 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 0
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Case 2:
– Subscriber A requests 3 items, then cancel subscribe.
– Subscriber B requests 6 items, then request more 6 items.
– Because Publisher submits only 11 items, so Subscriber B only receives 11 items (while requesting total 12), then receives onComplete signal from Publisher (via Publisher close()
method) when it still subscribes.
~~ Subscriber B >> [pool-1-thread-2] Subscribed...
Publisher >> [pool-2-thread-1] publishing item: 1 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: -1
~~ Subscriber A >> [pool-1-thread-1] Subscribed...
~~ Subscriber A >> [pool-1-thread-1] request new 3 items...
~~ Subscriber B >> [pool-1-thread-2] request new 6 items...
~~ Subscriber A >> [pool-1-thread-1] itemValue: 1
~~ Subscriber B >> [pool-1-thread-2] itemValue: 1
Publisher >> [pool-2-thread-1] publishing item: 2 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber A >> [pool-1-thread-3] itemValue: 2
~~ Subscriber B >> [pool-1-thread-4] itemValue: 2
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] publishing item: 3 ...
~~ Subscriber B >> [pool-1-thread-2] itemValue: 3
~~ Subscriber A >> [pool-1-thread-1] itemValue: 3
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
~~ Subscriber A >> [pool-1-thread-1] Cancel subscribe...
Publisher >> [pool-2-thread-1] publishing item: 4 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-3] itemValue: 4
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 2
Publisher >> [pool-2-thread-1] publishing item: 5 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-4] itemValue: 5
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] publishing item: 6 ...
~~ Subscriber B >> [pool-1-thread-2] itemValue: 6
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-2] request new 6 items...
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
Publisher >> [pool-2-thread-1] publishing item: 7 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-1] itemValue: 7
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 5
Publisher >> [pool-2-thread-1] publishing item: 8 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-3] itemValue: 8
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 4
Publisher >> [pool-2-thread-1] publishing item: 9 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-4] itemValue: 9
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 3
Publisher >> [pool-2-thread-1] publishing item: 10 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-2] itemValue: 10
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 2
Publisher >> [pool-2-thread-1] publishing item: 11 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-1] itemValue: 11
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] shutting down...
Publisher >> [pool-2-thread-1] Subscriber com.javasampleapproach.java9flow.submissionpublisher.MySubscriber@4278bf01 isSubscribed(): true
~~ Subscriber B >> [pool-1-thread-3] Complete!
Case 3: We change MAX_ITEM_TO_PUBLISH to 5:
– Subscriber A requests 3 items, then request more 3 items.
– Subscriber B requests 6 items, then request more 6 items.
– Because Publisher submits only 5 items, so Subscriber A and B only receives 5 items (while request total 6 items for each), then receive onComplete signal from Publisher (via Publisher close()
method) when they still subscribe.
~~ Subscriber A >> [pool-1-thread-1] Subscribed...
~~ Subscriber B >> [pool-1-thread-2] Subscribed...
Publisher >> [pool-2-thread-1] publishing item: 1 ...
~~ Subscriber B >> [pool-1-thread-2] request new 6 items...
~~ Subscriber A >> [pool-1-thread-1] request new 3 items...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 2
~~ Subscriber B >> [pool-1-thread-2] itemValue: 1
~~ Subscriber A >> [pool-1-thread-1] itemValue: 1
Publisher >> [pool-2-thread-1] publishing item: 2 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-4] itemValue: 2
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
~~ Subscriber A >> [pool-1-thread-3] itemValue: 2
Publisher >> [pool-2-thread-1] publishing item: 3 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-1] itemValue: 3
~~ Subscriber A >> [pool-1-thread-2] itemValue: 3
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 0
~~ Subscriber A >> [pool-1-thread-2] request new 3 items...
Publisher >> [pool-2-thread-1] publishing item: 4 ...
~~ Subscriber A >> [pool-1-thread-4] itemValue: 4
~~ Subscriber B >> [pool-1-thread-3] itemValue: 4
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 2
Publisher >> [pool-2-thread-1] publishing item: 5 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
~~ Subscriber B >> [pool-1-thread-2] itemValue: 5
~~ Subscriber A >> [pool-1-thread-1] itemValue: 5
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 1
Publisher >> [pool-2-thread-1] shutting down...
Publisher >> [pool-2-thread-1] Subscriber com.javasampleapproach.java9flow.submissionpublisher.MySubscriber@42d273fa isSubscribed(): true
Publisher >> [pool-2-thread-1] Subscriber com.javasampleapproach.java9flow.submissionpublisher.MySubscriber@dd73db isSubscribed(): true
~~ Subscriber B >> [pool-1-thread-3] Complete!
~~ Subscriber A >> [pool-1-thread-4] Complete!
Special case: Using consume(Consumer)
method:
package com.javasampleapproach.java9flow.submissionpublisher;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainApp {
public static void main(String[] args) {
final int MAX_BUFFER_CAPACITY = 128;
final ExecutorService executor = Executors.newFixedThreadPool(4);
MyPublisher publisher = new MyPublisher(executor, MAX_BUFFER_CAPACITY, 200, TimeUnit.MILLISECONDS);
publisher.consume((data) -> process(data));
}
static void process(Integer i) {
System.out.println("consume() testing: " + i.toString());
}
}
The result:
Publisher >> [pool-2-thread-1] publishing item: 1 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 9223372036854775806 //7FFFFFFFFFFFFFFE : Long.MAX_VALUE - 1
consume() testing: 1
Publisher >> [pool-2-thread-1] publishing item: 2 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
consume() testing: 2
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 9223372036854775805 //7FFFFFFFFFFFFFFD : Long.MAX_VALUE - 2
Publisher >> [pool-2-thread-1] publishing item: 3 ...
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
consume() testing: 3
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 9223372036854775804 //7FFFFFFFFFFFFFFC : Long.MAX_VALUE - 3
Publisher >> [pool-2-thread-1] publishing item: 4 ...
consume() testing: 4
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 9223372036854775803 //7FFFFFFFFFFFFFFB : Long.MAX_VALUE - 4
Publisher >> [pool-2-thread-1] publishing item: 5 ...
consume() testing: 5
Publisher >> [pool-2-thread-1] estimateMaximumLag: 1
Publisher >> [pool-2-thread-1] estimateMinimumDemand: 9223372036854775802 //7FFFFFFFFFFFFFFA : Long.MAX_VALUE - 5
Publisher >> [pool-2-thread-1] shutting down...
Publisher >> [pool-2-thread-1] Subscriber java.util.concurrent.SubmissionPublisher$ConsumerSubscriber@1c39c86 isSubscribed(): true
Look at estimateMinimumDemand which is the returned value of estimateMinimumDemand()
method.
When using consume()
method, we don’t specify any Subscriber for Publisher, so it will initiate estimateMinimumDemand value by Long.MAX_VALUE
, and subtract 1 every consumption.
Thanks for this great example!
One issue I have is that the main app never terminates after the publisher has closed itself and the subscribers are notified that the subscription is complete. What do I do wrong or is there anything missing in the example?
pyriisvctykgbwyvuduubpzkcdnpmp
ok
Aber was Sie sollte sicher sein, Erhalten der Schnell Geschwindigkeit Online, als langsamer Geschwindigkeit von Web kann rob Sie von alles,
was Sie sehnsüchtig Muss sicherlich zu sein auf der Suche nach.
743854 285490I got what you intend,bookmarked , extremely decent website. 313219
This is the right blog for everyone who is desires to learn about this topic. You understand a whole lot its virtually challenging to argue on hand (not that I really would want…HaHa). You definitely put a whole new spin on a topic thats been discussing for some time. Great stuff, just fantastic!
I simply wanted to inform you about how much I actually appreciate all you’ve contributed to help increase the value of the lives of people in this subject matter. Through your own articles, we have gone via just a newbie to a professional in the area. It can be truly a honor to your initiatives. Thanks
325182 78310Its superb as your other posts : D, regards for posting . 86328
As far as me being a member here, I am glad though that I am a member. When the article was published I received a username and password, so that I could participate in Comments, That would explain me stumbuling upon this post. But we’re certainly all members in the world of ideas.
You seem to be very professional in the way you write.*;,`.
Just observed your site a couple weeks ago and i appear to have been reading this task daily. One has a wide range of information what follows and that i seek out your thing for this webpage just too. Support the favorable labor!
You made some decent points there. I looked on the net for your issue and located most individuals will go as well as with the internet site.
Text
you are really a good webmaster. The website loading speed is incredible. It seems that you are doing any unique trick. In addition, The contents are masterpiece. you have done a great job on this topic!
Its like you learn my mind! You appear to understand a lot about this, like you wrote the book in it or something. I believe that you can do with a few p.c. to drive the message house a bit, but other than that, that is great blog. A great read. I will definitely be back.
certainly like your web-site but you need to check the spelling on quite a few of your posts. Several of them are rife with spelling issues and I to find it very bothersome to tell the truth on the other hand I¦ll definitely come back again.
After study a few of the blog posts on your website now, and I truly like your way of blogging. I bookmarked it to my bookmark website list and will be checking back soon. Pls check out my web site as well and let me know what you think.
Hello! I could have sworn I’ve been to this blog before but after browsing through some of the post I realized it’s new to me. Anyways, I’m definitely happy I found it and I’ll be book-marking and checking back frequently!
396931 404437Good read, I just passed this onto a colleague who was doing just a little research on that. And he just bought me lunch since I located it for him smile So let me rephrase that: Thank you for lunch! 907365
Fantastic goods from you, man. I have take into account your stuff prior to and you are just extremely wonderful. I really like what you’ve bought right here, certainly like what you’re saying and the way in which through which you are saying it. You make it enjoyable and you continue to take care of to keep it smart. I cant wait to read much more from you. This is actually a tremendous site.
Merely wanna input on few general things, The website design and style is perfect, the subject matter is rattling wonderful. “The enemy is anybody who’s going to get you killed, no matter which side he’s on.” by Joseph Heller.
After study a few of the blog posts on your website now, and I truly like your way of blogging. I bookmarked it to my bookmark website list and will be checking back soon. Pls check out my web site as well and let me know what you think.
261750 605870Your writing is fine and gives food for thought. I hope that Ill have much more time to read your articles . Regards. I wish you which you frequently publish new texts and invite you to greet me 290027
544989 831162I got what you intend, saved to fav, extremely good web site . 842454
Hello. remarkable job. I did not anticipate this. This is a splendid story. Thanks!
Rattling excellent info can be found on website.
I enjoy the efforts you have put in this, thanks for all the great blog posts.
I couldn’t resist commenting
857042 837040This internet site is my intake , real excellent layout and perfect topic material . 899550
Lovely just what I was searching for.Thanks to the author for taking his time on this one.
Nice post. I learn something more challenging on different blogs everyday. It will always be stimulating to read content from other writers and practice a little something from their store. I’d prefer to use some with the content on my blog whether you don’t mind. Natually I’ll give you a link on your web blog. Thanks for sharing.
Just wanna comment that you have a very nice site, I enjoy the style and design it actually stands out.
Hello! I could have sworn I’ve been to this blog before but after browsing through some of the post I realized it’s new to me. Anyways, I’m definitely happy I found it and I’ll be book-marking and checking back frequently!
Very interesting points you have noted, appreciate it for posting.
838137 318637You need to participate in a contest for among the top blogs on the internet. I will recommend this internet site! 281066