Skip to content

Add actor closure implementation on Java #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Java/actor-closure/Actor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import java.util.concurrent.CompletableFuture;

public interface Actor {
void process();

CompletableFuture<Object> send(String methodName, Object... args);
}
70 changes: 70 additions & 0 deletions Java/actor-closure/ActorClosureFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;

public class ActorClosureFactory {
private static Class<?>[] convertParamTypes(Object... args) {
Class<?>[] types = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
types[i] = args[i].getClass();
}

return types;
}

private static <T> T instantiate(Class<T> clazz, Object... args) {
Class<?>[] paramTypes = convertParamTypes(args);

try {
return clazz
.getConstructor(paramTypes)
.newInstance(args);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Could not instantiate " + clazz, e);
}
}

public static <T> Actor createActor(Class<T> clazz, Object... args) {
final AtomicBoolean isProcessing = new AtomicBoolean(false);
final Queue<Operation> queue = new ConcurrentLinkedQueue<>();
final T state = instantiate(clazz, args);

return new Actor() {
@Override
public void process() {
new Thread(() -> {
if (!isProcessing.compareAndSet(false, true))
return;

while (!queue.isEmpty()) {
Operation operation = queue.poll();
CompletableFuture<Object> resolve = operation.resolve();

try {
Method method = state.getClass().getMethod(operation.method(), convertParamTypes(operation.args()));
Object result = method.invoke(state, operation.args());
resolve.complete(result);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
resolve.completeExceptionally(e);
}
}

isProcessing.set(false);
}).start();
}

@Override
public CompletableFuture<Object> send(String method, Object... args) {
CompletableFuture<Object> resultReference = new CompletableFuture<>();

queue.add(new Operation(method, args, resultReference));
this.process();

return resultReference;
}
};
}
}
7 changes: 7 additions & 0 deletions Java/actor-closure/Operation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import java.util.concurrent.CompletableFuture;

public record Operation(
String method,
Object[] args,
CompletableFuture<Object> resolve
) {}
23 changes: 23 additions & 0 deletions Java/actor-closure/Point.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public class Point {
private int x;
private int y;

public Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}

public void move(Integer dx, Integer dy) {
this.x += dx;
this.y += dy;
}

public Actor cloneAsActor() {
return ActorClosureFactory.createActor(Point.class, this.x, this.y);
}

@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
}
31 changes: 31 additions & 0 deletions Java/actor-closure/Runner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import java.util.concurrent.ExecutionException;

public class Runner {
private static final String OUTPUT_METHOD = "toString";
private static final String MOVE_METHOD = "move";
private static final String CLONE_METHOD = "cloneAsActor";

public static void main(String[] args) throws ExecutionException, InterruptedException {
Actor actor = ActorClosureFactory.createActor(Point.class, 10, 20);

// "toString" function must return string with coords
String output = (String) actor.send(OUTPUT_METHOD).get();
System.out.println(output); // "(10, 20)"

// "cloneAsActor" function must create another actor
Actor clone = (Actor) actor.send(CLONE_METHOD).get();
System.out.println(actor == clone); // false

// "move" function should not return anything
Object move = clone.send(MOVE_METHOD, -5, 10).get();
System.out.println(move); // null

// "toString" on CLONED object must return changed coords
String movedClonedOutput = (String) clone.send(OUTPUT_METHOD).get();
System.out.println(movedClonedOutput); // "(5, 30)"

// "toString" on SOURCE object must return original unchanged coords
String movedSourceOutput = (String) actor.send(OUTPUT_METHOD).get();
System.out.println(movedSourceOutput); // "(10, 20)"
}
}