/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.javascript.commonjs.module.provider; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.mozilla.javascript.Context; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.commonjs.module.ModuleScript; /** * A module script provider that uses a module source provider to load modules * and caches the loaded modules. It softly references the loaded modules' * Rhino {@link Script} objects, thus a module once loaded can become eligible * for garbage collection if it is otherwise unused under memory pressure. * Instances of this class are thread safe. * @author Attila Szegedi * @version $Id: SoftCachingModuleScriptProvider.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $ */ public class SoftCachingModuleScriptProvider extends CachingModuleScriptProviderBase { private static final long serialVersionUID = 1L; private transient ReferenceQueue<Script> scriptRefQueue = new ReferenceQueue<Script>(); private transient ConcurrentMap<String, ScriptReference> scripts = new ConcurrentHashMap<String, ScriptReference>(16, .75f, getConcurrencyLevel()); /** * Creates a new module provider with the specified module source provider. * @param moduleSourceProvider provider for modules' source code */ public SoftCachingModuleScriptProvider( ModuleSourceProvider moduleSourceProvider) { super(moduleSourceProvider); } @Override public ModuleScript getModuleScript(Context cx, String moduleId, URI uri, URI base, Scriptable paths) throws Exception { // Overridden to clear the reference queue before retrieving the // script. for(;;) { ScriptReference ref = (ScriptReference)scriptRefQueue.poll(); if(ref == null) { break; } scripts.remove(ref.getModuleId(), ref); } return super.getModuleScript(cx, moduleId, uri, base, paths); } @Override protected CachedModuleScript getLoadedModule(String moduleId) { final ScriptReference scriptRef = scripts.get(moduleId); return scriptRef != null ? scriptRef.getCachedModuleScript() : null; } @Override protected void putLoadedModule(String moduleId, ModuleScript moduleScript, Object validator) { scripts.put(moduleId, new ScriptReference(moduleScript.getScript(), moduleId, moduleScript.getUri(), moduleScript.getBase(), validator, scriptRefQueue)); } private static class ScriptReference extends SoftReference<Script> { private final String moduleId; private final URI uri; private final URI base; private final Object validator; ScriptReference(Script script, String moduleId, URI uri, URI base, Object validator, ReferenceQueue<Script> refQueue) { super(script, refQueue); this.moduleId = moduleId; this.uri = uri; this.base = base; this.validator = validator; } CachedModuleScript getCachedModuleScript() { final Script script = get(); if(script == null) { return null; } return new CachedModuleScript(new ModuleScript(script, uri, base), validator); } String getModuleId() { return moduleId; } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { scriptRefQueue = new ReferenceQueue<Script>(); scripts = new ConcurrentHashMap<String, ScriptReference>(); final Map<String, CachedModuleScript> serScripts = (Map)in.readObject(); for(Map.Entry<String, CachedModuleScript> entry: serScripts.entrySet()) { final CachedModuleScript cachedModuleScript = entry.getValue(); putLoadedModule(entry.getKey(), cachedModuleScript.getModule(), cachedModuleScript.getValidator()); } } private void writeObject(ObjectOutputStream out) throws IOException { final Map<String, CachedModuleScript> serScripts = new HashMap<String, CachedModuleScript>(); for(Map.Entry<String, ScriptReference> entry: scripts.entrySet()) { final CachedModuleScript cachedModuleScript = entry.getValue().getCachedModuleScript(); if(cachedModuleScript != null) { serScripts.put(entry.getKey(), cachedModuleScript); } } out.writeObject(serScripts); } }