169

I am a normal C# developer but occasionally I develop application in Java. I'm wondering if there is any Java equivalent of C# async/await? In simple words what is the java equivalent of:

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();
    var urlContents = await client.GetStringAsync("http://msdn.microsoft.com");
    return urlContents.Length;
}
mehrandvd
  • 8,250
  • 10
  • 54
  • 102
user960567
  • 28,664
  • 58
  • 165
  • 292
  • 8
    Why this would be nice: [Callbacks as our Generations’ Go To Statement](http://tirania.org/blog/archive/2013/Aug-15.html) by Miguel de Icaza. – andrewdotn Nov 12 '13 at 04:00
  • Java's current solution is to not deal with actual values prefixed with `async`, but use `Future` or `Observable` values instead. – S.D. Feb 23 '18 at 04:32
  • 3
    There is no equivalent. And it hurts. One more missing feature you need complicated workarounds and libraries for, without ever reaching the same effect as these two, simple words. – spyro Dec 12 '19 at 17:35
  • It's worth noting that for a long time, Java designers have tried to keep the Java bytecode backward compatible with only changes to libraries and Syntatic-sugar around existing features. *See the fact that generics don't store run-time type information and lambders are implemented as object implementing an interface*. async/await would require very large changes to the bytecode to be possible and therefore I wouldn't expect to see it in java any time soon. – Philip Couling Feb 21 '20 at 14:10

15 Answers15

158

No, there isn't any equivalent of async/await in Java - or even in C# before v5.

It's a fairly complex language feature to build a state machine behind the scenes.

There's relatively little language support for asynchrony/concurrency in Java, but the java.util.concurrent package contains a lot of useful classes around this. (Not quite equivalent to the Task Parallel Library, but the closest approximation to it.)

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • Thanks. Is there any third party library for that? or any plan for the upcoming java – user960567 May 14 '13 at 09:19
  • 16
    @user960567: No, my point is that it's a *language* feature - it can't be put solely in libraries. I don't believe there's any equivalent scheduled for Java 8 at least. – Jon Skeet May 14 '13 at 09:23
  • Sorry, but for your comment "even in C# before v5", I think with Microsoft.Bcl.Async we can use async await in .NET 4.0 – user960567 May 14 '13 at 09:30
  • 11
    @user960567: You need to distinguish between the version of C# you're using and the version of .NET you're using. async/await is a *language* feature - it was introduced in C# 5. Yes, you can use Microsoft.Bcl.Async to use async/await targeting .NET 4, but you've still got to use a C# 5 compiler. – Jon Skeet May 14 '13 at 09:31
  • @JonSkeet Can we say netflix's RX https://github.com/Netflix/RxJava will accomplish the asynchronous we are expecting – rozar Mar 26 '14 at 06:27
  • 6
    @rozar: No, not really. There are already multiple options for asynchrony - but RxJava doesn't change the *language* in the way that C# did. I have nothing against Rx, but it's not the same thing as async in C# 5. – Jon Skeet Mar 26 '14 at 06:46
  • @JonSkeet you do agree that the JVM is highly concurrent and possesses many asynchronous mechanisms .. right? – DtechNet Oct 21 '15 at 00:01
  • 11
    @DtechNet: Well there's a lot of JVM machinery which is asynchronous, yes... that's very different from there being *actual language features* supporting asynchrony though. (There was a lot of asynchrony in .NET before async/await, too... but async/await makes it *far* easier to take advantage of that.) – Jon Skeet Oct 21 '15 at 05:45
  • I know this thread is old, but I think the answer marked as correct is outdated. There are solutions pointed out in the other answers which do not match the async/await syntax by 100%, but get fairly close. – Aarkon Jun 11 '19 at 09:36
  • 3
    @Aarkon: I would argue that unless there's explicit *language* support, the answer is still correct. It's not just a matter of libraries that make scheduling simpler - the whole way the C# compiler builds a state machine is important here. – Jon Skeet Jun 11 '19 at 11:09
  • Agreed, your answer is still technically correct and for sure you have a point saying that library support is something different than on the language core. Yet, since the original question was if there is "any Java equivalent" to async/await, it feels incomplete to me. – Aarkon Jun 11 '19 at 14:55
  • @Aarkon: The third paragraph explicitly talks about there being no *language* equivalent, but a bunch of classes already in the base libraries. I'm content with this answer - I don't intend to change it. – Jon Skeet Jun 11 '19 at 15:38
  • At least to me, reading your answer, it didn't feel like what I was looking for already knowing there were some libraries around that make it relatively easy to write async java. But of course, your mileage may vary. – Aarkon Jun 13 '19 at 10:04
  • 1
    @MohamedIqzas: Those aren't really equivalent to async/await. There are plenty of ways of writing parallel and/or asynchronous code in Java, but async/await provides additional *language-level* support that makes a big difference. – Jon Skeet Jun 26 '20 at 06:30
  • It's 2020 now and whoever still wants async/await in Java, you can simply use Kotlin and Kotlin coroutines. Fortunately it has Java interpolability. – dephinera Oct 15 '20 at 21:18
46

The await uses a continuation to execute additional code when the asynchronous operation completes (client.GetStringAsync(...)).

So, as the most close approximation I would use a CompletableFuture<T> (the Java 8 equivalent to .net Task<TResult>) based solution to process the Http request asynchronously.

UPDATED on 25-05-2016 to AsyncHttpClient v.2 released on Abril 13th of 2016:

So the Java 8 equivalent to the OP example of AccessTheWebAsync() is the following:

CompletableFuture<Integer> AccessTheWebAsync()
{
    AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
    return asyncHttpClient
       .prepareGet("http://msdn.microsoft.com")
       .execute()
       .toCompletableFuture()
       .thenApply(Response::getResponseBody)
       .thenApply(String::length);
}

This usage was taken from the answer to How do I get a CompletableFuture from an Async Http Client request? and which is according to the new API provided in version 2 of AsyncHttpClient released on Abril 13th of 2016, that has already intrinsic support for CompletableFuture<T>.

Original answer using version 1 of AsyncHttpClient:

To that end we have two possible approaches:

  • the first one uses non-blocking IO and I call it AccessTheWebAsyncNio. Yet, because the AsyncCompletionHandler is an abstract class (instead of a functional interface) we cannot pass a lambda as argument. So it incurs in inevitable verbosity due to the syntax of anonymous classes. However, this solution is the most close to the execution flow of the given C# example.

  • the second one is slightly less verbose however it will submit a new Task that ultimately will block a thread on f.get() until the response is complete.

First approach, more verbose but non-blocking:

static CompletableFuture<Integer> AccessTheWebAsyncNio(){
    final AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
    final CompletableFuture<Integer> promise = new CompletableFuture<>();
    asyncHttpClient
        .prepareGet("https://msdn.microsoft.com")
        .execute(new AsyncCompletionHandler<Response>(){
            @Override
            public Response onCompleted(Response resp) throws Exception {
                promise.complete(resp.getResponseBody().length());
                return resp;
            }
        });
    return promise;
}

Second approach less verbose but blocking a thread:

static CompletableFuture<Integer> AccessTheWebAsync(){
    try(AsyncHttpClient asyncHttpClient = new AsyncHttpClient()){
        Future<Response> f = asyncHttpClient
            .prepareGet("https://msdn.microsoft.com")
            .execute();
        return CompletableFuture.supplyAsync(
            () -> return f.join().getResponseBody().length());
    }
}
Community
  • 1
  • 1
Miguel Gamboa
  • 7,718
  • 5
  • 41
  • 79
  • 2
    Actually, that's the equivalent of the happy flow. It doesn't cover handling exceptions, finally and others. Including them will make the code much more complex and more prone to errors. – haimb Jan 16 '19 at 06:20
  • 1
    This isn't a continuation. This example misses the real purpose of async/await, which is to release the current thread to execute other things, and then to continue execution of this method on the current thread after a response arrives. (This is either necessary for the UI thread to be responsive, or to reduce memory usage.) What this example does is a plain blocking thread synchronization, plus some callbacks. – Aleksandr Dubinsky Nov 12 '19 at 21:52
  • 1
    @AleksandrDubinsky I agree with you when you point that the callback may not run on the caller thread. You are right. I disagree about blocking a thread. My updated answer of _UPDATED on 25-05-2016_ is non-blocking. – Miguel Gamboa Nov 13 '19 at 13:37
  • 1
    .... and this sample is exactly the reason why C# is so much simpler to write and read when doing asynchronous stuff. It's just a pain in Java. – spyro Dec 12 '19 at 17:37
34

Check out ea-async which does Java bytecode rewriting to simulate async/await pretty nicely. Per their readme: "It is heavily inspired by Async-Await on the .NET CLR"

Hafthor
  • 15,081
  • 9
  • 54
  • 62
  • 10
    Does anyone use it in production? – Mr.Wang from Next Door Dec 22 '16 at 01:54
  • 2
    It seems that EA does, I don't think they would spend money on something that is not suitable for production. – BrunoJCM Aug 21 '17 at 10:23
  • 2
    It's pretty normal to spend money on something and then decide that it's not suitable for production; that's the only way to learn. It is possible to use it without java agent settings in production; that should lower the bar a little (https://github.com/electronicarts/ea-async). – thoredge May 23 '18 at 07:49
17

async and await are syntactic sugars. The essence of async and await is state machine. The compiler will transform your async/await code into a state machine.

At the same time, in order for async/await to be really practicable in real projects, we need to have lots of Async I/O library functions already in place. For C#, most original synchronized I/O functions has an alternative Async version. The reason we need these Async functions is because in most cases, your own async/await code will boil down to some library Async method.

The Async version library functions in C# is kind of like the AsynchronousChannel concept in Java. For example, we have AsynchronousFileChannel.read which can either return a Future or execute a callback after the read operation is done. But it’s not exactly the same. All C# Async functions return Tasks (similar to Future but more powerful than Future).

So let’s say Java do support async/await, and we write some code like this:

public static async Future<Byte> readFirstByteAsync(String filePath) {
    Path path = Paths.get(filePath);
    AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);

    ByteBuffer buffer = ByteBuffer.allocate(100_000);
    await channel.read(buffer, 0, buffer, this);
    return buffer.get(0);
}

Then I would imagine the compiler will transform the original async/await code into something like this:

public static Future<Byte> readFirstByteAsync(String filePath) {

    CompletableFuture<Byte> result = new CompletableFuture<Byte>();

    AsyncHandler ah = new AsyncHandler(result, filePath);

    ah.completed(null, null);

    return result;
}

And here is the implementation for AsyncHandler:

class AsyncHandler implements CompletionHandler<Integer, ByteBuffer>
{
    CompletableFuture<Byte> future;
    int state;
    String filePath;

    public AsyncHandler(CompletableFuture<Byte> future, String filePath)
    {
        this.future = future;
        this.state = 0;
        this.filePath = filePath;
    }

    @Override
    public void completed(Integer arg0, ByteBuffer arg1) {
        try {
            if (state == 0) {
                state = 1;
                Path path = Paths.get(filePath);
                AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);

                ByteBuffer buffer = ByteBuffer.allocate(100_000);
                channel.read(buffer, 0, buffer, this);
                return;
            } else {
                Byte ret = arg1.get(0);
                future.complete(ret);
            }

        } catch (Exception e) {
            future.completeExceptionally(e);
        }
    }

    @Override
    public void failed(Throwable arg0, ByteBuffer arg1) {
        future.completeExceptionally(arg0);
    }
}
caisx25
  • 359
  • 2
  • 4
  • 17
    Syntatic sugar? Do you have any idea about how to wrap exceptions around async code, and loops around async code? – Akash Kava Jul 29 '16 at 11:31
  • 45
    Classes, too, are syntactic sugar. The compiler creates all the trees and lists of function pointers you would normally write by hand for you fully automatically. These functions / methods are syntactic sugar, too. They auto-generate all the gotos you would normally, being a real programmer, write by hand. Assembler is syntactic sugar, too. Real programmers manually write machine code and manually port it to all target architectures. – yeoman Oct 11 '16 at 12:31
  • 37
    Thinking abou itt, computers themselves are just syntactic sugar for l4m3 n00bz. Real programmers solder them tiny integrated circuits to a wooden board and connect them with gold wire because circuit boards are syntactic sugar, just like mass production, shoes, or food. – yeoman Oct 11 '16 at 12:35
15

There is no equivalent of C# async/await in Java at the language level. A concept known as Fibers aka cooperative threads aka lightweight threads could be an interesting alternative. You can find Java libraries providing support for fibers.

Java libraries implementing Fibers

You can read this article (from Quasar) for a nice introduction to fibers. It covers what threads are, how fibers can be implemented on the JVM and has some Quasar specific code.

FabienB
  • 1,082
  • 2
  • 11
  • 22
  • 11
    async/await in C# is not a Fiber. It just compiler magic that use continuation on Promise (the `Task` class) by registering a callback. – UltimaWeapon Jun 27 '16 at 05:59
  • 1
    @UltimaWeapon So what do you consider to be fibers? – Aleksandr Dubinsky Dec 14 '18 at 16:09
  • @AleksandrDubinsky One of the example is goroutine. – UltimaWeapon Dec 15 '18 at 10:19
  • 1
    @UltimaWeapon I was looking for an explanation. – Aleksandr Dubinsky Dec 16 '18 at 08:36
  • @AleksandrDubinsky I'm lazy to explain it. If you really want to know you may search the article about under the hood of goroutine. – UltimaWeapon Dec 16 '18 at 15:05
  • C# async/await use is nothing to do with Fibers. It is compiler-support for simplification of callbacks used in asynchronous I/O, not for asynchronous computation state management. – Tony B May 27 '19 at 16:02
  • Asynchrony and Concurency are not same, async is related to not waiting for a task to complete before going on with the program flow this has nothing to do with threading, it can be done on same thread. Concurreny is the ability to run multiple tasks at the same time which is what threading is all about. – Surya Pratap Jul 03 '19 at 07:42
8

As it was mentioned, there is no direct equivalent, but very close approximation could be created with Java bytecode modifications (for both async/await-like instructions and underlying continuations implementation).

I'm working right now on a project that implements async/await on top of JavaFlow continuation library, please check https://github.com/vsilaev/java-async-await

No Maven mojo is created yet, but you may run examples with supplied Java agent. Here is how async/await code looks like:

public class AsyncAwaitNioFileChannelDemo {

public static void main(final String[] argv) throws Exception {

    ...
    final AsyncAwaitNioFileChannelDemo demo = new AsyncAwaitNioFileChannelDemo();
    final CompletionStage<String> result = demo.processFile("./.project");
    System.out.println("Returned to caller " + LocalTime.now());
    ...
}


public @async CompletionStage<String> processFile(final String fileName) throws IOException {
    final Path path = Paths.get(new File(fileName).toURI());
    try (
            final AsyncFileChannel file = new AsyncFileChannel(
                path, Collections.singleton(StandardOpenOption.READ), null
            );              
            final FileLock lock = await(file.lockAll(true))
        ) {

        System.out.println("In process, shared lock: " + lock);
        final ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.size());

        await( file.read(buffer, 0L) );
        System.out.println("In process, bytes read: " + buffer);
        buffer.rewind();

        final String result = processBytes(buffer);

        return asyncResult(result);

    } catch (final IOException ex) {
        ex.printStackTrace(System.out);
        throw ex;
    }
}

@async is the annotation that flags a method as asynchronously executable, await() is a function that waits on CompletableFuture using continuations and a call to "return asyncResult(someValue)" is what finalizes associated CompletableFuture/Continuation

As with C#, control flow is preserved and exception handling may be done in regular manner (try/catch like in sequentially executed code)

Valery Silaev
  • 179
  • 2
  • 4
7

Java itself has no equivalent features, but third-party libraries exist which offer similar functionality, e.g.Kilim.

Alexei Kaigorodov
  • 12,459
  • 1
  • 18
  • 36
5

First, understand what async/await is. It is a way for a single-threaded GUI application or an efficient server to run multiple "fibers" or "co-routines" or "lightweight threads" on a single thread.

If you are ok with using ordinary threads, then the Java equivalent is ExecutorService.submit and Future.get. This will block until the task completes, and return the result. Meanwhile, other threads can do work.

If you want the benefit of something like fibers, you need support in the container (I mean in the GUI event loop or in the server HTTP request handler), or by writing your own.

For example, Servlet 3.0 offers asynchronous processing. JavaFX offers javafx.concurrent.Task. These don't have the elegance of language features, though. They work through ordinary callbacks.

XDS
  • 2,628
  • 1
  • 25
  • 40
Aleksandr Dubinsky
  • 19,357
  • 14
  • 64
  • 88
  • 3
    Here is an article quote restarting this answer's first paragraph //start quote For client applications, such as Windows Store, Windows desktop and Windows Phone apps, the primary benefit of async is responsiveness. These types of apps use async chiefly to keep the UI responsive. For server applications, the primary benefit of async is scalability. https://msdn.microsoft.com/en-us/magazine/dn802603.aspx – granadaCoder Dec 12 '18 at 18:46
4

Java doesn't have direct equivalent of C# language feature called async/await, however there's a different approach to the problem that async/await tries to solve. It's called project Loom, which will provide virtual threads for high-throughput concurrency. It will be available in some future version of OpenJDK.

This approach also solves "colored function problem" that async/await has.

Similar feature can be also found in Golang (goroutines).

Martin Obrátil
  • 165
  • 1
  • 7
  • I'm looking forward to this. I'm learning Java, coming from Node.js and Go, and this is painful. Node.js has async/await (the colored function problem never bothered me much) and Go has goroutines so you just write blocking code in the millions of cheap goroutines you spawn. Java seems to have neither and needs me to use things like java.util.concurrent or RxJava to make my code non-blocking. If my understanding of Project Loom is correct, it'll make coding asynchronous things in Java as easy as Go. You'd just write blocking code without worrying about running out of "threads" able to execute. – Matt Welke May 29 '21 at 18:14
3

There isn't anything native to java that lets you do this like async/await keywords, but what you can do if you really want to is use a CountDownLatch. You could then imitate async/await by passing this around (at least in Java7). This is a common practice in Android unit testing where we have to make an async call (usually a runnable posted by a handler), and then await for the result (count down).

Using this however inside your application as opposed to your test is NOT what I am recommending. That would be extremely shoddy as CountDownLatch depends on you effectively counting down the right number of times and in the right places.

stevebot
  • 21,481
  • 26
  • 107
  • 176
3

I make and released Java async/await library. https://github.com/stofu1234/kamaitachi

This library don't need compiler extension, and realize stackless IO processing in Java.

    async Task<int> AccessTheWebAsync(){ 
        HttpClient client= new HttpClient();
        var urlContents= await client.GetStringAsync("http://msdn.microsoft.com");
       return urlContents.Length;
    }

   ↓

    //LikeWebApplicationTester.java
    BlockingQueue<Integer> AccessTheWebAsync() {
       HttpClient client = new HttpClient();
       return awaiter.await(
            () -> client.GetStringAsync("http://msdn.microsoft.com"),
            urlContents -> {
                return urlContents.length();
            });
    }
    public void doget(){
        BlockingQueue<Integer> lengthQueue=AccessTheWebAsync();
        awaiter.awaitVoid(()->lengthQueue.take(),
            length->{
                System.out.println("Length:"+length);
            }
            );
    }
stofu
  • 31
  • 1
  • How would you handle String x = await f(); if(x == "Facebook") { LongAsyncFuncToRunFacebook();} else {LongAsyncFuncToRunSomethingElse();} AfterIfElseRunSomethingNewJustForFun(); – pratikpc Jan 04 '21 at 04:15
1

Java has unfortunately no equivalent of async/await. The closest you can get is probably with ListenableFuture from Guava and listener chaining, but it would be still very cumbersome to write for cases involving multiple asynchronous calls, as the nesting level would very quickly grow.

If you're ok with using a different language on top of JVM, fortunately there is async/await in Scala which is a direct C# async/await equivalent with an almost identical syntax and semantics: https://github.com/scala/async/

Note that although this functionality needed a pretty advanced compiler support in C#, in Scala it could be added as a library thanks to a very powerful macro system in Scala and therefore can be added even to older versions of Scala like 2.10. Additionally Scala is class-compatible with Java, so you can write the async code in Scala and then call it from Java.

There is also another similar project called Akka Dataflow http://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html which uses different wording but conceptually is very similar, however implemented using delimited continuations, not macros (so it works with even older Scala versions like 2.9).

Piotr Kołaczkowski
  • 2,446
  • 8
  • 13
0

If you're just after clean code which simulates the same effect as async/await in java and don't mind blocking the thread it is called on until it is finished, such as in a test, you could use something like this code:

interface Async {
    void run(Runnable handler);
}

static void await(Async async) throws InterruptedException {

    final CountDownLatch countDownLatch = new CountDownLatch(1);
    async.run(new Runnable() {

        @Override
        public void run() {
            countDownLatch.countDown();
        }
    });
    countDownLatch.await(YOUR_TIMEOUT_VALUE_IN_SECONDS, TimeUnit.SECONDS);
}

    await(new Async() {
        @Override
        public void run(final Runnable handler) {
            yourAsyncMethod(new CompletionHandler() {

                @Override
                public void completion() {
                    handler.run();
                }
            });
        }
    });
Pellet
  • 1,928
  • 1
  • 25
  • 18
0

AsynHelper Java library includes a set of utility classes/methods for such asynchronous calls (and wait).

If it is desired to run a set of method calls or code blocks asynchronously, the It includes an useful helper method AsyncTask.submitTasks as in below snippet.

AsyncTask.submitTasks(
    () -> getMethodParam1(arg1, arg2),
    () -> getMethodParam2(arg2, arg3)
    () -> getMethodParam3(arg3, arg4),
    () -> {
             //Some other code to run asynchronously
          }
    );

If it is desired to wait till all asynchronous codes are completed running, the AsyncTask.submitTasksAndWait varient can be used.

Also if it is desired to obtain a return value from each of the asynchronous method call or code block, the AsyncSupplier.submitSuppliers can be used so that the result can be then obtained by from the result suppliers array returned by the method. Below is the sample snippet:

Supplier<Object>[] resultSuppliers = 
   AsyncSupplier.submitSuppliers(
     () -> getMethodParam1(arg1, arg2),
     () -> getMethodParam2(arg3, arg4),
     () -> getMethodParam3(arg5, arg6)
   );

Object a = resultSuppliers[0].get();
Object b = resultSuppliers[1].get();
Object c = resultSuppliers[2].get();

myBigMethod(a,b,c);

If the return type of each method differ, use the below kind of snippet.

Supplier<String> aResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam1(arg1, arg2));
Supplier<Integer> bResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam2(arg3, arg4));
Supplier<Object> cResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam3(arg5, arg6));

myBigMethod(aResultSupplier.get(), bResultSupplier.get(), cResultSupplier.get());

The result of the asynchronous method calls/code blocks can also be obtained at a different point of code in the same thread or a different thread as in the below snippet.

AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam1(arg1, arg2), "a");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam2(arg3, arg4), "b");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam3(arg5, arg6), "c");


//Following can be in the same thread or a different thread
Optional<String> aResult = AsyncSupplier.waitAndGetFromSupplier(String.class, "a");
Optional<Integer> bResult = AsyncSupplier.waitAndGetFromSupplier(Integer.class, "b");
Optional<Object> cResult = AsyncSupplier.waitAndGetFromSupplier(Object.class, "c");

 myBigMethod(aResult.get(),bResult.get(),cResult.get());
Loganathan
  • 883
  • 10
  • 22
0

There is an "equivalent" of await developed by EA: https://github.com/electronicarts/ea-async. Refer to the Java example code:

import static com.ea.async.Async.await;
import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        if(!await(bank.decrement(cost))) {
            return completedFuture(false);
        }
        await(inventory.giveItem(itemTypeId));
        return completedFuture(true);
    }
}
walter33
  • 23
  • 4