/* 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.File; import java.io.IOException; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; /** * A base implementation for all module script providers that actually load * module scripts. Performs validation of identifiers, allows loading from * preferred locations (attempted before require.paths), from require.paths * itself, and from fallback locations (attempted after require.paths). Note * that while this base class strives to be as generic as possible, it does * have loading from an URI built into its design, for the simple reason that * the require.paths is defined in terms of URIs. * @author Attila Szegedi * @version $Id: ModuleSourceProviderBase.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $ */ public abstract class ModuleSourceProviderBase implements ModuleSourceProvider, Serializable { private static final long serialVersionUID = 1L; public ModuleSource loadSource(String moduleId, Scriptable paths, Object validator) throws IOException, URISyntaxException { if(!entityNeedsRevalidation(validator)) { return NOT_MODIFIED; } ModuleSource moduleSource = loadFromPrivilegedLocations( moduleId, validator); if(moduleSource != null) { return moduleSource; } if(paths != null) { moduleSource = loadFromPathArray(moduleId, paths, validator); if(moduleSource != null) { return moduleSource; } } return loadFromFallbackLocations(moduleId, validator); } public ModuleSource loadSource(URI uri, URI base, Object validator) throws IOException, URISyntaxException { return loadFromUri(uri, base, validator); } private ModuleSource loadFromPathArray(String moduleId, Scriptable paths, Object validator) throws IOException { final long llength = ScriptRuntime.toUint32( ScriptableObject.getProperty(paths, "length")); // Yeah, I'll ignore entries beyond Integer.MAX_VALUE; so sue me. int ilength = llength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)llength; for(int i = 0; i < ilength; ++i) { final String path = ensureTrailingSlash( ScriptableObject.getTypedProperty(paths, i, String.class)); try { URI uri = new URI(path); if (!uri.isAbsolute()) { uri = new File(path).toURI().resolve(""); } final ModuleSource moduleSource = loadFromUri( uri.resolve(moduleId), uri, validator); if(moduleSource != null) { return moduleSource; } } catch(URISyntaxException e) { throw new MalformedURLException(e.getMessage()); } } return null; } private static String ensureTrailingSlash(String path) { return path.endsWith("/") ? path : path.concat("/"); } /** * Override to determine whether according to the validator, the cached * module script needs revalidation. A validator can carry expiry * information. If the cached representation is not expired, it doesn' * t need revalidation, otherwise it does. When no cache revalidation is * required, the external resource will not be contacted at all, so some * level of expiry (staleness tolerance) can greatly enhance performance. * The default implementation always returns true so it will always require * revalidation. * @param validator the validator * @return returns true if the cached module needs revalidation. */ protected boolean entityNeedsRevalidation(Object validator) { return true; } /** * Override in a subclass to load a module script from a logical URI. The * URI is absolute but does not have a file name extension such as ".js". * It is up to the ModuleSourceProvider implementation to add such an * extension. * @param uri the URI of the script, without file name extension. * @param base the base URI the uri was resolved from. * @param validator a validator that can be used to revalidate an existing * cached source at the URI. Can be null if there is no cached source * available. * @return the loaded module script, or null if it can't be found, or * {@link ModuleSourceProvider#NOT_MODIFIED} if it revalidated the existing * cached source against the URI. * @throws IOException if the module script was found, but an I/O exception * prevented it from being loaded. * @throws URISyntaxException if the final URI could not be constructed */ protected abstract ModuleSource loadFromUri(URI uri, URI base, Object validator) throws IOException, URISyntaxException; /** * Override to obtain a module source from privileged locations. This will * be called before source is attempted to be obtained from URIs specified * in require.paths. * @param moduleId the ID of the module * @param validator a validator that can be used to validate an existing * cached script. Can be null if there is no cached script available. * @return the loaded module script, or null if it can't be found in the * privileged locations, or {@link ModuleSourceProvider#NOT_MODIFIED} if * the existing cached module script is still valid. * @throws IOException if the module script was found, but an I/O exception * prevented it from being loaded. * @throws URISyntaxException if the final URI could not be constructed. */ protected ModuleSource loadFromPrivilegedLocations( String moduleId, Object validator) throws IOException, URISyntaxException { return null; } /** * Override to obtain a module source from fallback locations. This will * be called after source is attempted to be obtained from URIs specified * in require.paths. * @param moduleId the ID of the module * @param validator a validator that can be used to validate an existing * cached script. Can be null if there is no cached script available. * @return the loaded module script, or null if it can't be found in the * privileged locations, or {@link ModuleSourceProvider#NOT_MODIFIED} if * the existing cached module script is still valid. * @throws IOException if the module script was found, but an I/O exception * prevented it from being loaded. * @throws URISyntaxException if the final URI could not be constructed. */ protected ModuleSource loadFromFallbackLocations( String moduleId, Object validator) throws IOException, URISyntaxException { return null; } }