Merging Hot Flux SourcesActiveMQ some consumers not picking up tasks if they arrive after producerwhy spill failure happens for Custom Data Type in HadoopWhy does JMockIt mock the getter X.getE() for class X in this snippet?Error:java: javacTask: source release 8 requires target release 1.8Working with Flux in a Scheduled TaskHow to map a flux with mono?Dynamically merging FluxesMerge Flux emissions with duplication?Flux from WebClient behaves differently than Flux from File.readLinesHow to use groupBy on a hot source
What is this airplane?
AMPScript SMS InsertDE() function not working in SMS
How can one's career as a reviewer be ended?
Is it expected that a reader will skip parts of what you write?
How creative should the DM let an artificer be in terms of what they can build?
How to “listen” to existing circuit
Has there been a multiethnic Star Trek character?
Increase speed altering column on large table to NON NULL
How to communicate to my GM that not being allowed to use stealth isn't fun for me?
Is using 'echo' to display attacker-controlled data on the terminal dangerous?
If I leave the US through an airport, do I have to return through the same airport?
UTC timestamp format for launch vehicles
What are neighboring ports?
Proving that a Russian cryptographic standard is too structured
Does putting salt first make it easier for attacker to bruteforce the hash?
Russian word for a male zebra
Is it possible to have 2 different but equal size real number sets that have the same mean and standard deviation?
How can I remove material from this wood beam?
Fermat's statement about the ancients: How serious was he?
Who won a Game of Bar Dice?
How do free-speech protections in the United States apply in public to corporate misrepresentations?
A word that means "blending into a community too much"
With Ubuntu 18.04, how can I have a hot corner that locks the computer?
Printing Pascal’s triangle for n number of rows in Python
Merging Hot Flux Sources
ActiveMQ some consumers not picking up tasks if they arrive after producerwhy spill failure happens for Custom Data Type in HadoopWhy does JMockIt mock the getter X.getE() for class X in this snippet?Error:java: javacTask: source release 8 requires target release 1.8Working with Flux in a Scheduled TaskHow to map a flux with mono?Dynamically merging FluxesMerge Flux emissions with duplication?Flux from WebClient behaves differently than Flux from File.readLinesHow to use groupBy on a hot source
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
In Spring Boot 2 with Reactor, I am attempting to merge two Flux
hot sources. However, the merge
only ever seems to report the first of the two Flux
parameters in merge
. How do I get the merge
to recognize the second Flux
.
In the example below, the System.err
in B-2
doesn't even print when outgoing1a
is the first parameter. If I make outgoing2
the first, then A-2
doesn't print.
Below is the full example;
package com.example.demo;
import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
public class Weather
String city;
Integer temperature;
public Weather(String city, Integer temperature)
this.city = city;
this.temperature = temperature;
@Override
public String toString()
return "Weather [city=" + city + ", temperature=" + temperature + "]";
public static void main(String[] args)
BlockingQueue<Weather> queue = new LinkedBlockingQueue<>();
BlockingQueue<Weather> queue2 = new LinkedBlockingQueue<>();
// Assume Spring @Repository "A-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "LDN", "NYC", "PAR", "ZUR")
queue.add(new Weather(s, d));
try Thread.sleep(250); catch (InterruptedException e)
).start();
// Assume Spring @Repository "B-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "MOS", "TLV")
queue2.add(new Weather(s, d));
try Thread.sleep(1000); catch (InterruptedException e)
).start();
// Assume Spring @Service "A-2" = real-time LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue.take());
System.err.println("1 " + queue.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-1"));
// Assume Spring @Service "B-2" = real-time MOS, TLV
Flux<Weather> outgoing2 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue2.take());
System.err.println("2 " + queue2.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-2"));
// Assume Spring @Service "A-3" = 5 second summary of LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1a = Flux.from(outgoing1)
.groupBy(c -> c.city)
.flatMap(g -> g
.sample(Duration.ofSeconds(5))
)
.log("C");
// Assume Spring @Service "C" - merges "A-3" and "B-2"
// only prints outgoing1a
Flux.merge(outgoing1a, outgoing2).subscribe(System.out::println);
// only prints outgoing2
//Flux.merge(outgoing2, outgoing1a).subscribe(System.out::println);
java spring-webflux project-reactor
add a comment |
In Spring Boot 2 with Reactor, I am attempting to merge two Flux
hot sources. However, the merge
only ever seems to report the first of the two Flux
parameters in merge
. How do I get the merge
to recognize the second Flux
.
In the example below, the System.err
in B-2
doesn't even print when outgoing1a
is the first parameter. If I make outgoing2
the first, then A-2
doesn't print.
Below is the full example;
package com.example.demo;
import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
public class Weather
String city;
Integer temperature;
public Weather(String city, Integer temperature)
this.city = city;
this.temperature = temperature;
@Override
public String toString()
return "Weather [city=" + city + ", temperature=" + temperature + "]";
public static void main(String[] args)
BlockingQueue<Weather> queue = new LinkedBlockingQueue<>();
BlockingQueue<Weather> queue2 = new LinkedBlockingQueue<>();
// Assume Spring @Repository "A-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "LDN", "NYC", "PAR", "ZUR")
queue.add(new Weather(s, d));
try Thread.sleep(250); catch (InterruptedException e)
).start();
// Assume Spring @Repository "B-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "MOS", "TLV")
queue2.add(new Weather(s, d));
try Thread.sleep(1000); catch (InterruptedException e)
).start();
// Assume Spring @Service "A-2" = real-time LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue.take());
System.err.println("1 " + queue.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-1"));
// Assume Spring @Service "B-2" = real-time MOS, TLV
Flux<Weather> outgoing2 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue2.take());
System.err.println("2 " + queue2.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-2"));
// Assume Spring @Service "A-3" = 5 second summary of LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1a = Flux.from(outgoing1)
.groupBy(c -> c.city)
.flatMap(g -> g
.sample(Duration.ofSeconds(5))
)
.log("C");
// Assume Spring @Service "C" - merges "A-3" and "B-2"
// only prints outgoing1a
Flux.merge(outgoing1a, outgoing2).subscribe(System.out::println);
// only prints outgoing2
//Flux.merge(outgoing2, outgoing1a).subscribe(System.out::println);
java spring-webflux project-reactor
add a comment |
In Spring Boot 2 with Reactor, I am attempting to merge two Flux
hot sources. However, the merge
only ever seems to report the first of the two Flux
parameters in merge
. How do I get the merge
to recognize the second Flux
.
In the example below, the System.err
in B-2
doesn't even print when outgoing1a
is the first parameter. If I make outgoing2
the first, then A-2
doesn't print.
Below is the full example;
package com.example.demo;
import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
public class Weather
String city;
Integer temperature;
public Weather(String city, Integer temperature)
this.city = city;
this.temperature = temperature;
@Override
public String toString()
return "Weather [city=" + city + ", temperature=" + temperature + "]";
public static void main(String[] args)
BlockingQueue<Weather> queue = new LinkedBlockingQueue<>();
BlockingQueue<Weather> queue2 = new LinkedBlockingQueue<>();
// Assume Spring @Repository "A-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "LDN", "NYC", "PAR", "ZUR")
queue.add(new Weather(s, d));
try Thread.sleep(250); catch (InterruptedException e)
).start();
// Assume Spring @Repository "B-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "MOS", "TLV")
queue2.add(new Weather(s, d));
try Thread.sleep(1000); catch (InterruptedException e)
).start();
// Assume Spring @Service "A-2" = real-time LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue.take());
System.err.println("1 " + queue.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-1"));
// Assume Spring @Service "B-2" = real-time MOS, TLV
Flux<Weather> outgoing2 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue2.take());
System.err.println("2 " + queue2.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-2"));
// Assume Spring @Service "A-3" = 5 second summary of LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1a = Flux.from(outgoing1)
.groupBy(c -> c.city)
.flatMap(g -> g
.sample(Duration.ofSeconds(5))
)
.log("C");
// Assume Spring @Service "C" - merges "A-3" and "B-2"
// only prints outgoing1a
Flux.merge(outgoing1a, outgoing2).subscribe(System.out::println);
// only prints outgoing2
//Flux.merge(outgoing2, outgoing1a).subscribe(System.out::println);
java spring-webflux project-reactor
In Spring Boot 2 with Reactor, I am attempting to merge two Flux
hot sources. However, the merge
only ever seems to report the first of the two Flux
parameters in merge
. How do I get the merge
to recognize the second Flux
.
In the example below, the System.err
in B-2
doesn't even print when outgoing1a
is the first parameter. If I make outgoing2
the first, then A-2
doesn't print.
Below is the full example;
package com.example.demo;
import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
public class Weather
String city;
Integer temperature;
public Weather(String city, Integer temperature)
this.city = city;
this.temperature = temperature;
@Override
public String toString()
return "Weather [city=" + city + ", temperature=" + temperature + "]";
public static void main(String[] args)
BlockingQueue<Weather> queue = new LinkedBlockingQueue<>();
BlockingQueue<Weather> queue2 = new LinkedBlockingQueue<>();
// Assume Spring @Repository "A-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "LDN", "NYC", "PAR", "ZUR")
queue.add(new Weather(s, d));
try Thread.sleep(250); catch (InterruptedException e)
).start();
// Assume Spring @Repository "B-1"
new Thread(() ->
for (int d = 1; d < 1000; d += 1)
for (String s: new String[] "MOS", "TLV")
queue2.add(new Weather(s, d));
try Thread.sleep(1000); catch (InterruptedException e)
).start();
// Assume Spring @Service "A-2" = real-time LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue.take());
System.err.println("1 " + queue.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-1"));
// Assume Spring @Service "B-2" = real-time MOS, TLV
Flux<Weather> outgoing2 = Flux.<Weather>create(
sink ->
for (int i = 0; i < 1000; i++)
try
sink.next(queue2.take());
System.err.println("2 " + queue2.size());
catch (InterruptedException e)
e.printStackTrace();
sink.complete();
).publishOn(Schedulers.newSingle("outgoing-2"));
// Assume Spring @Service "A-3" = 5 second summary of LDN, NYC, PAR, ZUR
Flux<Weather> outgoing1a = Flux.from(outgoing1)
.groupBy(c -> c.city)
.flatMap(g -> g
.sample(Duration.ofSeconds(5))
)
.log("C");
// Assume Spring @Service "C" - merges "A-3" and "B-2"
// only prints outgoing1a
Flux.merge(outgoing1a, outgoing2).subscribe(System.out::println);
// only prints outgoing2
//Flux.merge(outgoing2, outgoing1a).subscribe(System.out::println);
java spring-webflux project-reactor
java spring-webflux project-reactor
edited Mar 24 at 20:04
lafual
asked Mar 24 at 19:57
lafuallafual
777
777
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
There are a few things at play here.
- Note the following recommendation of the
.merge
operator...
Note that merge is tailored to work with asynchronous sources or finite sources. When dealing with an infinite source that doesn't already publish on a dedicated Scheduler, you must isolate that source in its own Scheduler, as merge would otherwise attempt to drain it before subscribing to another source.
Your outbound Fluxes use
.publishOn
, but that only affects operators chained after the.publishOn
operator. i.e. It doesn't affect anything before the.publishOn
. Specifically, it does not affect the thread on which the code in lambda passed toFlux.create
executes. You can see this if you add.log()
before the.publishOn
in each of the outbound Fluxes.Your lambda passed to
Flux.create
calls a blocking method (queue.take
).
Since you call subscribe(...)
on the merged Flux in the main
thread, your lambda passed to Flux.create
executes in the main
thread, and blocks it.
The easiest fix is to use .subscribeOn
instead of .publishOn
so that your code in the lambda passed to Flux.create
operates on a different thread (other than main
). This will prevent the main
thread from blocking, and allow the merged output from both outbound streams to be interleaved.
Thank you very much for taking the time to answer.subscribeOn
instead orpublishOn
did indeed fix the issue. I will play with theThread.sleep()
which writes to theBlockingQueue
and adelay()
after themerge()
to see which flavor ofsubscribeOn
I will ultimately need.
– lafual
Mar 25 at 5:51
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55327981%2fmerging-hot-flux-sources%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
There are a few things at play here.
- Note the following recommendation of the
.merge
operator...
Note that merge is tailored to work with asynchronous sources or finite sources. When dealing with an infinite source that doesn't already publish on a dedicated Scheduler, you must isolate that source in its own Scheduler, as merge would otherwise attempt to drain it before subscribing to another source.
Your outbound Fluxes use
.publishOn
, but that only affects operators chained after the.publishOn
operator. i.e. It doesn't affect anything before the.publishOn
. Specifically, it does not affect the thread on which the code in lambda passed toFlux.create
executes. You can see this if you add.log()
before the.publishOn
in each of the outbound Fluxes.Your lambda passed to
Flux.create
calls a blocking method (queue.take
).
Since you call subscribe(...)
on the merged Flux in the main
thread, your lambda passed to Flux.create
executes in the main
thread, and blocks it.
The easiest fix is to use .subscribeOn
instead of .publishOn
so that your code in the lambda passed to Flux.create
operates on a different thread (other than main
). This will prevent the main
thread from blocking, and allow the merged output from both outbound streams to be interleaved.
Thank you very much for taking the time to answer.subscribeOn
instead orpublishOn
did indeed fix the issue. I will play with theThread.sleep()
which writes to theBlockingQueue
and adelay()
after themerge()
to see which flavor ofsubscribeOn
I will ultimately need.
– lafual
Mar 25 at 5:51
add a comment |
There are a few things at play here.
- Note the following recommendation of the
.merge
operator...
Note that merge is tailored to work with asynchronous sources or finite sources. When dealing with an infinite source that doesn't already publish on a dedicated Scheduler, you must isolate that source in its own Scheduler, as merge would otherwise attempt to drain it before subscribing to another source.
Your outbound Fluxes use
.publishOn
, but that only affects operators chained after the.publishOn
operator. i.e. It doesn't affect anything before the.publishOn
. Specifically, it does not affect the thread on which the code in lambda passed toFlux.create
executes. You can see this if you add.log()
before the.publishOn
in each of the outbound Fluxes.Your lambda passed to
Flux.create
calls a blocking method (queue.take
).
Since you call subscribe(...)
on the merged Flux in the main
thread, your lambda passed to Flux.create
executes in the main
thread, and blocks it.
The easiest fix is to use .subscribeOn
instead of .publishOn
so that your code in the lambda passed to Flux.create
operates on a different thread (other than main
). This will prevent the main
thread from blocking, and allow the merged output from both outbound streams to be interleaved.
Thank you very much for taking the time to answer.subscribeOn
instead orpublishOn
did indeed fix the issue. I will play with theThread.sleep()
which writes to theBlockingQueue
and adelay()
after themerge()
to see which flavor ofsubscribeOn
I will ultimately need.
– lafual
Mar 25 at 5:51
add a comment |
There are a few things at play here.
- Note the following recommendation of the
.merge
operator...
Note that merge is tailored to work with asynchronous sources or finite sources. When dealing with an infinite source that doesn't already publish on a dedicated Scheduler, you must isolate that source in its own Scheduler, as merge would otherwise attempt to drain it before subscribing to another source.
Your outbound Fluxes use
.publishOn
, but that only affects operators chained after the.publishOn
operator. i.e. It doesn't affect anything before the.publishOn
. Specifically, it does not affect the thread on which the code in lambda passed toFlux.create
executes. You can see this if you add.log()
before the.publishOn
in each of the outbound Fluxes.Your lambda passed to
Flux.create
calls a blocking method (queue.take
).
Since you call subscribe(...)
on the merged Flux in the main
thread, your lambda passed to Flux.create
executes in the main
thread, and blocks it.
The easiest fix is to use .subscribeOn
instead of .publishOn
so that your code in the lambda passed to Flux.create
operates on a different thread (other than main
). This will prevent the main
thread from blocking, and allow the merged output from both outbound streams to be interleaved.
There are a few things at play here.
- Note the following recommendation of the
.merge
operator...
Note that merge is tailored to work with asynchronous sources or finite sources. When dealing with an infinite source that doesn't already publish on a dedicated Scheduler, you must isolate that source in its own Scheduler, as merge would otherwise attempt to drain it before subscribing to another source.
Your outbound Fluxes use
.publishOn
, but that only affects operators chained after the.publishOn
operator. i.e. It doesn't affect anything before the.publishOn
. Specifically, it does not affect the thread on which the code in lambda passed toFlux.create
executes. You can see this if you add.log()
before the.publishOn
in each of the outbound Fluxes.Your lambda passed to
Flux.create
calls a blocking method (queue.take
).
Since you call subscribe(...)
on the merged Flux in the main
thread, your lambda passed to Flux.create
executes in the main
thread, and blocks it.
The easiest fix is to use .subscribeOn
instead of .publishOn
so that your code in the lambda passed to Flux.create
operates on a different thread (other than main
). This will prevent the main
thread from blocking, and allow the merged output from both outbound streams to be interleaved.
answered Mar 24 at 21:14
Phil ClayPhil Clay
81215
81215
Thank you very much for taking the time to answer.subscribeOn
instead orpublishOn
did indeed fix the issue. I will play with theThread.sleep()
which writes to theBlockingQueue
and adelay()
after themerge()
to see which flavor ofsubscribeOn
I will ultimately need.
– lafual
Mar 25 at 5:51
add a comment |
Thank you very much for taking the time to answer.subscribeOn
instead orpublishOn
did indeed fix the issue. I will play with theThread.sleep()
which writes to theBlockingQueue
and adelay()
after themerge()
to see which flavor ofsubscribeOn
I will ultimately need.
– lafual
Mar 25 at 5:51
Thank you very much for taking the time to answer.
subscribeOn
instead or publishOn
did indeed fix the issue. I will play with the Thread.sleep()
which writes to the BlockingQueue
and a delay()
after the merge()
to see which flavor of subscribeOn
I will ultimately need.– lafual
Mar 25 at 5:51
Thank you very much for taking the time to answer.
subscribeOn
instead or publishOn
did indeed fix the issue. I will play with the Thread.sleep()
which writes to the BlockingQueue
and a delay()
after the merge()
to see which flavor of subscribeOn
I will ultimately need.– lafual
Mar 25 at 5:51
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55327981%2fmerging-hot-flux-sources%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown