• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kit-data-manager / ro-crate-java / #359

16 Apr 2025 04:37PM UTC coverage: 85.989%. First build
#359

Pull #233

github

Pfeil
fix: add null and existence checks for JSON-LD file in JsonLdExpander
Pull Request #233: Version 2.1.0

532 of 694 new or added lines in 19 files covered. (76.66%)

1878 of 2184 relevant lines covered (85.99%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.25
/src/main/java/edu/kit/datamanager/ro_crate/reader/ZipStreamReader.java
1
package edu.kit.datamanager.ro_crate.reader;
2

3
import com.fasterxml.jackson.databind.ObjectMapper;
4
import com.fasterxml.jackson.databind.node.ObjectNode;
5
import edu.kit.datamanager.ro_crate.objectmapper.MyObjectMapper;
6
import java.io.File;
7
import java.io.FileOutputStream;
8
import java.io.IOException;
9
import java.io.InputStream;
10
import java.io.OutputStream;
11
import java.nio.file.Path;
12
import java.util.UUID;
13
import net.lingala.zip4j.io.inputstream.ZipInputStream;
14
import net.lingala.zip4j.model.LocalFileHeader;
15
import org.apache.commons.io.FileUtils;
16

17
/**
18
 * A ZIP file reader implementation of the StreamReaderStrategy interface.
19
 * This class handles reading and extraction of RO-Crate content from ZIP archives
20
 * into a temporary directory structure, which allows for accessing the contained files.
21
 *
22
 * @author jejkal
23
 */
24
public class ZipStreamReader implements StreamReaderStrategy {
25

26
    protected final String ID = UUID.randomUUID().toString();
1✔
27
    protected Path temporaryFolder = Path.of(String.format("./.tmp/ro-crate-java/zipStreamReader/%s/", ID));
1✔
28
    protected boolean isExtracted = false;
1✔
29

30
    /**
31
     * Crates a ZipStreamReader with the default configuration as described in
32
     * the class documentation.
33
     */
34
    public ZipStreamReader() {
1✔
35
    }
1✔
36

37
    /**
38
     * Creates a ZipStreamReader which will extract the contents temporary to
39
     * the given location instead of the default location.
40
     *
41
     * @param folderPath the custom directory to extract content to for
42
     * temporary access.
43
     * @param shallAddUuidSubfolder if true, the reader will extract into
44
     * subdirectories of the given directory. These subdirectories will have
45
     * UUIDs as their names.
46
     */
47
    public ZipStreamReader(Path folderPath, boolean shallAddUuidSubfolder) {
1✔
48
        if (shallAddUuidSubfolder) {
1✔
49
            this.temporaryFolder = folderPath.resolve(ID);
1✔
50
        } else {
51
            this.temporaryFolder = folderPath;
1✔
52
        }
53
    }
1✔
54

55
    /**
56
     * @return the identifier which may be used as the name for a subfolder in
57
     * the temporary directory.
58
     */
59
    public String getID() {
60
        return ID;
1✔
61
    }
62

63
    /**
64
     * @return the folder (considered temporary) where the zipped crate will be
65
     * or has been extracted to.
66
     */
67
    public Path getTemporaryFolder() {
68
        return temporaryFolder;
1✔
69
    }
70

71
    /**
72
     * @return whether the crate has already been extracted into the temporary
73
     * folder.
74
     */
75
    public boolean isExtracted() {
76
        return isExtracted;
1✔
77
    }
78

79
    /**Read the crate metadata and content from the provided input stream.
80
     * 
81
     * @param stream The input stream.
82
     */
83
    private void readCrate(InputStream stream) {
84
        try {
85
            File folder = temporaryFolder.toFile();
1✔
86
            // ensure the directory is clean
87
            if (folder.exists()) {
1✔
88
                if (folder.isDirectory()) {
1✔
89
                    FileUtils.cleanDirectory(folder);
1✔
NEW
90
                } else if (folder.isFile()) {
×
NEW
91
                    FileUtils.delete(folder);
×
92
                }
93
            } else {
94
                FileUtils.forceMkdir(folder);
1✔
95
            }
96

97
            LocalFileHeader localFileHeader;
98
            int readLen;
99
            byte[] readBuffer = new byte[4096];
1✔
100

101
            try (ZipInputStream zipInputStream = new ZipInputStream(stream)) {
1✔
102
                while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
1✔
103
                    String fileName = localFileHeader.getFileName();
1✔
104
                    File extractedFile = new File(folder, fileName).getCanonicalFile();
1✔
105
                    if (!extractedFile.toPath().startsWith(folder.getCanonicalPath())) {
1✔
NEW
106
                        throw new IOException("Entry is outside of target directory: " + fileName);
×
107
                    }
108
                    try (OutputStream outputStream = new FileOutputStream(extractedFile)) {
1✔
109
                        while ((readLen = zipInputStream.read(readBuffer)) != -1) {
1✔
110
                            outputStream.write(readBuffer, 0, readLen);
1✔
111
                        }
112
                    }
113
                }
1✔
114
            }
115
            this.isExtracted = true;
1✔
116
            // register deletion on exit
117
            FileUtils.forceDeleteOnExit(folder);
1✔
NEW
118
        } catch (IOException ex) {
×
NEW
119
            logger.error("Failed to read crate from input stream.", ex);
×
120
        }
1✔
121
    }
1✔
122

123
    @Override
124
    public ObjectNode readMetadataJson(InputStream stream) {
125
        if (!isExtracted) {
1✔
126
            this.readCrate(stream);
1✔
127
        }
128

129
        ObjectMapper objectMapper = MyObjectMapper.getMapper();
1✔
130
        File jsonMetadata = temporaryFolder.resolve("ro-crate-metadata.json").toFile();
1✔
131

132
        try {
133
            return objectMapper.readTree(jsonMetadata).deepCopy();
1✔
NEW
134
        } catch (IOException e) {
×
NEW
135
            logger.error("Failed to deserialize crate metadata.", e);
×
NEW
136
            return null;
×
137
        }
138
    }
139

140
    @Override
141
    public File readContent(InputStream stream) {
142
        if (!isExtracted) {
1✔
NEW
143
            this.readCrate(stream);
×
144
        }
145
        return temporaryFolder.toFile();
1✔
146
    }
147
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc