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

Mutations

31

1.1
Location : readFile
Killed by : com.renomad.minum.utils.FileReaderTests
negated conditional → KILLED

32

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

34

1.1
Location : readFile
Killed by : com.renomad.minum.utils.FileReaderTests
negated conditional → KILLED

37

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 → TIMED_OUT

43

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

51

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_IOExceptionThrown_WebFramework(com.renomad.minum.FunctionalTests)
changed conditional boundary → KILLED

56

1.1
Location : readTheFile
Killed by : none
negated conditional → TIMED_OUT

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

57

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

62

1.1
Location : readTheFile
Killed by : com.renomad.minum.utils.FileReaderTests
negated conditional → KILLED

76

1.1
Location : lambda$readTheFile$4
Killed by : com.renomad.minum.utils.FileReaderTests
negated conditional → KILLED

98

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

103

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