// Path: src/main/java/fr/insalyon/telecom/jooflux/internal/Aspects.java
// public class Aspects {
// 
//     public static MethodHandle before(MethodHandle target, MethodHandle beforeFilter) {
//         MethodType targetType = target.type();
//         MethodType filterType = beforeFilter.type();
//         int parameterCount = targetType.parameterCount();
// 
//         if (filterType.parameterCount() != 1 && !filterType.parameterType(0).equals(Object[].class)) {
//             throw new IllegalArgumentException("beforeFilter must have a single argument of type Object[]");
//         }
//         if (!filterType.returnType().equals(Object[].class)) {
//             throw new IllegalArgumentException("beforeFilter must have a Object[] return type");
//         }
// 
//         MethodHandle spreader = target.asSpreader(Object[].class, parameterCount);
//         MethodHandle filter = filterArguments(spreader, 0, beforeFilter);
//         MethodHandle collector = filter.asCollector(Object[].class, parameterCount);
// 
//         return collector.asType(targetType);
// 
//     }
// 
//     public static MethodHandle after(MethodHandle target, MethodHandle afterFilter) {
//         MethodType targetType = target.type();
//         MethodType filterType = afterFilter.type();
// 
//         if (filterType.parameterCount() != 1 && !filterType.parameterType(0).equals(Object.class)) {
//             throw new IllegalArgumentException("afterFilter must have a single argument of type Object");
//         }
//         if (!filterType.returnType().equals(Object.class)) {
//             throw new IllegalArgumentException("afterFilter must have a Object return type");
//         }
// 
//         MethodHandle asObject = target.asType(targetType.changeReturnType(Object.class));
//         MethodHandle filter = filterReturnValue(asObject, afterFilter);
// 
//         return filter.asType(targetType);
//     }
// }
// 
// Path: src/main/java/fr/insalyon/telecom/jooflux/internal/CallSiteRegistry.java
// public class CallSiteRegistry {
// 
//     private CallSiteRegistry() {
//         super();
//     }
// 
//     // Thread-safe thanks to the JVM internals!
//     private static class CallSiteRegistrySingletonHolder {
//         private static final CallSiteRegistry instance = new CallSiteRegistry();
//     }
// 
//     public static CallSiteRegistry getInstance() {
//         return CallSiteRegistrySingletonHolder.instance;
//     }
// 
//     private final ConcurrentHashMap<String, Registration> registry = new ConcurrentHashMap<>();
// 
//     public void put(String target, String type, CallSite callSite) {
//         registry.putIfAbsent(target, new Registration(target, type));
//         registry.get(target).getCallSites().add(callSite);
//     }
// 
//     public int numberOfRegisteredCallSites() {
//         return registry.size();
//     }
// 
//     public Set<String> callSiteKeys() {
//         return registry.keySet();
//     }
// 
//     public Registration callSiteRegistrationFor(String key) {
//         return registry.get(key);
//     }
// 
//     public String callSiteTypeFor(String key) {
//         return registry.get(key).getType();
//     }
// 
//     public Set<CallSite> callSitesFor(String key) {
//         return registry.get(key).getCallSites();
//     }
// }


import fr.insalyon.telecom.jooflux.internal.Aspects;
import fr.insalyon.telecom.jooflux.internal.CallSiteRegistry;
import org.pmw.tinylog.Logger;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Set;

    @Override
    public void applyBeforeAspect(String callSitesKey, String aspectClass, String aspectMethod) {
        try {
            Class<?> klass = getClass().getClassLoader().loadClass(aspectClass);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.findStatic(klass, aspectMethod, MethodType.methodType(Object[].class, Object[].class));
            Set<CallSite> callSites = registry.callSitesFor(callSitesKey);
            if (callSites == null) {
                throw new IllegalArgumentException("No call sites have been registered for key: " + callSitesKey);
            }
            for (CallSite callSite : callSites) {
                callSite.setTarget(Aspects.before(callSite.getTarget(), handle));
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    @Override
    public void applyAfterAspect(String callSitesKey, String aspectClass, String aspectMethod) {
        try {
            Class<?> klass = getClass().getClassLoader().loadClass(aspectClass);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.findStatic(klass, aspectMethod, MethodType.methodType(Object.class, Object.class));
            Set<CallSite> callSites = registry.callSitesFor(callSitesKey);
            if (callSites == null) {
                throw new IllegalArgumentException("No call sites have been registered for key: " + callSitesKey);
            }
            for (CallSite callSite : callSites) {
// Path: src/main/java/fr/insalyon/telecom/jooflux/internal/Aspects.java
// public class Aspects {
// 
//     public static MethodHandle before(MethodHandle target, MethodHandle beforeFilter) {
//         MethodType targetType = target.type();
//         MethodType filterType = beforeFilter.type();
//         int parameterCount = targetType.parameterCount();
// 
//         if (filterType.parameterCount() != 1 && !filterType.parameterType(0).equals(Object[].class)) {
//             throw new IllegalArgumentException("beforeFilter must have a single argument of type Object[]");
//         }
//         if (!filterType.returnType().equals(Object[].class)) {
//             throw new IllegalArgumentException("beforeFilter must have a Object[] return type");
//         }
// 
//         MethodHandle spreader = target.asSpreader(Object[].class, parameterCount);
//         MethodHandle filter = filterArguments(spreader, 0, beforeFilter);
//         MethodHandle collector = filter.asCollector(Object[].class, parameterCount);
// 
//         return collector.asType(targetType);
// 
//     }
// 
//     public static MethodHandle after(MethodHandle target, MethodHandle afterFilter) {
//         MethodType targetType = target.type();
//         MethodType filterType = afterFilter.type();
// 
//         if (filterType.parameterCount() != 1 && !filterType.parameterType(0).equals(Object.class)) {
//             throw new IllegalArgumentException("afterFilter must have a single argument of type Object");
//         }
//         if (!filterType.returnType().equals(Object.class)) {
//             throw new IllegalArgumentException("afterFilter must have a Object return type");
//         }
// 
//         MethodHandle asObject = target.asType(targetType.changeReturnType(Object.class));
//         MethodHandle filter = filterReturnValue(asObject, afterFilter);
// 
//         return filter.asType(targetType);
//     }
// }
// 
// Path: src/main/java/fr/insalyon/telecom/jooflux/internal/CallSiteRegistry.java
// public class CallSiteRegistry {
// 
//     private CallSiteRegistry() {
//         super();
//     }
// 
//     // Thread-safe thanks to the JVM internals!
//     private static class CallSiteRegistrySingletonHolder {
//         private static final CallSiteRegistry instance = new CallSiteRegistry();
//     }
// 
//     public static CallSiteRegistry getInstance() {
//         return CallSiteRegistrySingletonHolder.instance;
//     }
// 
//     private final ConcurrentHashMap<String, Registration> registry = new ConcurrentHashMap<>();
// 
//     public void put(String target, String type, CallSite callSite) {
//         registry.putIfAbsent(target, new Registration(target, type));
//         registry.get(target).getCallSites().add(callSite);
//     }
// 
//     public int numberOfRegisteredCallSites() {
//         return registry.size();
//     }
// 
//     public Set<String> callSiteKeys() {
//         return registry.keySet();
//     }
// 
//     public Registration callSiteRegistrationFor(String key) {
//         return registry.get(key);
//     }
// 
//     public String callSiteTypeFor(String key) {
//         return registry.get(key).getType();
//     }
// 
//     public Set<CallSite> callSitesFor(String key) {
//         return registry.get(key).getCallSites();
//     }
// }


// Path: src/main/java/fr/insalyon/telecom/jooflux/internal/jmx/JooFluxManagement.java
import fr.insalyon.telecom.jooflux.internal.Aspects;
import fr.insalyon.telecom.jooflux.internal.CallSiteRegistry;
import org.pmw.tinylog.Logger;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Set;


    @Override
    public void applyBeforeAspect(String callSitesKey, String aspectClass, String aspectMethod) {
        try {
            Class<?> klass = getClass().getClassLoader().loadClass(aspectClass);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.findStatic(klass, aspectMethod, MethodType.methodType(Object[].class, Object[].class));
            Set<CallSite> callSites = registry.callSitesFor(callSitesKey);
            if (callSites == null) {
                throw new IllegalArgumentException("No call sites have been registered for key: " + callSitesKey);
            }
            for (CallSite callSite : callSites) {
                callSite.setTarget(Aspects.before(callSite.getTarget(), handle));
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    @Override
    public void applyAfterAspect(String callSitesKey, String aspectClass, String aspectMethod) {
        try {
            Class<?> klass = getClass().getClassLoader().loadClass(aspectClass);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.findStatic(klass, aspectMethod, MethodType.methodType(Object.class, Object.class));
            Set<CallSite> callSites = registry.callSitesFor(callSitesKey);
            if (callSites == null) {
                throw new IllegalArgumentException("No call sites have been registered for key: " + callSitesKey);
            }
            for (CallSite callSite : callSites) {
                callSite.setTarget(Aspects.after(callSite.getTarget(), handle));