Skip to content

Commit

Permalink
fix(objectionary#2791):fix caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Yanich96 committed Jan 22, 2024
1 parent a8cbc92 commit bfeb36c
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public Scalar<Integer> value(
this.update.apply(
tojo,
this.make(
this.optimization(tojo, common).apply(new XMLDocument(src)), src
this.optimization(tojo, common).apply(src), src
).toAbsolutePath()
);
return 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import org.eolang.maven.AssembleMojo;
import org.eolang.maven.Place;
import org.eolang.maven.footprint.FtDefault;
Expand All @@ -41,19 +38,14 @@
* Returns already optimized XML if it's found in the cache.
*
* @since 0.28.11
* @todo #2746:30min Fix caching mechanism in {@link OptCached}. Current
* The last modified time of the files between stages may be different,
* so it is not correct to do an equality comparison ({@code .equals(...)}).
* The last modification time of the file at the current stage
* must be less than or equal to the last modification time of file in cache at the next stage.
* The following tests show that fetching from the cache doesn't work correctly:
* - {@link OptCachedTest#returnsFromCacheCorrectProgram(Path path)},
* - {@link OptCachedTest#returnsFromCacheButTimesSaveAndExecuteDifferent(Path path)}.
* @todo #2746:30min Unify caching mechanism on stages: parse, optimize, pull and so on.
* Current implementations of caching on parsing stage and optimize stages work differently.
* In ParseMojo we have condition {@code if (tojo.hasHash()) }, in OptimizeMojo or ShakeMojo we
* compare creation time of files.
* Don't forget to enable the tests.
* @todo #2791:30min Get the XML name from its path,
* but doesn't use {@code xml.xpath("/program/@name").get(0)},
* in classes {@link OptCached}, {@link OptTrain}, {@link OptSpy}.
*/
public final class OptCached implements Optimization {

Expand Down Expand Up @@ -82,54 +74,60 @@ public OptCached(
}

@Override
public XML apply(final XML xml) {
public XML apply(final Path path) throws FileNotFoundException {
try {
final XML optimized;
if (this.contains(xml)) {
optimized = new XMLDocument(this.cached(xml));
if (this.contains(path)) {
optimized = new XMLDocument(this.cached(path));
} else {
optimized = this.delegate.apply(xml);
optimized = this.delegate.apply(path);
new FtDefault(this.folder).save(
xml.xpath("/program/@name").get(0),
new XMLDocument(path).xpath("/program/@name").get(0),
AssembleMojo.IR_EXTENSION,
optimized::toString
);
}
return optimized;
} catch (final IOException ex) {
throw new IllegalStateException(String.format("Can't optimize '%s'", xml), ex);
throw new IllegalStateException(
String.format("Can't optimize '%s'", new XMLDocument(path)),
ex
);
}
}

/**
* Returns the path to the cached program.
* Pay attention that the path is not checked for existence.
* @param xml Eo program.
* @param path Path eo program.
* @return Path to the cached program.
* @throws IOException If fails.
*/
private Path cached(final XML xml) {
return new Place(xml.xpath("/program/@name").get(0))
private Path cached(final Path path) throws IOException {
return new Place(
new XMLDocument(path)
.xpath("/program/@name").get(0)
)
.make(this.folder, AssembleMojo.IR_EXTENSION);
}

/**
* Checks if the cache contains the program.
* @param xml Eo program.
* @param path Path eo program.
* @return True if the cache contains the program.
* @throws IOException If fails.
*/
private boolean contains(final XML xml) throws IOException {
final Path path = this.cached(xml);
final Optional<String> time = xml.xpath("/program/@time").stream().findFirst();
private boolean contains(final Path path) throws IOException {
final Path cache = this.cached(path);
final boolean res;
if (Files.exists(path) && time.isPresent()) {
res = Files.readAttributes(path, BasicFileAttributes.class)
.creationTime()
if (Files.exists(cache)) {
res = !Files
.getLastModifiedTime(cache)
.toInstant()
.truncatedTo(ChronoUnit.MINUTES)
.equals(
ZonedDateTime.parse(time.get()).toInstant().truncatedTo(ChronoUnit.MINUTES)
);
.isBefore(
Files.getLastModifiedTime(path)
.toInstant()
);
} else {
res = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import com.jcabi.log.Logger;
import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.yegor256.xsline.Shift;
import com.yegor256.xsline.Train;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import org.eolang.maven.Place;
import org.eolang.maven.SpyTrain;
Expand Down Expand Up @@ -66,13 +68,16 @@ public OptSpy(final Train<Shift> trn, final Path target) {
}

@Override
public XML apply(final XML xml) {
final Place place = new Place(xml.xpath("/program/@name").get(0));
final Path dir = place.make(this.target, "");
public XML apply(final Path path) throws FileNotFoundException {
final Path dir = new Place(
new XMLDocument(path)
.xpath("/program/@name")
.get(0))
.make(this.target, "");
Logger.debug(
this, "Optimization steps will be tracked to %s",
new Rel(dir)
);
return new OptTrain(new SpyTrain(this.train, dir)).apply(xml);
return new OptTrain(new SpyTrain(this.train, dir)).apply(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@
package org.eolang.maven.optimization;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.yegor256.xsline.Shift;
import com.yegor256.xsline.StClasspath;
import com.yegor256.xsline.TrClasspath;
import com.yegor256.xsline.TrDefault;
import com.yegor256.xsline.TrFast;
import com.yegor256.xsline.Train;
import com.yegor256.xsline.Xsline;
import java.io.FileNotFoundException;
import java.nio.file.Path;

/**
* Optimisation train of XLS`s.
Expand Down Expand Up @@ -87,7 +90,7 @@ public OptTrain() {
* @param shifts XLS shifts.
*/
public OptTrain(final Train<Shift> shifts) {
this(xml -> xml, shifts);
this(path -> new XMLDocument(path), shifts);
}

/**
Expand Down Expand Up @@ -117,7 +120,7 @@ public OptTrain(
}

@Override
public XML apply(final XML xml) {
return new Xsline(this.shifts).pass(this.delegate.apply(xml));
public XML apply(final Path path) throws FileNotFoundException {
return new Xsline(this.shifts).pass(this.delegate.apply(path));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
package org.eolang.maven.optimization;

import com.jcabi.xml.XML;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.function.Function;

/**
Expand All @@ -32,5 +34,20 @@
* @since 0.28.11
*/
@FunctionalInterface
public interface Optimization extends Function<XML, XML> {
public interface Optimization extends ThrowingFunction<Path, XML, FileNotFoundException> {

/**
* Return function if it works without exception.
*
* @return The function result
*/
default Function<Path, XML> unchecked() {
return input -> {
try {
return this.apply(input);
} catch (final FileNotFoundException exception) {
throw new PathNotFoundException(input, exception);
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2023 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.maven.optimization;

import java.nio.file.Path;

/**
* If the path isn't valid.
* @since 0.35.0
*/
public class PathNotFoundException extends RuntimeException {

/**
* Ctor.
* @param path The path.
* @param cause The cause.
*/
PathNotFoundException(final Path path, final Throwable cause) {
super(
String.format(
"Cannot get XML from path '%s'", path
),
cause
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2023 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.maven.optimization;

/**
* Abstraction for XML optimizations.
* @param <I> Input type.
* @param <R> Return type.
* @param <E> Exception type.
* @since 0.35.0
*/
@FunctionalInterface
public interface ThrowingFunction<I, R, E extends Exception> {
/**
* Applies this function to the given argument.
*
* @param input The function argument
* @return The function result
* @throws E If fails
*/
R apply(I input) throws E;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import com.jcabi.xml.XMLDocument;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
Expand All @@ -43,7 +45,6 @@
import org.hamcrest.io.FileMatchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -118,7 +119,6 @@ void optimizesIfExpired(@TempDir final Path temp) throws Exception {
* then enable the test.
* Also, see this <a href="https://github.com/objectionary/eo/issues/2727">issue</a>.
*/
@Disabled
@Test
void getsAlreadyOptimizedResultsFromCache(@TempDir final Path temp) throws Exception {
final TextOf cached = new TextOf(
Expand All @@ -132,6 +132,15 @@ void getsAlreadyOptimizedResultsFromCache(@TempDir final Path temp) throws Excep
.resolve(hash)
.resolve("foo/x/main.xmir")
);
Files.setLastModifiedTime(
cache.resolve(
Paths
.get(OptimizeMojo.OPTIMIZED)
.resolve(hash)
.resolve("foo/x/main.xmir")
),
FileTime.fromMillis(System.currentTimeMillis() + 50_000)
);
new FakeMaven(temp)
.withHelloWorld()
.with("cache", cache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import com.jcabi.xml.XMLDocument;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.cactoos.io.ResourceOf;
Expand All @@ -37,7 +39,6 @@
import org.hamcrest.io.FileMatchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

Expand Down Expand Up @@ -82,7 +83,6 @@ void shakesSuccessfully(@TempDir final Path temp) throws IOException {
);
}

@Disabled
@Test
void getsAlreadyShakenResultsFromCache(@TempDir final Path temp) throws Exception {
final TextOf cached = new TextOf(
Expand All @@ -96,6 +96,10 @@ void getsAlreadyShakenResultsFromCache(@TempDir final Path temp) throws Exceptio
.resolve(hash)
.resolve("foo/x/main.xmir")
);
Files.setLastModifiedTime(
cache.resolve(Paths.get(ShakeMojo.SHAKEN).resolve(hash).resolve("foo/x/main.xmir")),
FileTime.fromMillis(System.currentTimeMillis() + 50_000)
);
new FakeMaven(temp)
.withHelloWorld()
.with("cache", cache)
Expand Down

0 comments on commit bfeb36c

Please sign in to comment.