ActorContext.getParent for Java API #22413
This commit is contained in:
parent
5ff44194a3
commit
2eb226ed32
213 changed files with 1319 additions and 1523 deletions
86
akka-docs/rst/java/code/jdocs/io/IODocTest.java
Normal file
86
akka-docs/rst/java/code/jdocs/io/IODocTest.java
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.AbstractActor;
|
||||
//#imports
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.io.Inet;
|
||||
import akka.io.Tcp;
|
||||
import akka.io.TcpMessage;
|
||||
import akka.io.TcpSO;
|
||||
import akka.util.ByteString;
|
||||
//#imports
|
||||
|
||||
public class IODocTest {
|
||||
|
||||
static public class Demo extends AbstractActor {
|
||||
ActorRef connectionActor = null;
|
||||
ActorRef listener = getSelf();
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.matchEquals("connect", msg -> {
|
||||
//#manager
|
||||
final ActorRef tcp = Tcp.get(system).manager();
|
||||
//#manager
|
||||
//#connect
|
||||
final InetSocketAddress remoteAddr = new InetSocketAddress("127.0.0.1",
|
||||
12345);
|
||||
tcp.tell(TcpMessage.connect(remoteAddr), getSelf());
|
||||
//#connect
|
||||
//#connect-with-options
|
||||
final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1",
|
||||
1234);
|
||||
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
|
||||
options.add(TcpSO.keepAlive(true));
|
||||
tcp.tell(TcpMessage.connect(remoteAddr, localAddr, options, null, false), getSelf());
|
||||
//#connect-with-options
|
||||
})
|
||||
//#connected
|
||||
.match(Tcp.Connected.class, conn -> {
|
||||
connectionActor = getSender();
|
||||
connectionActor.tell(TcpMessage.register(listener), getSelf());
|
||||
})
|
||||
//#connected
|
||||
//#received
|
||||
.match(Tcp.Received.class, recv -> {
|
||||
final ByteString data = recv.data();
|
||||
// and do something with the received data ...
|
||||
})
|
||||
.match(Tcp.CommandFailed.class, failed -> {
|
||||
final Tcp.Command command = failed.cmd();
|
||||
// react to failed connect, bind, write, etc.
|
||||
})
|
||||
.match(Tcp.ConnectionClosed.class, closed -> {
|
||||
if (closed.isAborted()) {
|
||||
// handle close reasons like this
|
||||
}
|
||||
})
|
||||
//#received
|
||||
.matchEquals("bind", msg -> {
|
||||
final ActorRef handler = getSelf();
|
||||
//#bind
|
||||
final ActorRef tcp = Tcp.get(system).manager();
|
||||
final InetSocketAddress localAddr = new InetSocketAddress("127.0.0.1",
|
||||
1234);
|
||||
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
|
||||
options.add(TcpSO.reuseAddress(true));
|
||||
tcp.tell(TcpMessage.bind(handler, localAddr, 10, options, false), getSelf());
|
||||
//#bind
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
static ActorSystem system;
|
||||
|
||||
// This is currently only a compilation test, nothing is run
|
||||
}
|
||||
97
akka-docs/rst/java/code/jdocs/io/JavaReadBackPressure.java
Normal file
97
akka-docs/rst/java/code/jdocs/io/JavaReadBackPressure.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package jdocs.io;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.io.Inet;
|
||||
import akka.io.Tcp;
|
||||
import akka.io.TcpMessage;
|
||||
import akka.util.ByteString;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
public class JavaReadBackPressure {
|
||||
|
||||
static public class Listener extends AbstractActor {
|
||||
ActorRef tcp;
|
||||
ActorRef listener;
|
||||
|
||||
@Override
|
||||
//#pull-accepting
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Tcp.Bound.class, x -> {
|
||||
listener = getSender();
|
||||
// Accept connections one by one
|
||||
listener.tell(TcpMessage.resumeAccepting(1), getSelf());
|
||||
})
|
||||
.match(Tcp.Connected.class, x -> {
|
||||
ActorRef handler = getContext().actorOf(Props.create(PullEcho.class, getSender()));
|
||||
getSender().tell(TcpMessage.register(handler), getSelf());
|
||||
// Resume accepting connections
|
||||
listener.tell(TcpMessage.resumeAccepting(1), getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
//#pull-accepting
|
||||
|
||||
@Override
|
||||
public void preStart() throws Exception {
|
||||
//#pull-mode-bind
|
||||
tcp = Tcp.get(getContext().getSystem()).manager();
|
||||
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
|
||||
tcp.tell(
|
||||
TcpMessage.bind(self(), new InetSocketAddress("localhost", 0), 100, options, true),
|
||||
getSelf()
|
||||
);
|
||||
//#pull-mode-bind
|
||||
}
|
||||
|
||||
private void demonstrateConnect() {
|
||||
//#pull-mode-connect
|
||||
final List<Inet.SocketOption> options = new ArrayList<Inet.SocketOption>();
|
||||
tcp.tell(
|
||||
TcpMessage.connect(new InetSocketAddress("localhost", 3000), null, options, null, true),
|
||||
getSelf()
|
||||
);
|
||||
//#pull-mode-connect
|
||||
}
|
||||
}
|
||||
|
||||
static public class Ack implements Tcp.Event {
|
||||
}
|
||||
|
||||
static public class PullEcho extends AbstractActor {
|
||||
final ActorRef connection;
|
||||
|
||||
public PullEcho(ActorRef connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
//#pull-reading-echo
|
||||
@Override
|
||||
public void preStart() throws Exception {
|
||||
connection.tell(TcpMessage.resumeReading(), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Tcp.Received.class, message -> {
|
||||
ByteString data = message.data();
|
||||
connection.tell(TcpMessage.write(data, new Ack()), getSelf());
|
||||
})
|
||||
.match(Ack.class, message -> {
|
||||
connection.tell(TcpMessage.resumeReading(), getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
//#pull-reading-echo
|
||||
}
|
||||
|
||||
}
|
||||
128
akka-docs/rst/java/code/jdocs/io/JavaUdpMulticast.java
Normal file
128
akka-docs/rst/java/code/jdocs/io/JavaUdpMulticast.java
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io;
|
||||
|
||||
//#imports
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.io.Inet;
|
||||
import akka.io.Udp;
|
||||
import akka.io.UdpMessage;
|
||||
import akka.util.ByteString;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.DatagramSocket;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
//#imports
|
||||
|
||||
public class JavaUdpMulticast {
|
||||
//#inet6-protocol-family
|
||||
public static class Inet6ProtocolFamily extends Inet.DatagramChannelCreator {
|
||||
@Override
|
||||
public DatagramChannel create() throws Exception {
|
||||
return DatagramChannel.open(StandardProtocolFamily.INET6);
|
||||
}
|
||||
}
|
||||
//#inet6-protocol-family
|
||||
|
||||
//#multicast-group
|
||||
public static class MulticastGroup extends Inet.AbstractSocketOptionV2 {
|
||||
private String address;
|
||||
private String interf;
|
||||
|
||||
public MulticastGroup(String address, String interf) {
|
||||
this.address = address;
|
||||
this.interf = interf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBind(DatagramSocket s) {
|
||||
try {
|
||||
InetAddress group = InetAddress.getByName(address);
|
||||
NetworkInterface networkInterface = NetworkInterface.getByName(interf);
|
||||
s.getChannel().join(group, networkInterface);
|
||||
} catch (Exception ex) {
|
||||
System.out.println("Unable to join multicast group.");
|
||||
}
|
||||
}
|
||||
}
|
||||
//#multicast-group
|
||||
|
||||
public static class Listener extends AbstractActor {
|
||||
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
ActorRef sink;
|
||||
|
||||
public Listener(String iface, String group, Integer port, ActorRef sink) {
|
||||
this.sink = sink;
|
||||
|
||||
//#bind
|
||||
List<Inet.SocketOption> options = new ArrayList<>();
|
||||
options.add(new Inet6ProtocolFamily());
|
||||
options.add(new MulticastGroup(group, iface));
|
||||
|
||||
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
|
||||
// listen for datagrams on this address
|
||||
InetSocketAddress endpoint = new InetSocketAddress(port);
|
||||
mgr.tell(UdpMessage.bind(self(), endpoint, options), getSelf());
|
||||
//#bind
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Udp.Bound.class, bound -> {
|
||||
log.info("Bound to {}", bound.localAddress());
|
||||
sink.tell(bound, getSelf());
|
||||
})
|
||||
.match(Udp.Received.class, received -> {
|
||||
final String txt = received.data().decodeString("utf-8");
|
||||
log.info("Received '{}' from {}", txt, received.sender());
|
||||
sink.tell(txt, getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sender extends AbstractActor {
|
||||
LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
|
||||
|
||||
String iface;
|
||||
String group;
|
||||
Integer port;
|
||||
String message;
|
||||
|
||||
public Sender(String iface, String group, Integer port, String msg) {
|
||||
this.iface = iface;
|
||||
this.group = group;
|
||||
this.port = port;
|
||||
this.message = msg;
|
||||
|
||||
List<Inet.SocketOption> options = new ArrayList<>();
|
||||
options.add(new Inet6ProtocolFamily());
|
||||
|
||||
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
|
||||
mgr.tell(UdpMessage.simpleSender(options), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Udp.SimpleSenderReady.class, x -> {
|
||||
InetSocketAddress remote = new InetSocketAddress(group + "%" + iface, port);
|
||||
log.info("Sending message to " + remote);
|
||||
getSender().tell(UdpMessage.send(ByteString.fromString(message), remote), getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
100
akka-docs/rst/java/code/jdocs/io/JavaUdpMulticastTest.java
Normal file
100
akka-docs/rst/java/code/jdocs/io/JavaUdpMulticastTest.java
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
import akka.io.Udp;
|
||||
import akka.testkit.JavaTestKit;
|
||||
import akka.testkit.SocketUtil;
|
||||
|
||||
import jdocs.AbstractJavaTest;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class JavaUdpMulticastTest extends AbstractJavaTest {
|
||||
|
||||
static ActorSystem system;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
system = ActorSystem.create("JavaUdpMulticastTest");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUdpMulticast() throws Exception {
|
||||
new JavaTestKit(system) {{
|
||||
List<NetworkInterface> ipv6Ifaces = new ArrayList<>();
|
||||
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements(); ) {
|
||||
NetworkInterface interf = interfaces.nextElement();
|
||||
if (interf.isUp() && interf.supportsMulticast()) {
|
||||
for (Enumeration<InetAddress> addresses = interf.getInetAddresses(); addresses.hasMoreElements(); ) {
|
||||
InetAddress address = addresses.nextElement();
|
||||
if (address instanceof Inet6Address) {
|
||||
ipv6Ifaces.add(interf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ipv6Ifaces.isEmpty()) {
|
||||
system.log().info("JavaUdpMulticastTest skipped since no ipv6 interface supporting multicast could be found");
|
||||
} else {
|
||||
// lots of problems with choosing the wrong interface for this test depending
|
||||
// on the platform (awsdl0 can't be used on OSX, docker[0-9] can't be used in a docker machine etc.)
|
||||
// therefore: try hard to find an interface that _does_ work, and only fail if there was any potentially
|
||||
// working interfaces but all failed
|
||||
for (Iterator<NetworkInterface> interfaceIterator = ipv6Ifaces.iterator(); interfaceIterator.hasNext(); ) {
|
||||
NetworkInterface ipv6Iface = interfaceIterator.next();
|
||||
// host assigned link local multicast address http://tools.ietf.org/html/rfc3307#section-4.3.2
|
||||
// generate a random 32 bit multicast address with the high order bit set
|
||||
final String randomAddress = Long.toHexString(((long) Math.abs(new Random().nextInt())) | (1L << 31)).toUpperCase();
|
||||
final StringBuilder groupBuilder = new StringBuilder("FF02:");
|
||||
for (int i = 0; i < 2; i += 1) {
|
||||
groupBuilder.append(":");
|
||||
groupBuilder.append(randomAddress.subSequence(i * 4, i * 4 + 4));
|
||||
}
|
||||
final String group = groupBuilder.toString();
|
||||
final Integer port = SocketUtil.temporaryUdpIpv6Port(ipv6Iface);
|
||||
final String msg = "ohi";
|
||||
final ActorRef sink = getRef();
|
||||
final String iface = ipv6Iface.getName();
|
||||
|
||||
final ActorRef listener = system.actorOf(Props.create(JavaUdpMulticast.Listener.class, iface, group, port, sink));
|
||||
|
||||
try {
|
||||
expectMsgClass(Udp.Bound.class);
|
||||
final ActorRef sender = system.actorOf(Props.create(JavaUdpMulticast.Sender.class, iface, group, port, msg));
|
||||
expectMsgEquals(msg);
|
||||
// success with one interface is enough
|
||||
break;
|
||||
|
||||
} catch (AssertionError ex) {
|
||||
if (!interfaceIterator.hasNext()) throw ex;
|
||||
else {
|
||||
system.log().info("Failed to run test on interface {}", ipv6Iface.getDisplayName());
|
||||
}
|
||||
} finally {
|
||||
// unbind
|
||||
system.stop(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() {
|
||||
JavaTestKit.shutdownActorSystem(system);
|
||||
system = null;
|
||||
}
|
||||
}
|
||||
85
akka-docs/rst/java/code/jdocs/io/UdpConnectedDocTest.java
Normal file
85
akka-docs/rst/java/code/jdocs/io/UdpConnectedDocTest.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io;
|
||||
|
||||
import akka.japi.pf.ReceiveBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.AbstractActor;
|
||||
//#imports
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.io.Inet;
|
||||
import akka.io.UdpConnected;
|
||||
import akka.io.UdpConnectedMessage;
|
||||
import akka.io.UdpSO;
|
||||
import akka.util.ByteString;
|
||||
//#imports
|
||||
|
||||
public class UdpConnectedDocTest {
|
||||
|
||||
static public class Demo extends AbstractActor {
|
||||
ActorRef connectionActor = null;
|
||||
ActorRef handler = getSelf();
|
||||
ActorSystem system = getContext().getSystem();
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
ReceiveBuilder builder = receiveBuilder();
|
||||
builder.matchEquals("connect", message -> {
|
||||
//#manager
|
||||
final ActorRef udp = UdpConnected.get(system).manager();
|
||||
//#manager
|
||||
//#connect
|
||||
final InetSocketAddress remoteAddr =
|
||||
new InetSocketAddress("127.0.0.1", 12345);
|
||||
udp.tell(UdpConnectedMessage.connect(handler, remoteAddr), getSelf());
|
||||
//#connect
|
||||
//#connect-with-options
|
||||
final InetSocketAddress localAddr =
|
||||
new InetSocketAddress("127.0.0.1", 1234);
|
||||
final List<Inet.SocketOption> options =
|
||||
new ArrayList<Inet.SocketOption>();
|
||||
options.add(UdpSO.broadcast(true));
|
||||
udp.tell(UdpConnectedMessage.connect(handler, remoteAddr, localAddr, options), getSelf());
|
||||
//#connect-with-options
|
||||
});
|
||||
//#connected
|
||||
builder.match(UdpConnected.Connected.class, conn -> {
|
||||
connectionActor = getSender(); // Save the worker ref for later use
|
||||
});
|
||||
//#connected
|
||||
//#received
|
||||
builder
|
||||
.match(UdpConnected.Received.class, recv -> {
|
||||
final ByteString data = recv.data();
|
||||
// and do something with the received data ...
|
||||
})
|
||||
.match(UdpConnected.CommandFailed.class, failed -> {
|
||||
final UdpConnected.Command command = failed.cmd();
|
||||
// react to failed connect, etc.
|
||||
})
|
||||
.match(UdpConnected.Disconnected.class, x -> {
|
||||
// do something on disconnect
|
||||
});
|
||||
//#received
|
||||
builder.matchEquals("send", x -> {
|
||||
ByteString data = ByteString.empty();
|
||||
//#send
|
||||
connectionActor.tell(UdpConnectedMessage.send(data), getSelf());
|
||||
//#send
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateConnect() {
|
||||
}
|
||||
|
||||
}
|
||||
164
akka-docs/rst/java/code/jdocs/io/UdpDocTest.java
Normal file
164
akka-docs/rst/java/code/jdocs/io/UdpDocTest.java
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io;
|
||||
|
||||
//#imports
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.io.Udp;
|
||||
import akka.io.UdpConnected;
|
||||
import akka.io.UdpConnectedMessage;
|
||||
import akka.io.UdpMessage;
|
||||
import akka.util.ByteString;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
//#imports
|
||||
|
||||
public class UdpDocTest {
|
||||
|
||||
//#sender
|
||||
public static class SimpleSender extends AbstractActor {
|
||||
final InetSocketAddress remote;
|
||||
|
||||
public SimpleSender(InetSocketAddress remote) {
|
||||
this.remote = remote;
|
||||
|
||||
// request creation of a SimpleSender
|
||||
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
|
||||
mgr.tell(UdpMessage.simpleSender(), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Udp.SimpleSenderReady.class, message -> {
|
||||
getContext().become(ready(sender()));
|
||||
//#sender
|
||||
getSender().tell(UdpMessage.send(ByteString.fromString("hello"), remote), getSelf());
|
||||
//#sender
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Receive ready(final ActorRef send) {
|
||||
return receiveBuilder()
|
||||
.match(String.class, message -> {
|
||||
send.tell(UdpMessage.send(ByteString.fromString(message), remote), getSelf());
|
||||
//#sender
|
||||
if (message.equals("world")) {
|
||||
send.tell(PoisonPill.getInstance(), getSelf());
|
||||
}
|
||||
//#sender
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#sender
|
||||
|
||||
//#listener
|
||||
public static class Listener extends AbstractActor {
|
||||
final ActorRef nextActor;
|
||||
|
||||
public Listener(ActorRef nextActor) {
|
||||
this.nextActor = nextActor;
|
||||
|
||||
// request creation of a bound listen socket
|
||||
final ActorRef mgr = Udp.get(getContext().getSystem()).getManager();
|
||||
mgr.tell(
|
||||
UdpMessage.bind(self(), new InetSocketAddress("localhost", 0)),
|
||||
getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Udp.Bound.class, bound -> {
|
||||
//#listener
|
||||
nextActor.tell(bound.localAddress(), getSender());
|
||||
//#listener
|
||||
getContext().become(ready(getSender()));
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Receive ready(final ActorRef socket) {
|
||||
return receiveBuilder()
|
||||
.match(Udp.Received.class, r -> {
|
||||
// echo server example: send back the data
|
||||
socket.tell(UdpMessage.send(r.data(), r.sender()), getSelf());
|
||||
// or do some processing and forward it on
|
||||
final Object processed = // parse data etc., e.g. using PipelineStage
|
||||
// #listener
|
||||
r.data().utf8String();
|
||||
//#listener
|
||||
nextActor.tell(processed, getSelf());
|
||||
})
|
||||
.matchEquals(UdpMessage.unbind(), message -> {
|
||||
socket.tell(message, getSelf());
|
||||
})
|
||||
.match(Udp.Unbound.class, message -> {
|
||||
getContext().stop(getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#listener
|
||||
|
||||
//#connected
|
||||
public static class Connected extends AbstractActor {
|
||||
final InetSocketAddress remote;
|
||||
|
||||
public Connected(InetSocketAddress remote) {
|
||||
this.remote = remote;
|
||||
|
||||
// create a restricted a.k.a. “connected” socket
|
||||
final ActorRef mgr = UdpConnected.get(getContext().getSystem()).getManager();
|
||||
mgr.tell(UdpConnectedMessage.connect(self(), remote), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(UdpConnected.Connected.class, message -> {
|
||||
getContext().become(ready(sender()));
|
||||
//#connected
|
||||
getSender()
|
||||
.tell(UdpConnectedMessage.send(ByteString.fromString("hello")),
|
||||
getSelf());
|
||||
//#connected
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Receive ready(final ActorRef connection) {
|
||||
return receiveBuilder()
|
||||
.match(UdpConnected.Received.class, r -> {
|
||||
// process data, send it on, etc.
|
||||
// #connected
|
||||
if (r.data().utf8String().equals("hello")) {
|
||||
connection.tell(
|
||||
UdpConnectedMessage.send(ByteString.fromString("world")),
|
||||
getSelf());
|
||||
}
|
||||
// #connected
|
||||
})
|
||||
.match(String.class, str -> {
|
||||
connection
|
||||
.tell(UdpConnectedMessage.send(ByteString.fromString(str)),
|
||||
getSelf());
|
||||
})
|
||||
.matchEquals(UdpConnectedMessage.disconnect(), message -> {
|
||||
connection.tell(message, getSelf());
|
||||
})
|
||||
.match(UdpConnected.Disconnected.class, x -> {
|
||||
getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#connected
|
||||
|
||||
}
|
||||
243
akka-docs/rst/java/code/jdocs/io/japi/EchoHandler.java
Normal file
243
akka-docs/rst/java/code/jdocs/io/japi/EchoHandler.java
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.io.Tcp.CommandFailed;
|
||||
import akka.io.Tcp.ConnectionClosed;
|
||||
import akka.io.Tcp.Event;
|
||||
import akka.io.Tcp.Received;
|
||||
import akka.io.Tcp.Write;
|
||||
import akka.io.Tcp.WritingResumed;
|
||||
import akka.io.TcpMessage;
|
||||
import akka.util.ByteString;
|
||||
|
||||
//#echo-handler
|
||||
public class EchoHandler extends AbstractActor {
|
||||
|
||||
final LoggingAdapter log = Logging
|
||||
.getLogger(getContext().getSystem(), getSelf());
|
||||
|
||||
final ActorRef connection;
|
||||
final InetSocketAddress remote;
|
||||
|
||||
public static final long MAX_STORED = 100000000;
|
||||
public static final long HIGH_WATERMARK = MAX_STORED * 5 / 10;
|
||||
public static final long LOW_WATERMARK = MAX_STORED * 2 / 10;
|
||||
|
||||
private long transferred;
|
||||
private int storageOffset = 0;
|
||||
private long stored = 0;
|
||||
private Queue<ByteString> storage = new LinkedList<ByteString>();
|
||||
|
||||
private boolean suspended = false;
|
||||
|
||||
private static class Ack implements Event {
|
||||
public final int ack;
|
||||
public Ack(int ack) {
|
||||
this.ack = ack;
|
||||
}
|
||||
}
|
||||
|
||||
public EchoHandler(ActorRef connection, InetSocketAddress remote) {
|
||||
this.connection = connection;
|
||||
this.remote = remote;
|
||||
|
||||
writing = writing();
|
||||
|
||||
// sign death pact: this actor stops when the connection is closed
|
||||
getContext().watch(connection);
|
||||
|
||||
// start out in optimistic write-through mode
|
||||
getContext().become(writing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return writing;
|
||||
}
|
||||
|
||||
private final Receive writing;
|
||||
|
||||
private Receive writing() {
|
||||
return receiveBuilder()
|
||||
.match(Received.class, msg -> {
|
||||
final ByteString data = msg.data();
|
||||
connection.tell(TcpMessage.write(data, new Ack(currentOffset())), getSelf());
|
||||
buffer(data);
|
||||
|
||||
})
|
||||
.match(Integer.class, msg -> {
|
||||
acknowledge(msg);
|
||||
})
|
||||
.match(CommandFailed.class, msg -> {
|
||||
final Write w = (Write) msg.cmd();
|
||||
connection.tell(TcpMessage.resumeWriting(), getSelf());
|
||||
getContext().become(buffering((Ack) w.ack()));
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
if (msg.isPeerClosed()) {
|
||||
if (storage.isEmpty()) {
|
||||
getContext().stop(self());
|
||||
} else {
|
||||
getContext().become(closing());
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
//#buffering
|
||||
|
||||
final static class BufferingState {
|
||||
int toAck = 10;
|
||||
boolean peerClosed = false;
|
||||
}
|
||||
|
||||
protected Receive buffering(final Ack nack) {
|
||||
final BufferingState state = new BufferingState();
|
||||
|
||||
return receiveBuilder()
|
||||
.match(Received.class, msg -> {
|
||||
buffer(msg.data());
|
||||
|
||||
})
|
||||
.match(WritingResumed.class, msg -> {
|
||||
writeFirst();
|
||||
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
if (msg.isPeerClosed())
|
||||
state.peerClosed = true;
|
||||
else
|
||||
getContext().stop(self());
|
||||
|
||||
})
|
||||
.match(Integer.class, ack -> {
|
||||
acknowledge(ack);
|
||||
|
||||
if (ack >= nack.ack) {
|
||||
// otherwise it was the ack of the last successful write
|
||||
|
||||
if (storage.isEmpty()) {
|
||||
if (state.peerClosed)
|
||||
getContext().stop(self());
|
||||
else
|
||||
getContext().become(writing);
|
||||
|
||||
} else {
|
||||
if (state.toAck > 0) {
|
||||
// stay in ACK-based mode for a short while
|
||||
writeFirst();
|
||||
--state.toAck;
|
||||
} else {
|
||||
// then return to NACK-based again
|
||||
writeAll();
|
||||
if (state.peerClosed)
|
||||
getContext().become(closing());
|
||||
else
|
||||
getContext().become(writing);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
//#buffering
|
||||
|
||||
//#closing
|
||||
protected Receive closing() {
|
||||
return receiveBuilder()
|
||||
.match(CommandFailed.class, msg -> {
|
||||
// the command can only have been a Write
|
||||
connection.tell(TcpMessage.resumeWriting(), getSelf());
|
||||
getContext().become(closeResend(), false);
|
||||
})
|
||||
.match(Integer.class, msg -> {
|
||||
acknowledge(msg);
|
||||
if (storage.isEmpty())
|
||||
getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
protected Receive closeResend() {
|
||||
return receiveBuilder()
|
||||
.match(WritingResumed.class, msg -> {
|
||||
writeAll();
|
||||
getContext().unbecome();
|
||||
})
|
||||
.match(Integer.class, msg -> {
|
||||
acknowledge(msg);
|
||||
})
|
||||
.build();
|
||||
}
|
||||
//#closing
|
||||
|
||||
//#storage-omitted
|
||||
|
||||
@Override
|
||||
public void postStop() {
|
||||
log.info("transferred {} bytes from/to [{}]", transferred, remote);
|
||||
}
|
||||
|
||||
//#helpers
|
||||
protected void buffer(ByteString data) {
|
||||
storage.add(data);
|
||||
stored += data.size();
|
||||
|
||||
if (stored > MAX_STORED) {
|
||||
log.warning("drop connection to [{}] (buffer overrun)", remote);
|
||||
getContext().stop(self());
|
||||
|
||||
} else if (stored > HIGH_WATERMARK) {
|
||||
log.debug("suspending reading at {}", currentOffset());
|
||||
connection.tell(TcpMessage.suspendReading(), getSelf());
|
||||
suspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void acknowledge(int ack) {
|
||||
assert ack == storageOffset;
|
||||
assert !storage.isEmpty();
|
||||
|
||||
final ByteString acked = storage.remove();
|
||||
stored -= acked.size();
|
||||
transferred += acked.size();
|
||||
storageOffset += 1;
|
||||
|
||||
if (suspended && stored < LOW_WATERMARK) {
|
||||
log.debug("resuming reading");
|
||||
connection.tell(TcpMessage.resumeReading(), getSelf());
|
||||
suspended = false;
|
||||
}
|
||||
}
|
||||
//#helpers
|
||||
|
||||
protected int currentOffset() {
|
||||
return storageOffset + storage.size();
|
||||
}
|
||||
|
||||
protected void writeAll() {
|
||||
int i = 0;
|
||||
for (ByteString data : storage) {
|
||||
connection.tell(TcpMessage.write(data, new Ack(storageOffset + i++)), getSelf());
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeFirst() {
|
||||
connection.tell(TcpMessage.write(storage.peek(), new Ack(storageOffset)), getSelf());
|
||||
}
|
||||
|
||||
//#storage-omitted
|
||||
}
|
||||
//#echo-handler
|
||||
81
akka-docs/rst/java/code/jdocs/io/japi/EchoManager.java
Normal file
81
akka-docs/rst/java/code/jdocs/io/japi/EchoManager.java
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.SupervisorStrategy;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.io.Tcp;
|
||||
import akka.io.Tcp.Bind;
|
||||
import akka.io.Tcp.Bound;
|
||||
import akka.io.Tcp.Connected;
|
||||
import akka.io.TcpMessage;
|
||||
|
||||
public class EchoManager extends AbstractActor {
|
||||
|
||||
final LoggingAdapter log = Logging
|
||||
.getLogger(getContext().getSystem(), getSelf());
|
||||
|
||||
final Class<?> handlerClass;
|
||||
|
||||
public EchoManager(Class<?> handlerClass) {
|
||||
this.handlerClass = handlerClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupervisorStrategy supervisorStrategy() {
|
||||
return SupervisorStrategy.stoppingStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStart() throws Exception {
|
||||
//#manager
|
||||
final ActorRef tcpManager = Tcp.get(getContext().getSystem()).manager();
|
||||
//#manager
|
||||
tcpManager.tell(
|
||||
TcpMessage.bind(self(), new InetSocketAddress("localhost", 0), 100),
|
||||
getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRestart(Throwable arg0) throws Exception {
|
||||
// do not restart
|
||||
getContext().stop(self());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Bound.class, msg -> {
|
||||
log.info("listening on [{}]", msg.localAddress());
|
||||
})
|
||||
.match(Tcp.CommandFailed.class, failed -> {
|
||||
if (failed.cmd() instanceof Bind) {
|
||||
log.warning("cannot bind to [{}]", ((Bind) failed.cmd()).localAddress());
|
||||
getContext().stop(self());
|
||||
} else {
|
||||
log.warning("unknown command failed [{}]", failed.cmd());
|
||||
}
|
||||
})
|
||||
.match(Connected.class, conn -> {
|
||||
log.info("received connection from [{}]", conn.remoteAddress());
|
||||
final ActorRef connection = getSender();
|
||||
final ActorRef handler = getContext().actorOf(
|
||||
Props.create(handlerClass, connection, conn.remoteAddress()));
|
||||
//#echo-manager
|
||||
connection.tell(TcpMessage.register(handler,
|
||||
true, // <-- keepOpenOnPeerClosed flag
|
||||
true), getSelf());
|
||||
//#echo-manager
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
35
akka-docs/rst/java/code/jdocs/io/japi/EchoServer.java
Normal file
35
akka-docs/rst/java/code/jdocs/io/japi/EchoServer.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
public class EchoServer {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
final Config config = ConfigFactory.parseString("akka.loglevel=DEBUG");
|
||||
final ActorSystem system = ActorSystem.create("EchoServer", config);
|
||||
try {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final ActorRef watcher = system.actorOf(Props.create(Watcher.class, latch), "watcher");
|
||||
final ActorRef nackServer = system.actorOf(Props.create(EchoManager.class, EchoHandler.class), "nack");
|
||||
final ActorRef ackServer = system.actorOf(Props.create(EchoManager.class, SimpleEchoHandler.class), "ack");
|
||||
watcher.tell(nackServer, ActorRef.noSender());
|
||||
watcher.tell(ackServer, ActorRef.noSender());
|
||||
latch.await(10, TimeUnit.MINUTES);
|
||||
} finally {
|
||||
system.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
186
akka-docs/rst/java/code/jdocs/io/japi/IODocTest.java
Normal file
186
akka-docs/rst/java/code/jdocs/io/japi/IODocTest.java
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
|
||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||
import jdocs.AbstractJavaTest;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
//#imports
|
||||
import java.net.InetSocketAddress;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.io.Tcp;
|
||||
import akka.io.Tcp.Bound;
|
||||
import akka.io.Tcp.CommandFailed;
|
||||
import akka.io.Tcp.Connected;
|
||||
import akka.io.Tcp.ConnectionClosed;
|
||||
import akka.io.Tcp.Received;
|
||||
import akka.io.TcpMessage;
|
||||
import akka.util.ByteString;
|
||||
//#imports
|
||||
|
||||
import akka.testkit.JavaTestKit;
|
||||
import akka.testkit.AkkaSpec;
|
||||
|
||||
public class IODocTest extends AbstractJavaTest {
|
||||
|
||||
static
|
||||
//#server
|
||||
public class Server extends AbstractActor {
|
||||
|
||||
final ActorRef manager;
|
||||
|
||||
public Server(ActorRef manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public static Props props(ActorRef manager) {
|
||||
return Props.create(Server.class, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStart() throws Exception {
|
||||
final ActorRef tcp = Tcp.get(getContext().getSystem()).manager();
|
||||
tcp.tell(TcpMessage.bind(self(),
|
||||
new InetSocketAddress("localhost", 0), 100), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Bound.class, msg -> {
|
||||
manager.tell(msg, getSelf());
|
||||
|
||||
})
|
||||
.match(CommandFailed.class, msg -> {
|
||||
getContext().stop(self());
|
||||
|
||||
})
|
||||
.match(Connected.class, conn -> {
|
||||
manager.tell(conn, getSelf());
|
||||
final ActorRef handler = getContext().actorOf(
|
||||
Props.create(SimplisticHandler.class));
|
||||
getSender().tell(TcpMessage.register(handler), getSelf());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
//#server
|
||||
|
||||
static
|
||||
//#simplistic-handler
|
||||
public class SimplisticHandler extends AbstractActor {
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Received.class, msg -> {
|
||||
final ByteString data = msg.data();
|
||||
System.out.println(data);
|
||||
getSender().tell(TcpMessage.write(data), getSelf());
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
//#simplistic-handler
|
||||
|
||||
static
|
||||
//#client
|
||||
public class Client extends AbstractActor {
|
||||
|
||||
final InetSocketAddress remote;
|
||||
final ActorRef listener;
|
||||
|
||||
public static Props props(InetSocketAddress remote, ActorRef listener) {
|
||||
return Props.create(Client.class, remote, listener);
|
||||
}
|
||||
|
||||
public Client(InetSocketAddress remote, ActorRef listener) {
|
||||
this.remote = remote;
|
||||
this.listener = listener;
|
||||
|
||||
final ActorRef tcp = Tcp.get(getContext().getSystem()).manager();
|
||||
tcp.tell(TcpMessage.connect(remote), getSelf());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(CommandFailed.class, msg -> {
|
||||
listener.tell("failed", getSelf());
|
||||
getContext().stop(self());
|
||||
|
||||
})
|
||||
.match(Connected.class, msg -> {
|
||||
listener.tell(msg, getSelf());
|
||||
getSender().tell(TcpMessage.register(self()), getSelf());
|
||||
getContext().become(connected(sender()));
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Receive connected(final ActorRef connection) {
|
||||
return receiveBuilder()
|
||||
.match(ByteString.class, msg -> {
|
||||
connection.tell(TcpMessage.write((ByteString) msg), getSelf());
|
||||
})
|
||||
.match(CommandFailed.class, msg -> {
|
||||
// OS kernel socket buffer was full
|
||||
})
|
||||
.match(Received.class, msg -> {
|
||||
listener.tell(msg.data(), getSelf());
|
||||
})
|
||||
.matchEquals("close", msg -> {
|
||||
connection.tell(TcpMessage.close(), getSelf());
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
//#client
|
||||
|
||||
@ClassRule
|
||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||
new AkkaJUnitActorSystemResource("IODocTest", AkkaSpec.testConf());
|
||||
|
||||
private final ActorSystem system = actorSystemResource.getSystem();
|
||||
|
||||
@Test
|
||||
public void testConnection() {
|
||||
new JavaTestKit(system) {
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
final ActorRef server = system.actorOf(Server.props(getRef()), "server1");
|
||||
final InetSocketAddress listen = expectMsgClass(Bound.class).localAddress();
|
||||
final ActorRef client = system.actorOf(Client.props(listen, getRef()), "client1");
|
||||
|
||||
final Connected c1 = expectMsgClass(Connected.class);
|
||||
final Connected c2 = expectMsgClass(Connected.class);
|
||||
assert c1.localAddress().equals(c2.remoteAddress());
|
||||
assert c2.localAddress().equals(c1.remoteAddress());
|
||||
|
||||
client.tell(ByteString.fromString("hello"), getRef());
|
||||
final ByteString reply = expectMsgClass(ByteString.class);
|
||||
assert reply.utf8String().equals("hello");
|
||||
|
||||
watch(client);
|
||||
client.tell("close", getRef());
|
||||
expectTerminated(client);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
45
akka-docs/rst/java/code/jdocs/io/japi/Message.java
Normal file
45
akka-docs/rst/java/code/jdocs/io/japi/Message.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Copyright (C) 2013-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
//#message
|
||||
public class Message {
|
||||
|
||||
static public class Person {
|
||||
private final String first;
|
||||
private final String last;
|
||||
|
||||
public Person(String first, String last) {
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public String getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public String getLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Person[] persons;
|
||||
private final double[] happinessCurve;
|
||||
|
||||
public Message(Person[] persons, double[] happinessCurve) {
|
||||
this.persons = persons;
|
||||
this.happinessCurve = happinessCurve;
|
||||
}
|
||||
|
||||
public Person[] getPersons() {
|
||||
return persons;
|
||||
}
|
||||
|
||||
public double[] getHappinessCurve() {
|
||||
return happinessCurve;
|
||||
}
|
||||
}
|
||||
//#message
|
||||
134
akka-docs/rst/java/code/jdocs/io/japi/SimpleEchoHandler.java
Normal file
134
akka-docs/rst/java/code/jdocs/io/japi/SimpleEchoHandler.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
|
||||
package jdocs.io.japi;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.AbstractActor;
|
||||
import akka.event.Logging;
|
||||
import akka.event.LoggingAdapter;
|
||||
import akka.io.Tcp.ConnectionClosed;
|
||||
import akka.io.Tcp.Event;
|
||||
import akka.io.Tcp.Received;
|
||||
import akka.io.TcpMessage;
|
||||
import akka.util.ByteString;
|
||||
|
||||
//#simple-echo-handler
|
||||
public class SimpleEchoHandler extends AbstractActor {
|
||||
|
||||
final LoggingAdapter log = Logging
|
||||
.getLogger(getContext().getSystem(), getSelf());
|
||||
|
||||
final ActorRef connection;
|
||||
final InetSocketAddress remote;
|
||||
|
||||
public static final long maxStored = 100000000;
|
||||
public static final long highWatermark = maxStored * 5 / 10;
|
||||
public static final long lowWatermark = maxStored * 2 / 10;
|
||||
|
||||
public SimpleEchoHandler(ActorRef connection, InetSocketAddress remote) {
|
||||
this.connection = connection;
|
||||
this.remote = remote;
|
||||
|
||||
// sign death pact: this actor stops when the connection is closed
|
||||
getContext().watch(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Received.class, msg -> {
|
||||
final ByteString data = msg.data();
|
||||
buffer(data);
|
||||
connection.tell(TcpMessage.write(data, ACK), getSelf());
|
||||
// now switch behavior to “waiting for acknowledgement”
|
||||
getContext().become(buffering(), false);
|
||||
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private final Receive buffering() {
|
||||
return receiveBuilder()
|
||||
.match(Received.class, msg -> {
|
||||
buffer(msg.data());
|
||||
|
||||
})
|
||||
.match(Event.class, msg -> msg == ACK, msg -> {
|
||||
acknowledge();
|
||||
|
||||
})
|
||||
.match(ConnectionClosed.class, msg -> {
|
||||
if (msg.isPeerClosed()) {
|
||||
closing = true;
|
||||
} else {
|
||||
// could also be ErrorClosed, in which case we just give up
|
||||
getContext().stop(self());
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
//#storage-omitted
|
||||
public void postStop() {
|
||||
log.info("transferred {} bytes from/to [{}]", transferred, remote);
|
||||
}
|
||||
|
||||
private long transferred;
|
||||
private long stored = 0;
|
||||
private Queue<ByteString> storage = new LinkedList<ByteString>();
|
||||
|
||||
private boolean suspended = false;
|
||||
private boolean closing = false;
|
||||
|
||||
private final Event ACK = new Event() {};
|
||||
|
||||
//#simple-helpers
|
||||
protected void buffer(ByteString data) {
|
||||
storage.add(data);
|
||||
stored += data.size();
|
||||
|
||||
if (stored > maxStored) {
|
||||
log.warning("drop connection to [{}] (buffer overrun)", remote);
|
||||
getContext().stop(self());
|
||||
|
||||
} else if (stored > highWatermark) {
|
||||
log.debug("suspending reading");
|
||||
connection.tell(TcpMessage.suspendReading(), getSelf());
|
||||
suspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void acknowledge() {
|
||||
final ByteString acked = storage.remove();
|
||||
stored -= acked.size();
|
||||
transferred += acked.size();
|
||||
|
||||
if (suspended && stored < lowWatermark) {
|
||||
log.debug("resuming reading");
|
||||
connection.tell(TcpMessage.resumeReading(), getSelf());
|
||||
suspended = false;
|
||||
}
|
||||
|
||||
if (storage.isEmpty()) {
|
||||
if (closing) {
|
||||
getContext().stop(self());
|
||||
} else {
|
||||
getContext().unbecome();
|
||||
}
|
||||
} else {
|
||||
connection.tell(TcpMessage.write(storage.peek(), ACK), getSelf());
|
||||
}
|
||||
}
|
||||
//#simple-helpers
|
||||
//#storage-omitted
|
||||
}
|
||||
//#simple-echo-handler
|
||||
37
akka-docs/rst/java/code/jdocs/io/japi/Watcher.java
Normal file
37
akka-docs/rst/java/code/jdocs/io/japi/Watcher.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package jdocs.io.japi;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Terminated;
|
||||
import akka.actor.AbstractActor;
|
||||
|
||||
public class Watcher extends AbstractActor {
|
||||
|
||||
static public class Watch {
|
||||
final ActorRef target;
|
||||
public Watch(ActorRef target) {
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
|
||||
final CountDownLatch latch;
|
||||
|
||||
public Watcher(CountDownLatch latch) {
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Receive createReceive() {
|
||||
return receiveBuilder()
|
||||
.match(Watch.class, msg -> {
|
||||
getContext().watch(msg.target);
|
||||
})
|
||||
.match(Terminated.class, msg -> {
|
||||
latch.countDown();
|
||||
if (latch.getCount() == 0) getContext().stop(self());
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue