Add file-based StateStorage 25/104625/4
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 28 Feb 2023 15:18:35 +0000 (16:18 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 28 Feb 2023 16:27:36 +0000 (17:27 +0100)
For plain maven builds, backed by DefaultBuildContext, we need to deal
with the fact the context does absolutely nothing. Add a file-based
storage engine.

Since we are persiting YangToSourcesState, also guard it with a magic
integer to allow for compatibility.

JIRA: YANGTOOLS-1491
Change-Id: I2763fbddf5a485ec4220e89c5fdc97f2a11f208f
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileStateStorage.java [new file with mode: 0644]
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/StateStorage.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesState.java
plugin/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/AbstractCodeGeneratorTest.java

diff --git a/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileStateStorage.java b/plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileStateStorage.java
new file mode 100644 (file)
index 0000000..6db7c32
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A {@link StateStorage} based on a single project file.
+ */
+final class FileStateStorage extends StateStorage {
+    private final Path stateFile;
+
+    FileStateStorage(final Path stateFile) {
+        this.stateFile = requireNonNull(stateFile);
+    }
+
+    @Override
+    @Nullable YangToSourcesState loadState() throws IOException {
+        if (Files.exists(stateFile)) {
+            try (var in = new DataInputStream(Files.newInputStream(stateFile))) {
+                return YangToSourcesState.readFrom(in);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    void storeState(final YangToSourcesState state) throws IOException {
+        Files.createDirectories(stateFile.getParent());
+        try (var out = new DataOutputStream(Files.newOutputStream(stateFile))) {
+            state.writeTo(out);
+        }
+    }
+}
\ No newline at end of file
index 2f508589e44407307cca84f246f087ecf8685f45..de93466fdd36b15d98884438a905177c5b264c2d 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang2sources.plugin;
 
 import java.io.IOException;
+import java.nio.file.Path;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.slf4j.Logger;
@@ -20,11 +21,20 @@ import org.sonatype.plexus.build.incremental.BuildContext;
 @NonNullByDefault
 abstract class StateStorage {
     private static final Logger LOG = LoggerFactory.getLogger(StateStorage.class);
+    private static final String DUMMY_KEY = StateStorage.class.getName();
+    private static final Object DUMMY_VALUE = new Object();
 
-    static StateStorage of(final BuildContext buildContext) {
-        // FIXME: detect no-op BuildContext and fall back to a file
-        LOG.debug("{} Using BuildContext to store state", YangToSourcesProcessor.LOG_PREFIX);
-        return new BuildContextStateStorage(buildContext);
+    static StateStorage of(final BuildContext buildContext, final Path fallbackFile) {
+        // Check if BuildContext works. If it does, we use it, otherwise we use fallback to the specified directory
+        buildContext.setValue(DUMMY_KEY, DUMMY_VALUE);
+        if (DUMMY_VALUE.equals(buildContext.getValue(DUMMY_KEY))) {
+            buildContext.setValue(DUMMY_KEY, null);
+            LOG.debug("{} Using BuildContext to store state", YangToSourcesProcessor.LOG_PREFIX);
+            return new BuildContextStateStorage(buildContext);
+        }
+
+        LOG.debug("{} Using {} to store state", YangToSourcesProcessor.LOG_PREFIX, fallbackFile);
+        return new FileStateStorage(fallbackFile);
     }
 
     abstract @Nullable YangToSourcesState loadState() throws IOException;
index 351bab7c90064236c2bb9d5aabc46ed30d8421bf..26770b164f228758dcbc87aca0634b1646111123 100644 (file)
@@ -91,7 +91,7 @@ class YangToSourcesProcessor {
         this.project = requireNonNull(project);
         this.inspectDependencies = inspectDependencies;
         this.yangProvider = requireNonNull(yangProvider);
-        stateStorage = StateStorage.of(buildContext);
+        stateStorage = StateStorage.of(buildContext, stateFilePath(project.getBuild().getDirectory()));
         parserFactory = DEFAULT_PARSER_FACTORY;
     }
 
@@ -379,4 +379,10 @@ class YangToSourcesProcessor {
 
         return generatorToFiles.build();
     }
+
+    @VisibleForTesting
+    static @NonNull Path stateFilePath(final String projectBuildDirectory) {
+        // ${project.build.directory}/maven-status/yang-maven-plugin/execution.state
+        return Path.of(projectBuildDirectory, "maven-status", "yang-maven-plugin", "execution.state");
+    }
 }
index 2beebec6f666411e47dad085b2e2150bf55da56e..934c62e8238831b48692cc0af27754b7176d1650 100644 (file)
@@ -25,17 +25,25 @@ import org.opendaylight.yangtools.concepts.WritableObject;
 record YangToSourcesState(
         @NonNull ImmutableMap<String, FileGeneratorArg> fileGeneratorArgs,
         @NonNull FileStateSet outputFiles) implements WritableObject {
+    // 'ymp' followed by a byte value '1'
+    private static final int MAGIC = 0x796D7001;
+
     YangToSourcesState {
         requireNonNull(fileGeneratorArgs);
         requireNonNull(outputFiles);
     }
 
     static @NonNull YangToSourcesState readFrom(final DataInput in) throws IOException {
+        final int magic = in.readInt();
+        if (magic != MAGIC) {
+            throw new IOException("Unrecognized magic " + Integer.toHexString(magic));
+        }
         return new YangToSourcesState(readConfigurations(in), FileStateSet.readFrom(in));
     }
 
     @Override
     public void writeTo(final DataOutput out) throws IOException {
+        out.writeInt(MAGIC);
         writeConfigurations(out, fileGeneratorArgs.values());
         outputFiles.writeTo(out);
     }
index 89af5b178f6ca9b37cb081a61121cabec4b783ee..a0c74cc7bd045583bd454afc8db086897206c154 100644 (file)
@@ -18,6 +18,7 @@ import static org.mockito.Mockito.mockStatic;
 
 import com.google.common.collect.Iterators;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.util.List;
 import java.util.ServiceLoader;
 import org.apache.maven.model.Build;
@@ -46,7 +47,8 @@ public abstract class AbstractCodeGeneratorTest {
     private Plugin plugin;
 
     @Before
-    public void before() {
+    public void before() throws IOException {
+        Files.deleteIfExists(YangToSourcesProcessor.stateFilePath("target/"));
         doReturn("target/").when(build).getDirectory();
         doReturn(build).when(project).getBuild();