2020import com .networknt .schema .Schema ;
2121import com .networknt .schema .SchemaLocation ;
2222import com .networknt .schema .SchemaRegistry ;
23- import com .predic8 .membrane .annot .K8sHelperGenerator ;
23+ import com .predic8 .membrane .annot .Grammar ;
2424import org .jetbrains .annotations .NotNull ;
2525import org .slf4j .Logger ;
2626import org .slf4j .LoggerFactory ;
@@ -47,19 +47,27 @@ public class GenericYamlParser {
4747 private static final String EMPTY_DOCUMENT_WARNING = "Skipping empty document. Maybe there are two --- separators but no configuration in between." ;
4848
4949 /**
50- * Parses Membrane resources from a YAML input stream.
51- * @param resource the input stream to parse. The method takes care of closing the stream.
52- * @param generator the K8s helper generator
53- * @param observer the bean cache observer
54- * @return the bean registry
55- */
56- public static BeanRegistry parseMembraneResources (@ NotNull InputStream resource , K8sHelperGenerator generator , BeanCacheObserver observer ) throws IOException {
50+ * Entry point used by the runtime to consume a YAML stream and turn it into
51+ * a {@link BeanRegistry} that the router can work with.
52+ * <ul>
53+ * <li>Reads the entire stream as UTF-8.</li>
54+ * <li>Splits multi-document YAML ("---" separators).</li>
55+ * <li>Validates each document against the JSON Schema provided by {@code grammar}.</li>
56+ * <li>Emits helpful line/column locations for malformed multi-document input.</li>
57+ * </ul>
58+ * The returned registry is fully populated and {@link BeanCache#fireConfigurationLoaded()} has been called.
59+ * @param resource the input stream to parse. The method takes care of closing the stream.
60+ * @param grammar the grammar to use for type resolution and schema location
61+ * @param observer the bean cache observer
62+ * @return the bean registry
63+ */
64+ public static BeanRegistry parseMembraneResources (@ NotNull InputStream resource , Grammar grammar , BeanCacheObserver observer ) throws IOException {
5765 BeanCache registry ;
5866 try (resource ) {
59- registry = new BeanCache (observer , generator );
67+ registry = new BeanCache (observer , grammar );
6068 registry .start ();
6169
62- new GenericYamlParser (generator , new String (resource .readAllBytes (), UTF_8 ))
70+ new GenericYamlParser (grammar , new String (resource .readAllBytes (), UTF_8 ))
6371 .getBeanDefinitions ()
6472 .forEach (bd -> registry .handle (WatchAction .ADDED , bd ));
6573
@@ -78,7 +86,19 @@ public static BeanRegistry parseMembraneResources(@NotNull InputStream resource,
7886
7987 List <BeanDefinition > beanDefs = new ArrayList <>();
8088
81- public GenericYamlParser (K8sHelperGenerator generator , String yaml ) throws IOException {
89+ /**
90+ * Parses one or more YAML documents into bean definitions.
91+ * <p>
92+ * The input string may contain multiple YAML documents separated by '---'. Each non-empty
93+ * document is validated against the schema provided by {@link Grammar} and then
94+ * turned into a {@link BeanDefinition}. Validation errors are mapped back to line/column
95+ * numbers using {@link JsonLocationMap} to produce helpful error messages.
96+ * </p>
97+ * @param grammar provides schema location and Java type resolution
98+ * @param yaml the raw YAML content (may contain multi-document stream)
99+ * @throws IOException if schema loading or validation fails
100+ */
101+ public GenericYamlParser (Grammar grammar , String yaml ) throws IOException {
82102 JsonLocationMap jsonLocationMap = new JsonLocationMap ();
83103 List <JsonNode > rootNodes = jsonLocationMap .parseWithLocations (yaml );
84104 for (int i = 0 ; i < rootNodes .size (); i ++) {
@@ -89,7 +109,7 @@ public GenericYamlParser(K8sHelperGenerator generator, String yaml) throws IOExc
89109 continue ;
90110 }
91111 try {
92- validate (generator , rootNodes .get (i ));
112+ validate (grammar , rootNodes .get (i ));
93113 } catch (YamlSchemaValidationException e ) {
94114 JsonLocation location = jsonLocationMap .getLocationMap ().get (
95115 e .getErrors ().getFirst ().getInstanceNode ());
@@ -117,8 +137,8 @@ private static String getBeanType(JsonNode jsonNode) {
117137 return jsonNode .fieldNames ().next ();
118138 }
119139
120- public static void validate (K8sHelperGenerator generator , JsonNode input ) throws IOException , YamlSchemaValidationException {
121- Schema schema = SchemaRegistry .withDefaultDialect (DRAFT_2020_12 , builder -> {}).getSchema (SchemaLocation .of (generator .getSchemaLocation ()));
140+ public static void validate (Grammar grammar , JsonNode input ) throws IOException , YamlSchemaValidationException {
141+ Schema schema = SchemaRegistry .withDefaultDialect (DRAFT_2020_12 , builder -> {}).getSchema (SchemaLocation .of (grammar .getSchemaLocation ()));
122142 schema .initializeValidators ();
123143 List <Error > errors = schema .validate (input );
124144 if (!errors .isEmpty ()) {
@@ -127,14 +147,26 @@ public static void validate(K8sHelperGenerator generator, JsonNode input) throws
127147 }
128148
129149
130- public static Object readMembraneObject (String kind , K8sHelperGenerator generator , JsonNode node , BeanRegistry registry ) throws ParsingException {
150+ /**
151+ * Parse a top-level Membrane resource of the given {@code kind}.
152+ * <p>Ensures the node contains exactly one key (the kind), resolves the Java class via the
153+ * grammar and delegates to {@link #parse(ParsingContext, Class, JsonNode)}.</p>
154+ */
155+ public static Object readMembraneObject (String kind , Grammar grammar , JsonNode node , BeanRegistry registry ) throws ParsingException {
131156 ensureSingleKey (node );
132- Class <?> clazz = generator .getElement (kind );
157+ Class <?> clazz = grammar .getElement (kind );
133158 if (clazz == null )
134159 throw new ParsingException ("Did not find java class for kind '%s'." .formatted (kind ), node );
135- return GenericYamlParser .parse (new ParsingContext (kind , registry , generator ), clazz , node .get (kind ));
160+ return GenericYamlParser .parse (new ParsingContext (kind , registry , grammar ), clazz , node .get (kind ));
136161 }
137162
163+ /**
164+ * Creates and populates an instance of {@code clazz} from the given YAML/JSON node.
165+ * - Arrays: only valid for {@code @MCElement(noEnvelope=true)}; items are parsed and passed to the single {@code @MCChildElement} list setter.
166+ * - Objects: each field is mapped to a setter resolved by {@link MethodSetter#getMethodSetter(ParsingContext, Class, String)};
167+ * values are produced by {@link #resolveSetterValue(MethodSetter, ParsingContext, JsonNode, String)}. A top-level {@code "$ref"} injects a previously defined bean.
168+ * All failures are wrapped in a {@link ParsingException} with location information.
169+ */
138170 public static <T > T parse (ParsingContext ctx , Class <T > clazz , JsonNode node ) throws ParsingException {
139171 try {
140172 T obj = clazz .getConstructor ().newInstance ();
@@ -207,6 +239,10 @@ private static List<Object> parseListIncludingStartEvent(ParsingContext context,
207239 return res ;
208240 }
209241
242+ /**
243+ * Parses a single-item map node like { kind: {...} } by extracting the only key and
244+ * delegating to {@link #parseMapToObj(ParsingContext, JsonNode, String)}.
245+ */
210246 private static Object parseMapToObj (ParsingContext context , JsonNode node ) throws ParsingException {
211247 ensureSingleKey (node );
212248 String key = node .fieldNames ().next ();
0 commit comments