FileReader.java

1
package com.renomad.minum.utils;
2
3
import com.renomad.minum.logging.ILogger;
4
5
import java.io.ByteArrayOutputStream;
6
import java.io.IOException;
7
import java.io.RandomAccessFile;
8
import java.nio.ByteBuffer;
9
import java.nio.channels.FileChannel;
10
import java.util.Map;
11
import java.util.concurrent.locks.ReentrantLock;
12
13
import static com.renomad.minum.utils.FileUtils.checkForBadFilePatterns;
14
15
/**
16
 * Reads files from disk, optionally storing into a LRU cache.
17
 */
18
public final class FileReader implements IFileReader {
19
20
    private final Map<String, byte[]> lruCache;
21
    private final boolean useCacheForStaticFiles;
22
    private final ILogger logger;
23
    private final ReentrantLock cacheLock = new ReentrantLock();
24
25
    public FileReader(Map<String, byte[]> lruCache, boolean useCacheForStaticFiles, ILogger logger) {
26
        this.lruCache = lruCache;
27
        this.useCacheForStaticFiles = useCacheForStaticFiles;
28
        this.logger = logger;
29
    }
30
31
    @Override
32
    public byte[] readFile(String path) throws IOException {
33 2 1. readFile : negated conditional → KILLED
2. readFile : negated conditional → KILLED
        if (useCacheForStaticFiles && lruCache.containsKey(path)) {
34 1 1. readFile : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
            cacheLock.lock();
35
            try {
36
                byte[] bytes = lruCache.get(path);
37
                logger.logTrace(() -> "in FileReader.readFile, just obtained %d bytes from the cache using a path of %s".formatted(bytes.length, path));
38 1 1. readFile : replaced return value with null for com/renomad/minum/utils/FileReader::readFile → KILLED
                return bytes;
39
            } finally {
40 1 1. readFile : removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED
                cacheLock.unlock();
41
            }
42
        }
43 1 1. readFile : removed call to com/renomad/minum/utils/FileUtils::checkForBadFilePatterns → KILLED
        checkForBadFilePatterns(path);
44 1 1. readFile : replaced return value with null for com/renomad/minum/utils/FileReader::readFile → KILLED
        return readTheFile(path, logger, useCacheForStaticFiles, lruCache);
45
    }
46
47
    byte[] readTheFile(String path, ILogger logger, boolean useCacheForStaticFiles, Map<String, byte[]> lruCache) throws IOException {
48
        try (RandomAccessFile reader = new RandomAccessFile(path, "r");
49
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
50
            FileChannel channel = reader.getChannel();
51
            int bufferSize = 8 * 1024;
52 2 1. readTheFile : negated conditional → KILLED
2. readTheFile : changed conditional boundary → KILLED
            if (bufferSize > channel.size()) {
53
                bufferSize = (int) channel.size();
54
            }
55
            ByteBuffer buff = ByteBuffer.allocate(bufferSize);
56
57 2 1. readTheFile : changed conditional boundary → TIMED_OUT
2. readTheFile : negated conditional → KILLED
            while (channel.read(buff) > 0) {
58 1 1. readTheFile : removed call to java/io/ByteArrayOutputStream::write → KILLED
                out.write(buff.array(), 0, buff.position());
59
                buff.clear();
60
            }
61
62
            byte[] bytes = out.toByteArray();
63 1 1. readTheFile : negated conditional → KILLED
            if (bytes.length == 0) {
64
                logger.logTrace(() -> path + " filesize was 0, returning empty byte array");
65
                return new byte[0];
66
            } else {
67
                String s = path + " filesize was " + bytes.length + " bytes.";
68
                logger.logTrace(() -> s);
69
70
                if (useCacheForStaticFiles) {
71
                    logger.logDebug(() -> "Storing " + path + " in the cache");
72
                    cacheLock.lock();
73
                    try {
74
                        byte[] putResult = lruCache.put(path, bytes);
75
                        logger.logTrace(() -> ("in FileReader.readTheFile, just added %d bytes with a key " +
76
                                "of %s. %s").formatted(bytes.length, path,
77 1 1. lambda$readTheFile$4 : negated conditional → TIMED_OUT
                                putResult == null ? "No previous value for this key existed" :
78
                                        ("The previous length of data for this key was " + putResult.length + " bytes")));
79
                    } finally {
80
                        cacheLock.unlock();
81
                    }
82
                }
83
                return bytes;
84
            }
85
        }
86
    }
87
88
    /**
89
     * Returns the lock used to prevent concurrent modification
90
     * exceptions when mutating the LRU cache data.
91
     * <br>
92
     * This may be useful if you need to access the LRU cache,
93
     * which, owing to how a least-recently-used cache works,
94
     * will cause the data to mutate, and which requires to
95
     * be protected with locks.
96
     */
97
    @Override
98
    public ReentrantLock getCacheLock() {
99 1 1. getCacheLock : replaced return value with null for com/renomad/minum/utils/FileReader::getCacheLock → KILLED
        return cacheLock;
100
    }
101
102
    @Override
103
    public Map<String, byte[]> getLruCache() {
104 1 1. getLruCache : replaced return value with Collections.emptyMap for com/renomad/minum/utils/FileReader::getLruCache → KILLED
        return this.lruCache;
105
    }
106
}

Mutations

33

1.1
Location : readFile
Killed by : com.renomad.minum.templating.TemplatingTests
negated conditional → KILLED

2.2
Location : readFile
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_IOExceptionThrown_WebFramework(com.renomad.minum.FunctionalTests)
negated conditional → KILLED

34

1.1
Location : readFile
Killed by : com.renomad.minum.utils.FileReaderTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

38

1.1
Location : readFile
Killed by : com.renomad.minum.utils.FileReaderTests
replaced return value with null for com/renomad/minum/utils/FileReader::readFile → KILLED

40

1.1
Location : readFile
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED
Covering tests

43

1.1
Location : readFile
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_IOExceptionThrown_WebFramework(com.renomad.minum.FunctionalTests)
removed call to com/renomad/minum/utils/FileUtils::checkForBadFilePatterns → KILLED

44

1.1
Location : readFile
Killed by : com.renomad.minum.templating.TemplatingTests
replaced return value with null for com/renomad/minum/utils/FileReader::readFile → KILLED

52

1.1
Location : readTheFile
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_IOExceptionThrown_WebFramework(com.renomad.minum.FunctionalTests)
negated conditional → KILLED

2.2
Location : readTheFile
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_Response_MultiCookies(com.renomad.minum.FunctionalTests)
changed conditional boundary → KILLED

57

1.1
Location : readTheFile
Killed by : com.renomad.minum.templating.TemplatingTests
negated conditional → KILLED

2.2
Location : readTheFile
Killed by : none
changed conditional boundary → TIMED_OUT

58

1.1
Location : readTheFile
Killed by : com.renomad.minum.templating.TemplatingTests
removed call to java/io/ByteArrayOutputStream::write → KILLED

63

1.1
Location : readTheFile
Killed by : com.renomad.minum.templating.TemplatingTests
negated conditional → KILLED

77

1.1
Location : lambda$readTheFile$4
Killed by : none
negated conditional → TIMED_OUT

99

1.1
Location : getCacheLock
Killed by : com.renomad.minum.web.WebFrameworkTests
replaced return value with null for com/renomad/minum/utils/FileReader::getCacheLock → KILLED

104

1.1
Location : getLruCache
Killed by : com.renomad.minum.FunctionalTests.test_PathFunction_Response_Range(com.renomad.minum.FunctionalTests)
replaced return value with Collections.emptyMap for com/renomad/minum/utils/FileReader::getLruCache → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0