Skip to content

Commit 3b30a54

Browse files
predic8rrayst
andauthored
Chain: Migrate Example to YAML - Fix for component names (#2529)
* extend: enhance chain functionality, add reusable chain and CORS support examples (#2527) * enhance: add roadmap entry for configuration-independent bean lookup, improve `ChainInterceptor` bean retrieval logic, and update formatting in `DefaultRouter` * refactor: add @NotNull annotations, improve Javadoc, and enhance comments in `BeanContainer`, `BeanRegistry`, and `MethodSetter` for better null-safety and readability * refactor: MethodSetter logic now handles MCOtherAttributes-only elements without global keyword resolution * fix: compiler error * refactor * refactor --------- Co-authored-by: Tobias Polley <polley@predic8.de> Co-authored-by: Tobias Polley <mail@tobias-polley.de>
1 parent 6cec04d commit 3b30a54

14 files changed

Lines changed: 209 additions & 39 deletions

File tree

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanContainer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.predic8.membrane.annot.Grammar;
1818
import com.predic8.membrane.annot.bean.BeanFactory;
1919
import com.predic8.membrane.annot.yaml.GenericYamlParser;
20+
import org.jetbrains.annotations.*;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
2223

@@ -71,7 +72,7 @@ public String toString() {
7172
return "BeanContainer: %s of %s singleton: %s".formatted( definition.getName(),definition.getKind(),singleton.get());
7273
}
7374

74-
private synchronized Object define(BeanRegistryImplementation registry, Grammar grammar) {
75+
private synchronized @NotNull Object define(BeanRegistryImplementation registry, Grammar grammar) {
7576
log.debug("defining bean: {}", definition.getNode());
7677
try {
7778
if ("bean".equals(definition.getKind())) {
@@ -86,7 +87,7 @@ private synchronized Object define(BeanRegistryImplementation registry, Grammar
8687
}
8788
}
8889

89-
public Object getOrCreate(BeanRegistryImplementation registry, Grammar grammar) {
90+
public @NotNull Object getOrCreate(BeanRegistryImplementation registry, Grammar grammar) {
9091
boolean prototype = isPrototypeScope(getDefinition());
9192

9293
// Prototypes are created anew every time.

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistry.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ public interface BeanRegistry {
3939
*/
4040
<T> Optional<T> getBean(Class<T> clazz);
4141

42+
/**
43+
* Retrieves a bean with the specified name.
44+
* @param name the name of the bean
45+
* @param clazz the class of the bean
46+
* @return Optional containing the bean
47+
* @param <T> the bean type
48+
*/
49+
<T> Optional<T> getBean(String name, Class<T> clazz);
50+
4251
/**
4352
* Registers a bean with the specified name.
4453
*

annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryImplementation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ public <T> Optional<T> getBean(Class<T> clazz) {
172172
return beans.size() == 1 ? Optional.of(beans.getFirst()) : Optional.empty();
173173
}
174174

175+
public <T> Optional<T> getBean(String beanname, Class<T> clazz) {
176+
return getFirstByName(beanname)
177+
.map(bc -> bc.getOrCreate(this, grammar))
178+
.map(clazz::cast);
179+
}
180+
175181
public void register(String beanName, Object bean) {
176182
if (bean == null)
177183
throw new IllegalArgumentException("bean must not be null");

annot/src/main/java/com/predic8/membrane/annot/beanregistry/SpringContextAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public <T> Optional<T> getBean(Class<T> clazz) {
4747
throw new UnsupportedOperationException();
4848
}
4949

50+
@Override
51+
public <T> Optional<T> getBean(String name, Class<T> clazz) {
52+
throw new UnsupportedOperationException();
53+
}
54+
5055
@Override
5156
public void register(String beanName, Object bean) {
5257
throw new UnsupportedOperationException();

annot/src/main/java/com/predic8/membrane/annot/yaml/McYamlIntrospector.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.lang.reflect.*;
2121
import java.util.*;
22+
import java.util.concurrent.atomic.AtomicInteger;
2223
import java.util.stream.*;
2324

2425
import static java.lang.Character.*;
@@ -145,6 +146,18 @@ public static String getSetterName(Method setter) {
145146
return toLowerCase(property.charAt(0)) + property.substring(1);
146147
}
147148

149+
public static boolean hasOtherAttributes(Class<?> clazz) {
150+
return stream(clazz.getMethods()).filter(m -> m.isAnnotationPresent(MCOtherAttributes.class)).count() > 0;
151+
}
152+
153+
public static boolean hasAttributes(Class<?> clazz) {
154+
return stream(clazz.getMethods()).filter(m -> m.isAnnotationPresent(MCAttribute.class)).count() > 0;
155+
}
156+
157+
public static boolean hasChildren(Class<?> clazz) {
158+
return stream(clazz.getMethods()).filter(m -> m.isAnnotationPresent(MCChildElement.class)).count() > 0;
159+
}
160+
148161
public static <T> Method getAnySetter(Class<T> clazz) {
149162
return stream(clazz.getMethods())
150163
.filter(McYamlIntrospector::isSetter)

annot/src/main/java/com/predic8/membrane/annot/yaml/MethodSetter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ public MethodSetter(Method setter, Class<?> beanClass) {
5555
}
5656
Class<?> beanClass = null;
5757
if (setter == null) {
58+
// if the element ONLY has a MCOtherAttributes and no MCAttributes and no MCChildElement setters, we avoid
59+
// global keyword resolution: the keyword will always be a key for MCOtherAttributes
60+
if (hasOtherAttributes(clazz) && !hasAttributes(clazz) && !hasChildren(clazz)) {
61+
return new MethodSetter(getAnySetter(clazz), null);
62+
}
63+
5864
try {
5965
beanClass = ctx.grammar().getLocal(ctx.context(), key);
6066
if (beanClass == null)

annot/src/test/java/com/predic8/membrane/annot/YAMLComponentsParsingTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,35 @@ public void componentRefersToAnotherComponent() {
339339
);
340340
}
341341

342+
@Test
343+
public void componentIdIsWordFromGrammar() {
344+
assertStructure(
345+
parse("""
346+
components:
347+
bearerToken:
348+
bearerToken:
349+
header: Authorization
350+
---
351+
api:
352+
flow:
353+
- oauth2authserver:
354+
issuer: https://issuer
355+
otherFields: abc
356+
$ref: "#/components/bearerToken"
357+
"""),
358+
clazz("Components"),
359+
clazz("ApiElement",
360+
property("flow", list(
361+
clazz("OAuth2AuthServerElement",
362+
property("issuer", value("https://issuer")),
363+
property("otherFields", value("abc")),
364+
property("bearerToken",
365+
clazz("BearerTokenElement",
366+
property("header", value("Authorization")))))
367+
)))
368+
);
369+
}
370+
342371
@Test
343372
public void topLevelElementNotAllowedAsNestedChild() {
344373
var ex = assertThrows(RuntimeException.class, () -> parseWithTopLevelOnlySources("""

core/src/main/java/com/predic8/membrane/core/interceptor/chain/ChainInterceptor.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,17 @@
1414

1515
package com.predic8.membrane.core.interceptor.chain;
1616

17-
import com.predic8.membrane.annot.MCAttribute;
18-
import com.predic8.membrane.annot.MCElement;
19-
import com.predic8.membrane.annot.Required;
20-
import com.predic8.membrane.core.exchange.Exchange;
21-
import com.predic8.membrane.core.interceptor.Interceptor;
22-
import com.predic8.membrane.core.interceptor.Outcome;
23-
import com.predic8.membrane.core.interceptor.flow.AbstractFlowWithChildrenInterceptor;
24-
import com.predic8.membrane.core.util.ConfigurationException;
25-
26-
import java.util.List;
27-
import java.util.Optional;
17+
import com.predic8.membrane.annot.*;
18+
import com.predic8.membrane.core.exchange.*;
19+
import com.predic8.membrane.core.interceptor.*;
20+
import com.predic8.membrane.core.interceptor.flow.*;
21+
import com.predic8.membrane.core.util.*;
22+
import org.springframework.beans.factory.*;
23+
24+
import java.util.*;
2825

2926
/**
30-
* @description A Chain groups multiple interceptors into reusable components, reducing redundancy in API configurations.
27+
* @description A Chain groups multiple interceptors into reusable components, reducing redundancy in API configurations.
3128
*/
3229
@MCElement(name = "chain")
3330
public class ChainInterceptor extends AbstractFlowWithChildrenInterceptor {
@@ -42,9 +39,28 @@ public void init() {
4239
}
4340

4441
private List<Interceptor> getInterceptorChainForRef(String ref) {
45-
return Optional.of(router.getBeanFactory().getBean(ref, ChainDef.class))
46-
.map(ChainDef::getFlow)
47-
.orElseThrow(() -> new ConfigurationException("No chain found for reference: " + ref));
42+
return getBean(ref, ChainDef.class)
43+
.orElseThrow(() -> new ConfigurationException("No chain found for reference: " + ref))
44+
.getFlow();
45+
}
46+
47+
/**
48+
* TODO: Temporary fix till we have a central configuration independant lookup
49+
*/
50+
private <T> Optional<T> getBean(String name, Class<T> clazz) {
51+
if (router.getRegistry() != null) {
52+
var bean = router.getRegistry().getBean(name, clazz);
53+
if (bean.isPresent())
54+
return bean;
55+
}
56+
// From XML
57+
if (router.getBeanFactory() != null) {
58+
try {
59+
return Optional.of(router.getBeanFactory().getBean(name, clazz));
60+
} catch (NoSuchBeanDefinitionException ignored) {
61+
}
62+
}
63+
return Optional.empty();
4864
}
4965

5066
@Override

core/src/main/java/com/predic8/membrane/core/router/DefaultRouter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ public void setRegistry(BeanRegistry registry) {
405405

406406
@Override
407407
public BeanRegistry getRegistry() {
408-
return mainComponents.getRegistry();
408+
return mainComponents.getRegistry();
409409
}
410410

411411
public void applyConfiguration(Configuration configuration) {

core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ public <T> Optional<T> getBean(Class<T> clazz) {
367367
return Optional.empty();
368368
}
369369

370+
@Override
371+
public <T> Optional<T> getBean(String beanName, Class<T> clazz) {
372+
return Optional.empty();
373+
}
374+
370375
@Override
371376
public void register(String beanName, Object object) {}
372377

0 commit comments

Comments
 (0)