package com.github.containersolutions.operator; import com.github.containersolutions.operator.api.Controller; import com.github.containersolutions.operator.api.ResourceController; import io.fabric8.kubernetes.api.builder.Function; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.CustomResourceDoneable; import javassist.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; class ControllerUtils { private final static double JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version")); private final static Logger log = LoggerFactory.getLogger(Operator.class); // this is just to support testing, this way we don't try to create class multiple times in memory with same name. // note that other solution is to add a random string to doneable class name private static Map<Class<? extends CustomResource>, Class<? extends CustomResourceDoneable<? extends CustomResource>>> doneableClassCache = new HashMap<>(); static String getDefaultFinalizer(ResourceController controller) { return getAnnotation(controller).finalizerName(); } static boolean getGenerationEventProcessing(ResourceController controller) { return getAnnotation(controller).generationAwareEventProcessing(); } static <R extends CustomResource> Class<R> getCustomResourceClass(ResourceController controller) { return (Class<R>) getAnnotation(controller).customResourceClass(); } static String getCrdName(ResourceController controller) { return getAnnotation(controller).crdName(); } public static <T extends CustomResource> Class<? extends CustomResourceDoneable<T>> getCustomResourceDoneableClass(ResourceController<T> controller) { try { Class<? extends CustomResource> customResourceClass = getAnnotation(controller).customResourceClass(); String className = customResourceClass.getPackage().getName() + "." + customResourceClass.getSimpleName() + "CustomResourceDoneable"; if (doneableClassCache.containsKey(customResourceClass)) { return (Class<? extends CustomResourceDoneable<T>>) doneableClassCache.get(customResourceClass); } ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader())); CtClass superClass = pool.get(CustomResourceDoneable.class.getName()); CtClass function = pool.get(Function.class.getName()); CtClass customResource = pool.get(customResourceClass.getName()); CtClass[] argTypes = {customResource, function}; CtClass customDoneable = pool.makeClass(className, superClass); CtConstructor ctConstructor = CtNewConstructor.make(argTypes, null, "super($1, $2);", customDoneable); customDoneable.addConstructor(ctConstructor); Class<? extends CustomResourceDoneable<T>> doneableClass; if (JAVA_VERSION >= 9) { doneableClass = (Class<? extends CustomResourceDoneable<T>>) customDoneable.toClass(customResourceClass); } else { doneableClass = (Class<? extends CustomResourceDoneable<T>>) customDoneable.toClass(); } doneableClassCache.put(customResourceClass, doneableClass); return doneableClass; } catch (CannotCompileException | NotFoundException e) { throw new IllegalStateException(e); } } private static Controller getAnnotation(ResourceController controller) { return controller.getClass().getAnnotation(Controller.class); } }