package io.cattle.platform.configitem.server.resource; import io.cattle.platform.archaius.util.ArchaiusUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.concurrent.Callable; import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import com.netflix.config.DynamicBooleanProperty; import com.netflix.config.DynamicStringListProperty; public abstract class AbstractCachingResourceRoot implements ResourceRoot { public static final DynamicStringListProperty IGNORE_PREFIX = ArchaiusUtil.getList("config.item.ignore.prefixes"); public static final DynamicBooleanProperty DYNAMIC = ArchaiusUtil.getBoolean("config.item.dynamic"); Collection<Resource> resources; String sourceRevision; Callable<byte[]> additionalRevisionData; public static boolean shouldIgnore(File base, String path) { for (String prefix : IGNORE_PREFIX.get()) { if (path.startsWith(prefix)) { return true; } } return base == null ? false : Files.isSymbolicLink(new File(base, path).toPath()); } @Override public Collection<Resource> getResources() throws IOException { return resources; } protected boolean isDynamic() { return DYNAMIC.get(); } @Override public synchronized final void scan() throws IOException { if (!isDynamic() && sourceRevision != null && resources != null) { return; } Collection<Resource> resources = scanResources(); DigestOutputStream outputStream = null; String revision = null; try { outputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256")); if (additionalRevisionData != null) { try { outputStream.write(additionalRevisionData.call()); } catch (Exception e) { throw new IOException(e); } } for (Resource resource : resources) { byte[] nameBytes = resource.getName().getBytes(); outputStream.write(nameBytes, 0, nameBytes.length); InputStream is = null; try { is = resource.getInputStream(); IOUtils.copy(is, outputStream); } finally { IOUtils.closeQuietly(is); } } outputStream.close(); revision = Hex.encodeHexString(outputStream.getMessageDigest().digest()); outputStream = null; } catch (NoSuchAlgorithmException e) { throw new IOException(e); } finally { IOUtils.closeQuietly(outputStream); } this.resources = resources; this.sourceRevision = revision; } protected abstract Collection<Resource> scanResources() throws IOException; @Override public String getSourceRevision() { return sourceRevision; } public Callable<byte[]> getAdditionalRevisionData() { return additionalRevisionData; } public void setAdditionalRevisionData(Callable<byte[]> additionalRevisionData) { this.additionalRevisionData = additionalRevisionData; } }