Java tutorial
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.ipaas.ifw.front; import java.io.*; import java.util.*; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.StringUtils; import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * ?? * @author wuxh */ public class FisResource { /** * ,?? */ private static final long INTERVAL = 60; public static final String CONTEXT_ATTR_NAME = "com.baidu.fis.resource"; public static final String STYLE_PLACEHOLDER = "<!--FIS_STYLE_PLACEHOLDER-->"; public static final String SCRIPT_PLACEHOLDER = "<!--FIS_SCRIPT_PLACEHOLDER-->"; private static final String DEFAULT_MAPPING_FILE_DIR = "./config/"; private static final String DEFAULT_RES_MAPPING_FILE = "map.json"; public static final String DEFAULT_NS_GLOBAL = "__global__"; private static Map<String, Map> resMap = new HashMap<String, Map>(); private Map<String, String> resLoadedMap; private Map<String, List<String>> staticResourceMap; private Map<String, Map<String, Object>> requireAsyncCollection; private LinkedHashSet<String> scriptPoolSet; private String framework; private static Map<String, Long> lastModifiedTimes = new HashMap<String, Long>(); /** * */ private static final Logger logger = LoggerFactory.getLogger(FisResource.class); /** * ?? */ static { // ? Timer timer = new Timer("Front Resource Mapping map.jsonWatchDog", true); timer.schedule(new TimerTask() { public void run() { try { //logger.info("Loading Front Resource Mapping File!"); registerAll(FisResource.DEFAULT_MAPPING_FILE_DIR); } catch (FisException e) { logger.error("Loading Resource Mapping File error", e); } } }, 0, INTERVAL); } public FisResource() throws FisException { resLoadedMap = new HashMap<String, String>(); staticResourceMap = new HashMap<String, List<String>>(); requireAsyncCollection = new HashMap<String, Map<String, Object>>(); scriptPoolSet = new LinkedHashSet<String>(); } /** * ????URI,uri,map.json? * * @param key * - ?key,static/css/blog.css * @return String * @throws FisException */ public static String getResourceUri(String name) throws FisException { Map<String, Map> nsResMap = getNsResMap(name); Map<String, Object> res = getRes(nsResMap, name); return getResourceUri(name, nsResMap, res); } /** * css?? * * @return */ public static String getCssHook() { return STYLE_PLACEHOLDER; } /** * js Framework * @param value */ public void setFramework(String value) { this.framework = value; } /** * css???css * * @param content * @return * @throws FisException */ public String renderResponse(String content) throws FisException { if (content.indexOf(STYLE_PLACEHOLDER) >= 0) { content = content.replace(STYLE_PLACEHOLDER, this.getRenderFrag("css")); } reset(); return content; } public String replace(String html) throws FisException { html = html.replace(FisResource.STYLE_PLACEHOLDER, this.getRenderFrag("css")); html = html.replace(FisResource.SCRIPT_PLACEHOLDER, this.getRenderFrag("js") + this.getRenderScript()); return html; } /** * ??html? * * @param type * ?,js,css * @return * @throws FisException */ public String getRenderFrag(String type) throws FisException { String html = ""; if (staticResourceMap.containsKey(type)) { List<String> uris = staticResourceMap.get(type); if ("js" == type) { String resourceMap = this.getResourceMap(); if (StringUtils.isNotBlank(this.framework)) { if (this.staticResourceMap.containsKey("js") || !(null == resourceMap || "".equals(resourceMap))) { html += "<script type=\"text/javascript\" src=\"" + this.framework + "\"></script>"; } } if (!resourceMap.isEmpty()) { html += "<script type=\"text/javascript\">"; html += "require.resourceMap(" + resourceMap.toString() + ")"; html += "</script>"; } for (int i = 0, len = uris.size(); i < len; i++) { if (uris.get(i).equals(this.framework)) { continue; } html += "<script type=\"text/javascript\" src=\"" + uris.get(i) + "\"></script>"; } } else if ("css" == type) { for (int i = 0, len = uris.size(); i < len; i++) { html += "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + uris.get(i) + "\"/>"; } } } return html; } /** * script??html? * * @return String htmlscript? */ public String getRenderScript() { String html = ""; if (!scriptPoolSet.isEmpty()) { for (Iterator<String> it = scriptPoolSet.iterator(); it.hasNext();) { String script = it.next(); html += "<script type=\"text/javascript\">"; html += "\n!function(){" + script + " }();"; html += "</script>"; } } return html; } /** * ?js???json? * @return * @throws FisException */ private String getResourceMap() throws FisException { String ret = ""; Map<String, Map<String, Object>> resourceMap = new HashMap<String, Map<String, Object>>(); if (this.requireAsyncCollection.containsKey("res")) { for (Iterator<String> it = this.requireAsyncCollection.get("res").keySet().iterator(); it.hasNext();) { String id = it.next(); Map<String, Object> res = (Map<String, Object>) this.requireAsyncCollection.get("res").get(id); List<String> jsDeps = new ArrayList<String>(); if (res.containsKey("deps")) { List<String> deps = (List<String>) res.get("deps"); for (int i = 0, len = deps.size(); i < len; i++) { String name = deps.get(i); if (name.endsWith(".js")) { jsDeps.add(name); } } } Map<String, Object> item = new HashMap<String, Object>(); item.put("url", res.get("uri")); if (res.containsKey("pkg")) { item.put("pkg", res.get("pkg")); } if (!jsDeps.isEmpty()) { item.put("deps", jsDeps); } if (resourceMap.get("res") == null) { resourceMap.put("res", new HashMap<String, Object>()); } resourceMap.get("res").put(id, item); } } if (this.requireAsyncCollection.containsKey("pkg")) { Map<String, Object> pkg = this.requireAsyncCollection.get("pkg"); for (Iterator<String> it = pkg.keySet().iterator(); it.hasNext();) { String id = it.next(); Map<String, Object> res = this.requireAsyncCollection.get(id); resourceMap.get(pkg).put(id, new HashMap<String, Object>().put("url", res.get("uri"))); } } if (!resourceMap.isEmpty()) { ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally try { ret = mapper.writeValueAsString(resourceMap); } catch (Exception e) { throw new FisException("resourceMap ?json ", e); } } return ret; } /** * script?script? * * @param script */ public void addScript(String script) { scriptPoolSet.add(script); } /** * ????URI,map.json??? * @param name * @return * @throws FisException */ public String load(String name) throws FisException { return this.load(name, false); } public String require(String name) throws FisException { return this.load(name); } private void reset() { resLoadedMap = null; staticResourceMap = null; scriptPoolSet = null; requireAsyncCollection = null; framework = null; } /** * ??map.json * * @param namespace * @return * @throws FisException */ private static boolean register(String namespace) throws FisException { return register(FisResource.DEFAULT_MAPPING_FILE_DIR, namespace); } /** * ??map.json * * @param namespace * @return * @throws FisException */ private static boolean register(String resMappingFileDir, String namespace) throws FisException { String mappingFileName = "map.json"; if (namespace != FisResource.DEFAULT_NS_GLOBAL) { mappingFileName = namespace + "-map.json"; } String mappingFileFullPath = resMappingFileDir + "/" + mappingFileName; // ?? BufferedReader br = null; try { String fileName = FisResource.class.getClassLoader().getResource(mappingFileFullPath).getPath(); File mapJsonFile = new File(fileName); if (null == lastModifiedTimes.get(fileName) || lastModifiedTimes.get(fileName) != mapJsonFile.lastModified()) { ObjectMapper mapper = new ObjectMapper(); resMap.put(namespace, mapper.readValue(mapJsonFile, HashMap.class)); // ?lastModifiedTime lastModifiedTimes.put(fileName, mapJsonFile.lastModified()); } return true; } catch (Exception e) { throw new FisException("map json??", e); } finally { if (br != null) { try { br.close(); } catch (IOException e) { } } } } private static void registerAll(String resMappingFileDir) throws FisException { String configDirPath = FisResource.class.getClassLoader().getResource(resMappingFileDir).getFile(); File mapJsonDir = new File(configDirPath); String[] mapJsonFiles = mapJsonDir.list(); FilenameFilter defaultFileNameFilter = new FilenameFilter() { public boolean accept(File dir, String name) { boolean isMatch = name.equals(DEFAULT_RES_MAPPING_FILE); return isMatch; } }; FilenameFilter nameSpaceFileNameFilter = new FilenameFilter() { public boolean accept(File dir, String name) { boolean isMatch = name.endsWith("-" + DEFAULT_RES_MAPPING_FILE); return isMatch; } }; String[] childrenFileNames = mapJsonDir.list(defaultFileNameFilter); String[] nameSpacechildrenFileNames = mapJsonDir.list(nameSpaceFileNameFilter); if ((null == childrenFileNames || 0 == childrenFileNames.length) && (null == nameSpacechildrenFileNames || 0 == nameSpacechildrenFileNames.length)) { throw new FisException("Resource Mapping File Not Found!"); } // ? if (null != childrenFileNames || 0 < childrenFileNames.length) { for (int i = 0; i < childrenFileNames.length; i++) { FisResource.register(FisResource.DEFAULT_MAPPING_FILE_DIR, FisResource.DEFAULT_NS_GLOBAL); } } // ??? if (null != nameSpacechildrenFileNames || 0 < nameSpacechildrenFileNames.length) { for (int i = 0; i < nameSpacechildrenFileNames.length; i++) { String fileName = nameSpacechildrenFileNames[i]; int pos = fileName.indexOf("-" + FisResource.DEFAULT_RES_MAPPING_FILE); String namespace = fileName.substring(0, pos); FisResource.register(FisResource.DEFAULT_MAPPING_FILE_DIR, namespace); } } // for (int i = 0, len = mapJsonFiles.length; i < len; i++) { // String fileName = mapJsonFiles[i]; // if (fileName.equals(FisResource.DEFAULT_RES_MAPPING_FILE)) { // FisResource.register(FisResource.DEFAULT_MAPPING_FILE_DIR, // FisResource.DEFAULT_NS_GLOBAL); // } else { // int pos = fileName.indexOf("-" + // FisResource.DEFAULT_RES_MAPPING_FILE); // if (pos > 0) { // String namespace = fileName.substring(0, pos); // FisResource.register(FisResource.DEFAULT_MAPPING_FILE_DIR, // namespace); // } // } // } } /** * ????URI,uri,map.json? * * @param key * - ?key,static/css/blog.css * @return String * @throws FisException */ private static String getResourceUri(String name, Map<String, Map> nsResMap, Map<String, Object> res) throws FisException { String uri = ""; if (res == null || res.isEmpty()) { throw new FisException("undefined resource \"" + name); } if (res.containsKey("pkg")) { Map pkg = (Map) nsResMap.get("pkg").get(res.get("pkg")); uri = (String) pkg.get("uri"); } else { uri = (String) res.get("uri"); } return uri; } /** * ?name??json * * @param key * ?key,static/css/blog.css * @return String * @throws FisException */ private static Map<String, Object> getRes(Map<String, Map> nsResMap, String name) throws FisException { Map<String, Object> res = (Map<String, Object>) nsResMap.get("res").get(name); return res; } /** * ?????? * * @param key * - ?key,static/css/blog.css * @return String * @throws FisException */ private static Map<String, Map> getNsResMap(String name) throws FisException { Map<String, Object> res = null; int pos = name.indexOf(":"); String namespace = FisResource.DEFAULT_NS_GLOBAL; if (name.indexOf(":") >= 0) { namespace = name.substring(0, pos); } if (!(resMap.containsKey(namespace) || register(namespace))) { throw new FisException("missing map file of \"" + namespace); } Map<String, Map> nsResMap = resMap.get(namespace); return nsResMap; } /** * ????URI,map.json? * * @param name * - ?key,static/css/blog.css * @param async ? * @return String * @throws FisException */ private String load(String name, boolean async) throws FisException { String loadedURI = this.resLoadedMap.get(name); if (null != loadedURI && "" != loadedURI) { if (!async && (this.requireAsyncCollection.get("res") != null && this.requireAsyncCollection.get("res").containsKey(name))) { this.delAsyncDeps(name); } return loadedURI; } Map<String, Map> nsResMap = this.getNsResMap(name); Map<String, Object> res = this.getRes(nsResMap, name); if (res == null || res.isEmpty()) { throw new FisException("undefined resource \"" + name); } Map pkg = null; Map pkgHas = new HashMap(); String uri = this.getResourceUri(name, nsResMap, res); if (res.containsKey("pkg")) { pkg = (Map) nsResMap.get("pkg").get(res.get("pkg")); List<String> has = (List<String>) pkg.get("has"); for (int i = 0, len = has.size(); i < len; i++) { String resId = has.get(i); this.resLoadedMap.put(resId, uri); pkgHas = (Map<String, Object>) nsResMap.get("res").get(resId); if (!pkgHas.isEmpty()) { this.loadDeps(pkgHas, async); } } } else { if (res.containsKey("deps")) { List<String> deps = (List<String>) res.get("deps"); for (int i = 0, len = deps.size(); i < len; i++) { load(deps.get(i)); } } this.resLoadedMap.put(name, uri); this.loadDeps(res, async); } if (async && res.get("type").equals("js")) { if (this.requireAsyncCollection.get("res") == null) { this.requireAsyncCollection.put("res", new HashMap<String, Object>()); } if (pkg != null && !pkg.isEmpty()) { if (this.requireAsyncCollection.get("pkg") == null) { this.requireAsyncCollection.put("pkg", new HashMap<String, Object>()); } this.requireAsyncCollection.get("pkg").put((String) res.get("pkg"), pkg); this.requireAsyncCollection.get("res").putAll(pkgHas); } else { this.requireAsyncCollection.get("res").put(name, res); } } else { if (null == this.staticResourceMap.get(res.get("type"))) { this.staticResourceMap.put((String) res.get("type"), new ArrayList<String>()); } this.staticResourceMap.get(res.get("type")).add(uri); } loadedURI = uri; return loadedURI; } /** * ?? * @param array res ? * @param boolean async ? * @throws FISException */ private void loadDeps(Map<String, Object> res, boolean async) throws FisException { if (res.containsKey("extras") && ((Map<String, Object>) res.get("extras")).containsKey("async")) { List<String> asyncUris = (List<String>) ((Map<String, Object>) res.get("extras")).get("async"); for (int i = 0, len = asyncUris.size(); i < len; i++) { this.load(asyncUris.get(i), true); } } if (res.containsKey("deps")) { List<String> deps = (List<String>) res.get("deps"); for (int i = 0, len = deps.size(); i < len; i++) { this.load(deps.get(i), async); } } } /** * ???? * @param name */ private void delAsyncDeps(String name) { Map<String, Object> res = (Map<String, Object>) this.requireAsyncCollection.get("res").get(name); if (res.containsKey("pkg")) { Map<String, Object> pkg = (Map<String, Object>) this.requireAsyncCollection.get("pkg") .get(res.get("pkg")); if (!pkg.isEmpty()) { this.staticResourceMap.get("js").add((String) pkg.get("uri")); this.requireAsyncCollection.get("pkg").remove(res.get("pkg")); List<String> has = (List<String>) pkg.get("has"); for (int i = 0, len = has.size(); i < len; i++) { if (this.requireAsyncCollection.get("res").containsKey(has.get(i))) { this.delAsyncDeps(has.get(i)); } } } } else { this.staticResourceMap.get("js").add( (String) ((Map<String, Object>) this.requireAsyncCollection.get("res").get(name)).get("uri")); this.requireAsyncCollection.get("res").remove(name); } if (res.containsKey("deps")) { List<String> deps = (List<String>) res.get("deps"); for (int i = 0, len = deps.size(); i < len; i++) { String depsUri = deps.get(i); if (this.requireAsyncCollection.get("res").containsKey(depsUri)) { this.delAsyncDeps(depsUri); } } } } }