package jetbrains.buildServer.buildTriggers.url; import jetbrains.buildServer.log.Loggers; import jetbrains.buildServer.serverSide.CustomDataStorage; import jetbrains.buildServer.util.FileUtil; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.net.URL; import java.net.URLConnection; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.Map; /** * User: vbedrosova * Date: 06.12.10 * Time: 20:10 */ public class UrlChangesDetector { @NotNull private static final Logger LOG = Logger.getLogger(Loggers.VCS_CATEGORY + UrlChangesDetector.class); @NotNull private static final String DEBUG_RESOURCE_PATH_PARAM = "url.build.trigger.debug.resource.path"; public boolean isChanged(@NotNull final String urlStr, @NotNull final CustomDataStorage dataStorage, @Nullable final Map additionalParams) throws Exception { final URL url = new URL(urlStr); final String digest = getDigest(url.openStream(), getDebugResourceWriter(url, additionalParams)); LOG.debug("Calculated md5 digest for " + urlStr + ": " + digest); final String storedDigest = getStoredDigest(urlStr, dataStorage); LOG.debug("Retrieved md5 digest for " + urlStr + " from data storage: " + storedDigest); if (!digest.equals(storedDigest)) { LOG.debug("Detected md5 digest changed for " + urlStr + "new:" + digest + " old:" + storedDigest); storeDigest(urlStr, digest, dataStorage); return true; } return false; } private static void storeDigest(@NotNull String url, @NotNull final String digest, @NotNull CustomDataStorage dataStorage) { dataStorage.putValue(url, digest); } @Nullable private static String getStoredDigest(@NotNull String url, @NotNull CustomDataStorage dataStorage) { return dataStorage.getValue(url); } @NotNull private static String getDigest(@NotNull final InputStream source, @Nullable final FileWriter fw) throws IOException { try { final MessageDigest digest = MessageDigest.getInstance("MD5"); final DigestInputStream dis = new DigestInputStream(source, digest); final byte[] bytes = new byte[32768]; try { while (dis.read(bytes) > 0) { if (fw != null) fw.write(new String(bytes)); } } finally { dis.close(); close(fw); } return toHex(digest.digest()); } catch (NoSuchAlgorithmException e) { throw new IOException("MD5 not installed", e); } } private static void close(FileWriter fw) throws IOException { if (fw != null) { fw.close(); } } private static String toHex(byte[] arg) throws IOException { return String.format("%x", new BigInteger(arg)); } @Nullable private static FileWriter getDebugResourceWriter(@NotNull URL url, @Nullable final Map additionalParams) throws IOException{ if (additionalParams != null && additionalParams.containsKey(DEBUG_RESOURCE_PATH_PARAM)) { final File file = new File(additionalParams.get(DEBUG_RESOURCE_PATH_PARAM), url.toString().hashCode() + url.getFile()); FileUtil.delete(file); file.getParentFile().mkdirs(); return new FileWriter(file); } return null; } }