Skip to content

Commit 6afc45f

Browse files
authored
Merge branch 'master' into simplify-jsonschema-example
2 parents 096e52e + 9463751 commit 6afc45f

File tree

473 files changed

+4665
-3994
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

473 files changed

+4665
-3994
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,4 @@ maven-plugin/target/surefire/
127127
.vscode/
128128
/core/derby.log
129129
/distribution/conf/apis.yaml
130+
/distribution/conf/membrane.log

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM maven:3.9.10-amazoncorretto-21-al2023
1+
FROM maven:3.9.12-amazoncorretto-21-al2023
22

33
ADD pom.xml /app/
44
ADD annot/pom.xml /app/annot/

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -747,18 +747,18 @@ api:
747747
748748
### Transform XML into Text or JSON
749749
750-
Using `setProperty` you can extract values from XML request or response bodies and store it in properties. Then the properties are available as variables inside the `template` plugin.
751-
plugin.
750+
You can use XPath to extract values from an XML message and insert them into a `template`.
752751
753-
```xml
754-
755-
<api port="2000">
756-
<request>
757-
<setProperty name="fn" value="${/person/@firstname}" language="xpath"/>
758-
<template>Buenas Noches, ${property.fn}sito!</template>
759-
</request>
760-
<return/>
761-
</api>
752+
```yaml
753+
api:
754+
port: 2000
755+
flow:
756+
- request:
757+
- template:
758+
src: |
759+
Buenas noches, ${fn.xpath('/person/@firstname')}
760+
- return:
761+
status: 200
762762
```
763763
764764
See: [message-transformation examples](./distribution/examples/message-transformation)
@@ -876,7 +876,7 @@ Membrane offers lots of security features to protect backend servers.
876876
You can define APIs keys directly in your configuration, and Membrane will validate incoming requests against them.
877877
878878
### Example Configuration
879-
The following configuration secures the `Fruitshop API` by validating a key provided as a query parameter:
879+
The following configuration secures the `Fruitshop API` by validating an API key provided as a query parameter:
880880
881881
```xml
882882
<api port="2000">

annot/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<groupId>org.membrane-soa</groupId>
2626
<artifactId>service-proxy-parent</artifactId>
2727
<relativePath>../pom.xml</relativePath>
28-
<version>7.0.5-SNAPSHOT</version>
28+
<version>7.0.6-SNAPSHOT</version>
2929
</parent>
3030

3131
<dependencies>
@@ -89,6 +89,11 @@
8989
<version>3.0</version>
9090
<scope>test</scope>
9191
</dependency>
92+
<dependency>
93+
<groupId>org.mockito</groupId>
94+
<artifactId>mockito-core</artifactId>
95+
<scope>test</scope>
96+
</dependency>
9297
</dependencies>
9398

9499
<build>

annot/src/main/java/com/predic8/membrane/annot/yaml/BeanCacheObserver.java renamed to annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanCacheObserver.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@
1212
See the License for the specific language governing permissions and
1313
limitations under the License. */
1414

15-
package com.predic8.membrane.annot.yaml;
16-
17-
import com.predic8.membrane.annot.beanregistry.BeanDefinition;
18-
import com.predic8.membrane.annot.beanregistry.BeanDefinitionChanged;
19-
import com.predic8.membrane.annot.beanregistry.BeanRegistryImplementation;
15+
package com.predic8.membrane.annot.beanregistry;
2016

2117
import java.io.IOException;
2218

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,29 @@
2727
* and send it a series of change events.
2828
*/
2929
public interface BeanCollector {
30+
31+
default List<BeanDefinition> parseYamlBeanDefinitions(InputStream yamls, Grammar grammar) throws IOException {
32+
List<BeanDefinition> bds = GenericYamlParser.parseMembraneResources(yamls, grammar);
33+
for (BeanDefinition bd : bds) {
34+
handle(new BeanDefinitionChanged(WatchAction.ADDED, bd), false);
35+
}
36+
return bds;
37+
}
38+
39+
default void finishStaticConfiguration() {
40+
handle(new StaticConfigurationLoaded(), true);
41+
start();
42+
}
43+
3044
/**
3145
* Utility method to ingest a stream of YAML objects as a static configuration and then
3246
* start the bean registry.
3347
* @param yamls stream of YAML objects
3448
* @param grammar the grammar to use for parsing
3549
*/
3650
default void parseYamls(InputStream yamls, Grammar grammar) throws IOException {
37-
List<BeanDefinition> bds = GenericYamlParser.parseMembraneResources(yamls, grammar);
38-
for (int i = 0; i < bds.size(); i++) {
39-
handle(new BeanDefinitionChanged(WatchAction.ADDED, bds.get(i)), i == bds.size() - 1);
40-
}
41-
handle(new StaticConfigurationLoaded(), true);
42-
start();
51+
parseYamlBeanDefinitions(yamls, grammar);
52+
finishStaticConfiguration();
4353
}
4454

4555
/**

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

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,106 @@
1414

1515
package com.predic8.membrane.annot.beanregistry;
1616

17-
import com.fasterxml.jackson.databind.JsonNode;
17+
import com.predic8.membrane.annot.Grammar;
18+
import com.predic8.membrane.annot.bean.BeanFactory;
19+
import com.predic8.membrane.annot.yaml.GenericYamlParser;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import java.util.concurrent.atomic.*;
1824

1925
public class BeanContainer {
26+
private static final Logger log = LoggerFactory.getLogger(BeanContainer.class);
27+
2028
private final BeanDefinition definition;
2129
/**
2230
* Constructed bean after initialization.
2331
*/
24-
private volatile Object singleton;
32+
private final AtomicReference<Object> singleton = new AtomicReference<>();
2533

34+
/**
35+
* Creates a BeanDefinition where the bean has not yet been instantiated and initialized.
36+
*/
2637
public BeanContainer(BeanDefinition definition) {
2738
this.definition = definition;
2839
}
2940

41+
/**
42+
* Creates a BeanDefinition where the bean has already been instantiated and initialized.
43+
*/
44+
public BeanContainer(BeanDefinition definition, Object singleton) {
45+
this.definition = definition;
46+
this.singleton.set(singleton);
47+
}
3048

31-
public Object getSingleton() {
32-
return singleton;
49+
/**
50+
* Only to be used within this class.
51+
* Use {@link #getOrCreate(BeanRegistryImplementation, Grammar)} instead.
52+
*/
53+
private Object getSingleton() {
54+
return singleton.get();
3355
}
3456

35-
public void setSingleton(Object singleton) {
36-
this.singleton = singleton;
57+
/**
58+
* Only to be used within this class.
59+
* Use {@link #getOrCreate(BeanRegistryImplementation, Grammar)} instead.
60+
*/
61+
private void setSingleton(Object singleton) {
62+
this.singleton.set(singleton);
3763
}
3864

3965
public BeanDefinition getDefinition() {
4066
return definition;
4167
}
68+
69+
@Override
70+
public String toString() {
71+
return "BeanContainer: %s of %s singleton: %s".formatted( definition.getName(),definition.getKind(),singleton.get());
72+
}
73+
74+
private synchronized Object define(BeanRegistryImplementation registry, Grammar grammar) {
75+
log.debug("defining bean: {}", definition.getNode());
76+
try {
77+
if ("bean".equals(definition.getKind())) {
78+
return new BeanFactory(registry).create(definition.getNode().path("bean"));
79+
}
80+
return GenericYamlParser.readMembraneObject(definition.getKind(),
81+
grammar,
82+
definition.getNode(),
83+
registry);
84+
} catch (Exception e) {
85+
throw new RuntimeException(e);
86+
}
87+
}
88+
89+
public Object getOrCreate(BeanRegistryImplementation registry, Grammar grammar) {
90+
boolean prototype = isPrototypeScope(getDefinition());
91+
92+
// Prototypes are created anew every time.
93+
if (prototype) {
94+
return define(registry, grammar);
95+
}
96+
97+
// Singleton: ensure define() runs at most once per BeanContainer.
98+
synchronized (this) {
99+
Object existing = getSingleton();
100+
if (existing != null) {
101+
return existing;
102+
}
103+
104+
Object created = define(registry, grammar);
105+
setSingleton(created);
106+
return created;
107+
}
108+
}
109+
110+
private static boolean isPrototypeScope(BeanDefinition bd) {
111+
if (!bd.isBean())
112+
return bd.isPrototype();
113+
114+
return "PROTOTYPE".equalsIgnoreCase(
115+
bd.getNode().path("bean").path("scope").asText("SINGLETON")
116+
);
117+
}
118+
42119
}

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
limitations under the License. */
1414
package com.predic8.membrane.annot.beanregistry;
1515

16-
import com.fasterxml.jackson.databind.JsonNode;
17-
import com.predic8.membrane.annot.yaml.WatchAction;
18-
19-
import static com.predic8.membrane.annot.yaml.WatchAction.*;
16+
import com.fasterxml.jackson.databind.*;
17+
import com.predic8.membrane.annot.yaml.*;
2018

19+
/**
20+
* Immutable.
21+
*/
2122
public class BeanDefinition {
2223

2324
public static final String PROTOTYPE = "prototype";
@@ -78,6 +79,8 @@ public String getKind() {
7879
}
7980

8081
public String getScope() {
82+
if (node == null)
83+
return null;
8184
JsonNode meta = node.get("metadata");
8285
if (meta == null)
8386
return null;
@@ -100,4 +103,14 @@ public boolean isPrototype() {
100103
return PROTOTYPE.equals(getScope());
101104
}
102105

106+
@Override
107+
public String toString() {
108+
return "BeanDefinition{" +
109+
"name='" + name + '\'' +
110+
", namespace='" + namespace + '\'' +
111+
", uid='" + uid + '\'' +
112+
", node=" + node +
113+
", kind='" + kind + '\'' +
114+
'}';
115+
}
103116
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.predic8.membrane.annot.*;
1717

1818
import java.util.*;
19+
import java.util.function.*;
1920

2021
public interface BeanRegistry {
2122

@@ -26,4 +27,33 @@ public interface BeanRegistry {
2627
Grammar getGrammar();
2728

2829
<T> List<T> getBeans(Class<T> clazz);
30+
31+
/**
32+
* Retrieves a single bean of the specified type.
33+
*
34+
* @param clazz the class of the bean to retrieve
35+
* @param <T> the bean type
36+
* @return Optional containing the bean if exactly one exists, empty otherwise
37+
* @throws RuntimeException if multiple beans of the specified type exist
38+
*/
39+
<T> Optional<T> getBean(Class<T> clazz);
40+
41+
/**
42+
* Registers a bean with the specified name.
43+
*
44+
* @param beanName the name to register the bean under
45+
* @param bean instance to register (must not be null)
46+
*/
47+
void register(String beanName, Object bean);
48+
49+
/**
50+
* Registers a bean of the specified type with the given name if it is not already registered.
51+
* If a bean with the given name is already present, the existing instance is returned.
52+
* Otherwise, the supplier is used to create and register a new instance.
53+
* @param type the class type of the bean
54+
* @param supplier a supplier that provides a new instance of the bean if not already registered
55+
* @param <T> the generic type of the bean
56+
* @return the existing or newly created and registered bean instance
57+
*/
58+
<T> T registerIfAbsent(Class<T> type, Supplier<T> supplier);
2959
}

core/src/test/java/com/predic8/membrane/AllTests.java renamed to annot/src/main/java/com/predic8/membrane/annot/beanregistry/BeanRegistryAware.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2009, 2011 predic8 GmbH, www.predic8.com
1+
/* Copyright 2025 predic8 GmbH, www.predic8.com
22
33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.
@@ -11,17 +11,10 @@
1111
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
See the License for the specific language governing permissions and
1313
limitations under the License. */
14-
package com.predic8.membrane;
1514

16-
import com.predic8.membrane.core.*;
17-
import com.predic8.membrane.integration.*;
18-
import org.junit.platform.suite.api.SelectClasses;
19-
import org.junit.platform.suite.api.Suite;
15+
package com.predic8.membrane.annot.beanregistry;
16+
17+
public interface BeanRegistryAware {
18+
void setRegistry(BeanRegistry registry);
19+
}
2020

21-
@Suite
22-
@SelectClasses({
23-
UnitTests.class,
24-
IntegrationTestsWithoutInternet.class,
25-
IntegrationTestsWithInternet.class
26-
})
27-
public class AllTests {}

0 commit comments

Comments
 (0)