/** * Copyright (C) 2013-2016 The Rythm Engine project * for LICENSE and other details see: * https://github.com/rythmengine/rythmengine */ package org.rythmengine.resource; import org.rythmengine.RythmEngine; import org.rythmengine.extension.ICodeType; import org.rythmengine.extension.ITemplateResourceLoader; import org.rythmengine.logger.ILogger; import org.rythmengine.logger.Logger; import org.rythmengine.utils.S; import java.util.UUID; /** * Encapsulate the common logic of an {@link ITemplateResource} implementation */ public abstract class TemplateResourceBase implements ITemplateResource { protected final static ILogger logger = Logger.get(TemplateResourceBase.class); /** * When this field is set to null, it assumes using Rythm's singleton mode * <p/> * This field should be set to null if needs to serialize the template resource to some where, e.g. Cache */ protected final ITemplateResourceLoader loader; protected boolean isProdMode = false; /** * if an error happened */ Throwable error; /** * @return the error */ public Throwable getError() { return error; } /** * @param error the error to set */ public void setError(Throwable error) { this.error = error; } /** * There are certain cases that a type of resource does not need a specific loader. E.g the * {@link ToStringTemplateResource} */ public TemplateResourceBase() { loader = null; } public TemplateResourceBase(ITemplateResourceLoader loader) { if (null == loader) { throw new NullPointerException(); } this.loader = loader; this.isProdMode = getEngine().isProdMode(); } protected RythmEngine getEngine() { return loader.getEngine(); } @Override public int hashCode() { return getKey().hashCode(); } protected String cache; private long timestamp; protected long nextCheckPoint; public boolean equals(Object obj) { if (obj == this) return true; if (null == obj) return false; if (getClass().equals(obj.getClass())) { return ((TemplateResourceBase) obj).getKey().equals(getKey()); } return false; } private long checkInterval() { if (isProdMode) return -1; // never check when running in product mode Long intv = userCheckInterval(); return null == intv ? defCheckInterval() : intv; } protected abstract long defCheckInterval(); protected Long userCheckInterval() { return getEngine().conf().resourceRefreshInterval(); } protected abstract long lastModified(); protected abstract String reload(); @Override public String asTemplateContent() { if (null == cache) { cache = reload(); timestamp = lastModified(); } return cache; } @Override /** * Refresh the product is modified. * * @return true if the product has been modified */ public final boolean refresh() { long checkInterval = checkInterval(); if (checkInterval < 0) return false; // never check if (0 == checkInterval) { // System.out.println("always check resource as checkInterval set to zero"); // always check checkModified(); return isModified(); } // should I check now ? long now = System.currentTimeMillis(); if (nextCheckPoint < now) { checkModified(); nextCheckPoint = now + checkInterval; } return isModified(); } private void checkModified() { long modified = lastModified(); if (timestamp < modified) { // System.out.println("resource has been modified"); cache = null; // } else { // System.out.println("resource is not modified"); } } private boolean isModified() { return null == cache; } @Override public String getSuggestedClassName() { return "C" + UUID.randomUUID().toString().replace('-', '_'); } public static ICodeType getTypeOfPath(RythmEngine engine, String s) { String suffix = engine.conf().resourceNameSuffix(); if (s.endsWith(suffix)) { int pos = s.lastIndexOf(suffix); if (pos > -1) s = s.substring(0, pos); } ICodeType codeType = engine.conf().defaultCodeType(); if (s.endsWith(".html") || s.endsWith(".htm")) { codeType = ICodeType.DefImpl.HTML; } else if (s.endsWith(".js")) { codeType = ICodeType.DefImpl.JS; } else if (s.endsWith(".json")) { codeType = ICodeType.DefImpl.JSON; } else if (s.endsWith(".xml")) { codeType = ICodeType.DefImpl.XML; } else if (s.endsWith(".csv")) { codeType = ICodeType.DefImpl.CSV; } else if (s.endsWith(".css")) { codeType = ICodeType.DefImpl.CSS; } return codeType; } @Override public ICodeType codeType(RythmEngine engine) { Object key = getKey(); String s = S.str(key); return getTypeOfPath(engine, s); } @Override public ITemplateResourceLoader getLoader() { return loader; } protected static String path2CN(String path) { int colon = path.indexOf(":"); if (++colon > 0) { path = path.substring(colon); // strip the driver letter from windows path and scheme from the URL } while (path.startsWith("/")) path = path.substring(1); while (path.startsWith("\\")) path = path.substring(1); // -- do not strip the file suffix. other wise a.html and a.js template will fetch a same template class // in the end //int lastDotPos = path.lastIndexOf("."); //path = path.substring(0, lastDotPos); // replace characters that are invalid in a java identifier with '_' return path.replaceAll("[.\\\\/ -]", "_").replace('~', '_'); } }