Ok, here are two examples on the top of my head, of fairly trivial programs that are a pain to do right in Java.
1) A program that copies all data from standard input to the standard output. Try to write this without Googling. Simple enough, eh?
2) A program that serves as a middleware between servers and clients, both of which are behind NATs so they connect to your middleware, using gRPC methods. Servers use gRPC streams to maintain a connection, send their ID as the first message, receive request messages, and send reply messages. Clients send ID and request in a message, and receive reply message.
I hope you're willing to show me how wrong I am by writing the code :)
1 is really trivial and took me like 5 seconds to write:
public class Test {
public static void main(String[] args) {
System.in.transferTo(System.out);
}
}
2 is anything but trivial in any language, unless it is some kind of a language specifically designed to build gRPC programs.
Never versions of Java are quite pleasant to work with. There are a lots of quality-of-life improvements (switch expression, multi-line string literals, string templates (currently in preview), lots of useful stdlib APIs), as well as quite fundamental additions like Loom.
Ok, that's really convenient :) I suppose it would be much harder to do it without the transferTo function, but that would be beyond the point. I'll instead focus on the example 2.
> 2 is anything but trivial in any language, unless it is some kind of a language specifically designed to build gRPC programs.
> I suppose it would be much harder to do it without the transferTo function,
No, it would not:
public class Test {
public static void main(String[] args) throws Exception {
byte[] buf = new byte[4096];
int read;
while ((read = System.in.read(buf)) > 0) {
System.out.write(buf, 0, read);
}
}
}
I imagine it will look pretty much the same in the majority of languages which have similar I/O abstractions.
It is under 120 lines, uses Java SE API only, and the complexity is approximately the same. Go does have advantage of built-in selects and channels, which make parts of the logic nicer, but not by much IMO.
On the other hand, in Java almost no one uses standard library to write network apps like this. If I had to use external libraries, which would be the case in the majority of real-world projects, the implementation would look even better and clearer.
That's impressive. However, even the direct reimplementation has introduced a subtle bug - a connection that sends something other than "server" or "client" is left hanging and never closed (use nc with -N flag, and press Ctrl+D to send EOF to see what I mean - it should disconnect and yet it doesn't). While not a functional bug, it could still cause a DoS attack if someone wanted to hog all the available sockets.
In Go, this is avoided by `defer conn.Close()` which guarantees that the connection will be closed at the end of the function execution, no matter what. It's a sort-of nicer syntax of "try {} finally {}" that let's you write the cleanup code immediately after the setup code, instead of at the end.
> On the other hand, in Java almost no one uses standard library to write network apps like this
I'm curious about the way the average Java programmer would write such an application. Would you please write an example of such a program, using an external library that would make implementation both correct and easy to read?
I can try doing it later today, but I’m 100% sure that TCP example will look pretty much the same in Java, in terms of complexity.
> The gRPC protocol here is only an example. Feel free to use any protocol you like.
FWIW, this is a pretty huge change. gRPC is quite complex, as it includes protobuf, http/2 and its own RPC mechanism. A simple TCP protocol is peanuts compared to that.