package org.graalvm.component.installer.persist;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import org.graalvm.component.installer.ComponentArchiveReader;
import org.graalvm.component.installer.FailedOperationException;
import org.graalvm.component.installer.Feedback;
import org.graalvm.component.installer.MetadataException;
import org.graalvm.component.installer.SoftwareChannel;
import org.graalvm.component.installer.SystemUtils;
import org.graalvm.component.installer.model.ComponentInfo;
import org.graalvm.component.installer.model.ComponentStorage;
import org.graalvm.component.installer.remote.FileDownloader;
public class DirectoryCatalogProvider implements ComponentStorage, SoftwareChannel {
private final Path directory;
private final Feedback feedback;
private boolean verifyJars = true;
private boolean reportErrors = true;
private Map<String, Set<ComponentInfo>> dirContents = null;
public DirectoryCatalogProvider(Path directory, Feedback feedback) {
this.directory = directory;
this.feedback = feedback.withBundle(DirectoryCatalogProvider.class);
}
public boolean isReportErrors() {
return reportErrors;
}
public void setReportErrors(boolean reportErrors) {
this.reportErrors = reportErrors;
}
public void setVerifyJars(boolean verifyJars) {
this.verifyJars = verifyJars;
}
@Override
public Set<String> listComponentIDs() throws IOException {
initComponents();
return dirContents.keySet();
}
@Override
public ComponentInfo loadComponentFiles(ComponentInfo ci) throws IOException {
return ci;
}
@Override
public Set<ComponentInfo> loadComponentMetadata(String id) throws IOException {
initComponents();
return dirContents.get(id);
}
@Override
public Map<String, String> loadGraalVersionInfo() {
initComponents();
throw new UnsupportedOperationException("Not supported yet.");
}
private void initComponents() {
if (dirContents != null) {
return;
}
dirContents = new HashMap<>();
if (!Files.isDirectory(directory)) {
return;
}
try {
Files.list(directory).forEach((p -> {
try {
ComponentInfo info = maybeCreateComponent(p);
if (info != null) {
dirContents.computeIfAbsent(info.getId(), (id) -> new HashSet<>()).add(info);
}
} catch (MetadataException ex) {
if (reportErrors) {
feedback.error("ERR_DirectoryComponentMetadata", ex, p.toString(), ex.getLocalizedMessage());
}
} catch (IOException | FailedOperationException ex) {
if (reportErrors) {
feedback.error("ERR_DirectoryComponentError", ex, p.toString(), ex.getLocalizedMessage());
}
}
}));
} catch (IOException ex) {
}
}
private ComponentInfo maybeCreateComponent(Path localFile) throws IOException {
byte[] fileStart = null;
String serial;
if (Files.isRegularFile(localFile)) {
try (ReadableByteChannel ch = FileChannel.open(localFile, StandardOpenOption.READ)) {
ByteBuffer bb = ByteBuffer.allocate(8);
ch.read(bb);
fileStart = bb.array();
}
serial = SystemUtils.fingerPrint(SystemUtils.computeFileDigest(localFile, null));
} else {
fileStart = new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
serial = SystemUtils.digestString(localFile.toString(), false);
}
MetadataLoader ldr = null;
try {
for (ComponentArchiveReader provider : ServiceLoader.load(ComponentArchiveReader.class)) {
ldr = provider.createLoader(localFile, fileStart, serial, feedback, verifyJars);
if (ldr != null) {
ComponentInfo info = ldr.getComponentInfo();
info.setRemoteURL(localFile.toUri().toURL());
info.setOrigin(feedback.l10n("DIR_LocalFile"));
return info;
}
}
} finally {
if (ldr != null) {
ldr.close();
}
}
return null;
}
@Override
public ComponentStorage getStorage() throws IOException {
return this;
}
@Override
public FileDownloader configureDownloader(ComponentInfo info, FileDownloader dn) {
return dn;
}
}