package org.graalvm.component.installer.remote;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.graalvm.component.installer.ComponentCatalog;
import org.graalvm.component.installer.FailedOperationException;
import org.graalvm.component.installer.Feedback;
import org.graalvm.component.installer.IncompatibleException;
import org.graalvm.component.installer.InstallerStopException;
import org.graalvm.component.installer.SoftwareChannel;
import org.graalvm.component.installer.SoftwareChannelSource;
import org.graalvm.component.installer.model.ComponentInfo;
import org.graalvm.component.installer.model.ComponentRegistry;
import org.graalvm.component.installer.persist.AbstractCatalogStorage;
import org.graalvm.component.installer.persist.MetadataLoader;
public class MergeStorage extends AbstractCatalogStorage implements ComponentCatalog.DownloadInterceptor {
private final Map<ComponentInfo, SoftwareChannel> channelMap = new HashMap<>();
private final List<SoftwareChannel> channels = new ArrayList<>();
private final Map<SoftwareChannel, SoftwareChannelSource> channelInfos = new HashMap<>();
private boolean ignoreCatalogErrors;
private boolean acceptAllSources;
public MergeStorage(ComponentRegistry localRegistry, Feedback feedback) {
super(localRegistry, feedback, null);
}
public boolean isAcceptAllSources() {
return acceptAllSources;
}
public void setAcceptAllSources(boolean acceptAllSources) {
this.acceptAllSources = acceptAllSources;
}
public void addChannel(SoftwareChannelSource info, SoftwareChannel delegate) {
channels.add(delegate);
channelInfos.put(delegate, info);
}
public boolean isIgnoreCatalogErrors() {
return ignoreCatalogErrors;
}
public void setIgnoreCatalogErrors(boolean ignoreCatalogErrors) {
this.ignoreCatalogErrors = ignoreCatalogErrors;
}
private void reportError(Exception exc, SoftwareChannel errChannel) {
if (exc == null) {
return;
}
SoftwareChannelSource info = channelInfos.get(errChannel);
String l = info.getLabel();
if (l == null) {
l = info.getLocationURL();
}
feedback.error("REMOTE_CannotLoadChannel", exc, l, exc.getLocalizedMessage());
}
private boolean idsLoaded;
@Override
public Set<String> listComponentIDs() throws IOException {
Set<String> ids = new HashSet<>();
List<Exception> savedEx = new ArrayList<>();
List<SoftwareChannel> errChannels = new ArrayList<>();
boolean oneSucceeded = false;
Exception toThrow = null;
for (SoftwareChannel del : new ArrayList<>(channels)) {
try {
ids.addAll(del.getStorage().listComponentIDs());
oneSucceeded = true;
} catch (IncompatibleException ex) {
savedEx.add(ex);
errChannels.add(del);
channels.remove(del);
} catch (IOException | FailedOperationException ex) {
if (!isIgnoreCatalogErrors()) {
throw ex;
}
if (!idsLoaded) {
reportError(ex, del);
}
toThrow = ex;
channels.remove(del);
}
}
if (!oneSucceeded || ids.isEmpty()) {
for (int i = 0; i < savedEx.size(); i++) {
reportError(toThrow = savedEx.get(i), errChannels.get(i));
}
if (toThrow instanceof IOException) {
throw (IOException) toThrow;
} else if (toThrow != null) {
throw (InstallerStopException) toThrow;
}
}
idsLoaded = true;
return ids;
}
List<SoftwareChannel> getChannels() {
return channels;
}
private int getChannelPriority(SoftwareChannel ch) {
SoftwareChannelSource src = this.channelInfos.get(ch);
if (src != null) {
return src.getPriority();
} else {
int index = channels.indexOf(ch);
return index == -1 ? 0 : index;
}
}
@Override
public Set<ComponentInfo> loadComponentMetadata(String id) throws IOException {
Set<ComponentInfo> cis = new HashSet<>();
for (SoftwareChannel swch : channels) {
Set<ComponentInfo> newInfos = swch.getStorage().loadComponentMetadata(id);
if (newInfos == null || newInfos.isEmpty()) {
continue;
}
if (!acceptAllSources) {
newInfos.removeAll(cis);
}
for (ComponentInfo ci : newInfos) {
ci.setPriority(getChannelPriority(swch));
channelMap.put(ci, swch);
}
cis.addAll(newInfos);
if (!acceptAllSources) {
break;
}
}
return cis;
}
public SoftwareChannel getOrigin(ComponentInfo ci) {
return channelMap.get(ci);
}
@Override
public FileDownloader processDownloader(ComponentInfo info, FileDownloader dn) {
SoftwareChannel orig = getOrigin(info);
return orig != null ? orig.configureDownloader(info, dn) : dn;
}
@Override
public MetadataLoader interceptMetadataLoader(ComponentInfo info, MetadataLoader delegate) {
SoftwareChannel orig = getOrigin(info);
if (orig instanceof ComponentCatalog.DownloadInterceptor) {
return ((ComponentCatalog.DownloadInterceptor) orig).interceptMetadataLoader(info, delegate);
} else {
return delegate;
}
}
}