Iterate over models in SchemasStream 22/110122/6
authorlubos-cicut <lubos.cicut@pantheon.tech>
Tue, 6 Feb 2024 09:43:15 +0000 (10:43 +0100)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Wed, 7 Feb 2024 12:54:28 +0000 (12:54 +0000)
Use originally intended iterative approach to get next chunk of
schemas (for one model) in SchemasStream.

JIRA: NETCONF-1242
Change-Id: I205f817a83fa957535b23591a943aa7775a2cb7d
Signed-off-by: lubos-cicut <lubos.cicut@pantheon.tech>
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/ComponentsStream.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/SchemaStream.java [new file with mode: 0644]
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/SchemasStream.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/OpenApiEntity.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/SchemasEntity.java [deleted file]

index a9e236e2693799170d0e39727f1873af05bca751..064901cfe1848884c50ff91e919c443192ac88a5 100644 (file)
@@ -64,8 +64,8 @@ public final class ComponentsStream extends InputStream {
         var read = reader.read();
         while (read == -1) {
             if (!schemasWritten) {
-                reader = new InputStreamReader(new SchemasStream(context, writer, iterator, isForSingleModule),
-                    StandardCharsets.UTF_8);
+                reader = new InputStreamReader(new SchemasStream(context, writer, iterator, isForSingleModule, stream,
+                    generator), StandardCharsets.UTF_8);
                 read = reader.read();
                 schemasWritten = true;
                 continue;
@@ -98,7 +98,8 @@ public final class ComponentsStream extends InputStream {
         var read = channel.read(ByteBuffer.wrap(array, off, len));
         while (read == -1) {
             if (!schemasWritten) {
-                channel = Channels.newChannel(new SchemasStream(context, writer, iterator, isForSingleModule));
+                channel = Channels.newChannel(new SchemasStream(context, writer, iterator, isForSingleModule, stream,
+                    generator));
                 read = channel.read(ByteBuffer.wrap(array, off, len));
                 schemasWritten = true;
                 continue;
diff --git a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/SchemaStream.java b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/SchemaStream.java
new file mode 100644 (file)
index 0000000..dc75c69
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.openapi.impl;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.Deque;
+import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
+import org.opendaylight.restconf.openapi.model.OpenApiEntity;
+import org.opendaylight.restconf.openapi.model.SchemaEntity;
+
+public final class SchemaStream extends InputStream {
+    private final Deque<SchemaEntity> stack;
+    private final OpenApiBodyWriter writer;
+
+    private Reader reader;
+    private ReadableByteChannel channel;
+
+    public SchemaStream(final Deque<SchemaEntity> schemas, final OpenApiBodyWriter writer) {
+        this.stack = schemas;
+        this.writer = writer;
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (reader == null) {
+            if (stack.isEmpty()) {
+                return -1;
+            }
+            reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(writeNextEntity(stack.pop())),
+                StandardCharsets.UTF_8));
+        }
+
+        var read = reader.read();
+        while (read == -1) {
+            if (stack.isEmpty()) {
+                return -1;
+            }
+            reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(writeNextEntity(stack.pop())),
+                StandardCharsets.UTF_8));
+            read = reader.read();
+        }
+
+        return read;
+    }
+
+    @Override
+    public int read(final byte[] array, final int off, final int len) throws IOException {
+        if (channel == null) {
+            if (stack.isEmpty()) {
+                return -1;
+            }
+            channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(stack.pop())));
+        }
+
+        var read = channel.read(ByteBuffer.wrap(array, off, len));
+        while (read == -1) {
+            if (stack.isEmpty()) {
+                return -1;
+            }
+            channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(stack.pop())));
+            read = channel.read(ByteBuffer.wrap(array, off, len));
+        }
+
+        return read;
+    }
+
+    private byte[] writeNextEntity(final OpenApiEntity entity) throws IOException {
+        writer.writeTo(entity, null, null, null, null, null, null);
+        return writer.readFrom();
+    }
+}
index ff5d0d69bd6637cce43fa85809b40381c2867bfd..080348ef67247ac032f9ce4a81bee84fbd38b8b6 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.restconf.openapi.impl;
 
+import com.fasterxml.jackson.core.JsonGenerator;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -23,9 +25,7 @@ import java.util.Deque;
 import java.util.Iterator;
 import java.util.List;
 import org.opendaylight.restconf.openapi.jaxrs.OpenApiBodyWriter;
-import org.opendaylight.restconf.openapi.model.OpenApiEntity;
 import org.opendaylight.restconf.openapi.model.SchemaEntity;
-import org.opendaylight.restconf.openapi.model.SchemasEntity;
 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -45,95 +45,134 @@ public final class SchemasStream extends InputStream {
     private final OpenApiBodyWriter writer;
     private final EffectiveModelContext context;
     private final boolean isForSingleModule;
+    private final ByteArrayOutputStream stream;
+    private final JsonGenerator generator;
 
     private Reader reader;
     private ReadableByteChannel channel;
+    private boolean eof;
 
     public SchemasStream(final EffectiveModelContext context, final OpenApiBodyWriter writer,
-            final Iterator<? extends Module> iterator, final boolean isForSingleModule) {
+            final Iterator<? extends Module> iterator, final boolean isForSingleModule,
+            final ByteArrayOutputStream stream, final JsonGenerator generator) {
         this.iterator = iterator;
         this.context = context;
         this.writer = writer;
         this.isForSingleModule = isForSingleModule;
+        this.stream = stream;
+        this.generator = generator;
     }
 
     @Override
     public int read() throws IOException {
+        if (eof) {
+            return -1;
+        }
         if (reader == null) {
+            generator.writeObjectFieldStart("schemas");
+            generator.flush();
             reader = new BufferedReader(
-                new InputStreamReader(new ByteArrayInputStream(writeNextEntity(
-                    new SchemasEntity(toComponents(iterator, context, isForSingleModule)))),
-                    StandardCharsets.UTF_8));
+                new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8));
+            stream.reset();
         }
-        return reader.read();
+        var read = reader.read();
+        while (read == -1) {
+            if (iterator.hasNext()) {
+                reader = new BufferedReader(new InputStreamReader(
+                    new SchemaStream(toComponents(iterator.next(), context, isForSingleModule), writer),
+                        StandardCharsets.UTF_8));
+                read = reader.read();
+                continue;
+            }
+            generator.writeEndObject();
+            generator.flush();
+            reader = new BufferedReader(
+                new InputStreamReader(new ByteArrayInputStream(stream.toByteArray()), StandardCharsets.UTF_8));
+            stream.reset();
+            eof = true;
+            return reader.read();
+        }
+        return read;
     }
 
     @Override
     public int read(final byte[] array, final int off, final int len) throws IOException {
+        if (eof) {
+            return -1;
+        }
         if (channel == null) {
-            channel = Channels.newChannel(new ByteArrayInputStream(writeNextEntity(
-                new SchemasEntity(toComponents(iterator, context, isForSingleModule)))));
+            generator.writeObjectFieldStart("schemas");
+            generator.flush();
+            channel = Channels.newChannel(new ByteArrayInputStream(stream.toByteArray()));
+            stream.reset();
         }
-        return channel.read(ByteBuffer.wrap(array, off, len));
-    }
-
-    private byte[] writeNextEntity(final OpenApiEntity next) throws IOException {
-        writer.writeTo(next, null, null, null, null, null, null);
-        return writer.readFrom();
+        var read = channel.read(ByteBuffer.wrap(array, off, len));
+        while (read == -1) {
+            if (iterator.hasNext()) {
+                channel = Channels.newChannel(
+                    new SchemaStream(toComponents(iterator.next(), context, isForSingleModule), writer));
+                read = channel.read(ByteBuffer.wrap(array, off, len));
+                continue;
+            }
+            generator.writeEndObject();
+            generator.flush();
+            channel = Channels.newChannel(new ByteArrayInputStream(stream.toByteArray()));
+            stream.reset();
+            eof = true;
+            return channel.read(ByteBuffer.wrap(array, off, len));
+        }
+        return read;
     }
 
-    private static Deque<SchemaEntity> toComponents(final Iterator<? extends Module> iterator,
-            final EffectiveModelContext context, final boolean isForSingleModule) {
+    private static Deque<SchemaEntity> toComponents(final Module module, final EffectiveModelContext context,
+            final boolean isForSingleModule) {
         final var result = new ArrayDeque<SchemaEntity>();
-        while (iterator.hasNext()) {
-            final var module = iterator.next();
-            final var definitionNames = new DefinitionNames();
-            final var stack = SchemaInferenceStack.of(context);
-            final var moduleName = module.getName();
-            if (isForSingleModule) {
-                definitionNames.addUnlinkedName(moduleName + "_module");
-            }
-            final var children = new ArrayList<DataSchemaNode>();
-            for (final var rpc : module.getRpcs()) {
-                stack.enterSchemaTree(rpc.getQName());
-                final var rpcName = rpc.getQName().getLocalName();
-                final var rpcInput = rpc.getInput();
-                if (!rpcInput.getChildNodes().isEmpty()) {
-                    final var input = new SchemaEntity(rpcInput, moduleName + "_" + rpcName + INPUT_SUFFIX, null,
-                        OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
-                    result.add(input);
-                    stack.enterSchemaTree(rpcInput.getQName());
-                    for (final var child : rpcInput.getChildNodes()) {
-                        if (!children.contains(child)) {
-                            children.add(child);
-                            processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
-                                false);
-                        }
+        final var definitionNames = new DefinitionNames();
+        final var stack = SchemaInferenceStack.of(context);
+        final var moduleName = module.getName();
+        if (isForSingleModule) {
+            definitionNames.addUnlinkedName(moduleName + "_module");
+        }
+        final var children = new ArrayList<DataSchemaNode>();
+        for (final var rpc : module.getRpcs()) {
+            stack.enterSchemaTree(rpc.getQName());
+            final var rpcName = rpc.getQName().getLocalName();
+            final var rpcInput = rpc.getInput();
+            if (!rpcInput.getChildNodes().isEmpty()) {
+                final var input = new SchemaEntity(rpcInput, moduleName + "_" + rpcName + INPUT_SUFFIX, null,
+                    OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
+                result.add(input);
+                stack.enterSchemaTree(rpcInput.getQName());
+                for (final var child : rpcInput.getChildNodes()) {
+                    if (!children.contains(child)) {
+                        children.add(child);
+                        processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
+                            false);
                     }
-                    stack.exit();
                 }
-                final var rpcOutput = rpc.getOutput();
-                if (!rpcOutput.getChildNodes().isEmpty()) {
-                    final var output = new SchemaEntity(rpcOutput, moduleName + "_" + rpcName + OUTPUT_SUFFIX, null,
-                        OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
-                    result.add(output);
-                    stack.enterSchemaTree(rpcOutput.getQName());
-                    for (final var child : rpcOutput.getChildNodes()) {
-                        if (!children.contains(child)) {
-                            children.add(child);
-                            processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
-                                false);
-                        }
+                stack.exit();
+            }
+            final var rpcOutput = rpc.getOutput();
+            if (!rpcOutput.getChildNodes().isEmpty()) {
+                final var output = new SchemaEntity(rpcOutput, moduleName + "_" + rpcName + OUTPUT_SUFFIX, null,
+                    OBJECT_TYPE, stack, moduleName, false, definitionNames, EntityType.RPC);
+                result.add(output);
+                stack.enterSchemaTree(rpcOutput.getQName());
+                for (final var child : rpcOutput.getChildNodes()) {
+                    if (!children.contains(child)) {
+                        children.add(child);
+                        processDataAndActionNodes(child, moduleName, stack, definitionNames, result, moduleName,
+                            false);
                     }
-                    stack.exit();
                 }
                 stack.exit();
             }
+            stack.exit();
+        }
 
-            for (final var childNode : module.getChildNodes()) {
-                processDataAndActionNodes(childNode, moduleName, stack, definitionNames, result, moduleName,
-                    true);
-            }
+        for (final var childNode : module.getChildNodes()) {
+            processDataAndActionNodes(childNode, moduleName, stack, definitionNames, result, moduleName,
+                true);
         }
         return result;
     }
index fd53bcf48c2cf25fff236d533949798f70ec5c12..6bf21595971788c881f73e5887bd42bfc23dbb95 100644 (file)
@@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNull;
  * A response entity for complex generated type.
  */
 public abstract sealed class OpenApiEntity permits ComponentsEntity, InfoEntity, OpenApiVersionEntity, OperationEntity,
-        PathEntity, SchemaEntity, SchemasEntity, SecurityEntity, SecuritySchemesEntity, ServerEntity, ServersEntity {
+        PathEntity, SchemaEntity, SecurityEntity, SecuritySchemesEntity, ServerEntity, ServersEntity {
     /**
      * Generate JSON events into specified generator.
      *
diff --git a/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/SchemasEntity.java b/restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/SchemasEntity.java
deleted file mode 100644 (file)
index 25db223..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.restconf.openapi.model;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import java.io.IOException;
-import java.util.Deque;
-
-public final class SchemasEntity extends OpenApiEntity {
-    private final Deque<SchemaEntity> schemas;
-
-    public SchemasEntity(final Deque<SchemaEntity> schemas) {
-        this.schemas = schemas;
-    }
-
-    @Override
-    public void generate(final JsonGenerator generator) throws IOException {
-        generator.writeObjectFieldStart("schemas");
-        for (final var schema : schemas) {
-            schema.generate(generator);
-        }
-        generator.writeEndObject();
-    }
-}