Java tutorial
/** * This file is part of Aion-Lightning <aion-lightning.org>. * * Aion-Lightning is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Aion-Lightning is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Aion-Lightning. * If not, see <http://www.gnu.org/licenses/>. */ package com.aionemu.gameserver.cache; import com.aionemu.gameserver.configs.main.HTMLConfig; import javolution.util.FastMap; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; /** * @authors Layane, nbali, savormix, hex1r0, lord_rex */ public final class HTMLCache { private static final Logger log = LoggerFactory.getLogger(HTMLCache.class); private static final FileFilter HTML_FILTER = new FileFilter() { @Override public boolean accept(File file) { return file.isDirectory() || file.getName().endsWith(".xhtml"); } }; private static final File HTML_ROOT = new File(HTMLConfig.HTML_ROOT); private static final class SingletonHolder { private static final HTMLCache INSTANCE = new HTMLCache(); } public static HTMLCache getInstance() { return SingletonHolder.INSTANCE; } private FastMap<String, String> cache = new FastMap<String, String>(16000); private int loadedFiles; private int size; private HTMLCache() { reload(false); } @SuppressWarnings("unchecked") public synchronized void reload(boolean deleteCacheFile) { cache.clear(); loadedFiles = 0; size = 0; final File cacheFile = getCacheFile(); if (deleteCacheFile && cacheFile.exists()) { log.info("Cache[HTML]: Deleting cache file... OK."); cacheFile.delete(); } log.info("Cache[HTML]: Caching started... OK."); if (cacheFile.exists()) { log.info("Cache[HTML]: Using cache file... OK."); ObjectInputStream ois = null; try { ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(getCacheFile()))); cache = (FastMap<String, String>) ois.readObject(); for (String html : cache.values()) { loadedFiles++; size += html.length(); } } catch (Exception e) { log.warn("", e); reload(true); return; } finally { IOUtils.closeQuietly(ois); } } else { parseDir(HTML_ROOT); } log.info(String.valueOf(this)); if (cacheFile.exists()) { log.info("Cache[HTML]: Compaction skipped!"); } else { log.info("Cache[HTML]: Compacting htmls... OK."); final StringBuilder sb = new StringBuilder(8192); for (Entry<String, String> entry : cache.entrySet()) { try { final String oldHtml = entry.getValue(); final String newHtml = compactHtml(sb, oldHtml); size -= oldHtml.length(); size += newHtml.length(); entry.setValue(newHtml); } catch (RuntimeException e) { log.warn("Cache[HTML]: Error during compaction of " + entry.getKey(), e); } } log.info(String.valueOf(this)); } if (!cacheFile.exists()) { log.info("Cache[HTML]: Creating cache file... OK."); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(getCacheFile()))); oos.writeObject(cache); } catch (IOException e) { log.warn("", e); } finally { IOUtils.closeQuietly(oos); } } } private File getCacheFile() { return new File(HTMLConfig.HTML_CACHE_FILE); } private static final String[] TAGS_TO_COMPACT; static { // TODO: is there any other tag that should be replaced? final String[] tagsToCompact = { "html", "title", "body", "br", "br1", "p", "table", "tr", "td" }; final List<String> list = new ArrayList<String>(); for (String tag : tagsToCompact) { list.add("<" + tag + ">"); list.add("</" + tag + ">"); list.add("<" + tag + "/>"); list.add("<" + tag + " />"); } final List<String> list2 = new ArrayList<String>(); for (String tag : list) { list2.add(tag); list2.add(tag + " "); list2.add(" " + tag); } TAGS_TO_COMPACT = list2.toArray(new String[list.size()]); } private String compactHtml(StringBuilder sb, String html) { sb.setLength(0); sb.append(html); for (int i = 0; i < sb.length(); i++) { if (Character.isWhitespace(sb.charAt(i))) { sb.setCharAt(i, ' '); } } replaceAll(sb, " ", " "); replaceAll(sb, "< ", "<"); replaceAll(sb, " >", ">"); for (int i = 0; i < TAGS_TO_COMPACT.length; i += 3) { replaceAll(sb, TAGS_TO_COMPACT[i + 1], TAGS_TO_COMPACT[i]); replaceAll(sb, TAGS_TO_COMPACT[i + 2], TAGS_TO_COMPACT[i]); } replaceAll(sb, " ", " "); // String.trim() without additional garbage int fromIndex = 0; int toIndex = sb.length(); while (fromIndex < toIndex && sb.charAt(fromIndex) == ' ') { fromIndex++; } while (fromIndex < toIndex && sb.charAt(toIndex - 1) == ' ') { toIndex--; } return sb.substring(fromIndex, toIndex); } private void replaceAll(StringBuilder sb, String pattern, String value) { for (int index = 0; (index = sb.indexOf(pattern, index)) != -1;) { sb.replace(index, index + pattern.length(), value); } } public void reloadPath(File f) { parseDir(f); log.info("Cache[HTML]: Reloaded specified path."); } public void parseDir(File dir) { for (File file : dir.listFiles(HTML_FILTER)) { if (!file.isDirectory()) { loadFile(file); } else { parseDir(file); } } } public String loadFile(File file) { if (isLoadable(file)) { BufferedInputStream bis = null; try { bis = new BufferedInputStream(new FileInputStream(file)); byte[] raw = new byte[bis.available()]; bis.read(raw); String content = new String(raw, HTMLConfig.HTML_ENCODING); String relpath = getRelativePath(HTML_ROOT, file); size += content.length(); String oldContent = cache.get(relpath); if (oldContent == null) { loadedFiles++; } else { size -= oldContent.length(); } cache.put(relpath, content); return content; } catch (Exception e) { log.warn("Problem with htm file:", e); } finally { IOUtils.closeQuietly(bis); } } return null; } public String getHTML(String path) { return cache.get(path); } private boolean isLoadable(File file) { return file.exists() && !file.isDirectory() && HTML_FILTER.accept(file); } public boolean pathExists(String path) { return cache.containsKey(path); } @Override public String toString() { return "Cache[HTML]: " + String.format("%.3f", (float) size / 1024) + " kilobytes on " + loadedFiles + " file(s) loaded."; } public static String getRelativePath(File base, File file) { return file.toURI().getPath().substring(base.toURI().getPath().length()); } }