diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCChildElement.java b/annot/src/main/java/com/predic8/membrane/annot/MCChildElement.java
index 8f95eb513a..7bcdc44e14 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/MCChildElement.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/MCChildElement.java
@@ -31,4 +31,6 @@
* Allows the child to come from a schema other than Membrane core. Used for spring beans, e.g. ref to ssl bean
*/
boolean allowForeign() default false;
+
+ boolean excludeFromJson() default false; // excludes from JSON Schema (YAML)
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCElement.java b/annot/src/main/java/com/predic8/membrane/annot/MCElement.java
index ed8b6ff4f2..cb3cff1218 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/MCElement.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/MCElement.java
@@ -27,10 +27,15 @@
boolean mixed() default false;
+ /**
+ * Whether the element can be defined at the top-level of the config.
+ */
+ boolean topLevel() default false;
+
/**
* Whether the element can be a separate bean in the XML schema, or a separate document in YAML/JSON.
*/
- boolean topLevel() default true;
+ boolean component() default true;
String configPackage() default "";
@@ -54,4 +59,9 @@
* This does not have any effect on the XML grammar.
*/
boolean noEnvelope() default false;
+
+ /**
+ * Whether the element should be configurable as part of the interceptor flow
+ */
+ boolean excludeFromFlow() default false;
}
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 4aa3e3964d..3ff9059fc4 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/SpringConfigurationXSDGeneratingAnnotationProcessor.java
@@ -215,12 +215,10 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
scan(main, ii);
- if (ii.getAnnotation().topLevel())
- main.getTopLevels().put(ii.getAnnotation().name(), ii);
+ if (ii.getAnnotation().component())
+ main.getComponents().put(ii.getAnnotation().name(), ii);
if (ii.getAnnotation().noEnvelope()) {
- if (ii.getAnnotation().topLevel())
- throw new ProcessingException("@MCElement(..., noEnvelope=true, topLevel=true) is invalid.", ii.getElement());
if (ii.getAnnotation().mixed())
throw new ProcessingException("@MCElement(..., noEnvelope=true, mixed=true) is invalid.", ii.getElement());
if (ii.getChildElementSpecs().size() != 1)
@@ -254,8 +252,8 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
for (Entry e : main.getElements().entrySet()) {
if (!processingEnv.getTypeUtils().isAssignable(e.getKey().asType(), f.getKey().asType()))
continue;
- if (targetIsObject && !isTopLevelMCElement(e.getKey()))
- continue; // only allow topLevel MCElements for Object
+ if (targetIsObject && !isComponent(e.getKey()))
+ continue; // only allow component MCElements for Object
cedi.getElementInfo().add(e.getValue());
}
}
@@ -272,9 +270,9 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
for (MainInfo main : m.getMains()) {
for (Entry f : main.getElements().entrySet()) {
- ElementInfo ei2 = main.getTopLevels().get(f.getKey().getAnnotation(MCElement.class).name());
- if (ei2 != null && f.getValue() != ei2 && f.getValue().getAnnotation().topLevel())
- throw new ProcessingException("Duplicate top-level @MCElement name. Make at least one @MCElement(topLevel=false,...) .", f.getKey(), ei2.getElement());
+ ElementInfo ei2 = main.getComponents().get(f.getKey().getAnnotation(MCElement.class).name());
+ if (ei2 != null && f.getValue() != ei2 && f.getValue().getAnnotation().component())
+ throw new ProcessingException("Duplicate component @MCElement name. Make at least one @MCElement(component=false,...) .", f.getKey(), ei2.getElement());
List uniquenessErrors = getUniquenessError(f.getValue(), main);
if (!uniquenessErrors.isEmpty())
@@ -299,9 +297,9 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
}
}
- private boolean isTopLevelMCElement(TypeElement type) {
+ private boolean isComponent(TypeElement type) {
MCElement mcElement = type.getAnnotation(MCElement.class);
- return (mcElement != null) && mcElement.topLevel();
+ return (mcElement != null) && mcElement.component();
}
private List getUniquenessError(ElementInfo ii, MainInfo main) {
@@ -512,6 +510,10 @@ private boolean isRequired(Element e2) {
}
public void process(Model m) throws IOException {
+ if (new ComponentClassGenerator(processingEnv).writeJava(m))
+ return; // we will be called again to handle the newly generated class.
+ if (new BeanClassGenerator(processingEnv).writeJava(m))
+ return; // we will be called again to handle the newly generated class.
new Schemas(processingEnv).writeXSD(m);
new KubernetesBootstrapper(processingEnv).boot(m);
new JsonSchemaGenerator(processingEnv).write(m);
diff --git a/annot/src/main/java/com/predic8/membrane/annot/bean/BeanFactory.java b/annot/src/main/java/com/predic8/membrane/annot/bean/BeanFactory.java
new file mode 100644
index 0000000000..0a4d50e928
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/bean/BeanFactory.java
@@ -0,0 +1,252 @@
+/* Copyright 2025 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.bean;
+
+import com.fasterxml.jackson.databind.*;
+import com.predic8.membrane.annot.util.*;
+import com.predic8.membrane.annot.yaml.*;
+import org.jetbrains.annotations.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.stream.*;
+
+import static com.predic8.membrane.annot.util.ReflectionUtil.isWrapperOfPrimitive;
+
+/**
+ * Builds Java objects from a "bean" JSON node (YAML).
+ */
+public final class BeanFactory {
+
+ private final BeanRegistry registry;
+
+ public BeanFactory(BeanRegistry registry) {
+ this.registry = registry;
+ }
+
+ /**
+ * Creates an instance described by the given bean node.
+ */
+ public Object create(JsonNode beanBody) {
+ String className = getTextContent(beanBody, "class");
+
+ try {
+ Object instance = instantiate(
+ loadBeanClass(className),
+ parseConstructorArgList(beanBody.path("constructorArgs"))
+ );
+ applyProperties(instance, parsePropertyList(beanBody.path("properties")));
+ return instance;
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create bean for class: " + className, e);
+ }
+ }
+
+ // TODO simplify this. 'normal' code should not be required to use classloader magic
+ private Class> loadBeanClass(String className) throws ClassNotFoundException {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ try {
+ return Class.forName(className, true, classLoader);
+ } catch (ClassNotFoundException ignored) {
+ }
+ }
+
+ classLoader = registry.getGrammar().getClass().getClassLoader();
+ if (classLoader != null) {
+ try {
+ return Class.forName(className, true, classLoader);
+ } catch (ClassNotFoundException ignored) {
+ }
+ }
+
+ classLoader = BeanFactory.class.getClassLoader();
+ if (classLoader != null) {
+ try {
+ return Class.forName(className, true, classLoader);
+ } catch (ClassNotFoundException ignored) {
+ }
+ }
+
+ return Class.forName(className);
+ }
+
+ private class ConstructorArg {
+ String value, ref;
+
+ public ConstructorArg(JsonNode node) {
+ var item = node.isObject() && node.has("constructorArg") ? node.get("constructorArg") : node;
+
+ value = getTextOrNull(item, "value");
+ ref = getTextOrNull(item, "ref");
+ }
+ }
+
+ private class Property {
+ String name, value, ref;
+
+ public Property(JsonNode node) {
+ var item = node.isObject() && node.has("property") ? node.get("property") : node;
+
+ name = getTextContent(item, "name");
+ value = getTextOrNull(item, "value");
+ ref = getTextOrNull(item, "ref");
+ }
+
+ public boolean isBlank() {
+ return name == null || name.isBlank();
+ }
+ }
+
+ private List parseConstructorArgList(JsonNode arr) {
+ if (arr == null || !arr.isArray()) return List.of();
+
+ return StreamSupport.stream(arr.spliterator(), false)
+ .map(ConstructorArg::new)
+ .toList();
+ }
+
+ private List parsePropertyList(JsonNode arr) {
+ if (arr == null || !arr.isArray()) return List.of();
+
+ return StreamSupport.stream(arr.spliterator(), false)
+ .map(Property::new)
+ .toList();
+ }
+
+ private String getTextContent(JsonNode n, String key) {
+ JsonNode v = n.get(key);
+ if (v == null || !v.isTextual() || v.asText().isBlank())
+ throw new IllegalArgumentException("Missing/blank '" + key + "' in bean spec.");
+ return v.asText();
+ }
+
+ private String getTextOrNull(JsonNode n, String key) {
+ JsonNode v = n.get(key);
+ return v != null && v.isTextual() ? v.asText() : null;
+ }
+
+ private Object instantiate(Class> type, List args) throws Exception {
+ int n = args == null ? 0 : args.size();
+
+ Set> constructors = new LinkedHashSet<>();
+ constructors.addAll(Arrays.asList(type.getConstructors()));
+ constructors.addAll(Arrays.asList(type.getDeclaredConstructors()));
+
+ Constructor> best = null;
+ Object[] bestArgs = null;
+
+ for (Constructor> c : constructors) {
+ if (c.getParameterCount() != n) continue;
+ Object[] resolved = tryResolveCtorArgs(c.getParameterTypes(), args);
+ if (resolved != null) {
+ best = c;
+ bestArgs = resolved;
+ break;
+ }
+ }
+
+ if (best == null) {
+ throw new IllegalArgumentException("No matching constructor found for %s with %d argument(s).".formatted(type.getName(), n));
+ }
+
+ best.setAccessible(true);
+ return best.newInstance(bestArgs);
+ }
+
+ private Object[] tryResolveCtorArgs(Class>[] paramTypes, List args) {
+ try {
+ Object[] resolved = new Object[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ resolved[i] = resolveValueOrRef(paramTypes[i], args.get(i).value, args.get(i).ref);
+ }
+ return resolved;
+ } catch (Exception e) {
+ return null; // not compatible
+ }
+ }
+
+ private void applyProperties(Object target, List props) throws Exception {
+ for (Property p : props) {
+ if (p.isBlank())
+ throw new IllegalArgumentException("Property name must not be blank.");
+
+ Method setter = findSetter(target.getClass(), p.name);
+ if (setter != null) {
+ Class> pt = setter.getParameterTypes()[0];
+ setter.setAccessible(true);
+ setter.invoke(target, resolveValueOrRef(pt, p.value, p.ref));
+ continue;
+ }
+
+ Field f = findField(target.getClass(), p.name);
+ if (f != null) {
+ f.setAccessible(true);
+ f.set(target, resolveValueOrRef(f.getType(), p.value, p.ref));
+ continue;
+ }
+
+ throw new IllegalArgumentException("No setter/field found for property '%s' on %s".formatted(p.name, target.getClass().getName()));
+ }
+ }
+
+ private Method findSetter(Class> clazz, String prop) {
+ String setterName = getSetterName(prop);
+ for (Method method : clazz.getMethods()) {
+ if (matchesSetter(method, setterName)) return method;
+ }
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (matchesSetter(method, setterName)) return method;
+ }
+ return null;
+ }
+
+ private static boolean matchesSetter(Method method, String setterName) {
+ return method.getName().equals(setterName) && method.getParameterCount() == 1;
+ }
+
+ // e.g. bar -> setBar
+ private static @NotNull String getSetterName(String prop) {
+ if (prop == null || prop.isEmpty()) {
+ throw new IllegalArgumentException("Property name cannot be null or empty");
+ }
+ return "set" + Character.toUpperCase(prop.charAt(0)) + prop.substring(1);
+ }
+
+ private Field findField(Class> clazz, String name) {
+ Class> c = clazz;
+ while (c != null && c != Object.class) {
+ try {
+ return c.getDeclaredField(name);
+ } catch (NoSuchFieldException ignored) {
+ c = c.getSuperclass();
+ }
+ }
+ return null;
+ }
+
+ private Object resolveValueOrRef(Class> targetType, String value, String ref) {
+ if (ref != null && !ref.isBlank()) {
+ Object o = registry.resolveReference(ref);
+ if (o != null && !targetType.isInstance(o)) {
+ if (!(targetType.isPrimitive() && isWrapperOfPrimitive(targetType, o.getClass()))) {
+ throw new IllegalArgumentException("Ref '%s' is not assignable to %s".formatted(ref, targetType.getName()));
+ }
+ }
+ return o;
+ }
+ return ReflectionUtil.convert(value, targetType);
+ }
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/BeanClassGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/BeanClassGenerator.java
new file mode 100644
index 0000000000..75d9ddaa60
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/BeanClassGenerator.java
@@ -0,0 +1,165 @@
+/* Copyright 2025 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 javax.annotation.processing.ProcessingEnvironment;
+
+public class BeanClassGenerator extends ClassGenerator{
+
+ public BeanClassGenerator(ProcessingEnvironment processingEnv) {
+ super(processingEnv);
+ }
+
+ @Override
+ protected String getClassName() {
+ return "Bean";
+ }
+
+ @Override
+ protected String getClassImpl() {
+ return """
+ import com.predic8.membrane.annot.MCAttribute;
+ import com.predic8.membrane.annot.MCChildElement;
+ import com.predic8.membrane.annot.MCElement;
+ import com.predic8.membrane.annot.generator.Scope;
+
+ import java.util.ArrayList;
+ import java.util.List;
+
+ /**
+ * Spring-like bean definition usable in YAML components:
+ * components:
+ * :
+ * bean:
+ * class: com.example.MyInterceptor
+ * scope: SINGLETON
+ * constructorArgs:
+ * - constructorArg:
+ * value: foo
+ * properties:
+ * - property:
+ * name: bar
+ * value: baz
+ */
+ @MCElement(name = "bean")
+ public class Bean {
+
+ private String className;
+ private Scope scope = Scope.SINGLETON;
+ private List constructorArgs = new ArrayList<>();
+ private List properties = new ArrayList<>();
+
+ @MCAttribute
+ public void setClass(String className) {
+ this.className = className;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ @MCAttribute
+ public void setScope(Scope scope) {
+ this.scope = scope;
+ }
+
+ public Scope getScope() {
+ return scope;
+ }
+
+ @MCChildElement(order = 3)
+ public void setConstructorArgs(List constructorArgs) {
+ this.constructorArgs = constructorArgs;
+ }
+
+ public List getConstructorArgs() {
+ return constructorArgs;
+ }
+
+ @MCChildElement
+ public void setProperties(List properties) {
+ this.properties = properties;
+ }
+
+ public List getProperties() {
+ return properties;
+ }
+
+ public enum Scope {
+ SINGLETON,
+ PROTOTYPE
+ }
+
+ @MCElement(name = "constructorArg", component = false)
+ public static class ConstructorArg {
+ private String value;
+ private String ref;
+
+ @MCAttribute
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @MCAttribute
+ public void setRef(String ref) {
+ this.ref = ref;
+ }
+
+ public String getRef() {
+ return ref;
+ }
+ }
+
+ @MCElement(name = "property", component = false)
+ public static class Property {
+ private String name;
+ private String value;
+ private String ref;
+
+ @MCAttribute
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @MCAttribute
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @MCAttribute
+ public void setRef(String ref) {
+ this.ref = ref;
+ }
+
+ public String getRef() {
+ return ref;
+ }
+ }
+ }
+ """;
+ }
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/BlueprintParsers.java b/annot/src/main/java/com/predic8/membrane/annot/generator/BlueprintParsers.java
index b8003a3ce5..dbf8dcb855 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/BlueprintParsers.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/BlueprintParsers.java
@@ -78,7 +78,7 @@ public void writeParserDefinitior(Model m) throws IOException {
"\r\n" +
" public void init() {\r\n");
for (ElementInfo i : main.getIis()) {
- if (i.getAnnotation().topLevel()) {
+ if (i.getAnnotation().component()) {
bw.write(" registerGlobalBeanDefinitionParser(\"" + i.getAnnotation().name() + "\", new " + i.getParserClassSimpleName() + "());\r\n");
} else {
for (ChildElementDeclarationInfo cedi : i.getUsedBy()) {
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/ClassGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/ClassGenerator.java
new file mode 100644
index 0000000000..6c5e91fa90
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/ClassGenerator.java
@@ -0,0 +1,72 @@
+package com.predic8.membrane.annot.generator;
+
+import com.predic8.membrane.annot.model.MainInfo;
+import com.predic8.membrane.annot.model.Model;
+import org.jetbrains.annotations.NotNull;
+
+import javax.annotation.processing.FilerException;
+import javax.annotation.processing.ProcessingEnvironment;
+import java.io.IOException;
+import java.io.Writer;
+
+public abstract class ClassGenerator {
+
+ public static final String COPYRIGHT = """
+ /* Copyright 2025 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. */
+
+ """;
+
+ private final ProcessingEnvironment processingEnv;
+
+ public ClassGenerator(ProcessingEnvironment processingEnv) {
+ this.processingEnv = processingEnv;
+ }
+
+ /**
+ * @return true if the file was written, false if it already existed
+ */
+ public boolean writeJava(Model m) throws IOException {
+ for (MainInfo main : m.getMains()) {
+ try {
+ try (Writer w = processingEnv.getFiler().createSourceFile(getFileName(main)).openWriter()) {
+ w.write(COPYRIGHT);
+ w.write(getPackage(main));
+ w.write(getClassImpl());
+ }
+ return true;
+ } catch (FilerException e) {
+ String msg = e.getMessage();
+ if (msg != null && (msg.contains("Source file already created")
+ || msg.contains("Attempt to recreate a file for"))) {
+ return false;
+ }
+ throw e;
+ }
+ }
+ return false;
+ }
+
+ private @NotNull String getFileName(MainInfo main) {
+ return main.getAnnotation().outputPackage() + "." + getClassName();
+ }
+
+ private static @NotNull String getPackage(MainInfo main) {
+ return "package " + main.getAnnotation().outputPackage() + ";\r\n";
+ }
+
+ protected abstract String getClassName();
+
+ protected abstract String getClassImpl();
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/ComponentClassGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/ComponentClassGenerator.java
new file mode 100644
index 0000000000..c78fd3a9ba
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/ComponentClassGenerator.java
@@ -0,0 +1,58 @@
+/* Copyright 2025 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 javax.annotation.processing.ProcessingEnvironment;
+
+public class ComponentClassGenerator extends ClassGenerator{
+
+
+ public ComponentClassGenerator(ProcessingEnvironment processingEnv) {
+ super(processingEnv);
+ }
+
+ @Override
+ protected String getClassName() {
+ return "Components";
+ }
+
+ @Override
+ protected String getClassImpl() {
+ return """
+ import com.predic8.membrane.annot.MCElement;
+ import com.predic8.membrane.annot.MCOtherAttributes;
+
+ import java.util.Map;
+
+ @MCElement(name = "components", topLevel = true)
+ public class Components {
+
+ Map components;
+
+ public Map getComponents() {
+ return components;
+ }
+
+ @MCOtherAttributes
+ public void setComponents(Map components) {
+ if (this.components == null)
+ this.components = new java.util.LinkedHashMap<>();
+ if (components != null)
+ this.components.putAll(components);
+ }
+ }
+ """;
+ }
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java b/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java
index 6f12ba6aca..deb23e96b2 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java
@@ -121,10 +121,10 @@ private void handle(Model m, MainInfo main, ElementInfo ei) throws XMLStreamExce
xew.writeAttribute("name", ei.getAnnotation().name());
if (ei.getAnnotation().mixed())
xew.writeAttribute("mixed", "true");
- xew.writeAttribute("topLevel", Boolean.toString(ei.getAnnotation().topLevel()));
+ xew.writeAttribute("component", Boolean.toString(ei.getAnnotation().component()));
xew.writeAttribute("id", ei.getId());
xew.writeAttribute("deprecated", Boolean.toString(ei.isDeprecated()));
- if (!ei.getAnnotation().topLevel()) {
+ if (!ei.getAnnotation().component()) {
String primaryParentId = getPrimaryParentId(m, main, ei);
if (primaryParentId != null)
xew.writeAttribute("primaryParentId", primaryParentId);
@@ -174,7 +174,7 @@ private String getPrimaryParentId(Model m, MainInfo mi, ElementInfo ei) {
}
}
for (ElementInfo ei2 : possibleParents)
- if (ei2.getAnnotation().topLevel())
+ if (ei2.getAnnotation().component())
return ei2.getId();
possibleParents.remove(ei);
if (possibleParents.size() > 0)
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java
index 34b9b3a090..9fad57fcc0 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java
@@ -19,12 +19,14 @@
import com.predic8.membrane.annot.generator.kubernetes.model.*;
import com.predic8.membrane.annot.model.*;
import com.predic8.membrane.annot.model.doc.*;
+import org.jetbrains.annotations.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
import java.io.*;
import java.util.*;
+import java.util.stream.*;
import static com.predic8.membrane.annot.generator.kubernetes.model.SchemaFactory.*;
import static com.predic8.membrane.annot.generator.util.SchemaGeneratorUtil.*;
@@ -40,32 +42,19 @@
public class JsonSchemaGenerator extends AbstractGrammar {
public static final String MEMBRANE_SCHEMA_JSON_FILENAME = "membrane.schema.json";
+ public static final String COMPONENTS = "components";
- private final Map topLevelAdded = new HashMap<>();
+ // TODO keep this pattern or allow *?
+ public static final String COMPONENT_ID_PATTERN = "^[A-Za-z_][A-Za-z0-9_-]*$";
- public JsonSchemaGenerator(ProcessingEnvironment processingEnv) {
- super(processingEnv);
- }
+ private final Map componentAdded = new HashMap<>();
private boolean flowDefCreated = false;
private Schema schema;
- private static final Set excludeFromFlow = Set.of(
- "httpClient",
- "ruleMatching",
- "wadlRewriter",
- "global",
- "exchangeStore",
- "accountRegistration",
- "userFeature",
- "tcp",
- "wsaEndpointRewriter",
- "flowInitiator",
- "kubernetesValidation",
- "dispatching",
- "groovyTemplate",
- "adminApi"
- );
+ public JsonSchemaGenerator(ProcessingEnvironment processingEnv) {
+ super(processingEnv);
+ }
public void write(Model m) throws IOException {
for (MainInfo main : m.getMains()) {
@@ -77,7 +66,7 @@ private void assemble(Model m, MainInfo main) throws IOException {
// Reset so multiple calls would be possible
flowDefCreated = false;
schema = schema("membrane");
- topLevelAdded.clear();
+ componentAdded.clear();
addParserDefinitions(m, main);
addTopLevelProperties(m, main);
@@ -87,26 +76,27 @@ private void assemble(Model m, MainInfo main) throws IOException {
private void addTopLevelProperties(Model m, MainInfo main) {
schema.additionalProperties(false);
- List> kinds = new ArrayList<>();
+ List> kinds = main.getElements().values().stream()
+ .filter(e -> e.getAnnotation().topLevel())
+ .map(e -> createTopLevelProperty(e, m))
+ .collect(Collectors.toUnmodifiableList());
- main.getElements().values().forEach(e -> {
- if (!e.getAnnotation().topLevel())
- return;
+ if (!kinds.isEmpty())
+ schema.oneOf(kinds);
+ }
- String name = e.getAnnotation().name();
- String refName = "#/$defs/" + e.getXSDTypeName(m);
+ private AbstractSchema> createTopLevelProperty(ElementInfo e, Model m) {
- schema.property(ref(name).ref(refName));
+ String name = e.getAnnotation().name();
+ String refName = "#/$defs/" + e.getXSDTypeName(m);
- kinds.add(object()
- .additionalProperties(false)
- .property(ref(name)
- .ref(refName)
- .required(true)));
- });
+ schema.property(ref(name).ref(refName));
- if (!kinds.isEmpty())
- schema.oneOf(kinds);
+ return object()
+ .additionalProperties(false)
+ .property(ref(name)
+ .ref(refName)
+ .required(true));
}
private void addParserDefinitions(Model m, MainInfo main) {
@@ -126,30 +116,50 @@ private void addParserDefinitions(Model m, MainInfo main) {
private SchemaObject createParser(Model m, MainInfo main, ElementInfo elementInfo) {
String parserName = elementInfo.getXSDTypeName(m);
+ if (isComponentsMap(elementInfo)) {
+ return createComponentsMapParser(m, main, elementInfo, parserName);
+ }
+
// e.g. to prevent a request from needing a flow child noEnvelope=true is used
if (elementInfo.getAnnotation().noEnvelope()) {
// With noEnvelope=true, there should be exactly one child element
-
ChildElementInfo child = elementInfo.getChildElementSpecs().getFirst();
var childName = child.getPropertyName();
- if (!topLevelAdded.containsKey(childName) && !shouldGenerateParserType(child)) {
+ if (!componentAdded.containsKey(childName) && !shouldGenerateFlowParserType(child)) {
SchemaArray array = array(childName + "Parser");
processMCChilds(m, main, child.getEi(), array);
schema.definition(array);
- topLevelAdded.put(childName, true);
+ componentAdded.put(childName, true);
}
return ref(parserName).ref("#/$defs/%sParser".formatted(childName));
}
- SchemaObject parser = object(parserName)
- .additionalProperties(elementInfo.getOai() != null)
- .description(getDescriptionContent(elementInfo));
+ SchemaObject parser = getParserSchemaObject(elementInfo, parserName);
+
collectProperties(m, main, elementInfo, parser);
+
+ // Allow object-level component reference if any setter expects a component.
+ if (hasComponentChild(elementInfo, main) && !parser.hasProperty("$ref")) {
+ parser.property(string("$ref")
+ .description("JSON Pointer to a component.")
+ .required(false));
+ }
+
return parser;
}
+ private SchemaObject getParserSchemaObject(ElementInfo elementInfo, String parserName) {
+ return object(parserName)
+ .additionalProperties( elementInfo.isString())
+ .description(getDescriptionContent(elementInfo));
+ }
+
+ private boolean isComponentsMap(ElementInfo ei) {
+ return COMPONENTS.equals(ei.getAnnotation().name()) && ei.isObject();
+ }
+
private String getDescriptionContent(AbstractJavadocedInfo elementInfo) {
Doc doc = elementInfo.getDoc(processingEnv);
if (doc == null) {
@@ -167,12 +177,16 @@ private FileObject createFile(MainInfo main) throws IOException {
return processingEnv.getFiler()
.createResource(
CLASS_OUTPUT,
- main.getAnnotation().outputPackage().replaceAll("\\.spring$", ".json"),
+ getOutputPackage(main),
MEMBRANE_SCHEMA_JSON_FILENAME,
sources.toArray(new Element[0])
);
}
+ private static @NotNull String getOutputPackage(MainInfo main) {
+ return main.getAnnotation().outputPackage().replaceAll("\\.spring$", ".json");
+ }
+
private void processMCAttributes(ElementInfo i, SchemaObject so) {
i.getAis().forEach(ai -> {
@@ -180,10 +194,6 @@ private void processMCAttributes(ElementInfo i, SchemaObject so) {
if (ai.excludedFromJsonSchema())
return;
- // hide id only on top-level elements
- if ("id".equals(ai.getXMLName()) && i.getAnnotation().topLevel()) {
- return;
- }
so.property(createProperty(ai));
});
}
@@ -221,34 +231,47 @@ private void collectTextContent(ElementInfo i, SchemaObject so) {
private void processMCChilds(Model m, MainInfo main, ElementInfo i, AbstractSchema> so) {
for (ChildElementInfo cei : i.getChildElementSpecs()) {
-
AbstractSchema> parent2 = so;
-
if (cei.isList()) {
- if (shouldGenerateParserType(cei)) {
- var sos = new ArrayList();
- for (ElementInfo ei : main.getChildElementDeclarations().get(cei.getTypeDeclaration()).getElementInfo()) {
- if (excludeFromFlow.contains(ei.getAnnotation().name()))
- continue;
- sos.add(object()
- .additionalProperties(false)
- .property(ref(ei.getAnnotation().name()).ref("#/$defs/" + ei.getXSDTypeName(m))));
- }
- processList(i, so, cei, sos);
+ if (shouldGenerateFlowParserType(cei)) {
+ processList(i, so, cei, getSchemaObjects(m, main, cei));
continue;
}
parent2 = processList(i, so, cei, null);
- } else {
- // Check if we need a $ref or if it is allowed everywhere
- if (cei.getAnnotation().allowForeign()) {
- // parent2.addProperty(new SchemaObject("$ref").attribute("type", "string"));
- }
}
- addChildsAsProperties(m, main, cei, (SchemaObject) parent2);
+ addChildsAsProperties(m, main, cei, (SchemaObject) parent2, isComponentsList(i, cei), cei.isList());
+ }
+ }
+
+ private static @NotNull ArrayList getSchemaObjects(Model m, MainInfo main, ChildElementInfo cei) {
+ var sos = new ArrayList();
+
+ for (ElementInfo ei : main.getChildElementDeclarations().get(cei.getTypeDeclaration()).getElementInfo()) {
+ if (ei.getAnnotation().excludeFromFlow())
+ continue;
+
+ sos.add(object()
+ .additionalProperties(false)
+ .property(ref(ei.getAnnotation().name())
+ .ref("#/$defs/" + ei.getXSDTypeName(m))
+ .required(true)));
}
+ // Allow referencing a component instance directly on list-item level:
+ // flow:
+ // - $ref: ...
+ sos.add(object()
+ .additionalProperties(false)
+ .property( string("$ref").required(true)));
+ return sos;
}
- private boolean shouldGenerateParserType(ChildElementInfo cei) {
+ private boolean isComponentsList(ElementInfo parent, ChildElementInfo cei) {
+ return COMPONENTS.equals(parent.getAnnotation().name())
+ && parent.getAnnotation().noEnvelope()
+ && COMPONENTS.equals(cei.getPropertyName());
+ }
+
+ private boolean shouldGenerateFlowParserType(ChildElementInfo cei) {
return "flow".equals(cei.getPropertyName()) && !isFlowFromWebSocket(cei);
}
@@ -260,7 +283,7 @@ boolean isFlowFromWebSocket(ChildElementInfo cei) {
private AbstractSchema> processList(ElementInfo i, AbstractSchema> so, ChildElementInfo cei, ArrayList sos) {
SchemaObject items = object("items");
- if (shouldGenerateParserType(cei)) {
+ if (shouldGenerateFlowParserType(cei)) {
addFlowParserRef(so, sos);
return items;
}
@@ -291,15 +314,31 @@ private void addFlowParserRef(AbstractSchema> so, List sos) {
}
}
- private void addChildsAsProperties(Model m, MainInfo main, ChildElementInfo cei, SchemaObject parent2) {
- for (ElementInfo ei : getChildElementDeclarationInfo(main, cei).getElementInfo()) {
- parent2.property(ref(ei.getAnnotation().name())
- .ref("#/$defs/" + ei.getXSDTypeName(m)))
+ private void addChildsAsProperties(Model m, MainInfo main, ChildElementInfo cei, SchemaObject parent2, boolean componentsContext, boolean listItemContext) {
+ var eis = getChildElementDeclarationInfo(main, cei).getElementInfo().stream()
+ // Top-level elements cannot be configurable as nested children
+ .filter(ei -> !ei.getAnnotation().topLevel())
+ .toList();
+
+ // Generic list-item reference support:
+ // If this list can contain at least one @MCElement(component=true) type,
+ // allow "- $ref: ..." as an alternative list item shape.
+ if (listItemContext && !componentsContext && eis.stream().anyMatch(ei -> ei.getAnnotation().component())) {
+ parent2.property(string("$ref").required(false));
+ }
+
+ for (ElementInfo ei : eis) {
+
+ parent2.property(getRef(m, ei))
.description(getDescriptionContent(ei))
.required(cei.isRequired());
}
}
+ private static SchemaRef getRef(Model m, ElementInfo ei) {
+ return ref(ei.getAnnotation().name()).ref("#/$defs/" + ei.getXSDTypeName(m));
+ }
+
private static ChildElementDeclarationInfo getChildElementDeclarationInfo(MainInfo main, ChildElementInfo cei) {
return getChildElementDeclarations(main).get(cei.getTypeDeclaration());
}
@@ -326,12 +365,50 @@ private void writeSchema(MainInfo main, Schema schema) throws IOException {
}
}
+ private SchemaObject createComponentsMapParser(Model m, MainInfo main, ElementInfo elementInfo, String parserName) {
+ SchemaObject parser = object(parserName)
+ .additionalProperties(false) // only IDs via patternProperties
+ .description(getDescriptionContent(elementInfo));
+ parser.patternProperty(COMPONENT_ID_PATTERN, anyOf(getComponents(m, main)));
+ return parser;
+ }
+
+ private static @NotNull ArrayList getComponents(Model m, MainInfo main) {
+ var variants = new ArrayList();
+
+ for (ElementInfo comp : main.getElements().values()) {
+ if (!comp.getAnnotation().component())
+ continue;
+
+ if (comp.getAnnotation().topLevel())
+ continue;
+
+ variants.add(object()
+ .additionalProperties(false)
+ .property(ref(comp.getAnnotation().name())
+ .ref("#/$defs/" + comp.getXSDTypeName(m))
+ .required(true)));
+ }
+ return variants;
+ }
+
+ private boolean hasComponentChild(ElementInfo parent, MainInfo main) {
+ for (ChildElementInfo cei : parent.getChildElementSpecs()) {
+ var decl = getChildElementDeclarationInfo(main, cei);
+ if (decl == null) continue;
+
+ if (decl.getElementInfo().stream().anyMatch(ei -> ei.getAnnotation().component()))
+ return true;
+ }
+ return false;
+ }
+
// For description. Probably we'll include that later. (Temporarily deactivated!)
private String getDescriptionAsText(AbstractJavadocedInfo elementInfo) {
return escapeJsonContent(getDescriptionContent(elementInfo).replaceAll("<[^>]+>", "").replaceAll("\\s+", " ").trim());
}
- // For description. Probably we'll include that later. (Temporarily deactivated!
+ // For description. Probably we'll include that later. (Temporarily deactivated!)
private String getDescriptionAsHtml(AbstractJavadocedInfo elementInfo) {
return escapeJsonContent(getDescriptionContent(elementInfo).replaceAll("\\s+", " ").trim());
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/Parsers.java b/annot/src/main/java/com/predic8/membrane/annot/generator/Parsers.java
index 553d70f772..e9cf87c345 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/Parsers.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/Parsers.java
@@ -67,7 +67,7 @@ public void writeParserDefinitior(Model m) throws IOException {
"\r\n" +
" public static void registerBeanDefinitionParsers(NamespaceHandler nh) {\r\n");
for (ElementInfo i : main.getIis()) {
- if (i.getAnnotation().topLevel()) {
+ if (i.getAnnotation().component()) {
bw.write(" nh.registerGlobalBeanDefinitionParser(\"" + i.getAnnotation().name() + "\", new " + i.getParserClassSimpleName() + "());\r\n");
} else {
for (ChildElementDeclarationInfo cedi : i.getUsedBy()) {
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/Schemas.java b/annot/src/main/java/com/predic8/membrane/annot/generator/Schemas.java
index 66c4af1c38..b0de70fc48 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/Schemas.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/Schemas.java
@@ -91,7 +91,7 @@ private void assembleDeclarations(Writer w, Model m, MainInfo main) throws Proce
private void assembleElementDeclaration(Writer w, Model m, MainInfo main, ElementInfo i) throws ProcessingException, IOException {
String footer;
- if (i.getAnnotation().topLevel()) {
+ if (i.getAnnotation().component()) {
w.append("\r\n");
assembleDocumentation(w, i);
w.append("\r\n");
@@ -126,7 +126,7 @@ private void assembleElementInfo(Writer w, Model m, MainInfo main, ElementInfo i
w.append("\r\n");
assembleDocumentation(w, cei);
for (ElementInfo ei : main.getChildElementDeclarations().get(cei.getTypeDeclaration()).getElementInfo()) {
- if (ei.getAnnotation().topLevel())
+ if (ei.getAnnotation().component())
w.append("\r\n");
else
w.append("\r\n");
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/Scope.java b/annot/src/main/java/com/predic8/membrane/annot/generator/Scope.java
new file mode 100644
index 0000000000..d66ec77a73
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/Scope.java
@@ -0,0 +1,6 @@
+package com.predic8.membrane.annot.generator;
+
+public enum Scope {
+ SINGLETON,
+ PROTOTYPE
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractGrammar.java b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractGrammar.java
index 0c31bdc241..e12db0d8a6 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractGrammar.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractGrammar.java
@@ -62,13 +62,13 @@ protected WritableNames getElementNames(ElementInfo ei) {
return new WritableNames(ei);
}
- protected Stream getTopLevelStream(MainInfo main) {
+ protected Stream getComponentStream(MainInfo main) {
return main.getElements().values().stream()
- .filter(ei -> ei.getAnnotation().topLevel());
+ .filter(ei -> ei.getAnnotation().component());
}
- protected List getTopLevelElementInfos(MainInfo main) {
- return getTopLevelStream(main)
+ protected List getComponentElementInfos(MainInfo main) {
+ return getComponentStream(main)
.collect(Collectors.toList());
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/Grammar.java b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/Grammar.java
index 8f953170c3..e3adfb4a49 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/Grammar.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/Grammar.java
@@ -14,14 +14,17 @@
package com.predic8.membrane.annot.generator.kubernetes;
import com.predic8.membrane.annot.model.*;
+import org.jetbrains.annotations.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
import java.io.*;
import java.util.*;
+import java.util.function.*;
import java.util.stream.*;
+import static com.predic8.membrane.annot.generator.ClassGenerator.*;
import static java.util.stream.Stream.*;
/**
@@ -39,17 +42,12 @@ protected String fileName() {
}
@Override
- protected void write(Model m) throws IOException{
+ protected void write(Model m) throws IOException {
m.getMains().forEach(main -> {
try {
List sources = new ArrayList<>(main.getInterceptorElements());
sources.add(main.getElement());
-
- FileObject fileObject = processingEnv.getFiler().createSourceFile(
- main.getAnnotation().outputPackage() + "." + fileName(),
- sources.toArray(new Element[0]));
-
- try (BufferedWriter w = new BufferedWriter(fileObject.openWriter())) {
+ try (BufferedWriter w = new BufferedWriter(getSourceFile(main, sources).openWriter())) {
assemble(w, main);
}
} catch (IOException e) {
@@ -58,84 +56,75 @@ protected void write(Model m) throws IOException{
});
}
+ private JavaFileObject getSourceFile(MainInfo main, List sources) throws IOException {
+ return processingEnv.getFiler().createSourceFile(
+ main.getAnnotation().outputPackage() + "." + fileName(),
+ sources.toArray(new Element[0]));
+ }
+
private void assemble(Writer w, MainInfo main) throws IOException {
writeCopyright(w);
writeClassContent(w, main);
}
private void writeCopyright(Writer w) throws IOException {
- appendLine(w,
- "/* Copyright 2021 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/license/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.",
- "*/"
- );
+ appendLine(w, COPYRIGHT);
}
private void writeClassContent(Writer w, MainInfo mainInfo) throws IOException {
w.write("""
- package %s;
-
- import java.util.Map;
- import java.util.List;
- import java.util.HashMap;
- import java.util.ArrayList;
- import com.predic8.membrane.annot.Grammar;
-
- /**
- * Automatically generated by {@link %s}
- */
- public class %s implements Grammar {
- private static Map> elementMapping = new HashMap<>();
- private static Map>> localElementMapping = new HashMap<>();
- private static List crdSingularNames = new ArrayList<>();
-
- private static void localElementMappingPut(String context, String name, Class> clazz) {
- Map> local = localElementMapping.get(context);
- if (local == null) {
- local = new HashMap<>();
- localElementMapping.put(context, local);
- localElementMapping.put(context.toLowerCase(), local);
+ package %s;
+
+ import java.util.Map;
+ import java.util.List;
+ import java.util.HashMap;
+ import java.util.ArrayList;
+ import com.predic8.membrane.annot.Grammar;
+
+ /**
+ * Automatically generated by {@link %s}
+ */
+ public class %s implements Grammar {
+ private static Map> elementMapping = new HashMap<>();
+ private static Map>> localElementMapping = new HashMap<>();
+ private static List crdSingularNames = new ArrayList<>();
+
+ private static void localElementMappingPut(String context, String name, Class> clazz) {
+ Map> local = localElementMapping.get(context);
+ if (local == null) {
+ local = new HashMap<>();
+ localElementMapping.put(context, local);
+ localElementMapping.put(context.toLowerCase(), local);
+ }
+ local.put(name, clazz);
+ local.put(name.toLowerCase(), clazz);
}
- local.put(name, clazz);
- local.put(name.toLowerCase(), clazz);
- }
-
- @Override
- public Class> getLocal(String context, String key) {
- Map> local = localElementMapping.get(context);
- if (local == null)
- return null;
- return local.get(key);
- }
-
- @Override
- public Class> getElement(String key) {
- return elementMapping.get(key);
- }
-
- @Override
- public List getCrdSingularNames() {
- return crdSingularNames;
- }
-
- @Override
- public String getSchemaLocation() {
- return "classpath:/%s/membrane.schema.json";
- }
-
- static {
- """.formatted(
+
+ @Override
+ public Class> getLocal(String context, String key) {
+ Map> local = localElementMapping.get(context);
+ if (local == null)
+ return null;
+ return local.get(key);
+ }
+
+ @Override
+ public Class> getElement(String key) {
+ return elementMapping.get(key);
+ }
+
+ @Override
+ public List getCrdSingularNames() {
+ return crdSingularNames;
+ }
+
+ @Override
+ public String getSchemaLocation() {
+ return "classpath:/%s/membrane.schema.json";
+ }
+
+ static {
+ """.formatted(
mainInfo.getAnnotation().outputPackage(),
Grammar.class.getName(),
fileName(),
@@ -148,60 +137,54 @@ public String getSchemaLocation() {
w.write(assembleElementMapping(mainInfo) + "\n");
w.write("""
+ }
}
- }
- """);
+ """);
}
private String assembleElementMapping(MainInfo main) {
return concat(
// global
main.getIis().stream()
- .filter(ei -> ei.getAnnotation().topLevel())
- .map(ei -> String.format(" elementMapping.put(\"%s\", %s.class);" + System.lineSeparator()
- + " elementMapping.put(\"%s\", %s.class);",
- ei.getAnnotation().name(),
- ei.getElement().getQualifiedName(),
- ei.getAnnotation().name().toLowerCase(),
- ei.getElement().getQualifiedName())),
+ .filter(ei -> ei.getAnnotation().component())
+ .map(generateElementMappingString()),
// non-global
main.getIis().stream()
.flatMap(ei -> ei.getChildElementSpecs().stream().map(cei -> Pair.of(ei, cei)))
.flatMap(p -> main.getChildElementDeclarations().get(p.y.getTypeDeclaration())
.getElementInfo().stream().map(ei -> Pair.of(p.x, ei)))
- .filter(p -> !p.y.getAnnotation().topLevel())
- .map(p -> String.format(" localElementMappingPut(\"%s\", \"%s\", %s.class);",
- p.x.getAnnotation().name(),
- p.y.getAnnotation().name(),
- p.y.getElement().getQualifiedName())))
+ .filter(p -> !p.y.getAnnotation().component())
+ .map(generateLocalElementMapping()))
.collect(Collectors.joining(System.lineSeparator()));
}
+ private static @NotNull Function, String> generateLocalElementMapping() {
+ return p -> String.format(" localElementMappingPut(\"%s\", \"%s\", %s.class);",
+ p.x.getAnnotation().name(),
+ p.y.getAnnotation().name(),
+ p.y.getElement().getQualifiedName());
+ }
+
+ private static @NotNull Function generateElementMappingString() {
+ return ei -> String.format(" elementMapping.put(\"%s\", %s.class);" + System.lineSeparator()
+ + " elementMapping.put(\"%s\", %s.class);",
+ ei.getAnnotation().name(),
+ ei.getElement().getQualifiedName(),
+ ei.getAnnotation().name().toLowerCase(),
+ ei.getElement().getQualifiedName());
+ }
+
private String assembleCrdSingularNames(MainInfo main) {
- return getTopLevelStream(main)
+ return getComponentStream(main)
.map(ei -> ei.getAnnotation().name().toLowerCase())
.map(s -> " crdSingularNames.add(\"" + s + "\");")
.collect(Collectors.joining(System.lineSeparator()));
}
- private static class Pair {
- X x;
- Y y;
- public Pair(X x, Y y) {
- this.x = x;
- this.y = y;
- }
-
+ // TODO Merge with Pair record of core
+ private record Pair(X x, Y y) {
public static Pair of(X x, Y y) {
return new Pair<>(x, y);
}
-
- public X getX() {
- return x;
- }
-
- public Y getY() {
- return y;
- }
}
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sJsonSchemaGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sJsonSchemaGenerator.java
index 2ebd67b0eb..e35e2bad81 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sJsonSchemaGenerator.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sJsonSchemaGenerator.java
@@ -45,7 +45,7 @@ public void write(Model m) throws IOException {
}
private void assemble(Model m, MainInfo main) throws IOException {
- for (ElementInfo elementInfo : getTopLevelElementInfos(main)) {
+ for (ElementInfo elementInfo : getComponentElementInfos(main)) {
String name = elementInfo.getAnnotation().name().toLowerCase();
FileObject fo = createFileObject(main, name + ".schema.json");
try (BufferedWriter w = new BufferedWriter(fo.openWriter())) {
@@ -140,20 +140,28 @@ private void collectChildElements(Model m, MainInfo main, ElementInfo i, Abstrac
if (isList) {
SchemaObject items = object("items").additionalProperties(cei.getAnnotation().allowForeign());
- if (i.getAnnotation().noEnvelope()) {
- if (so instanceof SchemaArray sa)
- sa.items(items);
- else
- throw new ProcessingException("@MCElement(noEnvelope=true) is not an array. Implementation error?", i.getElement());
- } else {
- if (so instanceof SchemaObject sObj) {
- SchemaArray array = array(cei.getPropertyName());
- array.items(items);
- sObj.property(array.required(cei.isRequired()));
- } else {
- throw new ProcessingException("@MCElement(noEnvelope=false) is not an object. Implementation error?", i.getElement());
- }
- }
+ //TODO fix: the 'components' require this structure:
+// components:
+// - bean:
+// ...
+// - xmlProtection:
+// ...
+
+
+// if (i.getAnnotation().noEnvelope()) {
+// if (so instanceof SchemaArray sa)
+// sa.items(items);
+// else
+// throw new ProcessingException("@MCElement(noEnvelope=true) is not an array. Implementation error?", i.getElement());
+// } else {
+// if (so instanceof SchemaObject sObj) {
+// SchemaArray array = array(cei.getPropertyName());
+// array.items(items);
+// sObj.property(array.required(cei.isRequired()));
+// } else {
+// throw new ProcessingException("@MCElement(noEnvelope=false) is not an object. Implementation error?", i.getElement());
+// }
+// }
parent2 = items;
} else {
if (cei.getAnnotation().allowForeign()) {
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sYamlGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sYamlGenerator.java
index 32ed9bb2a6..8747add450 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sYamlGenerator.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/K8sYamlGenerator.java
@@ -59,7 +59,7 @@ protected void write(Model m) throws IOException {
private void assemble(Writer w, MainInfo main) throws IOException {
- for (ElementInfo element : getTopLevelElementInfos(main)) {
+ for (ElementInfo element : getComponentElementInfos(main)) {
writeCRD(w, element);
appendLine(w, "---");
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/model/SchemaObject.java b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/model/SchemaObject.java
index b4b7ab035c..ef48959290 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/model/SchemaObject.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/model/SchemaObject.java
@@ -28,11 +28,36 @@ public class SchemaObject extends AbstractSchema {
private List> oneOf;
+ private List> allOf;
+ private final Map> patternProperties = new LinkedHashMap<>();
+
SchemaObject(String name) {
super(name);
type = OBJECT;
}
+ /**
+ * Populates the given {@code ObjectNode} with the JSON schema representation
+ * of this {@code SchemaObject}, including additional properties, pattern properties,
+ * and combinations (allOf, oneOf).
+ *
+ * @param node the {@code ObjectNode} to populate with schema details
+ * @return the modified {@code ObjectNode} containing the schema representation
+ */
+ public ObjectNode json(ObjectNode node) {
+ super.json(node);
+
+ if (!additionalProperties && isObject()) {
+ node.put("additionalProperties", false);
+ }
+
+ addProperties(node);
+ addPatternProperties(node);
+ addAllOf(node);
+ addOneOf(node);
+ return node;
+ }
+
public SchemaObject property(AbstractSchema> as) {
for (AbstractSchema> p : properties)
if (p.getName().equals(as.getName()))
@@ -46,27 +71,54 @@ public SchemaObject additionalProperties(boolean additionalProperties) {
return this;
}
- public ObjectNode json(ObjectNode node) {
- super.json(node);
+ public SchemaObject patternProperty(String pattern, AbstractSchema> schema) {
+ patternProperties.put(pattern, schema);
+ return this;
+ }
- if (!additionalProperties && isObject()) {
- node.put("additionalProperties", false);
+ private void addOneOf(ObjectNode node) {
+ if (oneOf == null || oneOf.isEmpty())
+ return;
+
+ var oneOfArray = jnf.arrayNode();
+ for (AbstractSchema> s : oneOf) {
+ oneOfArray.add(s.json(jnf.objectNode()));
}
+ node.set("oneOf", oneOfArray);
+
+ }
- jsonProperties(node);
+ private void addAllOf(ObjectNode node) {
+ if (allOf == null || allOf.isEmpty())
+ return;
- if (oneOf != null && !oneOf.isEmpty()) {
- var oneOfArray = jnf.arrayNode();
- for (AbstractSchema> s : oneOf) {
- oneOfArray.add(s.json(jnf.objectNode()));
- }
- node.set("oneOf", oneOfArray);
+ var allOfArray = jnf.arrayNode();
+ for (AbstractSchema> s : allOf) {
+ allOfArray.add(s.json(jnf.objectNode()));
}
+ node.set("allOf", allOfArray);
- return node;
}
- private void jsonProperties(ObjectNode node) {
+ private void addPatternProperties(ObjectNode node) {
+ if (patternProperties.isEmpty())
+ return;
+
+ ObjectNode pp = jnf.objectNode();
+ for (var e : patternProperties.entrySet()) {
+ pp.set(e.getKey(), e.getValue().json(jnf.objectNode()));
+ }
+ node.set("patternProperties", pp);
+ }
+
+ /**
+ * Populates the specified {@code ObjectNode} with the properties of the JSON schema
+ * associated with this object. The method iterates over the defined properties, adding
+ * them to a "properties" node, and specifies any required properties in a "required" array.
+ *
+ * @param node the {@code ObjectNode} to populate with the properties and required fields
+ */
+ private void addProperties(ObjectNode node) {
if (properties.isEmpty())
return;
@@ -87,7 +139,7 @@ private void jsonProperties(ObjectNode node) {
node.set("properties", propertiesNode);
}
- private static ObjectNode createPropertyNode(AbstractSchema>property) {
+ private static ObjectNode createPropertyNode(AbstractSchema> property) {
ObjectNode propertyNode = property.json(jnf.objectNode());
if (property.getEnumValues() != null && !property.getEnumValues().isEmpty()) {
propertyNode.set("enum", getEnumNode(property));
@@ -110,4 +162,13 @@ public SchemaObject oneOf(List> oneOf) {
this.oneOf = oneOf;
return this;
}
+
+ public SchemaObject allOf(List> allOf) {
+ this.allOf = allOf;
+ return this;
+ }
+
+ public boolean hasProperty(String name) {
+ return properties.stream().anyMatch(p -> name.equals(p.getName()));
+ }
}
\ No newline at end of file
diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/ElementInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/ElementInfo.java
index 039e70d2da..305a7c4113 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/model/ElementInfo.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/model/ElementInfo.java
@@ -13,125 +13,133 @@
limitations under the License. */
package com.predic8.membrane.annot.model;
-import java.util.ArrayList;
-import java.util.List;
+import com.predic8.membrane.annot.*;
-import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.*;
+import java.util.*;
-import com.predic8.membrane.annot.AnnotUtils;
-import com.predic8.membrane.annot.MCElement;
+import static com.predic8.membrane.annot.model.OtherAttributesInfo.ValueType.*;
/**
* Mirrors {@link MCElement}.
*/
public class ElementInfo extends AbstractJavadocedInfo {
- private MCElement annotation;
- private final List usedBy = new ArrayList<>();
-
-
- private TypeElement element;
- private boolean hasIdField;
-
- private TextContentInfo tci;
-
- private List ais = new ArrayList<>();
- private List ceis = new ArrayList<>();
-
- private OtherAttributesInfo oai;
-
- public TypeElement getElement() {
- return element;
- }
- public void setElement(TypeElement element) {
- this.element = element;
- setDocedE(element);
- }
- public TextContentInfo getTci() {
- return tci;
- }
- public void setTci(TextContentInfo tci) {
- this.tci = tci;
- }
- public List getAis() {
- return ais;
- }
- public void setAis(List ais) {
- this.ais = ais;
- }
- public boolean isHasIdField() {
- return hasIdField;
- }
- public void setHasIdField(boolean hasIdField) {
- this.hasIdField = hasIdField;
- }
- public List getChildElementSpecs() {
- return ceis;
- }
- public void setCeis(List ceis) {
- this.ceis = ceis;
- }
-
- public String getParserClassSimpleName() {
- return AnnotUtils.javaify(getId().replace("-", "") + "Parser");
- }
-
- public MainInfo getMain(Model m) {
- for (MainInfo main : m.getMains()) {
- if (main.getAnnotation().outputPackage().equals(getAnnotation().configPackage()))
- return main;
- }
- return m.getMains().getFirst();
- }
-
- public String getClassName(Model m) {
- return getMain(m).getAnnotation().outputPackage() + "." + getParserClassSimpleName();
- }
-
- public String getXSDTypeName(Model m) {
+ private MCElement annotation;
+ private final List usedBy = new ArrayList<>();
+
+
+ private TypeElement element;
+ private boolean hasIdField;
+
+ private TextContentInfo tci;
+
+ private final List ais = new ArrayList<>();
+ private final List ceis = new ArrayList<>();
+
+ private OtherAttributesInfo oai;
+
+ public TypeElement getElement() {
+ return element;
+ }
+
+ public void setElement(TypeElement element) {
+ this.element = element;
+ setDocedE(element);
+ }
+
+ public TextContentInfo getTci() {
+ return tci;
+ }
+
+ public void setTci(TextContentInfo tci) {
+ this.tci = tci;
+ }
+
+ public List getAis() {
+ return ais;
+ }
+
+ public boolean isHasIdField() {
+ return hasIdField;
+ }
+
+ public void setHasIdField(boolean hasIdField) {
+ this.hasIdField = hasIdField;
+ }
+
+ public List getChildElementSpecs() {
+ return ceis;
+ }
+
+ public String getParserClassSimpleName() {
+ return AnnotUtils.javaify(getId().replace("-", "") + "Parser");
+ }
+
+ public MainInfo getMain(Model m) {
+ for (MainInfo main : m.getMains()) {
+ if (main.getAnnotation().outputPackage().equals(getAnnotation().configPackage()))
+ return main;
+ }
+ return m.getMains().getFirst();
+ }
+
+ public String getClassName(Model m) {
+ return getMain(m).getAnnotation().outputPackage() + "." + getParserClassSimpleName();
+ }
+
+ public String getXSDTypeName(Model m) {
// There are JSON Schema parsers that do not accept names like a.b.c
- return getClassName(m).replace(".","_");
- }
-
- public MCElement getAnnotation() {
- return annotation;
- }
-
- public void setAnnotation(MCElement annotation) {
- this.annotation = annotation;
- }
-
- public void addUsedBy(ChildElementDeclarationInfo cedi) {
- usedBy.add(cedi);
- }
-
- public List getUsedBy() {
- return usedBy;
- }
-
- public String getId() {
- if (!annotation.id().isEmpty())
- return annotation.id();
- return annotation.name();
- }
-
- public void setOai(OtherAttributesInfo oai) {
- this.oai = oai;
- }
-
- public OtherAttributesInfo getOai() {
- return oai;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ElementInfo other))
- return false;
+ return getClassName(m).replace(".", "_");
+ }
+
+ public MCElement getAnnotation() {
+ return annotation;
+ }
+
+ public void setAnnotation(MCElement annotation) {
+ this.annotation = annotation;
+ }
+
+ public void addUsedBy(ChildElementDeclarationInfo cedi) {
+ usedBy.add(cedi);
+ }
+
+ public List getUsedBy() {
+ return usedBy;
+ }
+
+ public String getId() {
+ if (!annotation.id().isEmpty())
+ return annotation.id();
+ return annotation.name();
+ }
+
+ public void setOai(OtherAttributesInfo oai) {
+ this.oai = oai;
+ }
+
+ public OtherAttributesInfo getOai() {
+ return oai;
+ }
+
+ public boolean isString() {
+ return oai != null && oai.getValueType() == STRING;
+ }
+
+ public boolean isObject() {
+ return oai != null && oai.getValueType() == OBJECT;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ElementInfo other))
+ return false;
return element.equals(other.element);
- }
+ }
- @Override
- public int hashCode() {
- return element.getQualifiedName().toString().hashCode();
- }
+ @Override
+ public int hashCode() {
+ return element.getQualifiedName().toString().hashCode();
+ }
}
\ No newline at end of file
diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/MainInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/MainInfo.java
index 3deca1000f..9e9c9d526e 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/model/MainInfo.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/model/MainInfo.java
@@ -67,7 +67,7 @@ public Map getElements() {
return elements;
}
- public Map getTopLevels() {
+ public Map getComponents() {
return globals;
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/OtherAttributesInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/OtherAttributesInfo.java
index cb536e09c6..06a28a2a1f 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/model/OtherAttributesInfo.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/model/OtherAttributesInfo.java
@@ -14,6 +14,9 @@
package com.predic8.membrane.annot.model;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
import com.predic8.membrane.annot.AnnotUtils;
import com.predic8.membrane.annot.ProcessingException;
@@ -21,6 +24,7 @@
public class OtherAttributesInfo extends AbstractJavadocedInfo {
private ExecutableElement otherAttributesSetter;
+ private TypeElement mapValueType;
public ExecutableElement getOtherAttributesSetter() {
return otherAttributesSetter;
@@ -29,6 +33,13 @@ public ExecutableElement getOtherAttributesSetter() {
public void setOtherAttributesSetter(ExecutableElement otherAttributesSetter) {
this.otherAttributesSetter = otherAttributesSetter;
setDocedE(otherAttributesSetter);
+
+ var typeArgs = ((DeclaredType) otherAttributesSetter.getParameters().getFirst().asType()).getTypeArguments();
+ if (typeArgs.size() != 2) {
+ throw new ProcessingException("@MCOtherAttributes must use Map.", otherAttributesSetter);
+ }
+
+ mapValueType = (TypeElement) ((DeclaredType) typeArgs.get(1)).asElement();
}
public String getSpringName() {
@@ -39,4 +50,18 @@ public String getSpringName() {
return AnnotUtils.dejavaify(s);
}
+ public ValueType getValueType() {
+ if (mapValueType.getQualifiedName().toString().equals("java.lang.String")) {
+ return ValueType.STRING;
+ }
+ if (mapValueType.getQualifiedName().toString().equals("java.lang.Object")) {
+ return ValueType.OBJECT;
+ }
+ throw new ProcessingException("Not supported: @McOtherAttributes void setAttr(Map attrs) where T is neither String nor Object.");
+ }
+
+ public enum ValueType {
+ STRING,
+ OBJECT
+ }
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/util/ReflectionUtil.java b/annot/src/main/java/com/predic8/membrane/annot/util/ReflectionUtil.java
new file mode 100644
index 0000000000..c55848d6aa
--- /dev/null
+++ b/annot/src/main/java/com/predic8/membrane/annot/util/ReflectionUtil.java
@@ -0,0 +1,54 @@
+package com.predic8.membrane.annot.util;
+
+public class ReflectionUtil {
+
+ /**
+ * Converts a string literal to the target Java type.
+ */
+ public static Object convert(String raw, Class> targetType) {
+ if (targetType == String.class) return raw;
+ if (raw == null) {
+ if (targetType.isPrimitive())
+ throw new IllegalArgumentException("Cannot assign null to primitive " + targetType.getName());
+ return null;
+ }
+
+ if (targetType == boolean.class || targetType == Boolean.class) return Boolean.parseBoolean(raw);
+ if (targetType == int.class || targetType == Integer.class) return Integer.parseInt(raw);
+ if (targetType == long.class || targetType == Long.class) return Long.parseLong(raw);
+ if (targetType == double.class || targetType == Double.class) return Double.parseDouble(raw);
+ if (targetType == float.class || targetType == Float.class) return Float.parseFloat(raw);
+ if (targetType == short.class || targetType == Short.class) return Short.parseShort(raw);
+ if (targetType == byte.class || targetType == Byte.class) return Byte.parseByte(raw);
+ if (targetType == char.class || targetType == Character.class) {
+ if (raw.length() != 1) throw new IllegalArgumentException("Expected single character, got: " + raw);
+ return raw.charAt(0);
+ }
+
+ if (targetType.isEnum()) {
+ //noinspection unchecked
+ return Enum.valueOf((Class extends Enum>) targetType, raw);
+ }
+
+ throw new IllegalArgumentException("Unsupported conversion to " + targetType.getName() + " from value: " + raw);
+ }
+
+ /**
+ * Determines if the given wrapper class is the corresponding wrapper type
+ * for the specified primitive type.
+ *
+ * @param primitive the primitive type to be checked (e.g., int.class, double.class)
+ * @param wrapper the wrapper class to be checked (e.g., Integer.class, Double.class)
+ * @return true if the wrapper class is the corresponding wrapper for the primitive type, false otherwise
+ */
+ public static boolean isWrapperOfPrimitive(Class> primitive, Class> wrapper) {
+ return (primitive == int.class && wrapper == Integer.class)
+ || (primitive == long.class && wrapper == Long.class)
+ || (primitive == boolean.class && wrapper == Boolean.class)
+ || (primitive == double.class && wrapper == Double.class)
+ || (primitive == float.class && wrapper == Float.class)
+ || (primitive == short.class && wrapper == Short.class)
+ || (primitive == byte.class && wrapper == Byte.class)
+ || (primitive == char.class && wrapper == Character.class);
+ }
+}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanDefinition.java b/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanDefinition.java
index 0c14290381..f1df1feae3 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanDefinition.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanDefinition.java
@@ -14,9 +14,13 @@
package com.predic8.membrane.annot.yaml;
import com.fasterxml.jackson.databind.JsonNode;
+import com.predic8.membrane.annot.*;
+import com.predic8.membrane.annot.bean.*;
import java.util.*;
+import static com.predic8.membrane.annot.yaml.WatchAction.*;
+
public class BeanDefinition {
public static final String PROTOTYPE = "prototype";
@@ -27,6 +31,10 @@ public class BeanDefinition {
private final JsonNode node;
private final WatchAction action;
private final String kind;
+
+ /**
+ * Constructed bean after initialization.
+ */
private Object bean;
/**
@@ -57,7 +65,7 @@ public BeanDefinition(String kind, String name, String namespace, String uid, Js
this.namespace = namespace;
this.uid = uid;
this.node = node;
- this.action = WatchAction.ADDED;
+ this.action = ADDED;
}
public JsonNode getNode() {
@@ -100,10 +108,31 @@ public String getScope() {
JsonNode annotations = meta.get("annotations");
if (annotations == null)
return null;
- return annotations.get("membrane-soa.org/scope").asText(); // TODO migrate to membrane-api.io
+ JsonNode scope = annotations.get("membrane-api.io/scope");
+ return scope == null ? null : scope.asText();
+ }
+
+ public boolean isComponent() {
+ return name != null && name.startsWith("#/components/");
+ }
+
+ public boolean isBean() {
+ return "bean".equals(kind);
}
public boolean isPrototype() {
return PROTOTYPE.equals(getScope());
}
+
+ public boolean isDeleted() {
+ return action == DELETED;
+ }
+
+ public boolean isModified() {
+ return action == MODIFIED;
+ }
+
+ public boolean isAdded() {
+ return action == ADDED;
+ }
}
diff --git a/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java b/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java
index 7ed672c7e0..7a3edddade 100644
--- a/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java
+++ b/annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java
@@ -13,16 +13,19 @@
limitations under the License. */
package com.predic8.membrane.annot.yaml;
-import com.fasterxml.jackson.databind.*;
-import com.predic8.membrane.annot.*;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.predic8.membrane.annot.Grammar;
+import com.predic8.membrane.annot.bean.BeanFactory;
import org.jetbrains.annotations.*;
-import org.slf4j.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.io.*;
import java.util.*;
-import java.util.concurrent.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingDeque;
-import static com.predic8.membrane.annot.yaml.BeanDefinition.*;
+import static com.predic8.membrane.annot.yaml.BeanDefinition.create4Kubernetes;
import static com.predic8.membrane.annot.yaml.WatchAction.*;
public class BeanRegistryImplementation implements BeanRegistry {
@@ -71,12 +74,19 @@ public void start() {
}
}
- private Object define(BeanDefinition bd) throws IOException, ParsingException {
+ private Object define(BeanDefinition bd) {
log.debug("defining bean: {}", bd.getNode());
- return GenericYamlParser.readMembraneObject(bd.getKind(),
- grammar,
- bd.getNode(),
- this);
+ try {
+ if ("bean".equals(bd.getKind())) {
+ return new BeanFactory(this).create(bd.getNode().path("bean"));
+ }
+ return GenericYamlParser.readMembraneObject(bd.getKind(),
+ grammar,
+ bd.getNode(),
+ this);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
/**
@@ -107,8 +117,9 @@ void handle(BeanDefinition bd) {
// can see both metadata and the action (including DELETED).
bds.put(bd.getUid(), bd);
- if (observer.isActivatable(bd))
+ if (!bd.isComponent() && observer.isActivatable(bd)) {
uidsToActivate.add(bd.getUid());
+ }
if (changeEvents.isEmpty())
activationRun();
@@ -122,16 +133,12 @@ private void activationRun() {
Object bean = define(bd);
bd.setBean(bean);
- Object oldBean = null;
- if (bd.getAction() == MODIFIED || bd.getAction() == DELETED)
- oldBean = uuidMap.get(bd.getUid());
-
// e.g. inform router about new proxy
- observer.handleBeanEvent(bd, bean, oldBean);
+ observer.handleBeanEvent(bd, bean, getOldBean(bd));
- if (bd.getAction() == ADDED || bd.getAction() == MODIFIED)
+ if (bd.isAdded() || bd.isModified())
uuidMap.put(bd.getUid(), bean);
- if (bd.getAction() == DELETED) {
+ if (bd.isDeleted()) {
uuidMap.remove(bd.getUid());
bds.remove(bd.getUid());
}
@@ -145,39 +152,53 @@ private void activationRun() {
uidsToActivate.remove(uid);
}
+ private @Nullable Object getOldBean(BeanDefinition bd) {
+ Object oldBean = null;
+ if (bd.isModified() || bd.isDeleted())
+ oldBean = uuidMap.get(bd.getUid());
+ return oldBean;
+ }
+
@Override
public Object resolveReference(String url) {
BeanDefinition bd = getFirstByName(url).orElseThrow(() -> new RuntimeException("Reference %s not found".formatted(url)));
- Object envelope = null;
- if (bd.getBean() != null)
- envelope = bd.getBean();
- if (envelope == null) {
- try {
- envelope = define(bd);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- if (!bd.isPrototype())
- bd.setBean(envelope);
- }
- return envelope;
- // TODO
-// if (spec instanceof Bean)
-// return ((Bean) spec).getBean();
+ boolean prototype = isPrototypeScope(bd);
+
+ if (!prototype && bd.getBean() != null)
+ return bd.getBean();
+
+ Object instance = define(bd);
+
+ if (!prototype)
+ bd.setBean(instance);
+
+ return instance;
}
private @NotNull Optional getFirstByName(String url) {
- return bds.values().stream().filter(bd -> bd.getName().equals(url)).findFirst();
+ return bds.values().stream().filter(bd -> url.equals(bd.getName())).findFirst();
}
@Override
public List
*/
-@MCElement(name="path", topLevel=false, mixed=true)
+@MCElement(name="path", component =false, mixed=true)
public class Path {
private String uri;
diff --git a/core/src/main/java/com/predic8/membrane/core/config/security/acme/DnsOperatorAcmeValidation.java b/core/src/main/java/com/predic8/membrane/core/config/security/acme/DnsOperatorAcmeValidation.java
index 110192e3cf..ab5bbd2622 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/security/acme/DnsOperatorAcmeValidation.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/security/acme/DnsOperatorAcmeValidation.java
@@ -15,7 +15,7 @@
import com.predic8.membrane.annot.MCElement;
-@MCElement(topLevel = false, name = "dnsOperator")
+@MCElement(component = false, name = "dnsOperator")
public class DnsOperatorAcmeValidation extends AcmeValidation {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/config/security/acme/FileStorage.java b/core/src/main/java/com/predic8/membrane/core/config/security/acme/FileStorage.java
index eca973641d..d69d09dc4f 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/security/acme/FileStorage.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/security/acme/FileStorage.java
@@ -18,7 +18,7 @@
import java.util.Objects;
-@MCElement(name = "fileStorage", topLevel = false)
+@MCElement(name = "fileStorage", component = false)
public class FileStorage implements AcmeSynchronizedStorage {
String dir;
diff --git a/core/src/main/java/com/predic8/membrane/core/config/security/acme/KubernetesStorage.java b/core/src/main/java/com/predic8/membrane/core/config/security/acme/KubernetesStorage.java
index b9b99ebfee..0a5c060c35 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/security/acme/KubernetesStorage.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/security/acme/KubernetesStorage.java
@@ -19,7 +19,7 @@
import java.util.Objects;
-@MCElement(name = "kubernetesStorage", topLevel = false)
+@MCElement(name = "kubernetesStorage", component = false)
public class KubernetesStorage implements AcmeSynchronizedStorage {
String baseURL;
String namespace;
diff --git a/core/src/main/java/com/predic8/membrane/core/config/security/acme/MemoryStorage.java b/core/src/main/java/com/predic8/membrane/core/config/security/acme/MemoryStorage.java
index 4828d460d3..1a9cf55976 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/security/acme/MemoryStorage.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/security/acme/MemoryStorage.java
@@ -19,7 +19,7 @@
* @description
* For testing purposes only.
*/
-@MCElement(name = "memoryStorage", topLevel = false)
+@MCElement(name = "memoryStorage", component = false)
public class MemoryStorage implements AcmeSynchronizedStorage {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/config/xml/Namespaces.java b/core/src/main/java/com/predic8/membrane/core/config/xml/Namespaces.java
index 75bf5ddc77..7292f8b4d9 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/xml/Namespaces.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/xml/Namespaces.java
@@ -21,7 +21,7 @@
import static javax.xml.XMLConstants.NULL_NS_URI;
-@MCElement(name="namespaces", topLevel = false)
+@MCElement(name="namespaces", component = false)
public class Namespaces {
private List namespaces;
@@ -43,7 +43,7 @@ public List getNamespace() {
return namespaces;
}
- @MCElement(name = "namespace", topLevel = false, id = "xml-namespace")
+ @MCElement(name = "namespace", component = false, id = "xml-namespace")
public static class Namespace {
public String prefix;
diff --git a/core/src/main/java/com/predic8/membrane/core/config/xml/XmlConfig.java b/core/src/main/java/com/predic8/membrane/core/config/xml/XmlConfig.java
index dd4da4024e..5d9c0f0069 100644
--- a/core/src/main/java/com/predic8/membrane/core/config/xml/XmlConfig.java
+++ b/core/src/main/java/com/predic8/membrane/core/config/xml/XmlConfig.java
@@ -16,7 +16,7 @@
import com.predic8.membrane.annot.*;
-@MCElement(name="xmlConfig",topLevel = true)
+@MCElement(name="xmlConfig", component = true)
public class XmlConfig {
private Namespaces namespaces;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/DispatchingInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/DispatchingInterceptor.java
index fc56d0e5b5..1ced96ac4a 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/DispatchingInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/DispatchingInterceptor.java
@@ -41,7 +41,7 @@
* object. The dispatching interceptor needs the service proxy to
* get information about the target.
*/
-@MCElement(name = "dispatching")
+@MCElement(name = "dispatching", excludeFromFlow = true)
public class DispatchingInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(DispatchingInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/EchoInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/EchoInterceptor.java
index f2a2dc2f4d..3d109cfdf2 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/EchoInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/EchoInterceptor.java
@@ -26,7 +26,7 @@
* request into a new response. The response has a status code of 200.
* Useful for testing.
*/
-@MCElement(name="echo", topLevel = false)
+@MCElement(name="echo", component = false)
public class EchoInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(EchoInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/ExchangeStoreInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/ExchangeStoreInterceptor.java
index 577d264d4b..0de4c77b33 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/ExchangeStoreInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/ExchangeStoreInterceptor.java
@@ -36,7 +36,7 @@
* might both be required for the exchange to be saved.
* @topic 4. Monitoring, Logging and Statistics
*/
-@MCElement(name="exchangeStore")
+@MCElement(name="exchangeStore", excludeFromFlow = true)
public class ExchangeStoreInterceptor extends AbstractInterceptor implements ApplicationContextAware {
private static final String BEAN_ID_ATTRIBUTE_CANNOT_BE_USED = "bean id attribute cannot be used";
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/GlobalInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/GlobalInterceptor.java
index b33a848ebf..476def64d3 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/GlobalInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/GlobalInterceptor.java
@@ -22,7 +22,7 @@
* @description The global chain applies plugins to all endpoints, enabling centralized features
* such as global user authentication, logging, and other cross-cutting concerns.
*/
-@MCElement(name = "global")
+@MCElement(name = "global", excludeFromFlow = true)
public class GlobalInterceptor extends AbstractFlowWithChildrenInterceptor {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/HTTPClientInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/HTTPClientInterceptor.java
index 4260021623..4496d1d1bd 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/HTTPClientInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/HTTPClientInterceptor.java
@@ -40,7 +40,7 @@
* its outgoing HTTP connection that is different from the global
* configuration in the transport.
*/
-@MCElement(name = "httpClient")
+@MCElement(name = "httpClient", excludeFromFlow= true)
public class HTTPClientInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(HTTPClientInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/RuleMatchingInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/RuleMatchingInterceptor.java
index d02258b045..ac6da7eb92 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/RuleMatchingInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/RuleMatchingInterceptor.java
@@ -28,7 +28,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.*;
@SuppressWarnings("unused")
-@MCElement(name="ruleMatching")
+@MCElement(name="ruleMatching", excludeFromFlow = true)
public class RuleMatchingInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(RuleMatchingInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/UserFeatureInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/UserFeatureInterceptor.java
index 50580ac007..28721aec26 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/UserFeatureInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/UserFeatureInterceptor.java
@@ -22,7 +22,7 @@
/**
* Handles features that are user-configured in proxies.xml .
*/
-@MCElement(name="userFeature")
+@MCElement(name="userFeature", excludeFromFlow = true)
public class UserFeatureInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(UserFeatureInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/WSDLInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/WSDLInterceptor.java
index 99504b1eef..67ecb950c7 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/WSDLInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/WSDLInterceptor.java
@@ -36,7 +36,7 @@
* @description
The wsdlRewriter rewrites endpoint addresses of services and XML Schema locations in WSDL documents.
* @topic 5. Web Services with SOAP and WSDL
*/
-@MCElement(name = "wsdlRewriter")
+@MCElement(name = "wsdlRewriter", excludeFromFlow = true)
public class WSDLInterceptor extends RelocatingInterceptor {
private final static Logger log = LoggerFactory.getLogger(WSDLInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/addHeader.java b/core/src/main/java/com/predic8/membrane/core/interceptor/addHeader.java
index 696cb6ef01..434cffafe8 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/addHeader.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/addHeader.java
@@ -17,7 +17,7 @@
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.http.HeaderField;
-@MCElement(name = "addHeader", topLevel = false)
+@MCElement(name = "addHeader", component = false)
public class addHeader {
private String name;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/adminapi/AdminApiInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/adminapi/AdminApiInterceptor.java
index 6336e070ed..4eef0a2d13 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/adminapi/AdminApiInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/adminapi/AdminApiInterceptor.java
@@ -53,7 +53,7 @@
import static java.net.URLDecoder.decode;
import static java.nio.charset.StandardCharsets.UTF_8;
-@MCElement(name = "adminApi")
+@MCElement(name = "adminApi", excludeFromFlow = true)
public class AdminApiInterceptor extends AbstractInterceptor {
static final DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java
index 4a1ae88d9e..13063d7188 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyExpressionExtractor.java
@@ -47,7 +47,7 @@
*
* @topic 3. Security and Validation
*/
-@MCElement(name="expressionExtractor", topLevel = false)
+@MCElement(name="expressionExtractor", component = false)
public class ApiKeyExpressionExtractor implements ApiKeyExtractor, Polyglot, XMLSupport {
private String expression = "";
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java
index 42b7b16db8..1f2a82bd94 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java
@@ -36,7 +36,7 @@
*
* @topic 3. Security and Validation
*/
-@MCElement(name="headerExtractor", topLevel = false)
+@MCElement(name="headerExtractor", component = false)
public class ApiKeyHeaderExtractor implements ApiKeyExtractor{
private HeaderName headerName = new HeaderName("X-Api-Key");
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyQueryParamExtractor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyQueryParamExtractor.java
index ed44c59ea4..271956ad80 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyQueryParamExtractor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyQueryParamExtractor.java
@@ -41,7 +41,7 @@
*
* @topic 3. Security and Validation
*/
-@MCElement(name="queryParamExtractor", topLevel = false)
+@MCElement(name="queryParamExtractor", component = false)
public class ApiKeyQueryParamExtractor implements ApiKeyExtractor{
private static final Logger log = LoggerFactory.getLogger(ApiKeyQueryParamExtractor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Key.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Key.java
index d6bf186672..bfb5804cbd 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Key.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Key.java
@@ -23,7 +23,7 @@
/**
* @description Contains api keys and scopes.
*/
-@MCElement(name = "secret", topLevel = false)
+@MCElement(name = "secret", component = false)
public class Key {
private final List scopes = new ArrayList<>();
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Scope.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Scope.java
index d38e22ea69..cf65e1dca6 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Scope.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/Scope.java
@@ -19,7 +19,7 @@
/**
* @description Contains a scope for use in ... elements.
*/
-@MCElement(name = "scope", topLevel = false, mixed = true)
+@MCElement(name = "scope", component = false, mixed = true)
public class Scope {
private String value;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/SimpleKeyStore.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/SimpleKeyStore.java
index 1fde1bb65c..2ebda0fa12 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/SimpleKeyStore.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/stores/inConfig/SimpleKeyStore.java
@@ -24,7 +24,7 @@
/**
* @description Stores api keys inline as XML.
*/
-@MCElement(name = "keys", topLevel = false, noEnvelope = true)
+@MCElement(name = "keys", component = false, noEnvelope = true)
public class SimpleKeyStore implements ApiKeyStore {
private final List keys = new ArrayList<>();
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java
index 0c22c0a8f8..c0e81664e2 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/CachingUserDataProvider.java
@@ -31,7 +31,7 @@
/**
* @description Caching User Data provider caches previous successful logins in order to make authentication faster
*/
-@MCElement(name="cachingUserDataProvider", topLevel=false)
+@MCElement(name="cachingUserDataProvider", component =false)
public class CachingUserDataProvider implements UserDataProvider {
private static final Logger log = LoggerFactory.getLogger(CachingUserDataProvider.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java
index ff35c19b99..a0518b4b6f 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmailTokenProvider.java
@@ -51,7 +51,7 @@
* will be replaced by the numeric token value.
*
*/
-@MCElement(name="emailTokenProvider", topLevel=false)
+@MCElement(name="emailTokenProvider", component =false)
public class EmailTokenProvider extends NumericTokenProvider {
private static final Logger log = LoggerFactory.getLogger(EmailTokenProvider.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java
index 707a7f950a..540bd943e6 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/EmptyTokenProvider.java
@@ -19,7 +19,7 @@
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.Router;
-@MCElement(name="emptyTokenProvider", topLevel=false)
+@MCElement(name="emptyTokenProvider", component =false)
public class EmptyTokenProvider implements TokenProvider {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java
index 1e0d870226..2b78fd9319 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/LDAPUserDataProvider.java
@@ -91,7 +91,7 @@
* attributes.
*
*/
-@MCElement(name="ldapUserDataProvider", topLevel=false)
+@MCElement(name="ldapUserDataProvider", component =false)
public class LDAPUserDataProvider implements UserDataProvider {
private static final Logger log = LoggerFactory.getLogger(LDAPUserDataProvider.class.getName());
@@ -110,10 +110,10 @@ public class LDAPUserDataProvider implements UserDataProvider {
AttributeMap map;
SSLParser sslParser;
- @MCElement(name="map", topLevel=false, id="ldapUserDataProvider-map", noEnvelope = true)
+ @MCElement(name="map", component =false, id="ldapUserDataProvider-map", noEnvelope = true)
public static class AttributeMap {
- @MCElement(name="attribute", topLevel=false)
+ @MCElement(name="attribute", component =false)
public static class Attribute {
String from;
String to;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java
index 8e9efd8d0c..3a2a4cfb55 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/SessionManager.java
@@ -39,7 +39,7 @@
* timeout is 5 minutes.
*
*/
-@MCElement(name="sessionManager", topLevel=false)
+@MCElement(name="sessionManager", component =false)
public class SessionManager extends AbstractXmlElement implements Cleaner {
private String cookieName;
private long timeout;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java
index 087e10bc8f..3331e8e2be 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java
@@ -96,7 +96,7 @@ private String createPasswdCompatibleHash(String algo, String password, String s
return Crypt.crypt(password, "$" + algo + "$" + salt);
}
- @MCElement(name="user", topLevel=false, id="staticUserDataProvider-user")
+ @MCElement(name="user", component =false, id="staticUserDataProvider-user")
public static class User {
final Map attributes = new HashMap<>();
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java
index 8fcdab8220..f18b1c2833 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TOTPTokenProvider.java
@@ -44,7 +44,7 @@
* Authenticator App to store the pre-shared secret and generate such tokens.
*
*/
-@MCElement(name="totpTokenProvider", topLevel=false)
+@MCElement(name="totpTokenProvider", component =false)
public class TOTPTokenProvider implements TokenProvider {
final Logger log = LoggerFactory.getLogger(TOTPTokenProvider.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java
index 193d1df521..40211a7bd3 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/TelekomSMSTokenProvider.java
@@ -71,7 +71,7 @@
*
*
*/
-@MCElement(name="telekomSMSTokenProvider", topLevel=false)
+@MCElement(name="telekomSMSTokenProvider", component =false)
public class TelekomSMSTokenProvider extends SMSTokenProvider {
private static final Logger log = LoggerFactory.getLogger(TelekomSMSTokenProvider.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java
index fd014f4154..329726654a 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/UnifyingUserDataProvider.java
@@ -37,7 +37,7 @@
* provider could verify the user, the login attempt fails.
*
*/
-@MCElement(name="unifyingUserDataProvider", topLevel=false, noEnvelope = true)
+@MCElement(name="unifyingUserDataProvider", component =false, noEnvelope = true)
public class UnifyingUserDataProvider implements UserDataProvider {
private List userDataProviders = new ArrayList<>();
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java
index ac8ab9ee9e..d69bda4ca7 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/WhateverMobileSMSTokenProvider.java
@@ -51,7 +51,7 @@
* WhateverMobile SMS Gateway.
*
*/
-@MCElement(name="whateverMobileSMSTokenProvider", topLevel=false)
+@MCElement(name="whateverMobileSMSTokenProvider", component =false)
public class WhateverMobileSMSTokenProvider extends SMSTokenProvider {
private static final Logger log = LoggerFactory.getLogger(WhateverMobileSMSTokenProvider.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java
index 26e9f748d1..445e58cedc 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/xen/XenAuthenticationInterceptor.java
@@ -106,7 +106,7 @@ public interface XenSessionManager {
String createSessionId(String xenSessionId);
}
- @MCElement(name = "inMemorySessionManager", topLevel = false)
+ @MCElement(name = "inMemorySessionManager", component = false)
public static class InMemorySessionManager implements XenSessionManager {
private final Map ourSessionIds = new ConcurrentHashMap<>();
private final Map xenSessionIds = new ConcurrentHashMap<>();
@@ -130,7 +130,7 @@ public String createSessionId(String xenSessionId) {
}
}
- @MCElement(name = "jwtSessionManager", topLevel = false, id = "xenAuthentication-jwtSessionManager")
+ @MCElement(name = "jwtSessionManager", component = false, id = "xenAuthentication-jwtSessionManager")
public static class JwtSessionManager implements XenSessionManager {
private String audience;
@@ -225,7 +225,7 @@ public void setJwk(Jwk jwk) {
this.jwk = jwk;
}
- @MCElement(name="jwk", mixed = true, topLevel = false, id="xenAuthentication-jwtSessionManager-jwk")
+ @MCElement(name="jwk", mixed = true, component = false, id="xenAuthentication-jwtSessionManager-jwk")
public static class Jwk extends Blob {
}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Balancer.java b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Balancer.java
index 293bbea953..1e5b09b0a0 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Balancer.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Balancer.java
@@ -20,7 +20,7 @@
import java.util.*;
-@MCElement(name="clusters", topLevel=false, noEnvelope = true)
+@MCElement(name="clusters", component =false, noEnvelope = true)
public class Balancer extends AbstractXmlElement {
public static final String DEFAULT_NAME = "Default";
private static final Logger log = LoggerFactory.getLogger(Balancer.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Cluster.java b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Cluster.java
index f7d76f7314..4feaa442fb 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Cluster.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Cluster.java
@@ -25,7 +25,7 @@
* @description Represents a load-balancing cluster (a named group of {@link Node}s).
* Provides status management (UP/DOWN/TAKEOUT), node lookup, and simple session tracking.
*/
-@MCElement(name="cluster", topLevel=false)
+@MCElement(name="cluster", component =false)
public class Cluster {
private static final Logger log = LoggerFactory.getLogger(Cluster.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Node.java b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Node.java
index 63b33f203a..652edfe8ee 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Node.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/balancer/Node.java
@@ -34,7 +34,7 @@
* @description Represents a backend node in a load-balancing Cluster.
*
Identity is host+port.
*/
-@MCElement(name="node", topLevel=false)
+@MCElement(name="node", component =false)
public class Node extends AbstractXmlElement {
public enum Status {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/AbortInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/AbortInterceptor.java
index ddde447367..52ea5751e0 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/AbortInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/AbortInterceptor.java
@@ -29,7 +29,7 @@
* allow normal processing.
* @topic 1. Proxies and Flow
*/
-@MCElement(name="abort", topLevel=false, noEnvelope = true)
+@MCElement(name="abort", component =false, noEnvelope = true)
public class AbortInterceptor extends AbstractFlowWithChildrenInterceptor {
private static final Logger log = LoggerFactory.getLogger(AbortInterceptor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/RequestInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/RequestInterceptor.java
index a295eb553d..74ec812ad2 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/RequestInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/RequestInterceptor.java
@@ -28,7 +28,7 @@
* <request> Element you can limit their application to requests only.
* @topic 1. Proxies and Flow
*/
-@MCElement(name = "request", topLevel = false, noEnvelope = true)
+@MCElement(name = "request", component = false, noEnvelope = true)
public class RequestInterceptor extends AbstractFlowWithChildrenInterceptor {
private static final Logger log = LoggerFactory.getLogger(RequestInterceptor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ResponseInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ResponseInterceptor.java
index b6a34279cd..c6ec24f2d7 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ResponseInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ResponseInterceptor.java
@@ -25,7 +25,7 @@
* <response> plugin you can limit their application to responses only.
* @topic 1. Proxies and Flow
*/
-@MCElement(name = "response", topLevel = false, noEnvelope = true)
+@MCElement(name = "response", component = false, noEnvelope = true)
public class ResponseInterceptor extends AbstractFlowWithChildrenInterceptor {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java
index 58cd7dc1d3..696595d495 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Case.java
@@ -26,7 +26,7 @@
import static com.predic8.membrane.core.lang.ExchangeExpression.Language.*;
import static com.predic8.membrane.core.lang.ExchangeExpression.expression;
-@MCElement(name = "case", topLevel = false)
+@MCElement(name = "case", component = false)
public class Case extends InterceptorContainer implements XMLSupport {
private static final Logger log = LoggerFactory.getLogger(Case.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Otherwise.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Otherwise.java
index 4ec437c5c6..6f7f41aa0b 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Otherwise.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/choice/Otherwise.java
@@ -15,5 +15,5 @@
import com.predic8.membrane.annot.MCElement;
-@MCElement(name = "otherwise", topLevel = false, noEnvelope = true)
+@MCElement(name = "otherwise", component = false, noEnvelope = true)
public class Otherwise extends InterceptorContainer {}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/formvalidation/FormValidationInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/formvalidation/FormValidationInterceptor.java
index e18b1db8db..3d5b013879 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/formvalidation/FormValidationInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/formvalidation/FormValidationInterceptor.java
@@ -37,7 +37,7 @@
@MCElement(name="formValidation")
public class FormValidationInterceptor extends AbstractInterceptor {
- @MCElement(name="field", topLevel=false, id="formValidation-field")
+ @MCElement(name="field", component =false, id="formValidation-field")
public static class Field extends AbstractXmlElement {
public String name;
public String regex;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/grease/strategies/JsonGrease.java b/core/src/main/java/com/predic8/membrane/core/interceptor/grease/strategies/JsonGrease.java
index 83b554a6de..9d89dfc5ba 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/grease/strategies/JsonGrease.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/grease/strategies/JsonGrease.java
@@ -25,7 +25,7 @@
import static com.predic8.membrane.core.http.MimeType.isJson;
-@MCElement(name = "greaseJson", topLevel = false)
+@MCElement(name = "greaseJson", component = false)
public class JsonGrease extends Greaser {
private static final ObjectMapper om = new ObjectMapper();
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/groovy/GroovyTemplateInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/groovy/GroovyTemplateInterceptor.java
index 900593df7e..dda4212813 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/groovy/GroovyTemplateInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/groovy/GroovyTemplateInterceptor.java
@@ -46,7 +46,7 @@
*
* @topic 2. Enterprise Integration Patterns
*/
-@MCElement(name = "groovyTemplate", mixed = true)
+@MCElement(name = "groovyTemplate", mixed = true, excludeFromFlow = true)
public class GroovyTemplateInterceptor extends AbstractInterceptor {
String src = "";
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/jwt/Jwks.java b/core/src/main/java/com/predic8/membrane/core/interceptor/jwt/Jwks.java
index 940ed1868e..42a504ce0a 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/jwt/Jwks.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/jwt/Jwks.java
@@ -91,7 +91,7 @@ public void setAuthorizationService(AuthorizationService authService) {
authorizationService = authService;
}
- @MCElement(name="jwk", mixed = true, topLevel = false, id="jwks-jwk")
+ @MCElement(name="jwk", mixed = true, component = false, id="jwks-jwk")
public static class Jwk extends Blob {
String kid;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/kubernetes/KubernetesValidationInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/kubernetes/KubernetesValidationInterceptor.java
index ed3c86f9a6..9349b95bf5 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/kubernetes/KubernetesValidationInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/kubernetes/KubernetesValidationInterceptor.java
@@ -142,7 +142,7 @@
* host: thomas-bayer.com
*
*/
-@MCElement(name = "kubernetesValidation")
+@MCElement(name = "kubernetesValidation", excludeFromFlow = true)
public class KubernetesValidationInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(KubernetesValidationInterceptor.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/log/AdditionalVariable.java b/core/src/main/java/com/predic8/membrane/core/interceptor/log/AdditionalVariable.java
index 0110f4f8bf..fb88a5008b 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/log/AdditionalVariable.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/log/AdditionalVariable.java
@@ -23,7 +23,7 @@
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
-@MCElement(name = "additionalVariable", topLevel = false, id = "accessLog-scope")
+@MCElement(name = "additionalVariable", component = false, id = "accessLog-scope")
public class AdditionalVariable {
private final SpelParserConfiguration spelConfig = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, this.getClass().getClassLoader());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/ntlm/HeaderNTLMRetriever.java b/core/src/main/java/com/predic8/membrane/core/interceptor/ntlm/HeaderNTLMRetriever.java
index e560d42f83..e868443d64 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/ntlm/HeaderNTLMRetriever.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/ntlm/HeaderNTLMRetriever.java
@@ -17,7 +17,7 @@
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.exchange.Exchange;
-@MCElement(name = "headerRetriever", topLevel = false)
+@MCElement(name = "headerRetriever", component = false)
public class HeaderNTLMRetriever implements NTLMRetriever {
String userHeaderName;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClaimList.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClaimList.java
index 834d74553a..685112ae31 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClaimList.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/ClaimList.java
@@ -33,7 +33,7 @@ public void setSupportedClaims(HashSet supportedClaims) {
this.supportedClaims = supportedClaims;
}
- @MCElement(name="scope", topLevel=false, id="claims-scope")
+ @MCElement(name="scope", component =false, id="claims-scope")
public static class Scope{
private String id;
private String claims;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/Client.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/Client.java
index 66e89eb3cc..20088b09e3 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/Client.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/Client.java
@@ -17,7 +17,7 @@
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.annot.Required;
-@MCElement(name="client", topLevel=false, id="staticClientList-client")
+@MCElement(name="client", component =false, id="staticClientList-client")
public class Client {
private String clientId;
private String clientSecret;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GithubAuthorizationService.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GithubAuthorizationService.java
index 4230972779..17e98c7e7b 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GithubAuthorizationService.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GithubAuthorizationService.java
@@ -15,7 +15,7 @@
import com.predic8.membrane.annot.MCElement;
-@MCElement(name="github", topLevel=false)
+@MCElement(name="github", component =false)
public class GithubAuthorizationService extends AuthorizationService {
@Override
public void init() {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GoogleAuthorizationService.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GoogleAuthorizationService.java
index 4aba9c7f76..b89bd121c2 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GoogleAuthorizationService.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/authorizationservice/GoogleAuthorizationService.java
@@ -15,7 +15,7 @@
import com.predic8.membrane.annot.MCElement;
-@MCElement(name="google", topLevel=false)
+@MCElement(name="google", component =false)
public class GoogleAuthorizationService extends AuthorizationService {
@Override
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
index 1a1e865584..9aaab05118 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/tokengenerators/BearerJwtTokenGenerator.java
@@ -172,7 +172,7 @@ public void setJwk(JwtSessionManager.Jwk jwk) {
this.jwk = jwk;
}
- @MCElement(name="jwk", mixed = true, topLevel = false, id="bearerJwtToken-jwk")
+ @MCElement(name="jwk", mixed = true, component = false, id="bearerJwtToken-jwk")
public static class Jwk extends Blob {
}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/FlowInitiator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/FlowInitiator.java
index ae172da40c..100a2e2bcb 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/FlowInitiator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/FlowInitiator.java
@@ -29,7 +29,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.*;
import static java.nio.charset.StandardCharsets.UTF_8;
-@MCElement(name = "flowInitiator")
+@MCElement(name = "flowInitiator", excludeFromFlow = true)
public class FlowInitiator extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(FlowInitiator.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/OAuth2PermissionCheckerInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/OAuth2PermissionCheckerInterceptor.java
index b95744e7a8..fed1ee8ef6 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/OAuth2PermissionCheckerInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2client/OAuth2PermissionCheckerInterceptor.java
@@ -85,7 +85,7 @@ public static abstract class ValueSource {
public abstract Object evaluate(Exchange exc);
}
- @MCElement(topLevel = false, name = "userInfo")
+ @MCElement(component = false, name = "userInfo")
public static class UserInfoValueSource extends ValueSource {
String field;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2server/Claim.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2server/Claim.java
index 515cdc45c4..e31e9a0e65 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2server/Claim.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2server/Claim.java
@@ -15,7 +15,7 @@
import com.predic8.membrane.annot.MCElement;
-@MCElement(name="claim", topLevel=false, id="supportedClaims-claim")
+@MCElement(name="claim", component =false, id="supportedClaims-claim")
public class Claim {
String claimName;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/OpenTelemetryInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/OpenTelemetryInterceptor.java
index 0d93e52371..0759c7d81f 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/OpenTelemetryInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/OpenTelemetryInterceptor.java
@@ -263,7 +263,7 @@ public double getSampleRate() {
return sampleRate;
}
- @MCElement(name = "customAttribute", topLevel = false)
+ @MCElement(name = "customAttribute", component = false)
public static class CustomAttribute {
private String name;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/exporter/OtlpExporter.java b/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/exporter/OtlpExporter.java
index 91a1e3d3a3..26cf0d0549 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/exporter/OtlpExporter.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/opentelemetry/exporter/OtlpExporter.java
@@ -33,7 +33,7 @@
/*
* Otlp Implementation for the OpenTelemetry protocol
*/
-@MCElement(name = "otlpExporter", topLevel = false)
+@MCElement(name = "otlpExporter", component = false)
public class OtlpExporter implements OtelExporter {
private static final int TIMEOUT_SECONDS = 30;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/registration/RegistrationInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/registration/RegistrationInterceptor.java
index 7a33d23d3b..3193cad33c 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/registration/RegistrationInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/registration/RegistrationInterceptor.java
@@ -32,7 +32,7 @@
/**
* @description Allows account registration (!Experimental!)
*/
-@MCElement(name = "accountRegistration")
+@MCElement(name = "accountRegistration", excludeFromFlow = true)
public class RegistrationInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(RegistrationInterceptor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java
index 0c2740e681..9c4c8445b6 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java
@@ -236,7 +236,7 @@ public String getShortDescription() {
return "Transforms REST requests into SOAP and responses vice versa.";
}
- @MCElement(name = "mapping", topLevel = false, id = "rest2Soap-mapping")
+ @MCElement(name = "mapping", component = false, id = "rest2Soap-mapping")
public static class Mapping extends AbstractXmlElement {
public String regex;
public String soapAction;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java
index b48a0780a6..c732689f6f 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java
@@ -39,7 +39,7 @@
*
* @topic 6. Misc
*/
-@MCElement(name = "rewriter", noEnvelope = true, topLevel = false)
+@MCElement(name = "rewriter", noEnvelope = true, component = false)
public class RewriteInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(RewriteInterceptor.class.getName());
@@ -50,7 +50,7 @@ public enum Type {
REDIRECT_PERMANENT,
}
- @MCElement(name = "map", topLevel = false, id = "rewriter-map")
+ @MCElement(name = "map", component = false, id = "rewriter-map")
public static class Mapping {
public String to;
public String from;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/json/JSONYAMLSchemaValidator.java b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/json/JSONYAMLSchemaValidator.java
index de3de379f2..1c4ee4218e 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/json/JSONYAMLSchemaValidator.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/schemavalidation/json/JSONYAMLSchemaValidator.java
@@ -14,15 +14,12 @@
package com.predic8.membrane.core.interceptor.schemavalidation.json;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
-import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.networknt.schema.*;
import com.networknt.schema.Error;
import com.networknt.schema.path.NodePath;
-import com.networknt.schema.serialization.YamlMapperFactory;
import com.predic8.membrane.core.exchange.*;
import com.predic8.membrane.core.interceptor.Interceptor.*;
import com.predic8.membrane.core.interceptor.*;
@@ -35,9 +32,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.*;
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java b/core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java
index f36a699554..d6f9b2115c 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/session/JwtSessionManager.java
@@ -232,7 +232,7 @@ public void setJwk(Jwk jwk) {
this.jwk = jwk;
}
- @MCElement(name = "jwk", mixed = true, topLevel = false, id = "jwtSessionManager-jwk")
+ @MCElement(name = "jwk", mixed = true, component = false, id = "jwtSessionManager-jwk")
public static class Jwk extends Blob {
}
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/tunnel/TCPInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/tunnel/TCPInterceptor.java
index 8d15840da0..197ec3b152 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/tunnel/TCPInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/tunnel/TCPInterceptor.java
@@ -25,7 +25,7 @@
* and not inspected.
* @default false
*/
-@MCElement(name = "tcp")
+@MCElement(name = "tcp", excludeFromFlow = true)
public class TCPInterceptor extends AbstractInterceptor {
public TCPInterceptor() {
diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/ws_addressing/WsaEndpointRewriterInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/ws_addressing/WsaEndpointRewriterInterceptor.java
index 3e887c8bd9..1b6dddd38d 100644
--- a/core/src/main/java/com/predic8/membrane/core/interceptor/ws_addressing/WsaEndpointRewriterInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/interceptor/ws_addressing/WsaEndpointRewriterInterceptor.java
@@ -27,7 +27,7 @@
import static com.predic8.membrane.core.interceptor.Outcome.ABORT;
import static com.predic8.membrane.core.interceptor.Outcome.*;
-@MCElement(name="wsaEndpointRewriter")
+@MCElement(name="wsaEndpointRewriter", excludeFromFlow = true)
public class WsaEndpointRewriterInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(WsaEndpointRewriterInterceptor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/kubernetes/Bean.java b/core/src/main/java/com/predic8/membrane/core/kubernetes/Bean.java
deleted file mode 100644
index c815380174..0000000000
--- a/core/src/main/java/com/predic8/membrane/core/kubernetes/Bean.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2022 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.core.kubernetes;
-
-import com.predic8.membrane.annot.MCChildElement;
-import com.predic8.membrane.annot.MCElement;
-
-/**
- * @description
- * "bean" should be used for Kubernetes only. Experimental.
- */
-@MCElement(name = "bean")
-public class Bean {
-
- Object bean;
-
- public Object getBean() {
- return bean;
- }
-
- @MCChildElement
- public void setBean(Object bean) {
- this.bean = bean;
- }
-}
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
index fe74f99f56..9d12344f8e 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/APIProxy.java
@@ -44,7 +44,7 @@
* @description The api proxy extends the serviceProxy with API related functions like OpenAPI support and path parameters.
* @topic 1. Proxies and Flow
*/
-@MCElement(name = "api")
+@MCElement(name = "api", topLevel = true)
public class APIProxy extends ServiceProxy implements Polyglot, XMLSupport {
private static final Logger log = LoggerFactory.getLogger(APIProxy.class.getName());
@@ -258,7 +258,7 @@ public void setLanguage(Language language) {
this.language = language;
}
- @MCElement(name = "description", topLevel = false, mixed = true)
+ @MCElement(name = "description", component = false, mixed = true)
public static class ApiDescription {
private String content;
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptor.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptor.java
index 348584cc05..9ed2b64c78 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPIInterceptor.java
@@ -22,7 +22,6 @@
import com.predic8.membrane.core.interceptor.*;
import com.predic8.membrane.core.openapi.*;
import com.predic8.membrane.core.openapi.validators.*;
-import com.predic8.membrane.core.proxies.Proxy;
import com.predic8.membrane.core.proxies.*;
import com.predic8.membrane.core.util.ConfigurationException;
import io.swagger.v3.oas.models.*;
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPISpec.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPISpec.java
index f270bb68a1..0a287cabee 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPISpec.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/OpenAPISpec.java
@@ -16,7 +16,6 @@
package com.predic8.membrane.core.openapi.serviceproxy;
-import com.fasterxml.jackson.annotation.*;
import com.predic8.membrane.annot.*;
import static com.predic8.membrane.core.openapi.serviceproxy.OpenAPISpec.YesNoOpenAPIOption.ASINOPENAPI;
@@ -24,7 +23,7 @@
/**
* @description Reads an OpenAPI description and deploys an API with the information of it.
*/
-@MCElement(name = "openapi", topLevel = false)
+@MCElement(name = "openapi", component = false)
public class OpenAPISpec implements Cloneable {
public String location;
diff --git a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/Rewrite.java b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/Rewrite.java
index 6f8957d69c..cb1032492a 100644
--- a/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/Rewrite.java
+++ b/core/src/main/java/com/predic8/membrane/core/openapi/serviceproxy/Rewrite.java
@@ -34,7 +34,7 @@
/**
* @description
*/
-@MCElement(name = "rewrite", topLevel = false)
+@MCElement(name = "rewrite", component = false)
public class Rewrite implements Cloneable {
private static final Logger log = LoggerFactory.getLogger(Rewrite.class.getName());
diff --git a/core/src/main/java/com/predic8/membrane/core/proxies/AbstractServiceProxy.java b/core/src/main/java/com/predic8/membrane/core/proxies/AbstractServiceProxy.java
index d5d027dca6..44ffb320e9 100644
--- a/core/src/main/java/com/predic8/membrane/core/proxies/AbstractServiceProxy.java
+++ b/core/src/main/java/com/predic8/membrane/core/proxies/AbstractServiceProxy.java
@@ -96,7 +96,7 @@ public void setPath(Path path) {
* Supports dynamic destinations through expressions.
*
*/
- @MCElement(name = "target", topLevel = false)
+ @MCElement(name = "target", component = false)
public static class Target implements XMLSupport {
private String host;
private int port = -1;
diff --git a/core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java b/core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java
index b92e7347ac..69f0c21905 100644
--- a/core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java
+++ b/core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java
@@ -57,7 +57,7 @@ public class SSLProxy implements Proxy {
private boolean useAsDefault = true;
private List sslInterceptors = new ArrayList<>();
- @MCElement(id = "sslProxy-target", name="target", topLevel = false)
+ @MCElement(id = "sslProxy-target", name="target", component = false)
public static class Target {
private int port = -1;
private String host;
diff --git a/core/src/main/java/com/predic8/membrane/core/sslinterceptor/GateKeeperClientInterceptor.java b/core/src/main/java/com/predic8/membrane/core/sslinterceptor/GateKeeperClientInterceptor.java
index 44563f58cc..1fb3aeadd7 100644
--- a/core/src/main/java/com/predic8/membrane/core/sslinterceptor/GateKeeperClientInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/sslinterceptor/GateKeeperClientInterceptor.java
@@ -18,8 +18,6 @@
import com.google.common.collect.*;
import com.predic8.membrane.annot.*;
import com.predic8.membrane.core.*;
-import com.predic8.membrane.core.exchange.*;
-import com.predic8.membrane.core.http.*;
import com.predic8.membrane.core.interceptor.*;
import com.predic8.membrane.core.transport.http.*;
import com.predic8.membrane.core.transport.http.client.*;
@@ -34,7 +32,7 @@
/**
* Connects to the predic8 Gatekeeper to check, whether access is allowed or not.
*/
-@MCElement(id = "sslProxy-gatekeeper", name = "gatekeeper", topLevel = false)
+@MCElement(id = "sslProxy-gatekeeper", name = "gatekeeper", component = false)
public class GateKeeperClientInterceptor implements SSLInterceptor {
protected final String name;
diff --git a/core/src/main/java/com/predic8/membrane/core/sslinterceptor/RouterIpResolverInterceptor.java b/core/src/main/java/com/predic8/membrane/core/sslinterceptor/RouterIpResolverInterceptor.java
index cff7e51d5d..473a9e1cd8 100644
--- a/core/src/main/java/com/predic8/membrane/core/sslinterceptor/RouterIpResolverInterceptor.java
+++ b/core/src/main/java/com/predic8/membrane/core/sslinterceptor/RouterIpResolverInterceptor.java
@@ -46,7 +46,7 @@
*
* This interceptor is helpful in scenarios with multiple redundant routers for inbound HTTP requests.
*/
-@MCElement(id = "sslProxy-routerIpResolver", name = "routerIpResolver", topLevel = false)
+@MCElement(id = "sslProxy-routerIpResolver", name = "routerIpResolver", component = false)
public class RouterIpResolverInterceptor implements SSLInterceptor {
private final Logger log = LoggerFactory.getLogger(RouterIpResolverInterceptor.class);
diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/client/AuthenticationConfiguration.java b/core/src/main/java/com/predic8/membrane/core/transport/http/client/AuthenticationConfiguration.java
index 04bf586fd4..bc0318606d 100644
--- a/core/src/main/java/com/predic8/membrane/core/transport/http/client/AuthenticationConfiguration.java
+++ b/core/src/main/java/com/predic8/membrane/core/transport/http/client/AuthenticationConfiguration.java
@@ -32,7 +32,7 @@
*
* @topic 4. Transports and Clients
*/
-@MCElement(name="authentication", topLevel=false)
+@MCElement(name="authentication", component =false)
public class AuthenticationConfiguration {
private String username;
diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/client/ConnectionConfiguration.java b/core/src/main/java/com/predic8/membrane/core/transport/http/client/ConnectionConfiguration.java
index 6634de41ba..478aa4d3cc 100644
--- a/core/src/main/java/com/predic8/membrane/core/transport/http/client/ConnectionConfiguration.java
+++ b/core/src/main/java/com/predic8/membrane/core/transport/http/client/ConnectionConfiguration.java
@@ -35,7 +35,7 @@
*
* @topic 4. Transports and Clients
*/
-@MCElement(name="connection", topLevel=false)
+@MCElement(name="connection", component =false)
public class ConnectionConfiguration {
private long keepAliveTimeout = 4000;
diff --git a/core/src/main/java/com/predic8/membrane/core/transport/http/client/ProxyConfiguration.java b/core/src/main/java/com/predic8/membrane/core/transport/http/client/ProxyConfiguration.java
index 7c0e05a2d7..51693fc2d1 100644
--- a/core/src/main/java/com/predic8/membrane/core/transport/http/client/ProxyConfiguration.java
+++ b/core/src/main/java/com/predic8/membrane/core/transport/http/client/ProxyConfiguration.java
@@ -45,7 +45,7 @@
*
* @topic 4. Transports and Clients
*/
-@MCElement(name="proxy", topLevel=false, id="proxy-configuration")
+@MCElement(name="proxy", component =false, id="proxy-configuration")
public class ProxyConfiguration {
private String host;
diff --git a/core/src/main/java/com/predic8/membrane/core/util/RedisConnector.java b/core/src/main/java/com/predic8/membrane/core/util/RedisConnector.java
index bd50168f97..c1491b7d97 100644
--- a/core/src/main/java/com/predic8/membrane/core/util/RedisConnector.java
+++ b/core/src/main/java/com/predic8/membrane/core/util/RedisConnector.java
@@ -24,7 +24,7 @@
import java.util.HashSet;
import java.util.Set;
-@MCElement(name = "redis", topLevel = true)
+@MCElement(name = "redis", component = true)
public class RedisConnector implements InitializingBean {
private JedisPool pool;
private JedisSentinelPool sentinelPool;
diff --git a/distribution/examples/extending-membrane/custom-interceptor/README.md b/distribution/examples/extending-membrane/custom-interceptor/README.md
index cbfea762c1..12f23dbeac 100644
--- a/distribution/examples/extending-membrane/custom-interceptor/README.md
+++ b/distribution/examples/extending-membrane/custom-interceptor/README.md
@@ -28,20 +28,24 @@ To run the example execute the following steps:
Using maven, we create a jar file and copy the compiled jar file into the libs directory of membrane to make the new interceptor available to the router.
-In the proxies.xml file, we define a name for our interceptor and write its fully qualified name, so we can use our interceptor on . You can see it in the line below.
+In the apis.yaml file, we define a name for our interceptor and write its fully qualified name, so we can use our interceptor in the flow:
-```
-
+```yaml
+components:
+ myInterceptor:
+ bean:
+ class: com.predic8.MyInterceptor
```
-Again in the proxies.xml file inside `` tag you can see that we added the interceptor using the beanname we defined above.
+Again in the apis.yaml file inside `api` you can see that we added the interceptor using the beanname we defined above.
-```
-
-
-
-
+```yaml
+api:
+ port: 2000
+ flow:
+ - $ref: "#/components/myInterceptor"
+ - return: {}
```
When we run the membrane using membrane.sh you can see that in the console that requests and responses are being intercepted by our custom interceptor.
diff --git a/distribution/examples/extending-membrane/custom-interceptor/apis.yaml b/distribution/examples/extending-membrane/custom-interceptor/apis.yaml
new file mode 100644
index 0000000000..f2a48f844e
--- /dev/null
+++ b/distribution/examples/extending-membrane/custom-interceptor/apis.yaml
@@ -0,0 +1,12 @@
+components:
+ myInterceptor:
+ bean:
+ class: com.predic8.MyInterceptor
+
+---
+
+api:
+ port: 2000
+ flow:
+ - $ref: "#/components/myInterceptor"
+ - return: {}
\ No newline at end of file
diff --git a/distribution/src/test/java/com/predic8/membrane/examples/withoutinternet/OfflineExampleTest.java b/distribution/src/test/java/com/predic8/membrane/examples/withoutinternet/OfflineExampleTest.java
index 2f81d3ff3f..d6edbf6923 100644
--- a/distribution/src/test/java/com/predic8/membrane/examples/withoutinternet/OfflineExampleTest.java
+++ b/distribution/src/test/java/com/predic8/membrane/examples/withoutinternet/OfflineExampleTest.java
@@ -1,3 +1,17 @@
+/* Copyright 2025 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.examples.withoutinternet;
import com.predic8.membrane.examples.util.AbstractSampleMembraneStartStopTestcase;
diff --git a/docs/JAVADOC.md b/docs/JAVADOC.md
index 636cd029bd..9df73c71a8 100644
--- a/docs/JAVADOC.md
+++ b/docs/JAVADOC.md
@@ -82,9 +82,9 @@ Therefore, while most of the project historically used XML, everything can be ex
`@MCElement` can only be used on classes. If you write `` or `foo:` in the configuration, this will cause the Java Class to be instantiated at startup.
- If `@MCElement(name="foo",topLevel=true)` has `topLevel==true` (which it has by default), ``/`foo:` can be used as the top-level element in the configuration. The Foo instance will be created as a Spring Bean (in the XML case) and take part of its life cycle. The most basic example is using ``/`router:` at the top level: This will create and start a `Router` instance which starts Membrane API Gateway.
+ If `@MCElement(name="foo",component=true)` has `component==true` (which it has by default), ``/`foo:` can be used as a component element in the configuration. The Foo instance will be created as a Spring Bean (in the XML case) and take part of its life cycle. The most basic example is using ``/`router:` at the top level: This will create and start a `Router` instance which starts Membrane API Gateway.
- Top-Level elements can carry a unique `id` attribute. This will register the instance Spring Bean with this ID.
+ Component elements can carry a unique `id` attribute. This will register the instance Spring Bean with this ID.
* **`@MCAttribute`**
Can be used on Setter methods. The Java Type of the Setter's parameter must be a simple type, enum, String or a `@MCElement` annotated class.