Skip to content
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

master: Add annotation functionality to jPos participant. #576

Open
wants to merge 7 commits into
base: master
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
1 change: 1 addition & 0 deletions jpos/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation libraries.sleepycat_je
implementation libraries.sshd
implementation libraries.eddsa
implementation libraries.bytebuddy

testImplementation libraries.commons_lang3
testImplementation libraries.hamcrest
Expand Down
3 changes: 2 additions & 1 deletion jpos/libraries.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ ext {
slf4j_api: "org.slf4j:slf4j-api:1.7.32",
slf4j_nop: "org.slf4j:slf4j-nop:1.7.32",
hdrhistogram: 'org.hdrhistogram:HdrHistogram:2.1.12',
yaml: "org.yaml:snakeyaml:2.0"
yaml: "org.yaml:snakeyaml:2.0",
bytebuddy: "net.bytebuddy:byte-buddy:1.14.10"
]
}

25 changes: 25 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/AnnotatedParticipant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.transaction.TransactionParticipant;

/**
* Marks a {@link TransactionParticipant} a participant defined using annotations, automatically
* binding the prepare method and parameters. This annotation is used
* in convention-over-configuration scenarios to simplify the integration of components and
* make testing easier.
*
* see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Apply on classes extending {@link TransactionParticipant} and implementing a {@link Prepare} method instead of the prepare(long, Serializable).
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AnnotatedParticipant{

}
24 changes: 24 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/ContextKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.transaction.Context;

/**
* Used to specify a key for retrieving contextual information from a {@link Context} object via
* the annotated parameter. This facilitates dynamic access to context-specific data,
* streamlining the process of working with application contexts.
*
* @see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Used in conjunction with {@link Prepare} and {@link AnnotatedParticipant} annotations.
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ContextKey {
public String value();
}
32 changes: 32 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/ContextKeys.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.transaction.Context;

/**
* Used to specify a key for retrieving contextual information from a {@link Context} object via
* the annotated parameter. This facilitates dynamic access to context-specific data,
* streamlining the process of working with application contexts.
*
* value are in/out keys
* read only allow for read only
* write only allows for writes
*
* @see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Used in conjunction with {@link Prepare} and {@link AnnotatedParticipant} annotations.
*
*
* @author Ozzy Espaillat
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ContextKeys {
public String[] value() default {};
public String[] write() default {};
public String[] read() default {};
}
25 changes: 25 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/Prepare.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.transaction.TransactionConstants;
import org.jpos.transaction.TransactionParticipant;

/**
* Indicates that the annotated method is called in the preparation phase of transaction
* processing, specifying the expected outcome through the {@code result} attribute.
* This replaces the {@link TransactionParticipant} prepare method.
*
* @see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Used in conjunction with {@link AnnotatedParticipant} annotations.
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Prepare {
public int result() default TransactionConstants.PREPARED;
}
23 changes: 23 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/Registry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.util.NameRegistrar;

/**
* Marks a parameter within a method as being from
* {@link NameRegistrar}
*
* @see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Used in conjunction with {@link Prepare} and {@link AnnotatedParticipant} annotations.
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Registry {
public String value() default "";
}
23 changes: 23 additions & 0 deletions jpos/src/main/java/org/jpos/annotation/Return.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jpos.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jpos.transaction.TransactionParticipant;

/**
* Specifies the return values of a method. This annotation is intended to replace
* the return type of the prepare method in the {@link TransactionParticipant} interface.
*
* @see {@linkplain https://marqeta.atlassian.net/wiki/spaces/~62f54a31d49df231b62a575d/blog/2023/12/01/3041525965/AutoWiring+Participants+with+jPos+-+Part+I}
*
* Usage: Used in conjunction with {@link Prepare} and {@link AnnotatedParticipant} annotations.
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Return {
String[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.jpos.annotation.resolvers;

public interface Priority {
default int getPriority() {
return 10;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.jpos.annotation.resolvers;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

import org.jpos.annotation.resolvers.exception.ReturnExceptionHandler;
import org.jpos.annotation.resolvers.exception.ReturnExceptionHandlerProvider;
import org.jpos.annotation.resolvers.parameters.Resolver;
import org.jpos.annotation.resolvers.parameters.ResolverServiceProvider;
import org.jpos.annotation.resolvers.response.ReturnHandler;
import org.jpos.annotation.resolvers.response.ReturnHandlerProvider;
import org.jpos.core.ConfigurationException;

public class ResolverFactory {
public static final ResolverFactory INSTANCE = new ResolverFactory();

protected final ResolverProviderList resolvers = new ResolverProviderList();

private ResolverFactory() {}

public Resolver getResolver(Parameter p) throws ConfigurationException {
Resolver r = null;
for (ResolverServiceProvider f: resolvers.getResolvers()) {
if (f.isMatch(p)) {
r = f.resolve(p);
r.configure(p);
break;
}
}
if (r == null) {
throw new ConfigurationException("Prepare parameter " + p.getName() + " does not have the required annotation.");
}
return r;
}

public ReturnHandler getReturnHandler(Method m) throws ConfigurationException {
ReturnHandler r = null;
for (ReturnHandlerProvider f: resolvers.getReturnHandlers()) {
if (f.isMatch(m)) {
r = f.resolve(m);
r.configure(m);
break;
}
}
if (r == null) {
throw new ConfigurationException("Could not find a valid provider for return " + m.getName());
}
return r;
}

public List<ReturnExceptionHandler> getExceptionHandlers(Method m) {
List<ReturnExceptionHandler> exceptionHandlers = new ArrayList<>();
for(ReturnExceptionHandlerProvider p: resolvers.getExceptionResolvers()) {
ReturnExceptionHandler r = p.resolve(m);
r.configure(m);
exceptionHandlers.add(r);
}
return exceptionHandlers;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.jpos.annotation.resolvers;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

import org.jpos.annotation.resolvers.exception.ReturnExceptionHandlerProvider;
import org.jpos.annotation.resolvers.parameters.ResolverServiceProvider;
import org.jpos.annotation.resolvers.response.ReturnHandlerProvider;

public class ResolverProviderList {
protected final List<ResolverServiceProvider> resolvers = new ArrayList<>();
protected final List<ReturnHandlerProvider> resultHandlers = new ArrayList<>();
protected final List<ReturnExceptionHandlerProvider> exceptionHandlers = new ArrayList<>();

ResolverProviderList() {
loadServiceProviders(resolvers, ResolverServiceProvider.class);
loadServiceProviders(resultHandlers, ReturnHandlerProvider.class);
loadServiceProviders(exceptionHandlers, ReturnExceptionHandlerProvider.class);
}

protected <T extends Priority> void loadServiceProviders(List<T> list, Class<T> svcClass) {
ServiceLoader<T> svcLoader = ServiceLoader.load(svcClass);
for(T serviceImp: svcLoader) {
list.add(serviceImp);
}
list.sort((o1, o2) -> Integer.compare(o1.getPriority(), o2.getPriority()));
}


public List<ReturnHandlerProvider> getReturnHandlers() {
return resultHandlers;
}
public List<ReturnExceptionHandlerProvider> getExceptionResolvers() {
return exceptionHandlers;
}

public List<ResolverServiceProvider> getResolvers() {
return resolvers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jpos.annotation.resolvers.exception;

import java.lang.reflect.Method;

import org.jpos.rc.CMF;
import org.jpos.transaction.Context;
import org.jpos.transaction.TransactionConstants;
import org.jpos.transaction.TransactionParticipant;

public class GenericExceptionHandlerProvider implements ReturnExceptionHandlerProvider {
static class GenericExceptionHandler implements ReturnExceptionHandler {

@Override
public boolean isMatch(Throwable e) {
return getException(e, Exception.class) != null;
}

@Override
public int doReturn(TransactionParticipant p, Context ctx, Throwable t) {
ctx.log("prepare exception in " + this.getClass().getName());
ctx.log(t);
setResultCode(ctx, CMF.INTERNAL_ERROR);

return TransactionConstants.ABORTED;
}

}

@Override
public ReturnExceptionHandler resolve(Method m) {
return new GenericExceptionHandler();
}

@Override
public int getPriority() {
return Integer.MAX_VALUE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jpos.annotation.resolvers.exception;

import java.lang.reflect.Method;

import org.jpos.rc.IRC;
import org.jpos.transaction.Context;
import org.jpos.transaction.ContextConstants;
import org.jpos.transaction.TransactionParticipant;

public interface ReturnExceptionHandler {
boolean isMatch(Throwable e);
int doReturn(TransactionParticipant p, Context ctx, Throwable obj);

default void configure(Method m) {}

default void setResultCode(Context ctx, IRC irc) {
ctx.put(ContextConstants.IRC, irc);
}

default <T> T getException(Throwable e, Class<T> type) {
int stackDepth= 10;
do {
if (type.isAssignableFrom(e.getClass())) return (T) e;
} while (null != (e = e.getCause()) && stackDepth-- > 0);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jpos.annotation.resolvers.exception;

import java.lang.reflect.Method;

import org.jpos.annotation.resolvers.Priority;

public interface ReturnExceptionHandlerProvider extends Priority {
ReturnExceptionHandler resolve(Method m);
}
Loading
Loading