Skip to content

Commit 4160c3b

Browse files
refactor: move globalInterceptor to beanFactory (#2510)
* refactor: move globalInterceptor to beanFactory * refactor: streamline exception messages, improve logging, and enhance Javadoc comments across classes --------- Co-authored-by: Christian Gördes <118011644+christiangoerdes@users.noreply.github.com>
1 parent 8615492 commit 4160c3b

11 files changed

Lines changed: 132 additions & 32 deletions

File tree

annot/pom.xml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@
1212
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
15-
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
15+
-->
16+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
17+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
1618

1719
<modelVersion>4.0.0</modelVersion>
1820
<artifactId>service-proxy-annot</artifactId>
1921
<name>${project.artifactId}</name>
2022
<packaging>jar</packaging>
2123

22-
<parent>
23-
<groupId>org.membrane-soa</groupId>
24-
<artifactId>service-proxy-parent</artifactId>
25-
<relativePath>../pom.xml</relativePath>
26-
<version>7.0.5</version>
27-
</parent>
24+
<parent>
25+
<groupId>org.membrane-soa</groupId>
26+
<artifactId>service-proxy-parent</artifactId>
27+
<relativePath>../pom.xml</relativePath>
28+
<version>7.0.5</version>
29+
</parent>
2830

2931
<dependencies>
3032
<dependency>
@@ -87,6 +89,11 @@
8789
<version>3.0</version>
8890
<scope>test</scope>
8991
</dependency>
92+
<dependency>
93+
<groupId>org.mockito</groupId>
94+
<artifactId>mockito-core</artifactId>
95+
<scope>test</scope>
96+
</dependency>
9097
</dependencies>
9198

9299
<build>

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,22 @@ public interface BeanRegistry {
2626
Grammar getGrammar();
2727

2828
<T> List<T> getBeans(Class<T> clazz);
29+
30+
/**
31+
* Retrieves a single bean of the specified type.
32+
*
33+
* @param clazz the class of the bean to retrieve
34+
* @param <T> the bean type
35+
* @return Optional containing the bean if exactly one exists, empty otherwise
36+
* @throws RuntimeException if multiple beans of the specified type exist
37+
*/
38+
<T> Optional<T> getBean(Class<T> clazz);
39+
40+
/**
41+
* Registers a bean with the specified name.
42+
*
43+
* @param beanName the name to register the bean under
44+
* @param bean instance to register (must not be null)
45+
*/
46+
void register(String beanName, Object bean);
2947
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,22 @@ public <T> List<T> getBeans(Class<T> clazz) {
173173
.map(clazz::cast)
174174
.toList();
175175
}
176+
177+
public <T> Optional<T> getBean(Class<T> clazz) {
178+
var beans = getBeans(clazz);
179+
if (beans.size() > 1) {
180+
var msg = "One bean was asked. But found %d beans of %s".formatted(beans.size(),clazz);
181+
log.error(msg);
182+
throw new RuntimeException(msg);
183+
}
184+
return beans.size() == 1 ? Optional.of(beans.getFirst()) : Optional.empty();
185+
}
186+
187+
public void register(String beanName, Object bean) {
188+
var uuid = UUID.randomUUID().toString();
189+
BeanContainer bc = new BeanContainer(new BeanDefinition("component", beanName,null, uuid, null));
190+
bc.setSingleton(bean);
191+
singletonBeans.put(uuid,bean);
192+
bcs.put(uuid, bc);
193+
}
176194
}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,7 @@ public static List<BeanDefinition> parseMembraneResources(@NotNull InputStream r
105105
try (resource) {
106106
return parseToBeanDefinitions(resource, grammar);
107107
} catch (JsonParseException e) {
108-
throw new IOException(
109-
"Invalid YAML: multiple configurations must be separated by '---' "
110-
+ "(at line " + e.getLocation().getLineNr()
111-
+ ", column " + e.getLocation().getColumnNr() + ").",
112-
e
113-
);
108+
throw new IOException("Invalid YAML: multiple configurations must be separated by '---' (at line %d, column %d).".formatted(e.getLocation().getLineNr(), e.getLocation().getColumnNr()), e);
114109
}
115110
}
116111

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* Copyright 2025 predic8 GmbH, www.predic8.com
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
package com.predic8.membrane.annot.beanregistry;
16+
17+
import org.junit.jupiter.api.*;
18+
import org.mockito.*;
19+
20+
import java.util.*;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
24+
class BeanRegistryImplementationTest {
25+
26+
private BeanRegistryImplementation registry;
27+
private BeanRegistryAware aware;
28+
29+
@BeforeEach
30+
void setup() {
31+
aware = Mockito.mock(BeanRegistryAware.class);
32+
registry = new BeanRegistryImplementation(null, aware, null);
33+
}
34+
35+
@Test
36+
void register() {
37+
A a1 = new A("a1");
38+
A a2 = new A("a2");
39+
A a3 = new A("a3");
40+
registry.register("bean1", a1);
41+
registry.register("bean2", a2);
42+
registry.register("bean3", a3);
43+
List<A> as = registry.getBeans(A.class);
44+
assertEquals(3, as.size());
45+
assertEquals(Set.of(a1,a2,a3),new HashSet<>(as));
46+
}
47+
48+
@Test
49+
void getBean() {
50+
A a1 = new A("a1");
51+
registry.register("bean1", a1);
52+
assertEquals(a1, registry.getBean(A.class).orElseThrow());
53+
}
54+
55+
class A {
56+
String value;
57+
58+
public A(String value) {
59+
this.value = value;
60+
}
61+
}
62+
}

core/src/main/java/com/predic8/membrane/core/Router.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ public class Router implements Lifecycle, ApplicationContextAware, BeanRegistryA
105105
protected final FlowController flowController;
106106
protected ExchangeStore exchangeStore = new LimitedMemoryExchangeStore();
107107
protected Transport transport;
108-
protected GlobalInterceptor globalInterceptor = new GlobalInterceptor();
109108
protected final ResolverMap resolverMap;
110109
protected final DNSCache dnsCache = new DNSCache();
111110
private final KubernetesWatcher kubernetesWatcher = new KubernetesWatcher(this);
@@ -497,7 +496,7 @@ public void setBeanName(String s) {
497496
*/
498497
@MCChildElement(order = 2)
499498
public void setGlobalInterceptor(GlobalInterceptor globalInterceptor) {
500-
this.globalInterceptor = globalInterceptor;
499+
registry.register("globalInterceptor", globalInterceptor);
501500
}
502501

503502
public String getId() {
@@ -530,10 +529,6 @@ public FlowController getFlowController() {
530529
return flowController;
531530
}
532531

533-
public GlobalInterceptor getGlobalInterceptor() {
534-
return globalInterceptor;
535-
}
536-
537532
public synchronized void setAsynchronousInitialization(boolean asynchronousInitialization) {
538533
this.asynchronousInitialization = asynchronousInitialization;
539534
notifyAll();

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ private static Router initRouterByYAML(MembraneCommandLine commandLine, String o
173173
}
174174

175175
private static Router initRouterByYAML(String location) throws Exception {
176-
var router = new HttpRouter();
176+
var router = new Router();
177177
router.setBaseLocation(location);
178178
router.setAsynchronousInitialization(true);
179179

core/src/main/java/com/predic8/membrane/core/interceptor/GlobalInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @description The global chain applies plugins to all endpoints, enabling centralized features
2323
* such as global user authentication, logging, and other cross-cutting concerns.
2424
*/
25-
@MCElement(name = "global", excludeFromFlow = true)
25+
@MCElement(name = "global", excludeFromFlow = true, component = false, topLevel = true)
2626
public class GlobalInterceptor extends AbstractFlowWithChildrenInterceptor {
2727

2828
@Override

core/src/main/java/com/predic8/membrane/core/transport/Transport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public abstract class Transport {
3333
* SSL and Non-SSL are mixed here, maybe split that in future
3434
*/
3535
protected final Set<IPortChangeListener> menuListeners = new HashSet<>();
36-
3736
private List<Interceptor> interceptors = new Vector<>();
37+
3838
private Router router;
3939
private boolean reverseDNS = true;
4040

@@ -62,7 +62,7 @@ public void init(Router router) throws Exception {
6262
interceptors.add(getExchangeStoreInterceptor());
6363
interceptors.add(getInterceptor(DispatchingInterceptor.class));
6464
interceptors.add(getInterceptor(ReverseProxyingInterceptor.class));
65-
interceptors.add(router.getGlobalInterceptor());
65+
router.getRegistry().getBean(GlobalInterceptor.class).ifPresent(i -> interceptors.add(i ));
6666
interceptors.add(getInterceptor(UserFeatureInterceptor.class));
6767
interceptors.add(getInterceptor(InternalRoutingInterceptor.class));
6868
interceptors.add(getInterceptor(HTTPClientInterceptor.class));

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -346,21 +346,16 @@ public List<Object> getBeans() {
346346
return List.of();
347347
}
348348

349-
350349
@Override
351350
public void parseYamls(InputStream yamls, Grammar grammar) throws IOException {
352351
BeanCollector.super.parseYamls(yamls, grammar);
353352
}
354353

355354
@Override
356-
public void handle(ChangeEvent changeEvent, boolean isLast) {
357-
358-
}
355+
public void handle(ChangeEvent changeEvent, boolean isLast) {}
359356

360357
@Override
361-
public void start() {
362-
363-
}
358+
public void start() {}
364359

365360
@Override
366361
public Grammar getGrammar() {
@@ -371,6 +366,14 @@ public Grammar getGrammar() {
371366
public <T> List<T> getBeans(Class<T> clazz) {
372367
return List.of();
373368
}
369+
370+
@Override
371+
public <T> Optional<T> getBean(Class<T> clazz) {
372+
return Optional.empty();
373+
}
374+
375+
@Override
376+
public void register(String beanName, Object object) {}
374377
}
375378

376379
private static APIProxy parse(String yaml, BeanRegistry reg) {

0 commit comments

Comments
 (0)