Skip to content
Merged
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
30 changes: 30 additions & 0 deletions aop/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
id 'java-library'
}

group = 'com.callv2'
version = '1.0-SNAPSHOT'

repositories {
mavenCentral()
}

dependencies {

implementation 'org.aspectj:aspectjrt:1.9.24'

testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testImplementation 'org.mockito:mockito-junit-jupiter:5.15.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

tasks.named('test') {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.callv2.aop.context;

import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;

public abstract class AbstractInvocationContext implements InvocationContext {

protected final ProceedingJoinPoint joinPoint;
protected final AtomicBoolean proceeded;
private final Instant contextedAt;

protected AbstractInvocationContext(final ProceedingJoinPoint joinPoint) {
this.joinPoint = joinPoint;
this.proceeded = new AtomicBoolean(false);
this.contextedAt = Instant.now();
}

protected AbstractInvocationContext(final PreInvocationContext preInvocationContext) {
this.joinPoint = preInvocationContext;
this.proceeded = new AtomicBoolean(preInvocationContext.proceeded());
this.contextedAt = preInvocationContext.getContextedAt();
}

@Override
public void set$AroundClosure(AroundClosure arc) {
this.joinPoint.set$AroundClosure(arc);
}

@Override
public String toShortString() {
return this.joinPoint.toShortString();
}

@Override
public String toLongString() {
return this.joinPoint.toLongString();
}

@Override
public Object getThis() {
return this.joinPoint.getThis();
}

@Override
public Object getTarget() {
return this.joinPoint.getTarget();
}

@Override
public Object[] getArgs() {
return this.joinPoint.getArgs();
}

@Override
public Signature getSignature() {
return this.joinPoint.getSignature();
}

@Override
public SourceLocation getSourceLocation() {
return this.joinPoint.getSourceLocation();
}

@Override
public String getKind() {
return this.joinPoint.getKind();
}

@Override
public StaticPart getStaticPart() {
return this.joinPoint.getStaticPart();
}

@Override
public Instant getContextedAt() {
return this.contextedAt;
}

@Override
public boolean proceeded() {
return this.proceeded.get();
}

}
13 changes: 13 additions & 0 deletions aop/src/main/java/com/callv2/aop/context/InvocationContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.callv2.aop.context;

import java.time.Instant;

import org.aspectj.lang.ProceedingJoinPoint;

public interface InvocationContext extends ProceedingJoinPoint {

Instant getContextedAt();

boolean proceeded();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.callv2.aop.context;

import java.time.Instant;

public interface PostInvocationContext extends InvocationContext {

Instant getProceededAt();

Object getResult();

Throwable getThrowable();

boolean wasSuccessful();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.callv2.aop.context;

public interface PreInvocationContext extends InvocationContext {

PostInvocationContext proceedWithContext();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.callv2.aop.context;

import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;

public final class SimplePostInvocationContext extends AbstractInvocationContext implements PostInvocationContext {

private final Object result;
private final Throwable throwable;
private final Instant proceededAt;
private final AtomicBoolean successful;

private SimplePostInvocationContext(
final PreInvocationContext preInvocationContext,
final Object result,
final Throwable throwable,
final Instant proceededAt,
final boolean successful) {
super(preInvocationContext);
this.result = result;
this.throwable = throwable;
this.proceededAt = proceededAt;
this.successful = new AtomicBoolean(successful);
}

protected static SimplePostInvocationContext from(final PreInvocationContext preInvocationContext) {

Object result;
Throwable throwable;
boolean successful;
final Instant proceededAt;

try {
result = preInvocationContext.proceed();
throwable = null;
successful = true;
} catch (Throwable e) {
result = null;
throwable = e;
successful = false;
} finally {
proceededAt = Instant.now();
}

return new SimplePostInvocationContext(
preInvocationContext,
result,
throwable,
proceededAt,
successful);

}

@Override
public Object proceed() throws Throwable {
if (this.proceeded.get()) {
if (successful.get())
return result;
else
throw throwable;
}

return this.joinPoint.proceed();
}

@Override
public Object proceed(Object[] args) throws Throwable {
if (this.proceeded.get()) {
if (successful.get())
return result;
else
throw throwable;
}

return this.joinPoint.proceed(args);
}

@Override
public Instant getProceededAt() {
return this.proceededAt;
}

@Override
public Object getResult() {
return this.result;
}

@Override
public Throwable getThrowable() {
return this.throwable;
}

@Override
public boolean wasSuccessful() {
return this.successful.get();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.callv2.aop.context;

import org.aspectj.lang.ProceedingJoinPoint;

public final class SimplePreInvocationContext extends AbstractInvocationContext implements PreInvocationContext {

private SimplePreInvocationContext(final ProceedingJoinPoint joinPoint) {
super(joinPoint);
}

public static SimplePreInvocationContext of(final ProceedingJoinPoint joinPoint) {
return new SimplePreInvocationContext(joinPoint);
}

@Override
public Object proceed() throws Throwable {
if (proceeded.getAndSet(true))
throw new IllegalStateException("Method already proceeded");
return joinPoint.proceed();
}

@Override
public Object proceed(Object[] args) throws Throwable {
if (proceeded.getAndSet(true))
throw new IllegalStateException("Method already proceeded");
return joinPoint.proceed(args);
}

@Override
public PostInvocationContext proceedWithContext() {
return SimplePostInvocationContext.from(this);
}

}
10 changes: 10 additions & 0 deletions aop/src/main/java/com/callv2/aop/executor/Executor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.callv2.aop.executor;

import org.aspectj.lang.ProceedingJoinPoint;

@FunctionalInterface
public interface Executor<J extends ProceedingJoinPoint> {

void execute(J joinPoint);

}
7 changes: 7 additions & 0 deletions aop/src/main/java/com/callv2/aop/executor/PostExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.callv2.aop.executor;

import com.callv2.aop.context.PostInvocationContext;

public interface PostExecutor extends Executor<PostInvocationContext> {

}
7 changes: 7 additions & 0 deletions aop/src/main/java/com/callv2/aop/executor/PreExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.callv2.aop.executor;

import com.callv2.aop.context.PreInvocationContext;

public interface PreExecutor extends Executor<PreInvocationContext> {

}
32 changes: 32 additions & 0 deletions aop/src/main/java/com/callv2/aop/executor/chain/ExecutorChain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.callv2.aop.executor.chain;

import org.aspectj.lang.ProceedingJoinPoint;

import com.callv2.aop.executor.Executor;

public abstract class ExecutorChain<O, J extends ProceedingJoinPoint, E extends Executor<J>> {

private ExecutorChain<O, J, E> next;
private final E executor;

protected ExecutorChain(final E executor) {
this.executor = executor;
}

public ExecutorChain<O, J, E> setNext(final ExecutorChain<O, J, E> executorChain) {
return this.next = executorChain;
}

public final O execute(final J joinpoint) throws Throwable {

this.executor.execute(joinpoint);

if (next != null)
return next.execute(joinpoint);

return resolve(joinpoint);
}

protected abstract O resolve(J joinPoint) throws Throwable;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.callv2.aop.executor.chain;

import com.callv2.aop.context.PostInvocationContext;
import com.callv2.aop.executor.PostExecutor;

public final class PostInvocationExecutorChain
extends ExecutorChain<Object, PostInvocationContext, PostExecutor> {

public PostInvocationExecutorChain(final PostExecutor executor) {
super(executor);
}

@Override
protected Object resolve(final PostInvocationContext joinPoint) throws Throwable {
if (joinPoint.wasSuccessful())
return joinPoint.getResult();
else
throw joinPoint.getThrowable();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.callv2.aop.executor.chain;

import com.callv2.aop.context.PostInvocationContext;
import com.callv2.aop.context.PreInvocationContext;
import com.callv2.aop.executor.PreExecutor;

public final class PreInvocationExecutorChain
extends ExecutorChain<PostInvocationContext, PreInvocationContext, PreExecutor> {

public PreInvocationExecutorChain(final PreExecutor executor) {
super(executor);
}

@Override
protected PostInvocationContext resolve(final PreInvocationContext joinPoint) throws Throwable {
return joinPoint.proceedWithContext();
}

}
Loading