/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.jsmodule.domain; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang.Validate; /** Manages the cache of the bundled files. * * @author ivan.bedecarats@globant.com. */ public class BundleCache { /** Map that contains the cache of the bundled files. * * The key is a {@link String} generated by concatenating an MD5 hash with * ".js". This MD5 hash was created by concatenating the paths of those files * which had been used for looking for the dependency files of the bundled * file. Each concatenated file path is ordered alphabetically and separated * by a |. * The value is a {@link String} which represents the content of the bundled * file cached in the Map. * */ private Map<String, String> bundledFiles = new HashMap<String, String>(); /** Generates a Key for a bundled file which will be cached in the Map. * * Implementation note: the key is generated by concatenating the list of * files separated by '|', calculating its MD5 hash, and concatenating a .js * suffix. The key is order dependent (no sorting is done to calculate the * key.) * * @param files The files used for generating the Key. These files are the * ones used for looking for the dependency files of bundled file. * Cannot be null nor empty. * * @return A unique key generated from the source files. Never return null. */ private String generateKey(final List<String> files) { Validate.notEmpty(files, "There must be at least one file."); StringBuilder concatenatedFile = new StringBuilder(); String generatedKey = null; for (int i = 0; i < files.size(); i++) { if (concatenatedFile.length() != 0) { concatenatedFile.append("|"); } concatenatedFile.append(files.get(i)); } MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Unable to generate the MD5 hash.", e); } messageDigest.reset(); String inputForHash = concatenatedFile.toString(); messageDigest.update(inputForHash.getBytes(Charset.forName("UTF8"))); byte[] resultBytes = messageDigest.digest(); generatedKey = new String(Hex.encodeHex(resultBytes)) + ".js"; return generatedKey; } /** Finds a {@link String} which represents the content of the Bundled File * for the given Key in the Map of Cached Files. * * @param key The key in the Map of Cached Files for the given files. Cannot * be null nor empty. * @return Gets a {@link String} which represents the content of the Cached * Bundled File for the given files. If there is no cached file in the map for * the given files then it returns null. */ public String findContent(final String key) { Validate.notEmpty(key, "The Key cannot be null nor empty."); return bundledFiles.get(key); } /** Finds the key in Map that contains the cache of the bundled files. * * @param files The files used for generating the Key. These files are the * ones used for looking for the dependency files which were later on bundled. * Cannot be null nor empty. * @return The key in the Map of Cached Files for the given files. If there is * no cached file for them, then it returns null. */ public String findKey(final List<String> files) { Validate.notEmpty(files, "There must be at least one file."); String generatedKey = generateKey(files); if (bundledFiles.containsKey(generatedKey)) { return generatedKey; } return null; } /** Stores a bundled file for the given files in the Map of Cached Files. * * @param files The files used for generating the Key. These files are the * ones used for looking for the dependency files which were later on bundled. * Cannot be null nor empty. * @param bundledContent A {@link String} which represents the of the Cached * Bundled. Cannot be empty. * File for the given files. * @return The {@link String} which represents the key of the newly stored * bundled file in the Map of Cached Files. It's never null. */ public String store(final List<String> files, final String bundledContent) { Validate.notEmpty(files, "There must be at least one file."); Validate.notEmpty(bundledContent, "The bundled content cannot be null"); String bundleKey = generateKey(files); bundledFiles.put(bundleKey, bundledContent); return bundleKey; } }