Skip to content

Commit 9c2fa18

Browse files
Add Yaml Router configuration (#2508)
* Add `Configuration` class * Refactor `Router` to use `Configuration` for managing settings and centralize configuration handling * Add copyright headers to source files and improve `Configuration` handling * Refactor imports in `RouterCLI` to improve readability and structure * Refactor imports in `RouterCLI` to improve readability and structure * fix retryInit default * Refactor `Router` to centralize `Configuration` usage and improve bean registry handling * fix tests * Refactor `YamlParser` to inline `TestRouter` and remove unused `TestRouter` class
1 parent 6e0138f commit 9c2fa18

59 files changed

Lines changed: 393 additions & 215 deletions

File tree

Some content is hidden

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

.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

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
/**
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
public interface BeanRegistryAware {
18+
void setRegistry(BeanRegistry registry);
19+
}
20+

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ public class BeanRegistryImplementation implements BeanRegistry, BeanCollector {
4141

4242
record UidAction(String uid, WatchAction action) {}
4343

44-
public BeanRegistryImplementation(BeanCacheObserver observer, Grammar grammar) {
44+
public BeanRegistryImplementation(BeanCacheObserver observer, BeanRegistryAware registryAware, Grammar grammar) {
4545
this.observer = observer;
4646
this.grammar = grammar;
47+
registryAware.setRegistry(this);
4748
}
4849

4950
private Object define(BeanDefinition bd) {

annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ public class YamlParser {
3838
*/
3939
private final BeanRegistry beanRegistry;
4040

41+
private static class TestRouter implements BeanRegistryAware {
42+
43+
@Override
44+
public void setRegistry(BeanRegistry registry) {
45+
46+
}
47+
}
48+
4149
public YamlParser(String resourceName) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, InterruptedException {
4250
Grammar generator = getGrammar();
4351

@@ -48,7 +56,7 @@ public YamlParser(String resourceName) throws ClassNotFoundException, NoSuchMeth
4856
String normalized = resourceName.startsWith("/") ?
4957
resourceName.substring(1) : resourceName;
5058

51-
BeanRegistryImplementation impl = new BeanRegistryImplementation(getLatchObserver(cdl), generator);
59+
BeanRegistryImplementation impl = new BeanRegistryImplementation(getLatchObserver(cdl), new TestRouter(), generator);
5260
impl.parseYamls(requireNonNull(cl.getResourceAsStream(normalized)), generator);
5361
beanRegistry = impl;
5462

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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.core;
16+
17+
import com.predic8.membrane.annot.MCAttribute;
18+
import com.predic8.membrane.annot.MCChildElement;
19+
import com.predic8.membrane.annot.MCElement;
20+
import com.predic8.membrane.core.interceptor.administration.AdminConsoleInterceptor;
21+
import com.predic8.membrane.core.proxies.Proxy;
22+
import com.predic8.membrane.core.util.URIFactory;
23+
24+
@MCElement(name = "configuration", topLevel = true, component = false)
25+
public class Configuration {
26+
27+
/**
28+
* Set production to true to run Membrane in production mode.
29+
* In production mode the security level is increased e.g. there is less information
30+
* in error messages sent to clients.
31+
*/
32+
private boolean production;
33+
34+
private boolean hotDeploy = true;
35+
36+
private int retryInitInterval = 5 * 60 * 1000; // 5 minutes
37+
38+
private boolean retryInit = false;
39+
40+
private String jmxRouterName;
41+
42+
private URIFactory uriFactory = new URIFactory(false);
43+
44+
/**
45+
* @param hotDeploy If true the hot deploy feature will be activated during init of the Router.
46+
* @description <p>Whether changes to the router's configuration file should automatically trigger a restart.
47+
* </p>
48+
* <p>
49+
* Monitoring the router's configuration file <i>proxies.xml</i> is only possible, if the router
50+
* is created by a Spring Application Context which supports monitoring.
51+
* </p>
52+
* <p>Calling this method does not start or stop the hot deploy feature. It is just for configuration before init is called.</p>
53+
* @default true
54+
*/
55+
@MCAttribute
56+
public void setHotDeploy(boolean hotDeploy) {
57+
this.hotDeploy = hotDeploy;
58+
}
59+
60+
public boolean isHotDeploy() {
61+
return hotDeploy;
62+
}
63+
64+
/**
65+
* @description number of milliseconds after which reinitialization of &lt;soapProxy&gt;s should be attempted periodically
66+
* @default 5 minutes
67+
*/
68+
@MCAttribute
69+
public void setRetryInitInterval(int retryInitInterval) {
70+
this.retryInitInterval = retryInitInterval;
71+
}
72+
73+
public int getRetryInitInterval() {
74+
return retryInitInterval;
75+
}
76+
77+
/**
78+
* @description <p>Whether the router should continue startup, if initialization of a rule (proxy, serviceProxy or soapProxy) failed
79+
* (for example, when a WSDL a component depends on could not be downloaded).</p>
80+
* <p>If false, the router will exit with code -1 just after startup, when the initialization of a rule failed.</p>
81+
* <p>If true, the router will continue startup, and all rules which could not be initialized will be <i>inactive</i> (=not
82+
* {@link Proxy#isActive()}).</p>
83+
* <h3>Inactive rules</h3>
84+
* <p>Inactive rules will simply be ignored for routing decisions for incoming requests.
85+
* This means that requests for inactive rules might be routed using different routes or result in a "400 Bad Request"
86+
* when no active route could be matched to the request.</p>
87+
* <p>Once rules become active due to reinitialization, they are considered in future routing decision.</p>
88+
* <h3>Reinitialization</h3>
89+
* <p>Inactive rules may be <i>reinitialized</i> and, if reinitialization succeeds, become active.</p>
90+
* <p>By default, reinitialization is attempted at regular intervals using a timer (see {@link #setRetryInitInterval(int)}).</p>
91+
* <p>Additionally, using the {@link AdminConsoleInterceptor}, an admin may trigger reinitialization of inactive rules at any time.</p>
92+
* @default false
93+
*/
94+
@MCAttribute
95+
public void setRetryInit(boolean retryInit) {
96+
this.retryInit = retryInit;
97+
}
98+
99+
public boolean isRetryInit() {
100+
return retryInit;
101+
}
102+
103+
/**
104+
* @description Sets the JMX name for this router. Also declare a global &lt;jmxExporter&gt; instance.
105+
*/
106+
@MCAttribute
107+
public void setJmxRouterName(String jmxRouterName) {
108+
this.jmxRouterName = jmxRouterName;
109+
}
110+
111+
public String getJmx() {
112+
return jmxRouterName;
113+
}
114+
115+
/**
116+
* @description <p>By default the error messages Membrane sends back to an HTTP client provide information to help the caller
117+
* find the problem. The caller might even get sensitive information. In production the error messages should not reveal
118+
* to much details. With this option you can put Membrane in production mode and reduce the amount of information in
119+
* error messages.</p>
120+
* @default false
121+
*/
122+
@MCAttribute
123+
public void setProduction(boolean production) {
124+
this.production = production;
125+
}
126+
127+
public boolean isProduction() {
128+
return production;
129+
}
130+
131+
/**
132+
* @description Sets the URI factory used by the router. Use this only, if you need to allow
133+
* special (off-spec) characters in URLs which are not supported by java.net.URI .
134+
*/
135+
@MCChildElement(order = -1, allowForeign = true)
136+
public void setUriFactory(URIFactory uriFactory) {
137+
this.uriFactory = uriFactory;
138+
}
139+
140+
public URIFactory getUriFactory() {
141+
return uriFactory;
142+
}
143+
}

0 commit comments

Comments
 (0)