diff --git a/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java b/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java index 99d0f98b08..a30a1fc687 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java +++ b/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java @@ -16,6 +16,7 @@ import com.predic8.membrane.annot.generator.*; import com.predic8.membrane.annot.generator.kubernetes.*; import com.predic8.membrane.annot.model.*; +import jakarta.annotation.Resource; import javax.annotation.processing.*; import javax.lang.model.*; @@ -172,10 +173,11 @@ public boolean process(Set annotations, RoundEnvironment // * in the last round, "roundEnv.processingOver()" is true try { - String status = "process() a=" + annotations.size() + - " r=" + roundEnv.getRootElements().size() + - " h=" + hashCode() + - (roundEnv.processingOver() ? " processing-over" : " "); + String status = "process() a=%d r=%d h=%d%s".formatted( + annotations.size(), + roundEnv.getRootElements().size(), + hashCode(), + roundEnv.processingOver() ? " processing-over" : " "); log(status); read(); @@ -184,7 +186,10 @@ public boolean process(Set annotations, RoundEnvironment if (!annotations.isEmpty()) { // a class with one of our annotation needs to be compiled - status = "working with " + getCachedElementsAnnotatedWith(roundEnv, MCMain.class).size() + " and " + getCachedElementsAnnotatedWith(roundEnv, MCElement.class).size(); + status = "working with %d and %d and %d".formatted( + getCachedElementsAnnotatedWith(roundEnv, MCMain.class).size(), + getCachedElementsAnnotatedWith(roundEnv, MCElement.class).size(), + getCachedElementsAnnotatedWith(roundEnv, Resource.class).size()); log(status); Model m = new Model(); @@ -287,6 +292,10 @@ public boolean process(Set annotations, RoundEnvironment } } + for (Element element : getCachedElementsAnnotatedWith(roundEnv, Resource.class)) { + m.getResources().add((TypeElement) element); + } + process(m); } @@ -524,6 +533,7 @@ public void process(Model m) throws IOException { new Parsers(processingEnv).writeParserDefinitior(m); new HelpReference(processingEnv).writeHelp(m); new NamespaceInfo(processingEnv).writeInfo(m); + new ResourceInfo(processingEnv).write(m); if (processingEnv.getElementUtils().getTypeElement("org.apache.aries.blueprint.ParserContext") != null) { new BlueprintParsers(processingEnv).writeParserDefinitior(m); new BlueprintParsers(processingEnv).writeParsers(m); diff --git a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java index 9e16951f38..06f05bed66 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java +++ b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java @@ -57,15 +57,14 @@ public interface BeanRegistry { void register(String beanName, Object bean); /** - * Registers a bean of the specified type with the given name if it is not already registered. - * If a bean with the given name is already present, the existing instance is returned. - * Otherwise, the supplier is used to create and register a new instance. + * Registers a bean of the specified type with the given name, if no other bean of the given type is registered. + * The supplier is only called, when the bean creation is requested, but no other bean of the given type has been + * registered. * @param type the class type of the bean * @param supplier a supplier that provides a new instance of the bean if not already registered * @param the generic type of the bean - * @return the existing or newly created and registered bean instance */ - T registerIfAbsent(Class type, Supplier supplier); + void registerFallbackIfAbsent(Class type, Supplier supplier); /** * Release all resources. diff --git a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java index 42868564a0..a13aa0c98e 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java +++ b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java @@ -27,6 +27,7 @@ import java.util.function.*; import static com.predic8.membrane.annot.yaml.WatchAction.ADDED; +import static java.util.List.of; /** * TODO: @@ -53,6 +54,8 @@ public class BeanRegistryImplementation implements BeanRegistry, BeanCollector, // uid -> bean container private final Map bcs = new ConcurrentHashMap<>(); // Order is not critical. Order is determined by uidsToActivate + private final List fallbacks = Collections.synchronizedList(new ArrayList<>()); + @GuardedBy("uidsToActivate") private final Set uidsToActivate = Collections.synchronizedSet(new LinkedHashSet<>()); // keeps order @@ -70,6 +73,25 @@ record UidAction(String uid, WatchAction action) { record PreDestroyCallback(Object bean, Method method) { } + record FallbackBeanDefiner(BeanRegistryImplementation registry, Class clazz, Supplier supplier) { + + public boolean produces(Class clazz) { + return clazz.isAssignableFrom(this.clazz()); + } + + public synchronized Object defineIfNecessary() { + if (!registry.hasDefinitionFor(clazz)) + return define(); + return registry.getBean(clazz).orElseThrow(); + } + + public synchronized Object define() { + Object bean = supplier.get(); + registry.register(null, bean); + return bean; + } + } + public BeanRegistryImplementation(Grammar grammar) { this.grammar = grammar; } @@ -154,6 +176,11 @@ public Object resolve(String url) { @Override public List getBeans() { + synchronized (fallbacks) { + for (FallbackBeanDefiner fallbackBeanDefiner : fallbacks) { + fallbackBeanDefiner.defineIfNecessary(); + } + } return bcs.values().stream().filter(bd -> !bd.getDefinition().isComponent()) .map(bc -> bc.getOrCreate(this, grammar)) .filter(Objects::nonNull) @@ -165,15 +192,29 @@ public Grammar getGrammar() { return grammar; } + protected boolean hasDefinitionFor(Class clazz) { + return bcs.values().stream().anyMatch(bd -> bd.produces(clazz)); + } + @Override public List getBeans(Class clazz) { - return bcs.values().stream() + List result = bcs.values().stream() .filter(bd -> bd.produces(clazz)) .map(bc -> bc.getOrCreate(this, grammar)) .filter(Objects::nonNull) .filter(clazz::isInstance) .map(clazz::cast) .toList(); + if (!result.isEmpty()) + return result; + + synchronized (fallbacks) { + for (FallbackBeanDefiner fallbackBeanDefiner : fallbacks) { + if (fallbackBeanDefiner.produces(clazz)) + return (List) of(fallbackBeanDefiner.defineIfNecessary()); + } + } + return Collections.emptyList(); } public Optional getBean(Class clazz) { @@ -183,7 +224,15 @@ public Optional getBean(Class clazz) { log.error(msg); throw new RuntimeException(msg); } - return beans.size() == 1 ? Optional.of(beans.getFirst()) : Optional.empty(); + if (!beans.isEmpty()) + return Optional.of(beans.getFirst()); + synchronized (fallbacks) { + for (FallbackBeanDefiner fallbackBeanDefiner : fallbacks) { + if (fallbackBeanDefiner.produces(clazz)) + return Optional.of((T) fallbackBeanDefiner.defineIfNecessary()); + } + } + return Optional.empty(); } public Optional getBean(String beanname, Class clazz) { @@ -208,16 +257,13 @@ public void register(String beanName, Object bean) { singletonBeans.put(uuid, bean); // the return value of 'put' is ignored, since bean registration with // random keys should not yield duplicates anyway. + + if (bean instanceof BeanRegistryAware beanRegistryAware) + beanRegistryAware.setRegistry(this); } - public T registerIfAbsent(Class type, Supplier supplier) { - synchronized (uniqueClassInitialization) { - return getBean(type).orElseGet(() -> getBean(type).orElseGet(() -> { - T created = supplier.get(); - register(null, created); - return created; - })); - } + public void registerFallbackIfAbsent(Class type, Supplier supplier) { + fallbacks.add(new FallbackBeanDefiner(this, type, supplier)); } private static @NotNull String computeBeanName(String beanName, String uuid) { diff --git a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java index 47eddf1ec3..2185b49173 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java +++ b/annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java @@ -72,7 +72,7 @@ public void register(String beanName, Object bean) { } @Override - public T registerIfAbsent(Class type, Supplier supplier) { + public void registerFallbackIfAbsent(Class type, Supplier supplier) { throw new UnsupportedOperationException(); } diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java b/annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java new file mode 100644 index 0000000000..d0418dbb60 --- /dev/null +++ b/annot/src/main/java/com/predic8/membrane/annot/generator/ResourceInfo.java @@ -0,0 +1,65 @@ +/* Copyright 2013 predic8 GmbH, www.predic8.com + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ +package com.predic8.membrane.annot.generator; + +import com.predic8.membrane.annot.ProcessingException; +import com.predic8.membrane.annot.model.*; +import com.predic8.membrane.annot.model.doc.Doc; +import com.predic8.membrane.annot.model.doc.Doc.Entry; + +import javax.annotation.processing.FilerException; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +/** + * Generates a resources.txt resource, which contains the fully qualified class names for all classes annotated with @Resource. + */ +public class ResourceInfo { + + private final ProcessingEnvironment processingEnv; + + public ResourceInfo(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; + } + + public void write(Model m) throws IOException { + try { + + for (MainInfo main : m.getMains()) { + List sources = new ArrayList<>(m.getResources()); + + FileObject o = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, + main.getAnnotation().outputPackage(), "resources.txt", sources.toArray(new Element[0])); + try (BufferedWriter bw = new BufferedWriter(o.openWriter())) { + for (TypeElement resource : m.getResources()) { + bw.write(resource.getQualifiedName() + "\n"); + } + } + } + } catch (FilerException e) { + if (e.getMessage().contains("Source file already created")) + return; + throw e; + } + } + +} diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/Model.java b/annot/src/main/java/com/predic8/membrane/annot/model/Model.java index 6e1dc0deb3..bb6f100236 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/Model.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/Model.java @@ -18,6 +18,8 @@ import com.predic8.membrane.annot.MCMain; +import javax.lang.model.element.TypeElement; + /** * Keeps track of all information during one (or several incremental) compiler runs. *

@@ -25,6 +27,7 @@ */ public class Model { private List mains = new ArrayList<>(); + private List resources = new ArrayList<>(); public List getMains() { return mains; @@ -33,4 +36,8 @@ public List getMains() { public void setMains(List mains) { this.mains = mains; } + + public List getResources() { + return resources; + } } \ No newline at end of file diff --git a/core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java b/core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java index 897f3156a5..9479cecb20 100644 --- a/core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java +++ b/core/src/main/java/com/predic8/membrane/core/exchangestore/LimitedMemoryExchangeStore.java @@ -20,6 +20,7 @@ import com.predic8.membrane.core.interceptor.Interceptor.Flow; import com.predic8.membrane.core.model.*; import com.predic8.membrane.core.proxies.*; +import jakarta.annotation.Resource; import org.slf4j.*; import java.text.*; @@ -34,6 +35,7 @@ * new exchanges arrive then old exchanges will be dropped (starting from oldest ascending) until the exchange can be * stored. The LimitedMemoryExchangeStore is the default ExchangeStore Membrane uses. */ +@Resource(type = ExchangeStore.class) @MCElement(name="limitedMemoryExchangeStore") public class LimitedMemoryExchangeStore extends AbstractExchangeStore { diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java b/core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java index 8967c07106..1638fa251d 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/FlowController.java @@ -17,6 +17,7 @@ import com.predic8.membrane.core.interceptor.Interceptor.*; import com.predic8.membrane.core.router.*; import com.predic8.membrane.core.transport.http.*; +import jakarta.annotation.Resource; import org.slf4j.*; import java.util.*; @@ -52,6 +53,7 @@ * {@link AbortException} is thrown. The stack is unwound calling * {@link Interceptor#handleAbort(Exchange)} on each interceptor on it. */ +@Resource public class FlowController { private static final Logger log = LoggerFactory.getLogger(FlowController.class); diff --git a/core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java b/core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java index 71d743d81c..09ac1653de 100644 --- a/core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java +++ b/core/src/main/java/com/predic8/membrane/core/kubernetes/client/KubernetesClientFactory.java @@ -14,6 +14,7 @@ package com.predic8.membrane.core.kubernetes.client; import com.predic8.membrane.core.transport.http.*; +import jakarta.annotation.Resource; import java.util.*; @@ -28,6 +29,7 @@ * Note: "baseUrl" is the KubernetesClient's only supported configuration. (KubernetesClientBuilder supports more, which * would need to be implemented here.) */ +@Resource public class KubernetesClientFactory { private WeakHashMap clients; private final HttpClientFactory httpClientFactory; diff --git a/core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java b/core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java index 7de357e2f3..8ede6dbe89 100644 --- a/core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java +++ b/core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java @@ -22,6 +22,8 @@ import com.predic8.membrane.core.transport.http.*; import com.predic8.membrane.core.transport.ssl.*; import com.predic8.membrane.core.util.*; +import jakarta.annotation.Priority; +import jakarta.annotation.Resource; import org.jetbrains.annotations.*; import org.slf4j.*; @@ -31,6 +33,7 @@ import static com.predic8.membrane.core.util.URIUtil.*; +@Resource public class RuleManager { private static final Logger log = LoggerFactory.getLogger(RuleManager.class.getName()); @@ -41,6 +44,15 @@ public class RuleManager { private final List ruleSources = new ArrayList<>(); private final Set listeners = new HashSet<>(); + @Priority(1) + public RuleManager(Router router) { + this.router = router; + } + + public RuleManager() { + + } + public enum RuleDefinitionSource { /** * rule defined in the spring context that created the router diff --git a/core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java b/core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java index 99671a6e78..acdd39fe71 100644 --- a/core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java +++ b/core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java @@ -23,6 +23,8 @@ import com.predic8.membrane.core.util.*; import com.predic8.membrane.core.util.functionalInterfaces.*; import com.predic8.xml.util.*; +import jakarta.annotation.Priority; +import jakarta.annotation.Resource; import org.jetbrains.annotations.*; import org.slf4j.*; import org.w3c.dom.ls.*; @@ -45,6 +47,7 @@ * Note that this class is not thread-safe! The ResolverMap is setup during * Membrane's single-threaded startup and is only used read-only thereafter. */ +@Resource @MCElement(name = "resolverMap") public class ResolverMap implements Cloneable, Resolver { @@ -157,6 +160,12 @@ public ResolverMap() { this(null, null); } + @Priority(1) + public ResolverMap(HttpClientFactory httpClientFactory, KubernetesClientFactory kubernetesClientFactory, Router router) { + this(httpClientFactory, kubernetesClientFactory); + addRuleResolver(router); + } + public ResolverMap(HttpClientFactory httpClientFactory, KubernetesClientFactory kubernetesClientFactory) { schemas = new String[10]; resolvers = new SchemaResolver[10]; diff --git a/core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java b/core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java index e548a9f18e..ef3bffd3ef 100644 --- a/core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java +++ b/core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java @@ -26,12 +26,26 @@ import com.predic8.membrane.core.transport.http.client.*; import com.predic8.membrane.core.transport.http.streampump.*; import com.predic8.membrane.core.util.*; +import jakarta.annotation.Priority; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.stream.Streams; +import org.jetbrains.annotations.NotNull; import org.slf4j.*; import org.springframework.beans.*; import org.springframework.context.*; +import javax.annotation.concurrent.GuardedBy; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.*; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; + public class DefaultMainComponents implements MainComponents { private static final Logger log = LoggerFactory.getLogger(DefaultMainComponents.class); @@ -44,25 +58,75 @@ public class DefaultMainComponents implements MainComponents { protected Transport transport; - private final TimerManager timerManager = new TimerManager(); - private final HttpClientFactory httpClientFactory = new HttpClientFactory(timerManager); - private final KubernetesClientFactory kubernetesClientFactory = new KubernetesClientFactory(httpClientFactory); - private final ResolverMap resolverMap; - - private final FlowController flowController; - private final RuleManager ruleManager; - - protected final Statistics statistics = new Statistics(); - + @GuardedBy("this") + boolean beansDefined = false; public DefaultMainComponents(DefaultRouter router) { log.debug("Creating new router."); this.router = router; - resolverMap = new ResolverMap(httpClientFactory, kubernetesClientFactory); - resolverMap.addRuleResolver(router); - flowController = new FlowController(router); - ruleManager= new RuleManager(); - ruleManager.setRouter(router); + } + + /** + * Registers fallbackBeanDefinitions with the registry for all classes annotated with @Resource . + * + * In case a class has multiple constructors, the one with the highest Priority annotation is used. + * + * The constructor's parameters are resolved from the registry. + */ + public synchronized void defineResourceBeans() { + if (beansDefined) + return; + beansDefined = true; + + loadResourceClasses().forEach(clazzName -> { + try { + Class aClass = getClass().getClassLoader().loadClass(clazzName); + Resource resource = aClass.getAnnotation(Resource.class); + //noinspection unchecked + registry.registerFallbackIfAbsent((Class) (resource.type().equals(Object.class) ? aClass : resource.type()), () -> createResource(aClass)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }); + } + + private List loadResourceClasses() { + try (InputStream is = requireNonNull(getClass().getResourceAsStream("/com/predic8/membrane/core/config/spring/resources.txt"))) { + return new BufferedReader(new InputStreamReader(is, UTF_8)).lines().toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Object createResource(Class aClass) { + try { + Constructor constructor = chooseConstructor(aClass.getConstructors()); + return constructor.newInstance(fillParameterList(constructor)); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private Object @NotNull [] fillParameterList(Constructor constructor) { + Object[] parameters = new Object[constructor.getParameterCount()]; + for (int i = 0; i < parameters.length; i++) { + parameters[i] = registry.getBean(constructor.getParameterTypes()[i]).orElseThrow(); + } + return parameters; + } + + private Constructor chooseConstructor(Constructor[] constructors) { + if (constructors.length == 1) + return constructors[0]; + constructors = Streams.of(constructors).filter(c -> c.isAnnotationPresent(Priority.class)).toArray(Constructor[]::new); + if (constructors.length == 0) + throw new RuntimeException("No constructor annotated with @Priority found."); + Arrays.sort(constructors, Comparator.comparingInt(c -> c.getAnnotation(Priority.class).value())); + return constructors[0]; } public void init() { @@ -73,16 +137,11 @@ public void init() { registry.register("router", router); } - registry.registerIfAbsent(HttpClientConfiguration.class, HttpClientConfiguration::new); - registry.registerIfAbsent(ExchangeStore.class, LimitedMemoryExchangeStore::new); - registry.registerIfAbsent(DNSCache.class, DNSCache::new); + defineResourceBeans(); - // Transport last - if (transport == null) { - transport = new HttpTransport(); - } + if (transport == null) + transport = registry.getBean(Transport.class).orElseThrow(); transport.init(router); - } public void setRules(Collection proxies) { @@ -93,7 +152,7 @@ public void setRules(Collection proxies) { @Override public RuleManager getRuleManager() { - return ruleManager; + return getRegistry().getBean(RuleManager.class).orElseThrow(); } public void setApplicationContext(ApplicationContext ctx) throws BeansException { @@ -136,17 +195,17 @@ public void setHttpClientConfig(HttpClientConfiguration httpClientConfig) { @Override public DNSCache getDnsCache() { - return getRegistry().getBean(DNSCache.class).orElseThrow(); // TODO + return getRegistry().getBean(DNSCache.class).orElseThrow(); } @Override public ResolverMap getResolverMap() { - return resolverMap; + return getRegistry().getBean(ResolverMap.class).orElseThrow(); } @Override public Statistics getStatistics() { - return statistics; + return getRegistry().getBean(Statistics.class).orElseThrow(); } public void setGlobalInterceptor(GlobalInterceptor globalInterceptor) { @@ -155,24 +214,25 @@ public void setGlobalInterceptor(GlobalInterceptor globalInterceptor) { @Override public TimerManager getTimerManager() { - return timerManager; + return registry.getBean(TimerManager.class).orElseThrow(); } @Override public KubernetesClientFactory getKubernetesClientFactory() { - return kubernetesClientFactory; + return getRegistry().getBean(KubernetesClientFactory.class).orElseThrow(); } public HttpClientFactory getHttpClientFactory() { - return httpClientFactory; + return getRegistry().getBean(HttpClientFactory.class).orElseThrow(); } public FlowController getFlowController() { - return flowController; + return getRegistry().getBean(FlowController.class).orElseThrow(); } public void setRegistry(BeanRegistry registry) { this.registry = registry; + defineResourceBeans(); } public BeanRegistry getRegistry() { diff --git a/core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java b/core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java index e843b9e48e..36618b97f7 100644 --- a/core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java +++ b/core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java @@ -35,6 +35,7 @@ import com.predic8.membrane.core.transport.http.client.*; import com.predic8.membrane.core.transport.http.streampump.*; import com.predic8.membrane.core.util.*; +import jakarta.annotation.PostConstruct; import org.slf4j.*; import org.springframework.beans.*; import org.springframework.beans.factory.*; @@ -119,6 +120,7 @@ public DefaultRouter() { * But it does not start the router itself. Use {@link #start()} to start the router. * If start() is called a separate call to init() is not needed. */ + @PostConstruct public void init() { log.debug("Initializing."); diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java index b32d3d6a85..51615bfda5 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpClientFactory.java @@ -13,8 +13,10 @@ limitations under the License. */ package com.predic8.membrane.core.transport.http; +import com.predic8.membrane.annot.MCElement; import com.predic8.membrane.core.transport.http.client.HttpClientConfiguration; import com.predic8.membrane.core.util.TimerManager; +import jakarta.annotation.Resource; import javax.annotation.Nullable; import java.util.Objects; @@ -29,6 +31,7 @@ *
  • The HttpClient only uses one connection pool.
  • * */ +@Resource public class HttpClientFactory { @Nullable private final TimerManager timerManager; diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java index d9b0671964..3022d05eb8 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/HttpTransport.java @@ -21,6 +21,7 @@ import com.predic8.membrane.core.transport.*; import com.predic8.membrane.core.transport.ssl.*; import com.predic8.membrane.core.util.*; +import jakarta.annotation.Resource; import org.slf4j.*; import java.io.*; @@ -45,6 +46,7 @@ * * */ +@Resource(type = Transport.class) @MCElement(name="transport") public class HttpTransport extends Transport { diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java b/core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java index c74645b51d..1beaad14c0 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/client/HttpClientConfiguration.java @@ -16,6 +16,7 @@ import com.predic8.membrane.annot.*; import com.predic8.membrane.core.config.security.*; import com.predic8.membrane.core.config.spring.*; +import jakarta.annotation.Resource; import org.springframework.beans.*; import org.springframework.context.*; @@ -45,6 +46,7 @@ * * @topic 4. Transports and Clients */ +@Resource @MCElement(name = "httpClientConfig") public class HttpClientConfiguration implements ApplicationContextAware { diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java b/core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java index 03c0307096..692b6df60b 100644 --- a/core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java +++ b/core/src/main/java/com/predic8/membrane/core/transport/http/streampump/Statistics.java @@ -13,6 +13,9 @@ limitations under the License. */ package com.predic8.membrane.core.transport.http.streampump; +import jakarta.annotation.Resource; + +@Resource public class Statistics { private final StreamPump.StreamPumpStats streamPumpStats = new StreamPump.StreamPumpStats(); diff --git a/core/src/main/java/com/predic8/membrane/core/util/DNSCache.java b/core/src/main/java/com/predic8/membrane/core/util/DNSCache.java index 1b1084b2a1..5f47b91dbc 100644 --- a/core/src/main/java/com/predic8/membrane/core/util/DNSCache.java +++ b/core/src/main/java/com/predic8/membrane/core/util/DNSCache.java @@ -14,6 +14,8 @@ package com.predic8.membrane.core.util; +import jakarta.annotation.Resource; + import java.net.InetAddress; import java.util.Collection; import java.util.Hashtable; @@ -24,6 +26,7 @@ *

    * Java 1.5 implementation of InetAddress Caching differs from Java 1.6 one. */ +@Resource public class DNSCache { private final Map hostNames = new Hashtable<>(); diff --git a/core/src/main/java/com/predic8/membrane/core/util/TimerManager.java b/core/src/main/java/com/predic8/membrane/core/util/TimerManager.java index b64370bd88..4b23d8748a 100644 --- a/core/src/main/java/com/predic8/membrane/core/util/TimerManager.java +++ b/core/src/main/java/com/predic8/membrane/core/util/TimerManager.java @@ -14,6 +14,7 @@ package com.predic8.membrane.core.util; import com.predic8.membrane.core.*; +import jakarta.annotation.Resource; import org.slf4j.*; import java.util.Timer; @@ -22,6 +23,7 @@ /** * Manages periodic tasks with a single timer. */ +@Resource public class TimerManager { private static final Logger log = LoggerFactory.getLogger(TimerManager.class); diff --git a/core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java b/core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java index 500bfaec48..60439836e7 100644 --- a/core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java +++ b/core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java @@ -376,9 +376,7 @@ public Optional getBean(String beanName, Class clazz) { public void register(String beanName, Object object) {} @Override - public T registerIfAbsent(Class type, Supplier supplier) { - return type.cast(null); - } + public void registerFallbackIfAbsent(Class type, Supplier supplier) {} @Override public void close() {}